Files
X-Financial/document/development/budget-expense-control-model-plan/index.html

1473 lines
42 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>X-Financial 预算费用规划推荐模型方案</title>
<style>
:root {
color-scheme: light;
--bg: #f5f7f4;
--surface: #ffffff;
--surface-soft: #f9fbf8;
--ink: #132019;
--ink-soft: #526257;
--muted: #718075;
--line: #dbe4dc;
--line-strong: #c5d2c7;
--green: #0f766e;
--green-dark: #0b4f49;
--green-soft: #e4f5f2;
--blue: #2563eb;
--blue-soft: #e8f0ff;
--amber: #b45309;
--amber-soft: #fff3d7;
--red: #c2410c;
--red-soft: #fff0e8;
--plum: #7c2d12;
--plum-soft: #f8eee8;
--shadow: 0 16px 34px rgba(37, 48, 40, 0.08);
--radius: 8px;
--mono: "Cascadia Code", "JetBrains Mono", Consolas, monospace;
--font: "Microsoft YaHei UI", "Microsoft YaHei", "PingFang SC", "Segoe UI", sans-serif;
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
background:
linear-gradient(180deg, rgba(228, 245, 242, 0.9), rgba(245, 247, 244, 0.82) 310px),
var(--bg);
color: var(--ink);
font-family: var(--font);
font-size: 14px;
line-height: 1.68;
letter-spacing: 0;
overflow-wrap: anywhere;
}
a {
color: inherit;
text-decoration: none;
}
code {
padding: 2px 6px;
border-radius: 6px;
background: rgba(15, 118, 110, 0.08);
color: var(--green-dark);
font-family: var(--mono);
font-size: 0.92em;
}
pre {
margin: 0;
min-width: 0;
max-width: 100%;
padding: 16px;
overflow: auto;
border: 1px solid var(--line);
border-radius: var(--radius);
background: #101a16;
color: #eef8f4;
font-family: var(--mono);
font-size: 12.5px;
line-height: 1.72;
}
.layout {
display: grid;
grid-template-columns: 270px minmax(0, 1fr);
min-height: 100dvh;
}
.sidebar {
position: sticky;
top: 0;
height: 100dvh;
padding: 24px 18px;
border-right: 1px solid var(--line);
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(18px);
overflow: auto;
}
.brand {
display: flex;
gap: 12px;
align-items: center;
padding-bottom: 22px;
border-bottom: 1px solid var(--line);
}
.brand-mark {
display: grid;
width: 40px;
height: 40px;
place-items: center;
border-radius: 8px;
background: #12342f;
color: #fff;
font-weight: 850;
box-shadow: 0 12px 20px rgba(15, 79, 73, 0.24);
}
.brand-title {
font-size: 15px;
font-weight: 850;
line-height: 1.25;
}
.brand-subtitle {
margin-top: 3px;
color: var(--muted);
font-size: 12px;
}
.nav-label {
margin: 22px 0 8px;
color: var(--muted);
font-size: 12px;
font-weight: 750;
}
.nav {
display: grid;
gap: 5px;
}
.nav a {
display: flex;
align-items: center;
min-height: 36px;
padding: 7px 10px;
border-radius: 8px;
color: var(--ink-soft);
font-size: 13px;
font-weight: 700;
}
.nav a:hover {
background: var(--green-soft);
color: var(--green-dark);
}
.nav-dot {
width: 7px;
height: 7px;
margin-right: 10px;
border-radius: 99px;
background: var(--line-strong);
}
.nav a:hover .nav-dot {
background: var(--green);
}
.side-note {
margin-top: 24px;
padding: 12px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface-soft);
color: var(--ink-soft);
font-size: 12px;
line-height: 1.55;
}
main {
min-width: 0;
padding: 34px 44px 58px;
}
.hero {
display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(330px, 0.9fr);
gap: 22px;
align-items: stretch;
margin-bottom: 22px;
}
.hero-copy {
min-width: 0;
padding: 30px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: rgba(255, 255, 255, 0.88);
box-shadow: var(--shadow);
}
.eyebrow {
display: inline-flex;
gap: 8px;
align-items: center;
margin-bottom: 14px;
color: var(--green-dark);
font-size: 12px;
font-weight: 850;
text-transform: uppercase;
}
.eyebrow::before {
content: "";
width: 24px;
height: 2px;
border-radius: 99px;
background: var(--green);
}
h1,
h2,
h3 {
margin: 0;
line-height: 1.28;
letter-spacing: 0;
}
h1 {
max-width: 760px;
color: #0b1f1b;
font-size: 34px;
font-weight: 900;
}
.lead {
max-width: 780px;
margin: 16px 0 0;
color: var(--ink-soft);
font-size: 15px;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 22px;
}
.tag {
display: inline-flex;
align-items: center;
min-height: 30px;
padding: 5px 10px;
border: 1px solid var(--line);
border-radius: 999px;
background: var(--surface-soft);
color: var(--ink-soft);
font-size: 12px;
font-weight: 750;
}
.hero-panel {
display: grid;
gap: 10px;
min-width: 0;
padding: 18px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: #16342f;
color: #ecf8f5;
box-shadow: var(--shadow);
}
.signal-row {
display: flex;
justify-content: space-between;
gap: 14px;
padding: 12px;
border: 1px solid rgba(236, 248, 245, 0.14);
border-radius: 8px;
background: rgba(255, 255, 255, 0.06);
}
.signal-row strong {
display: block;
font-size: 13px;
}
.signal-row > div:first-child {
min-width: 0;
}
.signal-row span {
display: block;
margin-top: 3px;
color: rgba(236, 248, 245, 0.72);
font-size: 12px;
}
.signal-value {
flex: 0 0 auto;
color: #bff4e8;
font-weight: 900;
}
section {
min-width: 0;
margin-top: 18px;
padding: 24px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: rgba(255, 255, 255, 0.92);
box-shadow: 0 8px 20px rgba(37, 48, 40, 0.04);
scroll-margin-top: 20px;
}
.section-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 22px;
margin-bottom: 16px;
}
.section-head h2 {
font-size: 22px;
font-weight: 900;
}
.section-head p {
max-width: 680px;
margin: 6px 0 0;
color: var(--ink-soft);
}
.section-kicker {
color: var(--green);
font-size: 12px;
font-weight: 850;
white-space: nowrap;
}
.grid-2,
.grid-3,
.grid-4 {
display: grid;
gap: 12px;
}
.grid-2 {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.grid-3 {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.grid-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.card,
.metric,
.lane,
.phase,
.check-item {
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface);
}
.card {
min-width: 0;
padding: 16px;
}
.card h3,
.lane h3,
.phase h3 {
color: var(--ink);
font-size: 15px;
font-weight: 850;
}
.card p,
.lane p,
.phase p {
margin: 8px 0 0;
color: var(--ink-soft);
font-size: 13px;
}
.metric {
min-width: 0;
padding: 14px;
background: linear-gradient(180deg, #fff, var(--surface-soft));
}
.metric-label {
color: var(--muted);
font-size: 12px;
font-weight: 750;
}
.metric-value {
margin-top: 6px;
color: var(--ink);
font-size: 22px;
font-weight: 900;
line-height: 1.1;
}
.metric-desc {
margin-top: 7px;
color: var(--ink-soft);
font-size: 12px;
line-height: 1.5;
}
.pill-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 12px;
}
.pill {
display: inline-flex;
align-items: center;
min-height: 28px;
padding: 4px 9px;
border-radius: 999px;
background: var(--green-soft);
color: var(--green-dark);
font-size: 12px;
font-weight: 750;
}
.flow {
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 10px;
margin-top: 14px;
}
.flow-step {
position: relative;
min-height: 120px;
padding: 14px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface-soft);
}
.flow-step:not(:last-child)::after {
content: "";
position: absolute;
top: 50%;
right: -9px;
width: 8px;
height: 8px;
border-top: 2px solid var(--line-strong);
border-right: 2px solid var(--line-strong);
transform: translateY(-50%) rotate(45deg);
background: transparent;
}
.flow-number {
display: inline-grid;
width: 24px;
height: 24px;
place-items: center;
margin-bottom: 9px;
border-radius: 6px;
background: var(--green);
color: #fff;
font-size: 12px;
font-weight: 900;
}
.flow-step strong {
display: block;
font-size: 13px;
line-height: 1.35;
}
.flow-step span {
display: block;
margin-top: 6px;
color: var(--ink-soft);
font-size: 12px;
line-height: 1.5;
}
.architecture {
display: grid;
gap: 10px;
margin-top: 12px;
}
.lane {
display: grid;
grid-template-columns: 160px minmax(0, 1fr);
gap: 14px;
padding: 14px;
align-items: start;
}
.lane h3 {
color: var(--green-dark);
}
.lane ul,
.card ul,
.phase ul,
.quality-list {
margin: 9px 0 0;
padding-left: 18px;
color: var(--ink-soft);
}
li + li {
margin-top: 5px;
}
.formula-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(300px, 0.8fr);
gap: 14px;
align-items: start;
}
.callout {
padding: 16px;
border: 1px solid var(--line);
border-left: 4px solid var(--green);
border-radius: var(--radius);
background: var(--green-soft);
color: var(--green-dark);
}
.callout strong {
display: block;
margin-bottom: 6px;
font-weight: 900;
}
.score-band {
display: grid;
gap: 9px;
}
.band {
display: grid;
grid-template-columns: 120px 82px minmax(0, 1fr);
gap: 10px;
align-items: center;
padding: 11px 12px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface-soft);
}
.band strong {
font-size: 13px;
}
.band-score {
font-family: var(--mono);
font-size: 12px;
font-weight: 850;
}
.band span {
color: var(--ink-soft);
font-size: 12px;
}
.band.recommended {
border-color: #a6ded3;
background: var(--green-soft);
}
.band.caution {
border-color: #f0cf88;
background: var(--amber-soft);
}
.band.review {
border-color: #b8c9f5;
background: var(--blue-soft);
}
.band.block {
border-color: #efb69a;
background: var(--red-soft);
}
.phase {
min-width: 0;
padding: 16px;
border-top: 4px solid var(--green);
}
.phase:nth-child(2) {
border-top-color: var(--blue);
}
.phase:nth-child(3) {
border-top-color: var(--amber);
}
.phase:nth-child(4) {
border-top-color: var(--red);
}
.screen {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
padding: 14px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: #f8faf8;
}
.screen-block {
min-width: 0;
min-height: 74px;
padding: 12px;
border: 1px solid var(--line);
border-radius: var(--radius);
background: var(--surface);
}
.screen-block.wide {
grid-column: 1 / -1;
}
.screen-block strong {
display: block;
font-size: 13px;
}
.screen-block span {
display: block;
margin-top: 5px;
color: var(--ink-soft);
font-size: 12px;
}
.checklist {
display: grid;
gap: 9px;
margin-top: 12px;
}
.check-item {
display: flex;
gap: 10px;
align-items: flex-start;
padding: 12px;
color: var(--ink-soft);
}
.check-item input {
width: 16px;
height: 16px;
margin-top: 4px;
accent-color: var(--green);
}
.check-item strong {
display: block;
color: var(--ink);
font-size: 13px;
}
.check-item span {
display: block;
margin-top: 3px;
font-size: 12px;
}
.check-item:has(input:checked) {
background: var(--green-soft);
color: var(--green-dark);
}
.check-item:has(input:checked) strong,
.check-item:has(input:checked) span {
text-decoration: line-through;
text-decoration-thickness: 1px;
}
.split-note {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 12px;
}
.note-green,
.note-amber {
padding: 14px;
border-radius: var(--radius);
font-size: 13px;
}
.note-green {
border: 1px solid #a6ded3;
background: var(--green-soft);
color: var(--green-dark);
}
.note-amber {
border: 1px solid #f0cf88;
background: var(--amber-soft);
color: #74460b;
}
.footer {
margin-top: 24px;
color: var(--muted);
font-size: 12px;
text-align: center;
}
@media (max-width: 1100px) {
.layout {
grid-template-columns: 1fr;
}
.sidebar {
position: static;
height: auto;
}
.nav {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.hero,
.grid-4,
.flow,
.formula-grid {
grid-template-columns: 1fr;
}
.flow-step:not(:last-child)::after {
display: none;
}
}
@media (max-width: 760px) {
main {
padding: 22px 14px 38px;
}
.sidebar {
padding: 16px 14px;
}
.brand {
padding-bottom: 14px;
}
.nav-label {
margin: 14px 0 8px;
}
.nav {
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 4px 8px;
}
.nav a {
min-height: 32px;
padding: 6px 8px;
font-size: 12px;
}
.side-note {
margin-top: 14px;
padding: 10px;
}
.signal-row {
display: grid;
grid-template-columns: 1fr;
gap: 4px;
}
.signal-value {
justify-self: start;
}
.hero-copy,
section {
padding: 18px;
}
h1 {
font-size: 26px;
}
.grid-2,
.grid-3,
.split-note,
.screen,
.lane,
.band {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="layout">
<aside class="sidebar">
<div class="brand">
<div class="brand-mark">BF</div>
<div>
<div class="brand-title">预算费用模型方案</div>
<div class="brand-subtitle">Budget & Fee Recommendation</div>
</div>
</div>
<div class="nav-label">方案目录</div>
<nav class="nav" aria-label="页面目录">
<a href="#summary"><span class="nav-dot"></span>方案结论</a>
<a href="#loop"><span class="nav-dot"></span>业务闭环</a>
<a href="#architecture"><span class="nav-dot"></span>模型架构</a>
<a href="#features"><span class="nav-dot"></span>数据与特征</a>
<a href="#formula"><span class="nav-dot"></span>计算口径</a>
<a href="#score"><span class="nav-dot"></span>评分推荐</a>
<a href="#applicant-profile"><span class="nav-dot"></span>画像公式</a>
<a href="#output"><span class="nav-dot"></span>输出协议</a>
<a href="#ui"><span class="nav-dot"></span>审批展示</a>
<a href="#roadmap"><span class="nav-dot"></span>落地路线</a>
<a href="#acceptance"><span class="nav-dot"></span>验收清单</a>
</nav>
<div class="side-note">
版本2026-05-27<br>
定位:在原“预算费用管控评分模型”基础上,升级为可上线评审的规划推荐模型方案。
</div>
</aside>
<main>
<header class="hero">
<div class="hero-copy">
<div class="eyebrow">X-Financial Model Proposal</div>
<h1>预算费用规划推荐模型方案</h1>
<p class="lead">
这个模型不只是给审批页打一个分,而是把“预算是否够、费用是否合理、是否影响预算节奏、下一步该怎么处理”整理成可解释、可审计、可持续迭代的推荐结果。
</p>
<div class="hero-actions">
<span class="tag">规则模型优先</span>
<span class="tag">硬约束可审计</span>
<span class="tag">LLM 只做解释层</span>
<span class="tag">审批反馈可回流</span>
</div>
</div>
<div class="hero-panel" aria-label="关键决策信号">
<div class="signal-row">
<div>
<strong>模型主目标</strong>
<span>帮预算管理者快速判断是否可承接</span>
</div>
<div class="signal-value">Recommend</div>
</div>
<div class="signal-row">
<div>
<strong>第一版边界</strong>
<span>不替代人工审批,不让 LLM 直接做硬判断</span>
</div>
<div class="signal-value">Guarded</div>
</div>
<div class="signal-row">
<div>
<strong>核心口径</strong>
<span>扣除当前申请已预占,避免重复计算</span>
</div>
<div class="signal-value">Traceable</div>
</div>
<div class="signal-row">
<div>
<strong>迭代方向</strong>
<span>规则评分 + 历史基准 + 解释生成</span>
</div>
<div class="signal-value">V1 → V3</div>
</div>
</div>
</header>
<section id="summary">
<div class="section-head">
<div>
<h2>方案结论</h2>
<p>建议把模型定位为“预算审批辅助决策引擎”,输出结构化推荐和解释依据,而不是单纯的页面分数。</p>
</div>
<div class="section-kicker">Executive Summary</div>
</div>
<div class="grid-4">
<div class="metric">
<div class="metric-label">推荐动作</div>
<div class="metric-value">4 类</div>
<div class="metric-desc">建议通过、谨慎通过、需要复核、不建议直接通过。</div>
</div>
<div class="metric">
<div class="metric-label">核心评分</div>
<div class="metric-value">100 分</div>
<div class="metric-desc">预算安全优先,费用必要性与信息完整度共同影响。</div>
</div>
<div class="metric">
<div class="metric-label">硬性约束</div>
<div class="metric-value">3 条</div>
<div class="metric-desc">超预算、强阻断控制、预算池无法匹配必须显式处理。</div>
</div>
<div class="metric">
<div class="metric-label">上线策略</div>
<div class="metric-value">分阶段</div>
<div class="metric-desc">先确定性规则,再接入历史基准和解释层。</div>
</div>
</div>
<div class="split-note">
<div class="note-green">
<strong>推荐采用的产品口径:</strong>
分数不是结论本身,结论应由“推荐动作 + 风险等级 + 触发依据 + 可执行建议”共同表达。
</div>
<div class="note-amber">
<strong>需要避免的方向:</strong>
不要让 LLM 直接判断是否通过,也不要只用一个综合分覆盖预算、业务必要性、历史异常等不同原因。
</div>
</div>
</section>
<section id="loop">
<div class="section-head">
<div>
<h2>业务闭环</h2>
<p>模型要服务完整预算链路:预算池建立、申请预占、审批推荐、报销核销、结果反馈。</p>
</div>
<div class="section-kicker">Business Loop</div>
</div>
<div class="flow">
<div class="flow-step">
<span class="flow-number">1</span>
<strong>预算计划建立</strong>
<span>按部门、项目、科目、成本中心形成预算池和预警线。</span>
</div>
<div class="flow-step">
<span class="flow-number">2</span>
<strong>费用申请预占</strong>
<span>申请提交后先占用预算,避免后续审批期间额度被重复使用。</span>
</div>
<div class="flow-step">
<span class="flow-number">3</span>
<strong>预算审批推荐</strong>
<span>预算管理者看到风险等级、计算依据、建议动作和补充要求。</span>
</div>
<div class="flow-step">
<span class="flow-number">4</span>
<strong>报销与核销</strong>
<span>申请通过后生成报销草稿,实际报销后转为已核销金额。</span>
</div>
<div class="flow-step">
<span class="flow-number">5</span>
<strong>结果反馈回流</strong>
<span>记录审批采纳、驳回原因、实际报销差异,反哺后续规则。</span>
</div>
</div>
</section>
<section id="architecture">
<div class="section-head">
<div>
<h2>模型架构</h2>
<p>采用“上下文构建 → 特征计算 → 硬约束判定 → 综合评分 → 推荐解释 → 反馈沉淀”的分层结构。</p>
</div>
<div class="section-kicker">Model Layers</div>
</div>
<div class="architecture">
<div class="lane">
<h3>上下文层</h3>
<div>
<p>统一组装申请单、预算池、历史费用、项目计划和审批身份,形成模型输入快照。</p>
<ul>
<li>复用 <code>build_claim_budget_context()</code> 的预算上下文。</li>
<li>补充申请事由、地点、费用类型、项目编号、附件摘要。</li>
<li>保留计算时刻的预算快照,方便事后审计。</li>
</ul>
</div>
</div>
<div class="lane">
<h3>特征层</h3>
<div>
<p>把业务数据转换成可解释特征,而不是直接把原始字段丢给模型。</p>
<ul>
<li>预算容量:审批前可用、审批后使用率、超预算金额。</li>
<li>单笔影响:本次金额占预算比例、是否突破预算节奏。</li>
<li>必要性证据:事由、项目、地点、附件、业务场景是否完整。</li>
<li>历史基准:同部门、同项目、同费用类型的历史均值和波动。</li>
</ul>
</div>
</div>
<div class="lane">
<h3>决策层</h3>
<div>
<p>先执行硬规则,再计算综合分,确保强管控场景不会被其他维度“平均掉”。</p>
<ul>
<li>硬规则:超预算、预算池阻断、预算无法匹配、关键字段缺失。</li>
<li>综合评分:预算安全、费用影响、规划匹配、历史异常、信息完整。</li>
<li>分档输出recommended、caution、review、block、reference。</li>
</ul>
</div>
</div>
<div class="lane">
<h3>解释层</h3>
<div>
<p>第一版直接用模板解释;后续允许 LLM 改写语气,但不能改写分数、等级和硬性结论。</p>
<ul>
<li>解释必须引用结构化依据,不允许凭空补充原因。</li>
<li>建议要能变成审批动作,例如补充材料、拆分费用、调整预算。</li>
<li>所有展示文案保留对应的 <code>basis_code</code>,方便追踪。</li>
</ul>
</div>
</div>
</div>
</section>
<section id="features">
<div class="section-head">
<div>
<h2>数据与特征</h2>
<p>模型输入需要覆盖“预算是否够”和“费用是否该花”两条线,避免只看额度、不看业务必要性。</p>
</div>
<div class="section-kicker">Feature Design</div>
</div>
<div class="grid-3">
<div class="card">
<h3>预算上下文</h3>
<ul>
<li><code>budget_applicable</code>:是否纳入预算管控。</li>
<li><code>matched</code>:是否匹配到预算池。</li>
<li><code>total_amount</code><code>reserved_amount</code><code>consumed_amount</code></li>
<li><code>current_reserved_amount</code>:当前申请已预占金额。</li>
<li><code>warning_threshold</code><code>control_action</code></li>
</ul>
</div>
<div class="card">
<h3>申请单主数据</h3>
<ul>
<li>申请金额、费用类型、申请事由。</li>
<li>业务地点、项目编号、成本中心。</li>
<li>直属领导意见、附件摘要。</li>
<li>申请时间、期望发生时间、是否紧急。</li>
</ul>
</div>
<div class="card">
<h3>增强特征</h3>
<ul>
<li>同类费用历史均值与分位数。</li>
<li>部门剩余预算消耗速度。</li>
<li>项目里程碑与预算计划匹配度。</li>
<li>申请人近期预算占用频次。</li>
</ul>
</div>
</div>
<div class="pill-list" aria-label="特征分组">
<span class="pill">预算容量</span>
<span class="pill">单笔影响</span>
<span class="pill">预算节奏</span>
<span class="pill">业务必要性</span>
<span class="pill">历史异常</span>
<span class="pill">信息完整度</span>
<span class="pill">审批反馈</span>
</div>
</section>
<section id="formula">
<div class="section-head">
<div>
<h2>计算口径</h2>
<p>申请提交时已经发生预算预占,所以审批分析必须扣除当前申请已预占金额,避免重复计算。</p>
</div>
<div class="section-kicker">Calculation</div>
</div>
<div class="formula-grid">
<pre><code>已使用基数 = 已预占金额 + 已核销金额 - 当前申请已预占金额
审批前可用预算 = 预算总额度 - 已使用基数
审批后占用 = 已使用基数 + 本次申请金额
此次费用占预算 = 本次申请金额 / 预算总额度
审批后使用率 = 审批后占用 / 预算总额度
超预算金额 = max(本次申请金额 - 审批前可用预算, 0)</code></pre>
<div class="callout">
<strong>关键原则</strong>
如果当前申请在提交阶段已经进入 <code>reserved_amount</code>,审批页就不能再把它当成新增占用直接叠加。否则预算管理者看到的风险会被放大,尤其是大额申请会明显失真。
</div>
</div>
</section>
<section id="score">
<div class="section-head">
<div>
<h2>评分推荐</h2>
<p>建议采用“硬规则优先 + 加权评分”的组合。硬规则决定是否必须复核或阻断,评分用于排序和解释风险程度。</p>
</div>
<div class="section-kicker">Decision Strategy</div>
</div>
<div class="grid-2">
<div class="card">
<h3>建议权重</h3>
<ul>
<li>预算安全40 分,覆盖可用额度、审批后使用率、超预算金额。</li>
<li>单笔影响18 分,判断本次费用对预算池的冲击。</li>
<li>规划匹配16 分,比较项目计划、预算周期和费用发生时间。</li>
<li>历史基准14 分,识别同类费用异常偏高或频繁占用。</li>
<li>信息完整12 分,衡量事由、项目、地点、附件是否足够。</li>
</ul>
</div>
<div class="score-band">
<div class="band recommended">
<strong>recommended</strong>
<div class="band-score">85-100</div>
<span>预算充足、信息完整、费用影响可承接,建议通过。</span>
</div>
<div class="band caution">
<strong>caution</strong>
<div class="band-score">70-84</div>
<span>预算可承接但影响较明显,建议谨慎通过并关注后续节奏。</span>
</div>
<div class="band review">
<strong>review</strong>
<div class="band-score">50-69</div>
<span>存在信息缺口、历史异常或接近预警线,需要复核。</span>
</div>
<div class="band block">
<strong>block</strong>
<div class="band-score">0-49</div>
<span>超预算或触发强管控动作,不建议直接通过。</span>
</div>
</div>
</div>
<div class="split-note">
<div class="note-green">
<strong>硬规则示例:</strong>
<code>control_action = block</code> 且超预算时,直接进入 <code>block</code>;预算池未匹配时输出 <code>reference</code>,不能伪装成低风险。
</div>
<div class="note-amber">
<strong>推荐动作要具体:</strong>
不只写“需关注”,而要给出“补充业务必要性、拆分费用、调整预算池、发起预算调剂”等可执行动作。
</div>
</div>
</section>
<section id="applicant-profile">
<div class="section-head">
<div>
<h2>申请人费用画像公式</h2>
<p>画像只用于调整复核强度和审核建议,不能直接把申请人定义为“异常人员”。核心是用同组基准解释个人费用节奏是否偏离。</p>
</div>
<div class="section-kicker">Applicant Profile</div>
</div>
<div class="formula-grid">
<pre><code>申请人画像风险分 =
高频申请分 * 20%
+ 高金额占用分 * 25%
+ 同组偏离分 * 25%
+ 历史调减退回分 * 15%
+ 本次申请偏离分 * 15%
等级:
0-39 正常
40-59 需关注
60-79 重点复核
80-100 强复核 / 升级审批</code></pre>
<div class="callout">
<strong>同组基准口径</strong>
同组不按全公司粗暴比较,而应按 <code>部门 + 岗位 + 费用类型 + 城市等级 + 项目类型 + 近 90/180 天</code> 聚合。销售、项目经理和研发不能混在一个基准池里比较。
</div>
</div>
<div class="grid-3" style="margin-top: 12px;">
<div class="card">
<h3>出差天数建议</h3>
<pre><code>同类基准天数 = peer_group.travel_days_p75
天数偏离率 = 本次出差天数 / 同类基准天数
建议天数 = min(
本次出差天数,
同类基准天数 + 业务缓冲天数
)</code></pre>
<p>当偏离率超过 1.5 时,建议补充行程安排或压缩天数;超过 2.0 时进入重点复核。</p>
</div>
<div class="card">
<h3>出差费用建议</h3>
<pre><code>本次日均费用 = 本次申请金额 / 本次出差天数
日均费用偏离率 =
本次日均费用 / 同城市同职级日均基准
建议金额上限 =
建议天数 * 日均基准 * 容忍系数</code></pre>
<p>容忍系数第一版建议使用 1.15 到 1.20,避免模型因为小幅波动给出过硬建议。</p>
</div>
<div class="card">
<h3>招待费用建议</h3>
<pre><code>人均招待金额 = 本次招待金额 / 参与人数
人均偏离率 = 人均招待金额 / 招待标准上限
同客户招待频率 = 近 90 天同客户招待次数
个人招待分位 = 个人近 90 天招待金额在同组分位</code></pre>
<p>人均偏离率超过 1.2 时建议调低标准;同客户 90 天内多次招待时要求补充客户推进阶段。</p>
</div>
</div>
<div class="split-note">
<div class="note-green">
<strong>建议生成公式:</strong>
<code>审核建议强度 = max(画像风险等级, 本次偏离等级, 硬规则等级)</code>。展示时必须说明是哪一类指标触发,而不是只给一个结论。
</div>
<div class="note-amber">
<strong>审核建议示例:</strong>
“该申请人近 90 天费用占用处于同组 P88本次出差天数为同类 P75 的 1.67 倍。建议将 5 天调整为 4 天,或补充客户拜访安排和项目阶段说明。”
</div>
</div>
</section>
<section id="output">
<div class="section-head">
<div>
<h2>输出协议</h2>
<p>接口输出需要同时服务审批页展示、看板统计和后续审计,因此自然语言必须和结构化字段分开。</p>
</div>
<div class="section-kicker">API Contract</div>
</div>
<pre><code>{
"score": 82,
"rating": "caution",
"risk_level": "medium",
"recommendation": "cautious_approve",
"summary": "预算整体可承接,但本次费用对预算池已有明显影响。",
"metrics": {
"claim_amount": "12000.00",
"total_amount": "50000.00",
"available_before_approval": "38000.00",
"claim_amount_ratio": "24.00",
"after_usage_rate": "72.00",
"over_budget_amount": "0.00"
},
"applicant_profile": {
"profile_score": 66,
"profile_level": "review",
"peer_percentile": 88,
"travel_days_ratio": "1.67",
"daily_cost_ratio": "1.34",
"suggested_days": 4,
"suggested_amount_cap": "6800.00"
},
"evidence": [
{
"basis_code": "budget.after_usage_rate.warning_near",
"text": "审批后预算使用率为 72.00%,接近预警区间。"
}
],
"suggested_actions": [
{
"action": "approve_with_note",
"text": "可继续审批,但建议备注本次费用对应的项目阶段和后续预算节奏。"
}
],
"explainable_snapshot": {
"budget_no": "BUD-TEST",
"control_action": "warn",
"warning_threshold": "80.00"
}
}</code></pre>
</section>
<section id="ui">
<div class="section-head">
<div>
<h2>审批展示</h2>
<p>预算管理者最需要看到的是“为什么是这个建议”和“如果要通过,还要补什么”。页面不应只展示一个分数。</p>
</div>
<div class="section-kicker">Approval UX</div>
</div>
<div class="screen">
<div class="screen-block wide">
<strong>顶部结论条</strong>
<span>展示推荐动作、风险等级、综合分和一句话摘要,例如“谨慎通过:预算可承接,但审批后使用率接近预警线”。</span>
</div>
<div class="screen-block">
<strong>关键指标</strong>
<span>本次金额、预算总额、审批前可用、审批后使用率、超预算金额。</span>
</div>
<div class="screen-block">
<strong>触发依据</strong>
<span>逐条展示规则依据,保留 basis_code方便定位到模型逻辑。</span>
</div>
<div class="screen-block">
<strong>建议动作</strong>
<span>通过、谨慎通过、补充材料、拆分费用、预算调剂、退回修改。</span>
</div>
<div class="screen-block">
<strong>申请人费用画像</strong>
<span>展示近 90 天频率、金额分位、天数偏离率和建议压缩区间,只作为审批参考。</span>
</div>
<div class="screen-block">
<strong>审批反馈</strong>
<span>记录预算管理者是否采纳建议,以及不采纳时的原因。</span>
</div>
</div>
</section>
<section id="roadmap">
<div class="section-head">
<div>
<h2>落地路线</h2>
<p>先把确定性口径做稳,再逐步接入历史基准和解释层,避免第一版就变成难以审计的黑盒。</p>
</div>
<div class="section-kicker">Implementation</div>
</div>
<div class="grid-4">
<div class="phase">
<h3>P0口径对齐</h3>
<p>确认预算预占、审批前可用、审批后占用的计算口径。</p>
<ul>
<li>梳理预算上下文字段。</li>
<li>固化不重复计算规则。</li>
<li>定义输出协议。</li>
</ul>
</div>
<div class="phase">
<h3>P1规则评分</h3>
<p>上线可审计的确定性模型,覆盖预算审批主流程。</p>
<ul>
<li>实现特征计算。</li>
<li>实现硬规则和分档。</li>
<li>补齐单元测试。</li>
</ul>
</div>
<div class="phase">
<h3>P2历史基准</h3>
<p>引入同类费用历史均值、部门消耗速度和预算节奏。</p>
<ul>
<li>沉淀历史统计任务。</li>
<li>增加异常检测特征。</li>
<li>看板展示采纳率。</li>
</ul>
</div>
<div class="phase">
<h3>P3解释增强</h3>
<p>在结构化依据上增加 LLM 文案层,提升可读性。</p>
<ul>
<li>LLM 不改分数和等级。</li>
<li>解释引用 basis_code。</li>
<li>增加人工反馈闭环。</li>
</ul>
</div>
</div>
</section>
<section id="acceptance">
<div class="section-head">
<div>
<h2>验收清单</h2>
<p>下面的清单会保存在当前浏览器本地,方便后续评审和开发跟踪。</p>
</div>
<div class="section-kicker">Checklist</div>
</div>
<div class="checklist">
<label class="check-item">
<input type="checkbox" data-check="contract">
<span>
<strong>输出协议稳定</strong>
<span>评分、等级、推荐动作、指标、依据、建议动作都有结构化字段。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="double-count">
<span>
<strong>预算预占不重复计算</strong>
<span>测试覆盖当前申请已预占、未预占、部分预占三类场景。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="hard-rules">
<span>
<strong>硬规则不可被平均抵消</strong>
<span>超预算和 block 控制动作必须进入高风险或阻断建议。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="ui">
<span>
<strong>审批页展示可解释</strong>
<span>预算管理者能看到风险原因、计算指标和可执行下一步。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="applicant-profile">
<span>
<strong>申请人画像公式可审计</strong>
<span>同组基准、偏离率、建议天数、建议金额上限都能追溯到结构化字段。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="feedback">
<span>
<strong>审批反馈可回流</strong>
<span>记录采纳、覆盖、退回、预算调剂等结果,为后续模型迭代准备数据。</span>
</span>
</label>
<label class="check-item">
<input type="checkbox" data-check="llm-boundary">
<span>
<strong>LLM 边界清晰</strong>
<span>LLM 只改写解释文案,不参与硬性通过、阻断和预算额度计算。</span>
</span>
</label>
</div>
</section>
<div class="footer">
X-Financial · 预算费用规划推荐模型方案 · 适用于预算审批、费用申请和报销核销闭环。
</div>
</main>
</div>
<script>
const storagePrefix = "x-financial-budget-model-plan:";
document.querySelectorAll("[data-check]").forEach((checkbox) => {
const key = storagePrefix + checkbox.dataset.check;
checkbox.checked = localStorage.getItem(key) === "1";
checkbox.addEventListener("change", () => {
localStorage.setItem(key, checkbox.checked ? "1" : "0");
});
});
</script>
</body>
</html>