From 606a88c805a1d5edceb196b59cc3dfbcc0a797b9 Mon Sep 17 00:00:00 2001 From: caoxiaozhu Date: Thu, 25 Jun 2026 11:50:11 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20stewardPlanModel=20=E9=80=82=E9=85=8D?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E5=8A=A8=E4=BD=9C=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E5=B9=B6=E6=9B=B4=E6=96=B0=E8=A7=84=E5=88=99=E8=A1=A8=E4=B8=8E?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - stewardPlanModel 适配新的意图注册表动作步骤结构 - 更新交通/通信/差旅等财务规则表,补 2026-06-25 work-log --- document/work-log/2026-06-25.md | 25 ++++++++++++++++++ .../rules/finance-rules/交通工具等级标准.xlsx | Bin 6072 -> 6072 bytes .../rules/finance-rules/交通费用预估表.xlsx | Bin 7197 -> 7197 bytes .../finance-rules/公司通信费报销规则.xlsx | Bin 5933 -> 5934 bytes server/rules/finance-rules/出差补助标准.xlsx | Bin 5931 -> 5931 bytes .../rules/finance-rules/地区淡旺季映射表.xlsx | Bin 11427 -> 11427 bytes .../rules/finance-rules/差旅职级映射表.xlsx | Bin 5783 -> 5783 bytes web/src/views/scripts/stewardPlanModel.js | 10 ++++--- 8 files changed, 32 insertions(+), 3 deletions(-) diff --git a/document/work-log/2026-06-25.md b/document/work-log/2026-06-25.md index 7ff2ac0..759bed4 100644 --- a/document/work-log/2026-06-25.md +++ b/document/work-log/2026-06-25.md @@ -28,15 +28,40 @@ - 影响:模型给出低置信度差旅申请意图时不再直接建预览,先反问确认;闲聊类输入不再误触发模型规划,响应更快、减轻后端压力。 - 局限:`agent-change-log` Skill 在当前环境不可调用,已按 AGENTS.md 规范手动增量更新本日志。 +- 22:40:我落地了注册表驱动的意图插槽架构,让新增意图从「改 6+ 处硬编码」降到「写一个描述符 + 执行函数 + 注册」,并端到端跑通了「查差旅标准」查询意图作为样板。 + - Git 提交检查:`git fetch --all --prune` 后本地与 origin/main 同步(不 ahead 不 behind);工作区有本次新增/修改的后端文件。 + - 背景:排查确认旧架构里 `task_type`/`assigned_agent`/`flow_id` 在 schema(Literal)、function call schema(enum)、model_plan_builder(白名单)、action_contracts(if/else)、action_executor(if/elif)五层硬编码,加一个意图要同步改 6+ 处,完全没有扩展点;且"查差旅标准"这类查询意图无任何位置(task_type enum 只有 expense_application/reimbursement)。 + - 修改①(注册表核心):新建 `steward_intent_registry.py`——`IntentDescriptor` 声明 task_type/assigned_agent/signal_keywords/ontology_field_allowlist/action_steps_builder/executor/flow_id/prompt_fragment;新建 `steward_intent_bootstrap.py` 在 import 时注册 3 个意图(expense_application/reimbursement/query_travel_standard)。 + - 修改②(schema 放宽):`schemas/steward.py` 把 `StewardTaskType`/`StewardAssignedAgent`/`StewardActionType`/`StewardFlowId` 从 Literal 改为 str,运行时校验下沉到 registry,让 schema 不再是扩展拦路虎。 + - 修改③(执行分发):`steward_action_executor.py` 的 `execute()` 从 if/elif 链改为优先查 registry(`resolve_intent_by_action`)委托 executor;新增 `_dispatch_application_action`/`_dispatch_reimbursement_action` 分发入口;`SUPPORTED_ACTIONS`/`NOOP_ACTIONS` 改为与 registry 聚合(`all_side_effect_actions`/`all_noop_actions`)。 + - 修改④(动作生成):`steward_action_contracts.py` 的 `build_task_action_steps` 改为查 registry 的 `action_steps_builder`;原 `_build_application_steps`/`_build_reimbursement_steps` 改公开供 registry 引用。 + - 修改⑤(function schema 动态化):`steward_intent_agent.py` 的 task_type/flow_id enum 改为 `all_task_types()`/`all_flow_ids()` 动态生成;system prompt 改为从 registry 拼接意图列表 + 每个 intent 的 prompt_fragment。 + - 修改⑥(白名单放开):`steward_model_plan_builder.py` 的 task_type 白名单改 `get_intent`;assigned_agent/flow_id/字段过滤全部改 registry 驱动;`_sanitize_model_ontology_fields`/`_sanitize_model_missing_fields` 改 per-task_type allowlist(`field_allowlist_for`);查询类意图(flow_id=None)跳过必填字段推断。 + - 修改⑦(查询执行器):新建 `steward_query_executors.py`——`build_travel_standard_query_steps` 生成单步无副作用动作;`execute_travel_standard_query` 从槽位取 location/employee_grade/standard_category,复用 `DEFAULT_TRAVEL_POLICY_CONFIG` 按职级×城市分级查住宿/交通标准,拼装 Markdown 回复(补助标准因未纳入运行时配置用占位说明)。 + - 修改⑧(门控适配):`steward_planner_extraction.py` 的 `_looks_like_ambiguous_travel_flow` 加查询信号词前置判断(命中查询意图直接返回 False,不走候选流程);`_build_task` 的 task_id/assigned_agent/label 改 registry 驱动;`steward_planner_fallback.py` 的 `_classify_irrelevant_input` 补充 registry signal_keywords 判断(避免查询类输入被判 off_topic);`steward_graph_planner.py` import bootstrap 触发注册。 + - 修改⑨(前端):`stewardPlanModel.js` 的 `TASK_TYPE_LABELS`/`AGENT_LABELS`/`EXECUTABLE_STEWARD_ACTION_TYPES` 加 query_travel_standard/execute_travel_standard_query/policy_query_assistant。 + - 验证:后端全量 steward 测试 **72 passed**(含新增 14 个:registry 7 + query executor 7);前端意图测试 **28 passed**;既有申请/报销/规划/动作执行/槽位决策链路全部无回归。 + - 容器:容器名为 `x-financial-local-linux`(非 `local-x-financial-linux`),已运行 19 小时;后端测试在该容器内执行,venv 在 `/tmp/x-financial-server-venv`。 + - 影响:现在加一个新意图(如查报销进度、查预算执行)只需:① 写 `IntentDescriptor` 声明 task_type/槽位/信号词/executor;② 注册进 bootstrap;③ 写执行函数。function schema、动作生成、执行分发、字段过滤、门控全部自动适配,零硬编码改动。 + - 局限:补助标准(allowance)尚未接入运行时配置,查询时返回占位说明;前端查询结果当前以 Markdown 消息展示,未做卡片化;registry 只在后端,前端 task_type 分发仍是 `resolveNextActionContext` 的 if/else(本次只加了 query 分支,未全面注册表化)。 + ## 遗留问题 - 09:18:官方 `quick_validate.py` 仍因当前 Python 环境缺少 `PyYAML` 无法运行,已用 frontmatter、必需文件、占位符和 diff check 做人工兜底。建议后续统一为 skill 校验脚本补齐依赖或增加无 PyYAML 的轻量校验路径。 - 09:23:当前环境没有找到 Skill Creator 的 `quick_validate.py` 脚本文件本体,因此本次继续采用人工兜底校验。建议后续恢复系统 Skill Creator 脚本路径,或把轻量校验脚本纳入仓库级工具。 - 21:30:`expense-application-fast-preview.test.mjs` 仍有 12 个既有失败(文案「小财管家」「此意图系统不支持」与 markdown 表格整块渲染相关),与本次意图门控改动无关,建议单独排查。 - 21:30:本次未纳入范围的三项已记录:时间过滤维度扩展(仅支持 N天前/昨天/今天)、排除词两处重复维护、`handleInlineDraftDeletionIntent` 命名与职责不符,建议后续分批处理。 +- 22:40:补助标准(allowance)未纳入 `DEFAULT_TRAVEL_POLICY_CONFIG`,查询差旅标准时住宿/交通有确定数值,补助只返回占位说明。补助数据在 `server/rules/finance-rules/出差补助标准.xlsx`,后续需把补助标准接入运行时配置并在 `resolve_travel_standard_snapshot` 补全。 +- 22:40:前端查询结果当前以普通 Markdown 消息展示,没有像申请预览那样的卡片化视图;查询意图的前端分发仍是 `resolveNextActionContext` 的 if/else,未全面注册表化(本次只加了 query 分支)。 +- 22:40:`server/rules/finance-rules/` 下有两个 Excel(交通工具等级标准、交通费用预估表)被标记为 modified,疑似容器运行时产物,非本次代码改动,未处理。 +- 22:40:`agent-change-log` Skill 在当前环境不可调用,已按 AGENTS.md 规范手动增量更新本日志。 ## TODO - [ ] 为 `quick_validate.py` 准备稳定运行环境,避免后续新增 Skill 时继续依赖人工兜底。(来源:09:18 技能校验) - [ ] 排查 `expense-application-fast-preview.test.mjs` 的 12 个既有失败(小财管家文案 / 表格整块渲染)。(来源:21:30 意图门控加固) - [ ] 评估意图门控剩余三项:时间过滤维度扩展、排除词常量抽取、`handleInlineDraftDeletionIntent` 重命名。(来源:21:30 意图门控加固) +- [ ] 把补助标准(`出差补助标准.xlsx`)接入运行时配置,补全 `resolve_travel_standard_snapshot` 的 allowance 数值查询。(来源:22:40 注册表架构) +- [ ] 前端查询结果卡片化,并把前端 task_type 分发也改成注册表驱动。(来源:22:40 注册表架构) +- [ ] 在真实 LLM 连通环境下,用「我去武汉出差的住宿标准是多少」端到端验证查询意图识别→执行→回复。(来源:22:40 注册表架构) +- [ ] 评估是否需要把 LangGraph 迁移 Phase 5(trace UI、legacy 收敛)与注册表架构的查询意图 trace 打通。(来源:22:40 注册表架构) diff --git a/server/rules/finance-rules/交通工具等级标准.xlsx b/server/rules/finance-rules/交通工具等级标准.xlsx index 8152f7c879468ac9fe3b67319237155f4c21fb5e..eeaa2562799dafcc2a4b883e52fcd6bf0c09657f 100644 GIT binary patch delta 423 zcmdm?zeArlz?+#xgn@y9gF!>~=0x5D96%~&iT{*;6R#@N`yV#oY5RX_O7-h4t#XU> zru)uua7q&Dh@Mgrz4e)MXW;I7|LAReEarV5fB)FH*2IeAt+JiFyg^WRrs<7ystXU# zlI?6_@c7jcwzNj&j>Nm#EnUabZ5#rAE#c!(eQp||k}9G(qa`w;=glIMs=&7X8)umo zGg@zXq2V}Dydar#alLQ7*_o40d@EyD?0f%t->jX5i<$1tlZ$`$bn`XUAV1~Fy%(L_ zL!K<2qsR4pVfKUQjXTf$(^?SOE6g&NdEHIZ%C%;|&} zuap04zVPd^yW9U8#IbH_jQ_7@&xjHVF*9T4C=0MLFl_$JXv)S643N#*9C@r@#vEQl zW)NfY1%YQ^ft`Xaj9>u=;YVNrRS|E9K(VMbJTND36Ez2``XFitrVYiM!F0Kp1(@C> UW(KBTi&=qb4RKd6T_ElO0A)g~iU0rr delta 423 zcmdm?zeArlz?+#xgn@y9gF(yX#zfu&96%~2&ogS}#H$MR*@q2y+Wvcew4FG)$G~Tn z_h~X2| zvYkx~9GYdPH1fRI&roB+f z>(j=$dR)&JW0NJd~k;e*V%;7a; z1~DdI5O@X_*eTe;2o`Vzn6+ETBV diff --git a/server/rules/finance-rules/交通费用预估表.xlsx b/server/rules/finance-rules/交通费用预估表.xlsx index ebcc26042c27ca1386e29851161cbbeec943c39f..1f68163691cabe9bd74a4d527b23d2f1f20ca49e 100644 GIT binary patch delta 423 zcmbPhG1r1Oz?+#xgn@y9gF!>~=0x5D96%~&iT{*;6R#@N`yV#oY5RX_O7-h4t#XU> zru)uua7q&Dh@Mgrz4e)MXW;I7|LAReEarV5fB)FH*2IeAt+JiFyg^WRrs<7ystXU# zlI?6_@c7jcwzNj&j>Nm#EnUabZ5#rAE#c!(eQp||k}9G(qa`w;=glIMs=&7X8)umo zGg@zXq2V}Dydar#alLQ7*_o40d@EyD?0f%t->jX5i<$1tlZ$`$bn`XUAV1~Fy%(L_ zL!K<2qsR4pVfKUQjXTf$(^?SOE6g&NdEHIZ%C%;|&} zuap04zVPd^yW9U8#IbH_jQ_7@&xjHVF*9T4C=0MLFl_$JXv)S643N#*99KEP3}zui zW)Netx#%;nfPz>PBUoUm_#?1DmxMP&;D)3%JTNCqOPPaJg-Y3h>1k5VVET@f1(+6> UHUrZE(pF%)SK1X!UzPR%0MPEMng9R* delta 423 zcmbPhG1r1Oz?+#xgn@y9gF(yX#zfu&96%~2&ogS}#H$MR*@q2y+Wvcew4FG)$G~Tn z_h~X2| zvYkx~9GYdPH1fRI&roB+f z>(j=$dR)&JW0NJd~ag`IyU=}iD z1~Dd^i#`JjD2O#Nf(4d}KLQJMNq9p9Zb(|g19P&plsQ;csFWR;o+jlCrte5ufN61Q TGcX+>Z3U)#rCq`FRcQ|ZMh&i~ diff --git a/server/rules/finance-rules/公司通信费报销规则.xlsx b/server/rules/finance-rules/公司通信费报销规则.xlsx index 2107e727e049ebc3b1153d25f077ef3b704d2af7..afd075c3ef3c9d56e04a48513f4090e602094f3e 100644 GIT binary patch delta 572 zcmZ3hw@!~Iz?+#xgn@y9gJGuZM4p43Gi7ha+%8?>@p|Gl`Fj7u20U&5Pfe+Qz2$CI z$ELV8i^i5CDgxUzK5pxJ=G+;$yWT%~8y|~#-^brSHm>!MZTTiRe-XdL%A;GPa-L7R zka*h9#gRd!)@gN+olqVRzg?8;V`Di*jlb*qI8>jTMyRBUXwGPfjOcl@$fPQ;t^dYZ zrp1ibTV7~5P82Ul=3HFwTW@yeq!ZuD*cJQUf8IB1XW?R|IC=Sb&!29-rW)j@oW10t zlY7XMWaC(obI$4yqXXm8>P0WCImX#^j&b{&S$jfu@BV9>?G<-Ta{cC_&CjPstUube z-*=t-U-N}um)UOlZxF}Y+Y9I}vw&zJ zUL$58J=sm*If$z-*vtsxZV`G6;?5NI0dt>=*dT&&vW}>kJSb3MhR#uEU=RidI1Dr} z-ksbissvWEGRM?~BF{n2ERmZr2`+h0UQN6vU!Q&0fT!)h*Gb!nk1xph z%<^{km*ok&apUZbtZcFRglv_+-@P}P-Mx`#9rb>(yHH+;z$y2dF1rMk=1kKY=TsLS zQnMCjWt?zDa;?u%j~#|KzcN&N&sQ`lT-|M>Sa2+BgGZ3}Bm>UR8zeTb4qELhX_sWq zy@lz`g++^5QUedoRqd%a`+L(so!e4nb3pBV@mka3$8M^7?B30P{2&G~>AEYtH@rbzsMeYfYQC zpZvP`zuuO(*^7U+e^kqm{_!>ctvYfb+>DtSGe=o~je%j#<}ZvUY)n}qo3%Kevw&z} zUL$58J=tC0If!c@*vtsxZWVeA;?5HG0drr7*dT&&vaYC^JSb4%rm8bA2m=Eg1{xUe zOl}fYQUIx0nPY0bnT3HNpPzw20;T{+H#Ev}Ot#{XntV@`2V~(JQ5ikH+xj@c} i$-Bff!H%8X=F}q1$iQ%&8QrnHlbOWT*lvOhVE_O&g~eU~ diff --git a/server/rules/finance-rules/出差补助标准.xlsx b/server/rules/finance-rules/出差补助标准.xlsx index e7c568079456cf0914c860ef061e1326d039eeae..e3a1667f2b0b1ab0344183626dc597b9db45033d 100644 GIT binary patch delta 433 zcmZ3jw_1-kz?+#xgn@y9gF!>~=0x5DfwnmQ zr|ti#Db=sHw8|~go9;Wu!6`|oBYH|j^www2oq@aS{iC<>v6%OL{QYC&S`#adx5{?z z@&-ZOnWi_+sV+P`OSZF#!Q)p)*wPx6I}-0|w{#s#w{ZygwS(RSkG9s{3Q-lzSfdBSeoIC~>2TdF=GTjlR}?@eZRZ{%4=yTx|^nEfDn9a;@3zyUf|%an~f* zZ~l~b{Jm>v=cN7q>*W8MFZ_Dz%~|`NaL3yR*4Jm-Gol1Z%*>cM$^x4|Fj}xN1A}I> zHpgogFhi2pkQv07>?`mLEMO*B#RwMIEc6I0FhkfIBJfPangbTBlQl)n!K#u)?ZEUB eQD-pyO4I^OtBRR{={PYfFg;t$6-+-C^8f(9&ax5! diff --git a/server/rules/finance-rules/地区淡旺季映射表.xlsx b/server/rules/finance-rules/地区淡旺季映射表.xlsx index 809ced675d4579555501a61369437e9fabc7be0c..c2f03ee29cb47d6859ec299dbf9b9e0b7fd8f073 100644 GIT binary patch delta 423 zcmZ1+xj2$Hz?+#xgn@y9gF!>~=0x5D96%~&iT{*;6R#@N`yV#oY5RX_O7-h4t#XU> zru)uua7q&Dh@Mgrz4e)MXW;I7|LAReEarV5fB)FH*2IeAt+JiFyg^WRrs<7ystXU# zlI?6_@c7jcwzNj&j>Nm#EnUabZ5#rAE#c!(eQp||k}9G(qa`w;=glIMs=&7X8)umo zGg@zXq2V}Dydar#alLQ7*_o40d@EyD?0f%t->jX5i<$1tlZ$`$bn`XUAV1~Fy%(L_ zL!K<2qsR4pVfKUQjXTf$(^?SOE6g&NdEHIZ%C%;|&} zuap04zVPd^yW9U8#IbH_jQ_7@&xjHVF*9T4C=0MLFl_$JXv)S643N#*98pqWMu(Ci zGl(&HkLok9z*4nlMzDak#v`zRfTlM@AWq8~9+;CCYMFyo-PW=L(=yu5U^+?L0!+`< UHUrbww5`Cju#PL3j@I!20IEo=w*UYD delta 423 zcmZ1+xj2$Hz?+#xgn@y9gF(yX#zfu&96%~2&ogS}#H$MR*@q2y+Wvcew4FG)$G~Tn z_h~X2| zvYkx~9GYdPH1fRI&roB+f z>(j=$dR)&JW0NJd~5hVp?bSN1z zgBX+ds6GP=ELCe}1Pf?uJOT>{XnI2g;g TGcbKk+X_qz>$rmHXdMp#A7`(` diff --git a/server/rules/finance-rules/差旅职级映射表.xlsx b/server/rules/finance-rules/差旅职级映射表.xlsx index 07cde05afde7200db2bb18d02f52e0476b1d2bcc..704644da2bd01ca20cd3fe775cf886966e6645a9 100644 GIT binary patch delta 423 zcmbQPJ6)GIz?+#xgn@y9gF!>~=0x5D96%~&iT{*;6R#@N`yV#oY5RX_O7-h4t#XU> zru)uua7q&Dh@Mgrz4e)MXW;I7|LAReEarV5fB)FH*2IeAt+JiFyg^WRrs<7ystXU# zlI?6_@c7jcwzNj&j>Nm#EnUabZ5#rAE#c!(eQp||k}9G(qa`w;=glIMs=&7X8)umo zGg@zXq2V}Dydar#alLQ7*_o40d@EyD?0f%t->jX5i<$1tlZ$`$bn`XUAV1~Fy%(L_ zL!K<2qsR4pVfKUQjXTf$(^?SOE6g&NdEHIZ%C%;|&} zuap04zVPd^yW9U8#IbH_jQ_7@&xjHVF*9T4C=0MLFl_$JXv)S643N#*9DyugMm>)q zGl(&HGygNNz#M@FMzDaq&?B$_i?BCDAV|a-9+;D-ikO2{ofokK(*mN-U^-0H0!&X7 UH3QS9M6JLyyO=AO_80R20DGXSQUCw| delta 423 zcmbQPJ6)GIz?+#xgn@y9gF(yX#zfu&96%~2&ogS}#H$MR*@q2y+Wvcew4FG)$G~Tn z_h~X2| zvYkx~9GYdPH1fRI&roB+f z>(j=$dR)&JW0NJd~5y%2&)bkiJ zgBX)H^FIR%%n@i{1PjOuJpv1`2zx^Wf<&z0fjN1qh&fo*c@aA>Eg TGcbKh)Cx?qi@Abne=!dL@UX34 diff --git a/web/src/views/scripts/stewardPlanModel.js b/web/src/views/scripts/stewardPlanModel.js index c92f128..84644b7 100644 --- a/web/src/views/scripts/stewardPlanModel.js +++ b/web/src/views/scripts/stewardPlanModel.js @@ -16,7 +16,8 @@ import { const TASK_TYPE_LABELS = { expense_application: '费用申请', - reimbursement: '费用报销' + reimbursement: '费用报销', + query_travel_standard: '差旅标准查询' } const AGENT_LABELS = { @@ -25,14 +26,17 @@ const AGENT_LABELS = { expense_application: '申请助手', reimbursement_assistant: '报销助手', reimbursement: '报销助手', - expense: '报销助手' + expense: '报销助手', + policy_query_assistant: '政策查询助手', + query_travel_standard: '政策查询助手' } const EXECUTABLE_STEWARD_ACTION_TYPES = new Set([ 'save_application_draft', 'submit_application', 'create_reimbursement_draft', - 'associate_attachments' + 'associate_attachments', + 'execute_travel_standard_query' ]) export function buildStewardPlanRequest({