docs/technical/code-execution.md
Last updated: 2026-07
本文档描述 Chat 模式下代码执行、Agent Mode、工具暂停审批和 HTML 产物预览的技术设计。产品说明见 docs/product/code-execution.md。
Chat 代码执行复用 Main 进程的本地沙箱基础设施,但在 Renderer 侧使用独立的高层工具集和 Agent Mode 编排。
Renderer
orchestrateGeneration()
├─ shouldSuggestAgentMode() # 仅首轮 auto,快速分类模型
│ └─ 命中 → 注入 agent-mode-suggestion 卡片并停止生成
└─ prepareAgentGenerationHarness()
├─ computeEffectiveAgentMode(agentModeValue, supported)
├─ createSandboxProvider() # desktop only
├─ buildToolsForSession()
│ ├─ web_search / parse_link # independent of agent mode
│ ├─ code_execution / read_file / create_download
│ ├─ filesystem tools: list/search/write/edit
│ ├─ load_skill / install_skill / user_exec
│ └─ MCP / knowledge base tools
└─ chatStream(..., { tools })
Main
src/main/sandbox/
├─ manager.ts # sandbox session, exec, file IO
├─ ipc-handlers.ts # renderer bridge
├─ node-runtime.ts # bundled/resolved Node runtime
├─ preview-server.ts # local HTML preview
└─ truncate.ts # output trimming
Chat 模式向模型注入的是 code_execution / read_file / create_download 等高层工具,工作目录为自动创建的临时沙箱目录,上传文件在首次工具调用时复制进沙箱。编排层先把 Auto 解析为有效的 On/Off,builder 只根据有效模式构建工具。
Chat 模式不再向模型注入底层 sandbox_* 工具,旧的 toolsets/sandbox.ts 已移除;历史消息仍由通用 tool-call UI 按工具名渲染。
文件:src/renderer/packages/model-calls/toolsets/code-execution.ts
| 工具 | 输入 | 输出 | 说明 |
|---|---|---|---|
code_execution | code, language, timeout? | { stdout, stderr, exitCode } | 执行短 Node.js、PowerShell(Windows)或 Bash 脚本。language 为 `node |
read_file | file_path, offset?, limit? | { content, startLine, endLine, totalLines, hint? } | 读取沙箱文件或经授权读取用户真实文件,按行数和输出字节数双重限制分页 |
create_download | file_path, display_name? | { downloadable, file_path, display_name } | 将沙箱文件标记为消息产物 |
当前实现刻意移除了 Python 工具链:
PATH 或 nvm 初始化状态。这个选择降低了安装包体积和签名风险,也让 Chat 代码执行聚焦于简单文件处理。
沙箱在第一次工具调用时创建。初始化过程会把对话中的上传文件复制到会话沙箱:
{filename}_parsed.txt。工具结果必须保持小体积。大输出写入沙箱文件,再通过 read_file 分页、create_download 下载或 HTML 预览展示。
read_file 和单目录 list_files 通过 SandboxProvider 调用受限 Node helper,不拼接 Bash 命令。Node helper 在 macOS/Linux 继续继承 SRT 隔离,在 Windows 使用原生工作目录。read_file 的 helper 输出单个 JSON 对象,并按 JSON 编码后的字节数限制内容,保证不会被通用 execCode 的 50KB stdout 截断逻辑破坏;内容达到上限时通过 endLine 继续分页。create_download 直接调用 persistArtifact。相对路径基于会话工作目录解析,持久化前进行真实路径和允许根目录校验,不再通过 Shell 执行 test / realpath。type AgentModeValue = 'auto' | 'on' | 'off'
interface AgentModeEntry {
value: AgentModeValue
locked: boolean
lockReason: 'file_upload' | 'load_skill' | 'message_sent' | null
}
agent scope 是代码执行、文件工具、MCP、知识库和 Skills 的统一门控。Web Search 使用独立的 web-browsing scope,不受 Agent Mode 影响。
src/renderer/stores/session/agent-harness.ts 中的 computeEffectiveAgentMode(agentModeValue, agentModeSupported):
export function computeEffectiveAgentMode(agentModeValue, agentModeSupported) {
if (!agentModeSupported || agentModeValue === 'off') return 'off'
return agentModeValue === 'on' ? 'on' : 'off'
}
规则:
off:effectiveAgentMode = 'off'。on:effectiveAgentMode = 'on',注入完整 agent 工具集。auto:effectiveAgentMode = 'off'。Auto 不再根据文件类型或数量自动升级;是否进入 Agent Mode 由首轮的建议分类器决定(见下)。文件触发已被移除,改为在会话首轮用一个独立的快速分类模型判断意图。逻辑位于 orchestration.ts 的 shouldSuggestAgentMode() 与 agent-mode-suggestion.ts。
触发条件(全部满足才运行分类器):
operationType === 'send_message' 且非 appendToMessage、非 skipAgentModeSuggestion。agent scope(agentModeSupported),且 agentModeValue === 'auto'。isFirstUserTurn())。流程:
threadNamingModel(廉价快速),无法创建时回退到对话模型。AGENT_MODE_SUGGESTION_PROMPT 让模型判断该消息是否需要代码执行、文件操作、本地工具、知识库、加载 Skill 等 Agent 能力,返回 {"suggest":boolean,"reason":string}。agent-mode-suggestion content part(携带 reason,与用户消息同语言),finishReason: 'agent-mode-suggested',停止本轮生成。用户点击卡片上的“使用 Agent Mode”才将会话切为 On 并继续;点击“继续普通回答”则按普通文本对话处理。effectiveAgentMode = 'off' 走普通文本生成,不注入 Agent 工具。| 触发 | 场景 | 效果 |
|---|---|---|
message_sent | 用户在 On 模式发送消息 | 锁定为 On |
load_skill | 模型加载 Skill | 锁定为 On |
AgentModeLockReason 类型仍保留 'file_upload' 取值以兼容历史会话数据,但当前代码不再因文件上传触发该锁定。
由于 auto 的 effectiveAgentMode 为 'off',普通 Auto 对话不注入任何 Agent 工具;用户接受建议后会话变为 on,此时注入完整工具集。buildToolsForSession() 的输入只允许有效的 on / off,不包含 Auto,也没有 initialActiveTools / prepareStep 渐进门控。
文件:src/renderer/stores/session/tools-builder.ts
构建顺序:
web-browsing scope 控制。read-file 时,用于读取内联附件/链接。on 且模型支持 agent scope 时注入。codeExecution provider 时注入 code_execution/read_file/create_download。load_skill、chatbox_cli、On 模式可直接使用的 user_exec,以及 code execution 可用时的 install_skill。Chat Agent 不再 fallback 注入底层 sandbox_* 工具。
工具调用可能在三类情况下进入 paused 状态:
| pauseReason | 触发 | 继续行为 |
|---|---|---|
tool_call_limit | 多轮工具调用达到上限 | 用户确认后执行原本暂停的工具调用 |
user_exec_approval | user_exec 命令未命中白名单、未通过 AI 安全评估且未开启 Full Access | 批准后执行命令,拒绝后写入拒绝结果 |
file_mutation_approval | 写入或编辑绑定目录之外的用户真实文件系统,且未开启 Full Access | 批准后执行文件变更,拒绝后写入拒绝结果 |
设计要点:
MessageToolCallPart.state = 'paused',随 session 持久化。continuePausedToolCall(),执行原工具并调用 orchestrateGeneration(..., appendToMessage: true),结果追加到同一条 assistant 消息。user_exec 先检查安全只读白名单,再对本地策略允许评估的命令执行 AI 结构化安全判断;未通过或评估失败时进入持久化暂停。Full Access 跳过逐次审批,但仍记录审批来源。orchestration.ts 通过 withToolCallLimitPause() 在达到上限时暂停,而不是返回一个普通 tool result 让模型继续循环。暂停发生在即将执行下一次工具前;用户点击继续后会执行该工具调用,并在同一条消息内继续生成。
当前常量位于 MAX_TOOL_CALLS_BEFORE_CONFIRMATION(src/shared/utils/tool-call-limit-pause.ts)。发布前应确认它符合产品目标值,避免测试用小阈值影响真实用户。
用户可以关闭这一确认暂停(针对长期挂机任务):
pauseOnToolCallLimit,会话级(SessionSettingsSchema,三态:undefined 跟随全局)与全局级(SettingsSchema,默认 true)各有一份,shouldPauseOnToolCallLimit() 按「会话覆盖全局、默认暂停」解析。PausedToolCallDetails)可选择仅当前会话或所有会话生效,写入设置后通过 disableToolCallLimitPauseAndContinue() 立即恢复当前暂停的批次。SessionSettings.tsx)中可重新打开;全局开关在设置 → 聊天设置(routes/settings/chat.tsx)中。HTML 下载产物在桌面端可复用本地预览组件展示:
http://127.0.0.1:{port}/sandbox/{relativePath}。relativePath 是会话沙箱内相对路径,不是系统绝对路径。由于访问范围限定在当前沙箱产物上下文内,桌面本地预览不额外使用随机 token。
工具结果有多层控制:
| 层次 | 目的 |
|---|---|
| Main 进程流式缓冲上限 | 防止 stdout/stderr 无限增长导致内存问题 |
| 命令输出 tail 截断 | 保留近期有效日志 |
| Renderer 工具结果截断 | 控制写入消息数据的大小 |
| UI 错误截断 | 避免错误 JSON 或长 stderr 撑爆工具卡片 |
原则:会话消息中只保存摘要、路径和短预览;大内容写入文件,按需读取。
user_exec 和 code_execution 都会把 sessionId、toolCallId 传到 Main 进程。Main 进程为每次执行生成 operationId,记录命令或代码哈希、有限预览、运行目录、超时、耗时、退出码和 stdout/stderr 字节数。成功执行不记录输出正文;失败和超时只保留脱敏后的有限预览。这样可以从消息中的 tool call 精确关联到本地执行日志,同时控制日志体积。
LocalSandboxProvider,支持 Agent Mode。重点测试:
agent-harness.test.ts:有效模式计算、harness 准备。agent-mode-suggestion.test.ts:首轮建议分类器的 prompt 构造与决策解析。tools-builder.test.ts:不同 agent mode、code execution provider、web search、sandbox_* 不可见。agent-mode-orchestration.test.ts:暂停/继续、流持久化和 append 行为。code-execution.test.ts:懒初始化、文件注入、执行和错误处理。file-read.test.ts:分页读取、JSON 字节预算和大输出结构完整性。manager.windows.test.ts:Windows 原生 Node/PowerShell、无 Bash 文件读写/目录列表、ripgrep 搜索与相对路径下载持久化。user-exec-whitelist.test.ts:自动审批白名单。| 决策 | 选择 | 理由 |
|---|---|---|
| Chat 工具形态 | 高层 code_execution 替代 sandbox_* | 降低模型误用底层 shell/file 工具的概率 |
| 运行时 | Node.js、PowerShell(Windows)、可选 Bash,不内置 Python 科学栈 | 控制安装包体积和签名风险,核心文件工具不依赖 Shell |
| Auto 进入条件 | 首轮快速分类模型建议 + 用户手动确认,移除文件类型自动升级 | 避免误判,把控制权交回用户,减少纯文本对话的上下文污染 |
| 暂停机制 | 持久化 paused tool call | 重启可恢复,继续后能 append 到原消息 |
| HTML 预览 | 本地 preview server + 沙箱相对路径 | 支持相对资源文件,不依赖远端 artifact 域名 |
| 工具结果 | 小结果入消息,大结果入文件 | 控制 session 数据体积和上下文缓存压力 |
docs/product/code-execution.md./tools-and-integrations.md./agent-skills.md