fix(ai): 活跃文档确定性兜底——分发层拦截,不再指望模型自觉 - #210
Merged
Merged
Conversation
提示词路线已失败两次:PR#187 加强 system prompt 措辞无效,PR#209 改挂用户 消息末位仍是概率性的。真机日志实证模型会无视注入(注入后 6 秒照样调 doc_list_project_files)。本 PR 在工具分发层做确定性兜底: - doc_open_file 打的就是当前活跃文档时直接短路,返回 alreadyOpen 成功反馈, 省掉一整轮「SSE 下发打开指令 → 等前端回执」的往返; - doc_list_project_files 结果尾部钉上活跃文档 id/name,让走神的模型下一轮 自己纠回来。 **跨文档场景不拦截**:doc_open_file 指向别的文档时照常执行,doc_list 本身 也从不阻断——「对比项目里另一份合同」这类正常流程不受影响。 实现为两个纯函数 helper(activeDocOpenShortCircuit / appendActiveDocNotice), 可脱离编排器单测。未改 AgentOrchestrator 构造器,EvalHarness 无需同步。 后端全量 313 测试通过(新增 7 个,覆盖短路命中/跨文档放行/参数缺失/非数字 id/无名回退/列表加钉/无活跃文档原样返回)。 另:经查 project-overview.vue 的 isTabVisible 不是缺陷——标签本就按左栏模式 分组(lastActiveIdsByMode + activeTabsByMode 存储),该函数为 false 时编辑区 本身就是空白、文档不可见,此时不发 activeContext 是正确行为。不做改动。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zeweihan
added a commit
that referenced
this pull request
Jul 28, 2026
复查 PR#209/#210 发现三个问题,均已修复并补测试: 1. **文档名会串(真缺陷)**:dispatchTool 底部跟踪逻辑在模型中途打开别的 文档时更新了 guard.activeFileId,却没动 guard.activeFileName。于是打开 文档 B 后名字仍是 A 的,后续短路反馈与列表加钉会输出「《A的名字》(id=B)」 ——等于主动喂给模型一条错误信息。现抽出 activeDocNameAfterOpen:切文档 即作废旧名(这一层拿不到新名),回退为通称。 2. **手拼 JSON 未转义**:短路返回值是字符串拼接的 JSON,文件名含半角引号或 反斜杠(macOS 合法字符)即产出坏 JSON。改为纯文本——doc_open_file 本身 返回的就是纯文本,格式反而更一致,且从根上消除该类问题。 3. **null 文档名漏给模型**:ContextAssemblerService 末位提醒直接插值 getName(),name 为空时输出「《null》」。两处各加 activeDocDisplayName 兜底,空名回退为「当前文档」。 另核查确认无问题(不改动): - 末位提醒不入库:持久化的是 request.getMessage() 原文,不会污染历史; - 短路跳过 applyToolSideEffects 安全:doc_open_file 无 @ToolMeta; - 短路传 tool 可能为 null 但两处消费点(:270 :607)均已 null 判断,无 NPE; - 短路对原生 function calling 与 XML 两条分发路径同时生效; - 检查点逻辑在短路之前执行,doc_open_file 非 MODIFIED 类,不受影响。 后端全量 318 测试通过(新增 5 个:切文档作废旧名/重开同一文档保留/此前无活跃 文档/引号反斜杠文件名/列表加钉空名回退)。 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
为什么还要第三个 PR
提示词路线已经失败两次:
<active_document>措辞doc_list_project_files[系统提醒]不该再等第三次真机复测才上确定性手段。本 PR 在工具分发层兜底。
改法
AgentOrchestrator.dispatchTool两处拦截:1.
doc_open_file打的就是活跃文档本身 → 短路直接返回
{"status":"success","alreadyOpen":true,...},不执行。省掉一整轮「SSE 下发打开指令 → 等前端回执」的往返。2.
doc_list_project_files结果尾部钉上活跃文档让走神的模型下一轮自己纠回来。
边界:跨文档场景不拦截
doc_open_file指向别的文档时照常执行;doc_list_project_files本身从不阻断,只在结果上加钉。「对比项目里另一份合同」这类正常流程完全不受影响。这是刻意的——把工具从清单里摘掉会更"彻底",但会直接堵死跨文档任务。
实现与测试
抽成两个纯函数 helper(
activeDocOpenShortCircuit/appendActiveDocNotice),可脱离编排器单测。未改AgentOrchestrator构造器,EvalHarness 无需同步(该地雷已踩过两次)。后端全量
mvn test(JDK 21):313 tests, 0 failures,新增 7 个断言覆盖短路命中、跨文档放行、参数缺失、非数字 id、无名回退、列表加钉、无活跃文档原样返回。附:撤回一个误判
上一轮我把
project-overview.vue的isTabVisible说成"潜在断点",查证后不成立:标签本来就按左栏模式分组(lastActiveIdsByMode+activeTabsByMode存储),该函数返回 false 时编辑区由同一个activeFileLeft驱动、本身就是空白,文档并不可见——此时不发 activeContext 是正确行为。不做改动。🤖 Generated with Claude Code