从零构建 Coding Agent
English

14. 扩展系统

当 Agent 开始被不同团队使用,你会不断收到定制需求:加一个内部搜索工具,拦截危险命令,改系统提示词,给状态栏显示 token 成本,保存任务完成后的报告。把这些需求都合并进核心代码,会让内核膨胀。扩展系统的目标是把可定制点产品化。

扩展不是脚本目录

扩展系统至少要提供三类能力:

  • 生命周期钩子:监听 session、turn、model request、tool call、shutdown。
  • 注册能力:注册工具、命令、快捷键、状态展示、渲染器。
  • 运行上下文:访问 cwd、会话、事件、UI、配置和安全 API。

扩展应该通过受控 API 影响 Agent,而不是直接改内部对象。否则一个扩展就能破坏日志、事件和权限模型。

生命周期钩子

一组实用钩子可能包括:

session_start
before_agent_start
turn_start
before_model_request
after_model_response
tool_call
tool_result
session_before_compact
session_shutdown

有些钩子只是通知,有些钩子可以阻断或改写。必须明确每个钩子的语义。例如 tool_call 可以返回 deny,阻止工具执行;turn_start 只用于观察,不允许改写消息。权限相关钩子默认 fail closed:扩展报错时宁可阻止危险操作,也不要继续执行。

注册工具

扩展注册工具时,需要提供和内置工具同等质量的定义:名称、描述、schema、执行函数、输出截断、错误语义、可选 UI 渲染。自定义工具如果会写文件,必须参与同一文件写队列;否则它会和内置 edit/write 互相覆盖。

工具描述也要进入系统提示词的“可用工具”部分。否则模型不知道何时使用扩展工具。描述应明确工具名,不要写“使用这个工具”,因为模型在平铺提示词里可能不知道“这个”指谁。

扩展与安全

扩展本身通常是任意代码。安装扩展等于信任它能访问进程权限范围内的资源。产品必须把这个事实告诉用户,并区分项目扩展、用户全局扩展和内置扩展。未信任项目不应该自动执行项目扩展。

扩展 API 还要防止绕过权限门。比如扩展注册了一个 dangerous_shell 工具,它仍应经过 tool call 权限检查。否则用户禁用了内置 bash,却被扩展工具绕过。

自托管检验

判断扩展 API 是否足够好的方法是:用它实现一部分产品自身能力。例如:

  • 状态栏通过扩展 API 注册。
  • 权限确认通过 tool_call 钩子实现。
  • 自定义压缩通过 session_before_compact 实现。
  • 自动 checkpoint 通过 turn_startsession_shutdown 实现。

如果这些需求都必须改核心代码,说明扩展面还不够完整。

练习

实现最小扩展系统。

验收标准:

  • 扩展可以注册一个只读工具。
  • 扩展可以在 tool call 前拒绝 bash 命令。
  • 扩展可以在系统提示词中追加一段工具指南。
  • 扩展错误会形成明确事件,不会静默吞掉。
  • 未信任项目下,项目扩展不会自动运行。