ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WeClaw_89|三张意图表合并成一张:一次「单源化」重构,如何用常驻断言把漂移锁死

WeClaw_89|三张意图表合并成一张:一次「单源化」重构,如何用常驻断言把漂移锁死 Hi带娃的我热爱AI 大模型应用落地、意识解码与 AI 开发工具链。 创业路上用技术换时间一起把 AI 变成生产力 WeClaw_89三张意图表合并成一张一次「单源化」重构如何用常驻断言把漂移锁死系列文章第 89 篇- 单一事实来源 · 派生视图 · 常驻不变量 · 等价搬迁 · 基线纪律 专栏信息《从零到一构建跨平台 AI 助手WeClaw 实战指南》专栏专栏定位面向开发者和技术决策者的实战专栏用真实案例和完整代码带你理解如何构建生产级 AI 应用本文记录一次教科书级的「多源漂移」治理。WeClaw 的意图-工具路由知识同时住在三张手写表里改了 A 忘了 B 就出运行时事故。这篇讲我们如何把三表合并成一张带四键域的单表工具/推荐/备选/互斥用字典推导保住全部旧调用方再用「校验脚本 pytest 红线 行为基线」三层常驻断言把不变量焊死——重构后 validate_tool_chain 的历史失败项当场清零。‍ 作者与项目作者简介翁勇刚 WENG YONGGANG新概念龙虾-WeClaw 开发团队负责人一群专注于跨平台 AI 应用的实践者理念“再复杂的技术也能用代码讲清楚” 项目地址https://github.com/wyg5208/weclaw.git 官网地址https://weclaw.link 作者 CSDNhttps://blog.csdn.net/yweng18⭐ 欢迎 Star⭐、Fork、贡献代码 摘要本文结构概览三张表各管一摊的原始设计问题的形态→ 一次真实的运行时事故如何暴露漂移动机→ 单表 四键域 派生视图的方案设计 → 与 git HEAD 旧表逐意图 diff 的等价性验证 → 三层常驻断言体系 → 裁定口径 B如何允许「正确的变化」通过门禁。核心结论多源数据结构的漂移不是纪律问题是结构问题——只要存在第二份拷贝事故只是时间问题安全的单源化 单表 派生视图旧调用方零改动 不变量断言防止退化回多源门禁的价值不在「全绿」在于失败清单可解释——每一条差异要么归入裁定白名单要么修掉。一、三张表三个「以为自己是权威」的编辑入口WeClaw 的意图引擎识别出 25 类意图后需要回答三个问题这个意图能用哪些工具优先用哪个哪些工具禁止碰这三个问题的答案分别住在三张手写字典里# intent_engine.py重构前三张表各 ~150 行INTENT_TOOL_MAPPING{# 问题 1意图 → 可用工具全集research:[search,knowledge_rag,ai_detection,...],...}INTENT_PRIORITY_MAP{# 问题 2意图 → 推荐/备选工具research:{recommended:[...],alternative:[...]},...}INTENT_EXCLUSIVE_TOOLS{# 问题 3意图 → 互斥工具research:{exclude:[...]},...}三张表的消费方遍布全链路tool_exposure.py用它们做分层暴露和 Schema 标注agent.py用它们做工具偏离的前置拒绝validate_tool_chain.py用它们做工具链完整性校验。1.1 漂移的形态同义漂移给research意图新增一个工具在INTENT_TOOL_MAPPING里加了忘了在INTENT_PRIORITY_MAP里标注优先级——工具能调但永远拿不到「推荐」前缀模型无从知晓它是一等公民。死引用工具下线了INTENT_PRIORITY_MAP里的标注还指着它。这个错误安静地潜伏着直到validate_tool_chain的标注可达性检查把它揪出来——校验失败但没人知道失败是从哪次改动开始的。互斥失配INTENT_EXCLUSIVE_TOOLS的 exclude 列表与 tools 列表悄悄重叠模型调一个「既推荐又禁止」的工具前置验证直接拒绝自己人。1.2 为什么纪律解决不了团队可以约定「改 A 必须查 B 和 C」但这个约定的执行者是人。人的检查清单会随时间衰减而代码库的修改频率不会。只要第二份拷贝存在每一次修改都是掷骰子。这是结构问题不是态度问题——解法只能是让第二份拷贝物理上不存在。二、方案设计单表为源旧表为派生视图2.1 合并策略一个意图一行四个键域把三张表按意图对齐合并成一张INTENT_TOOL_PROFILES每个意图一个条目、四个键域INTENT_TOOL_PROFILES:dict[str,dict[str,list[str]]]{research:{tools:[search,knowledge_rag,oss_admin,...],# 可用全集recommended:[search,knowledge_rag],# 一等公民alternative:[oss_admin],# 备选exclude:[meal_menu,games],# 互斥},casual_chat:{tools:[],recommended:[],alternative:[],exclude:[shell],},# ... 共 25 个意图}四个键域之间天然存在约束关系而这些约束现在可以在同一个字典条目内一眼看全标注recommended/alternative必须是可用集tools的子集互斥exclude不得与可用集相交。编辑者不需要「记得去查别的表」因为别的表已经不存在了。2.2 兼容策略旧三表变成一行推导直接删掉旧三表会引爆十几个调用点。我们选择让旧表退化为单表的派生视图——名字还在语义还在但不再持有独立数据INTENT_TOOL_MAPPING{intent:profile[tools]forintent,profileinINTENT_TOOL_PROFILES.items()}INTENT_PRIORITY_MAP{intent:{recommended:p[recommended],alternative:p[alternative]}forintent,pinINTENT_TOOL_PROFILES.items()}INTENT_EXCLUSIVE_TOOLS{intent:{exclude:p[exclude]}forintent,pinINTENT_TOOL_PROFILES.items()ifp.get(exclude)# 闲聊等无互斥的意图不生成空条目保持旧行为}调用方一行不用改但「改 A 忘改 B」从可能变成了不可能——B 是 A 算出来的。2.3 顺手清掉的死引用合并过程中做了一次全量工具名核对对照 tools.json清掉了全部死引用7 个意图的 tools 死标注、4 个未覆盖意图的归属修正、2 个标注死引用。这不是重构的副产品而是重构的红利——多源结构下没人敢做这种全量核对因为改一处就要同步三处单源结构下核对就是改一个字典条目。三、等价性验证与 git HEAD 旧表逐意图 diff重构最危险的时刻是「我确定没改行为但拿不出证据」。我们的证据链是一次性冒烟脚本直接从git HEAD:intent_engine.py提取旧三表注意旧表是带类型注解的AnnAssign节点AST 提取要两个分支都覆盖与新单表派生出的视图逐意图比对PM优先级表逐意图零差异死引用删除除外 ✅ EX互斥表逐意图零差异 ✅ TM工具映射差异意图数 8/25全部命中预期白名单 ✅ 意图顺序完全保持 ✅8 个 TM 差异意图逐条归因7 死标注并入 4 未覆盖归属 −2 死引用删除。没有一条差异无法解释——这是放行重构的唯一标准。四、三层常驻断言把不变量焊进流水线单次冒烟只能证明「这次重构是对的」常驻断言才能证明「未来不会退化回多源」。我们把不变量固化在三个层次4.1 校验脚本层check_9 单表不变量validate_tool_chain.py新增一组纯内存断言零 IO秒级单表键集与INTENT_CATEGORIES的 25 个意图精确对齐不多不少每个意图的 tools 无重复recommended ∪ alternative ⊆ tools ∪ CORE_TOOLS ∪ EXTENDED_TOOLS标注必须可达exclude ∩ tools ∅互斥不相交三个派生视图与单表逐键一致防止有人绕过单表直接改派生表同时根治了一个潜伏问题脚本里的「已知工具前缀」列表原本是 tool_exposure.py 的硬编码副本又一个多源改为 AST 解析源文件动态提取——副本脱节的病根直接拔掉。改造后validate_tool_chain 的4 项历史失败当场清零9 项检查全绿。4.2 pytest 红线层四条不可绕过的失败tests/test_intent_guardrails.py§3 用 pytest 固化同样的四条不变量。CI 里任何一条红了PR 合不进去——不变量从「脚本跑一下看看」升级为「合并门禁」。4.3 行为基线层G4 提示词行为 diff静态断言管不了「语义变了」比如给research意图换了推荐工具单表内部完全自洽但下游装配出的 System Prompt 和工具标注会变。这靠行为基线兜底——冻结 20 组代表场景意图置信度组合的装配输出重构后 diff。diff 结果只有 4 条 query 的 mapped_tools 漂移全部是死引用清理的预期结果research 丢掉死标注、补上真工具。基线重建走--rebuild-baseline 人工审阅 diff 的纪律流程——基线不是橡皮图章重建必须逐条解释。五、踩坑实录三个差点翻车的细节坑 1旧表是 AnnAssign 不是 Assign。冒烟脚本用ast.Assign提取 git HEAD 的旧表结果为空。旧表写法是INTENT_TOOL_MAPPING: dict[str, list[str]] {...}——带类型注解的赋值是AnnAssign节点。教训AST 提取字典字面量时两种节点都要处理。坑 2裁定口径必须先于实施。「PM 零变更」是验收标准之一但死引用清理必然改动 PM。如果口径没有事先写明「死引用删除属裁定的一部分」冒烟会 FAIL实施者会在「改回去」和「改口径」之间摇摆。口径写进方案实施只是执行——这是多 Phase 重构不返工的关键纪律。坑 3tests/ 目录被 .gitignore 整体忽略。重构提交后才发现三个护栏测试文件从未入库——git status对已 ignore 的目录不显示未跟踪文件所以「测试都在跑、都是绿的」和「测试根本不在仓库里」可以同时为真。补口方式参照仓库既有先例git add -f强制跟踪。教训护栏类文件必须显式入库并纳入 CIignore 规则要有豁免清单。六、可复用的方法论第二份拷贝就是定时炸弹任何「同步维护两份数据」的约定最终都会被某次匆忙的修改击穿。能合并就合并合并的优先级高于任何功能开发。派生视图是重构的安全带不要求调用方一次迁移到位旧接口变成新数据的投影迁移可以按自己的节奏分批发生。断言要分层次脚本断言管快速自查pytest 红线管合并门禁行为基线管语义漂移——三层各管一段缺一层就有盲区。门禁失败必须可解释不是「全绿才合并」而是「每一条红都能归因」。解释不了的失败才是真风险解释得了的失败是已知变更。结构约束优于流程约束把「记得同步三处」变成「只有一处可改」前者依赖人后者依赖编译器。七、总结三张意图表合并成一张单表代码量净增两行派生推导比原表还短但把一类事故从「可能发生」变成了「结构上不可能」。重构的真正成本不在写新代码而在等价性证明逐意图 diff和防退化建设三层断言——这两样东西做扎实了重构才配叫重构否则只是一次更危险的修改。下期预告《WeClaw_90给 LLM 快路径上保险6 秒最坏情况是怎么设计出来的》意图识别的 LLM 增强路径为什么不能复用主对话的重试策略超时、快失败重试、熔断器三件套的参数推导一次真实的服务抖动如何验证了熔断的必要性敬请期待版权声明本文为 CSDN 博主「翁勇刚」的原创文章遵循 CC 4.0 BY-SA 版权协议转载请附上原文出处链接及本声明。
返回列表