-
Notifications
You must be signed in to change notification settings - Fork 616
feat: large tool call offload #1276
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
335efef
docs(spec): add tool output guardrails
zerob13 54b9dab
fix(agent): tool output guardrails
zerob13 6b8b4d2
fix(agent): standardize tool offload extension
zerob13 71eba1d
feat: extract path to session
zerob13 8011332
fix: review issue
zerob13 fc9df4e
fix: error response on renderer
zerob13 2594b1f
feat: add read_file pagination and whitelist-based tool offload
zerob13 052554a
fix: independent reasoning time for each thinking block in agent loop
zerob13 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # 实施计划 | ||
|
|
||
| ## 现状梳理 | ||
|
|
||
| - 真正的工具路由在 `src/main/presenter/toolPresenter`: | ||
| - `ToolPresenter` + `ToolMapper`. | ||
| - `agentPresenter/tool` 下的 `ToolRegistry`/`toolRouter` 目前未被运行路径使用. | ||
| - `ToolCallProcessor` 会把工具结果直接拼接进 `conversationMessages`, 无大小控制. | ||
| - `directory_tree` 实现为无限递归. | ||
|
|
||
| ## 方案设计 | ||
|
|
||
| ### 1) `directory_tree` 深度控制 | ||
|
|
||
| - 更新 schema: | ||
| - `src/main/presenter/agentPresenter/acp/agentToolManager.ts` | ||
| - `directory_tree` 增加 `depth?: number`(默认 1, 最大 3). | ||
| - `src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts` | ||
| - `DirectoryTreeArgsSchema` 同步增加 `depth`. | ||
| - 递归限制: | ||
| - 在 `directoryTree` 内实现 `currentDepth` 控制. | ||
| - root 深度为 0, 仅当 `currentDepth < depth` 时继续展开. | ||
|
|
||
| ### 2) 工具输出 offload | ||
|
|
||
| - 触发阈值: 工具输出字符串长度 > 3000. | ||
| - offload 存储: | ||
| - 目录: `~/.deepchat/sessions/<conversationId>/` | ||
| - 文件名: `tool_<toolCallId>.offload` | ||
| - 内容: 原始完整工具输出(文本) | ||
| - stub 内容: | ||
| - 总字符数 | ||
| - 预览片段(1024 字符以内) | ||
| - 完整文件绝对路径 | ||
| - 执行位置: | ||
| - 在 `ToolCallProcessor` 中对工具输出 string 化后做长度判断. | ||
| - 仅替换 `tool_call_response` 和写入 `conversationMessages`. | ||
| - 保持 `tool_call_response_raw` 不变, 避免影响 MCP UI/搜索结果. | ||
|
|
||
| ### 3) 文件读取放行规则 | ||
|
|
||
| - 文件类工具在读取 `~/.deepchat` 时需要额外校验: | ||
| - 只放行 `~/.deepchat/sessions/<conversationId>` 下的文件. | ||
| - 会话不匹配则拒绝访问. | ||
| - 实现位置建议: | ||
| - 在 `AgentFileSystemHandler.validatePath` 增加路径前缀校验(读取时). | ||
| - 路径安全: | ||
| - 参考 `skillSyncPresenter/security.ts` 的路径规范化/安全校验逻辑. | ||
|
|
||
| ### 4) 错误呈现 | ||
|
|
||
| - 保证 error event 携带错误文本: | ||
| - `AgentLoopHandler`/`StreamGenerationHandler`/`AgentPresenter` 的 error 事件 | ||
| 统一包含 `error` 字段. | ||
| - UI 侧: | ||
| - `MessageBlockError.vue` 默认直接展示 raw text. | ||
| - 不依赖 i18n key 时也能显示完整错误内容. | ||
|
|
||
| ## 事件流 | ||
|
|
||
| 1. 工具调用完成 → `ToolCallProcessor` 取到输出. | ||
| 2. 输出超过 3000 字符 → offload 写文件 + 生成 stub. | ||
| 3. stub 进入 `conversationMessages` 和 `tool_call_response`. | ||
| 4. UI 展示 stub; 模型可用 file 工具读取完整路径. | ||
| 5. 出错时, error block 写入消息 + `STREAM_EVENTS.ERROR` 发送错误文本. | ||
|
|
||
| ## 数据/文件结构 | ||
|
|
||
| - `~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload` | ||
| - 原始完整工具输出文本 | ||
|
|
||
| ## 测试策略 | ||
|
|
||
| - 单元测试: | ||
| - `directory_tree` 深度限制(0/1/3/4). | ||
| - tool output 超过 3000 字符时触发 offload, stub 格式正确. | ||
| - 集成/手动: | ||
| - 触发 `directory_tree` 大输出, 确认不再触发 10MB 失败. | ||
| - 触发 provider error, UI 能直接看到 raw text. | ||
|
|
||
| ## 风险与对策 | ||
|
|
||
| - offload 文件增多: | ||
| - 可在后续增加清理策略(按时间或数量). | ||
| - conversationId 缺失场景: | ||
| - 需定义降级行为(例如仅截断不 offload). | ||
| - 若确认不存在此场景可忽略. | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # Agent 工具输出保护与错误呈现 | ||
|
|
||
| > 状态: Draft | ||
| > 日期: 2025-03-08 | ||
zerob13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ## 背景 | ||
|
|
||
| 当前 agent 运行中出现以下问题: | ||
|
|
||
| - Provider 报错会出现在主进程日志, 但 UI 未必能看到错误信息. | ||
| - `directory_tree` 无深度限制, 可能产生巨量输出, 触发 10MB 限制. | ||
| - 工具返回过大时会被直接注入到 LLM 上下文, 容易导致请求失败. | ||
|
|
||
| ## 目标 | ||
|
|
||
| - 让生成失败时的错误信息可见并可追溯. | ||
| - 给 `directory_tree` 增加深度控制, 最大不超过 3. | ||
| - 对过大的工具输出做 offload, 用小的 stub 替代进入上下文. | ||
|
|
||
| ## 非目标 | ||
|
|
||
| - 不改动或替换 `agentPresenter/tool` 下的 `ToolRegistry`/`toolRouter`. | ||
| - 不改变 MCP UI 资源与搜索结果的解析逻辑. | ||
|
|
||
| ## 用户故事 | ||
|
|
||
| 1. 作为用户, 我希望生成失败时能在 UI 直接看到原始错误文本. | ||
| 2. 作为模型, 我希望能指定目录树深度, 避免一次输出过大. | ||
| 3. 作为系统, 我希望工具输出过大时自动 offload, 仍可在需要时读取完整内容. | ||
|
|
||
| ## 验收标准 | ||
|
|
||
| ### 错误呈现 | ||
|
|
||
| - 生成失败会将消息状态置为 `error`, 并写入一个 error block. | ||
| - error block 直接显示 raw error 文本(不要求点击展开). | ||
| - `STREAM_EVENTS.ERROR` 会携带错误文本, 便于 UI 展示或通知. | ||
|
|
||
| ### `directory_tree` 深度控制 | ||
|
|
||
| - `directory_tree` 增加 `depth` 可选参数, 默认值为 1. | ||
| - depth 最大为 3, 超出直接校验失败. | ||
| - 深度计数方式为 root=0. | ||
| - depth=0: 仅返回根目录下条目, 不展开子目录. | ||
| - depth=1: 展开一级子目录, 不包含孙级. | ||
| - 响应格式保持不变: JSON 数组 `{ name, type, children? }`. | ||
|
|
||
| ### 工具输出 offload | ||
|
|
||
| - 当工具输出字符串长度 > 3000 字符时触发 offload. | ||
| - 完整内容写入: | ||
| - `~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload` | ||
| - LLM 只收到 stub, 包含: | ||
| - 总字符数 | ||
| - 预览片段 | ||
| - 完整文件的绝对路径 | ||
| - 模型可以通过文件类工具读取上述路径. | ||
| - 文件类读取工具仅放行当前会话 `conversationId` 对应目录. | ||
| - `tool_call_response_raw` 不被改写, 避免影响 MCP UI/搜索结果处理. | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # 任务拆分 | ||
|
|
||
| 1. 更新 `directory_tree` schema 与描述, 增加 `depth`(默认 1, 最大 3). | ||
| 2. 在 `AgentFileSystemHandler.directoryTree` 实现 depth 控制(root=0)并补充测试. | ||
| 3. 在 `ToolCallProcessor` 增加工具输出长度检测: | ||
| - 超过 3000 字符 → 写入 `~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload` | ||
| - 生成 stub 替换 `tool_call_response` 与上下文内容. | ||
| 4. 在文件工具读路径校验中放行 `~/.deepchat/sessions/<conversationId>`: | ||
| - 仅限当前会话. | ||
| 5. 统一 error event 的 `error` 字段传递, 并确保写入 error block. | ||
| 6. 更新 `MessageBlockError.vue` 默认展示 raw text(不依赖 i18n key). | ||
| 7. 运行 `pnpm run format` 与 `pnpm run lint`. |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.