My Agent Core Design - Tool Registry
Tool Registry
- 采用分层工具系统:
- 底层
ToolAdapter. - 上层
ToolRegistry负责权限、并发、查找、执行和 result normalize.
- 底层
- 所有 tool 来源进入 registry 后都转成统一 tool definition / tool result.
- Tool canonical name:
- registry 内 canonical tool name 必须全局唯一.
- 对模型暴露点号命名, 例如
code.read_file,mcp.<server_id>.<tool_name>,agent.create_sub_agent,internal.load_skill. - duplicate canonical name 在注册/启动时报错.
- 不覆盖、不自动加后缀.
- permission / hook / routing / replay 全部基于 canonical tool name.
- provider raw tool name 只保留在 metadata/debug.
- Provider tool name mangling:
- 如果 provider 不支持点号工具名, adapter 做可逆 name mangling.
- core 内仍使用 canonical name.
- 发给 provider 前可转换成 provider-safe name, 例如
internal__load_skill. - 收到 tool call 后必须映射回 canonical name.
- metadata 记录 raw provider tool name.
- Tool definition 字段:
- name: canonical tool name.
- description.
- input_schema.
- permission: readonly / write.
- tags: set[str].
- metadata.
- Tool tags 示例:
- internal
- readonly
- write
- agent
- mcp
- code
- declarative
- dangerous
- filesystem
- network
- permission / hook / config 可按 canonical name pattern + tags 匹配.
- 每个 run 计算 effective tool set:
- registry 保存所有已注册工具.
- run 启动前根据 agent_type, mode, permissions, config, task policy 选择本轮暴露给模型的 tools.
- AgentDefinition.suggested_tools 只作为工具偏好输入, 不作为权限授权.
- 未传 agent tool allowed_tools 时, 子 agent 使用其 agent type / mode / default policy 的默认工具集合.
- 子 agent 不直接继承父 agent 当前 effective tool set.
- sub agent 可以限制工具范围.
- 未进入 effective tool set 的工具不暴露给模型.
- 内置 default_worker_agent 的默认 effective tool set 使用最小 worker 集合:
agent.task_getagent.task_query_stepsagent.task_claim_stepagent.task_update_step- 常规只读文件工具, 例如
code.read_file.
- default_worker_agent 默认不暴露写文件工具.
- worker run 需要写工具时, 必须通过 worker 配置 / dispatch allowed_tools 显式请求, 并且仍要通过 mode、role、config 和 permission policy 收敛.
- sub/fork/worker agent 不允许直接写长期 memory.
internal.recall_memory是只读 internal tool, 可以把选中的 memory 注入memory_contextnode.internal.recall_memory默认只进入 main agent / Orchestrator 的 effective tool set; sub/fork/worker agent 永远不暴露该工具.- Memory Writer 不是普通 agent run 的 tool, 不进入 main/Orchestrator/sub/fork/worker 的 effective tool set.
- Memory Writer 只供 Memory Extraction Job 使用, 写入边界固定为
${SOONG_AGENT_HOME}/memory. - 静态 system prompt 可以说明所有内置工具类别和使用规则.
- provider 实际收到的 tool schema 只包含当前 run 的 effective tool set.
- effective tool set 是权限硬边界, system prompt 只是行为指导.
- 第一版不实现真实
tool.select/toolSelect工具. - 模型通过 system prompt 自行选择工具; core 只负责暴露可用工具子集和校验调用.
- 如果模型调用未进入 effective tool set 的工具, core 返回
tool_not_available. - sub agent / worker sub agent / fork agent 的黑名单规则写死在 effective tool set 计算中:
- 不暴露
agent.plan_template. - 不暴露 Task DAG 创建/内容修改工具.
- 不暴露 create_sub_agent / fork_agent / list_agent_definitions / list_workers / dispatch_worker.
- 只暴露其允许查询 ready step、claim step、读取或更新自己 claimed step 的 Task 工具.
- 不暴露
- Tool input schema:
- Registry 内部统一使用 JSON Schema dict.
- Python code tool 可以用 Pydantic model, 注册时导出 JSON Schema.
- 用户级
${SOONG_AGENT_HOME}/tools/*.json直接提供 JSON Schema. - MCP tools 转成 JSON Schema.
- handler 执行前由 core 做参数校验.
- Tool JSON Schema 使用 core 支持子集:
- 支持 object, string, number, integer, boolean, array.
- 支持 enum, required, description.
- 支持 nested object 和 nested array.
- 第一版不支持 oneOf / anyOf / allOf.
- 第一版不支持
$ref. - 第一版不支持 if/then/else 等 conditionals.
- 第一版不支持 patternProperties.
- registry 在工具注册/启动时先校验 core schema 子集.
- provider/model 兼容性在 run 启动前、组装
ModelRequest时校验. - 如果当前 provider/model 不支持某个 schema, run 直接 failed, 不等到模型调用时失败.
- Python code tool 注册方式:
- 支持显式
registry.register_tool(...). - 支持 decorator 语法糖.
- 复杂 tool 可以实现
ToolAdapter.
- 支持显式
- Tool handler 返回值:
- 复杂工具推荐返回
ToolResult. - 简单工具可以返回
str / dict / list / int / bool / None. - core 自动包装成统一
ToolResult.
- 复杂工具推荐返回
ToolResult必须关联 tool_call_id:- 每个 tool result 都必须带 tool_call_id.
- tool_call_id 来自 provider tool call id 或 core 生成 id.
- assistant tool_call 和 tool result 通过 tool_call_id 稳定配对, 不按顺序猜测.
ToolResult.content复用通用 ContentBlock, 支持 text/json/artifact_ref 等.
- readonly/write 权限只表示外部副作用或项目目录修改风险;
load_skill/recall_memory这类 internal readonly tool 允许写 core 内部 context node, 不算 write. - Tool call id 由 adapter 映射:
- provider 有 tool_call_id 时映射到 core
tool_call_id. - provider 没有 id 时由 adapter/core 生成.
- provider raw id 保留到 metadata, 方便 debug/replay.
- provider 有 tool_call_id 时映射到 core
- Tool 来源:
- code tools: SDK/CLI 代码注册的内置工具.
- MCP tools: 从用户级 MCP server 动态发现.
- agent tools: list_agent_definitions / create_sub_agent / fork_agent / list_workers / dispatch_worker / inspect_agent 等.
- plan/task tools: 内置
agent.plan_template和 Task DAG tools. - declarative tools: 用户级
${SOONG_AGENT_HOME}/tools/*.json.
- Memory Writer 属于 Memory Extraction Job 的内部受限能力, 不注册成普通 provider-visible tool.
- 内置 Plan tool:
agent.plan_template
- 内置 Task DAG tools 至少包括:
- agent.task_template
- agent.task_create
- agent.task_get
- agent.task_list
- agent.task_update
- agent.task_query_steps
- agent.task_claim_step
- agent.task_update_step
- agent.task_complete
- agent.task_fail
- agent.task_cancel
- agent tools 对父/Orchestrator 表现为普通 tool call / ToolResult, 但 handler 内部可以启动 child/sub/worker agent run.
- agent tools 启动子 agent 时通过 AgentDefinitionRegistry 解析 agent_definition_id.
- 声明式 tools 只支持用户级
${SOONG_AGENT_HOME}/tools/*.json, 项目目录不声明 executable tool. ${SOONG_AGENT_HOME}/tools/*.json执行协议:- stdin JSON 输入.
- stdout JSON 输出, 必须复用
ToolResultschema. - stderr 作为日志/错误信息.
- exit code 非 0 视为 tool error.
- 内置 code tools 至少包括:
code.read_filecode.list_dircode.searchcode.write_filecode.edit_filecode.run_command
code.read_file:- readonly.
- 按行读取.
- 输入包括 path, start_line, max_lines.
- 默认 max_lines=200.
- 最大 max_lines=1000.
- 每行最多返回 4096 bytes; 超过时截断该行并标记 truncated_lines.
- 文件未完整返回时标记 truncated=true, next_start_line.
- 读取超大文本不 artifact 化全文, 因为源文件本身已经存在.
- 二进制文件不直接返回内容, 返回 binary metadata / error result.
- file_not_found 返回 tool error, 不使 run failed.
- 读取 instruction 文件时, core 额外注册 dynamic system block, 详见 Instructions.
code.list_dir:- readonly.
- 输入包括 path, recursive=false, limit.
- 默认非递归.
- 可列项目外目录, 但敏感路径需要 permission callback.
code.search:- readonly.
- 基于
rg实现. - 输入包括 query, path, glob, limit.
- 默认搜索
<project>. - 如果模型传项目外绝对路径, 需要敏感路径检查和 permission callback.
code.write_file:- write.
- 输入包括 path, content, create_dirs=true, overwrite=false.
- 默认不覆盖已存在文件; 文件存在时返回 path_conflict.
- 覆盖必须显式 overwrite=true, 并走 write permission.
code.edit_file:- write.
- 输入支持两种编辑形式:
edits: old/new 精确替换列表.unified_diff: unified diff patch 字符串.
edits和unified_diff必须二选一, 不能同时提供.- 共同输入包括 path, create_if_missing=false.
- 默认不创建文件.
- old text 必须唯一匹配; 找不到返回 text_not_found, 多处匹配返回 ambiguous_edit.
- replace_all=true 时允许替换所有匹配.
- unified_diff 必须只修改当前 path, 不支持 rename/delete/binary patch.
- unified_diff 只支持单文件 diff; 多文件修改必须由模型分别调用多次
code.edit_file. - unified_diff 必须能干净应用到当前文件内容; context 不匹配返回 patch_apply_failed.
- 不提供 dry_run.
code.run_command:- write + dangerous.
- 默认需要 permission.
- 第一版不提供 OS 级 sandbox / container sandbox.
- 第一版安全边界来自 argv list、cwd 限制、allowed roots、env allowlist、timeout、permission/hook、输出截断/artifact 和 secret redaction.
- 输入使用 argv list, 不提供通用 shell string.
- shell string 只能通过用户级 declarative tool 显式定义.
- 默认 cwd 为
<project>. - 可选 cwd, 但必须位于
<project>或 allowed roots 内. - 模型不能传 env; env 只能来自 config/tool definition allowlist.
- 默认 timeout 120000ms, 最大 600000ms.
- stdout/stderr 过大时截断 + artifact.
- 敏感路径策略:
- 适用于
code.read_file,code.list_dir,code.search. - 默认敏感路径包括
~/.ssh,~/.gnupg,~/.aws,~/.config/gcloud,*.pem,*.key,.env,.env.*. - 命中敏感路径时触发 permission callback.
- 没有 permission callback 时 deny.
- 适用于
- 其他 tool 大输出:
- 除
code.read_file外, tool 输出过大时保存 artifact, tool_result 只返回截断摘要和 artifact_ref.
- 除
- Command/declarative tool 执行环境:
- 第一版不提供 OS 级 sandbox / container sandbox.
- 默认 working directory 使用 session.cwd.
- tool definition 可以声明更具体的
working_dir, 但必须经过允许范围校验. - 不允许模型在 tool call 中直接指定 cwd.
- 默认使用最小 env allowlist.
- 默认只传 PATH / HOME / TMPDIR 等必要环境变量和 config 显式允许的 env.
- secret env 不默认透传.
- tool 需要 secret/env 时必须通过配置显式声明.
- Command/declarative tool timeout:
- config 提供
tools.default_timeout_ms. - config 提供
tools.max_timeout_ms. - tool definition 可以声明
timeout_ms. - tool timeout override 不能超过
tools.max_timeout_ms. - 不允许模型在 tool call 中提高 timeout.
- config 提供
- Command output 处理:
- 小 stdout/stderr 可以进入 tool_result content 或 summary.
- 大 stdout/stderr 写 artifact.
- tool_result 只保留 summary + artifact_ref.
- stderr 默认作为日志/debug.
- exit code 非 0 时, stderr 摘要进入 error tool_result.
- Command tool 支持两种模式:
command_type=exec: 使用 argv list, 默认优先.command_type=shell: 允许复杂 shell 命令, 但必须标记 dangerous 或 write, 并经过 permission/hook.
- Declarative command tool JSON schema:
- name.
- description.
- input_schema.
- permission.
- tags.
- command_type.
- command.
- args.
- working_dir.
- env_allowlist.
- timeout_ms.
- stdout_limit_bytes.
- stderr_limit_bytes.
- 未知字段默认报错.
- Declarative command args 支持受限模板:
- 只允许 `` 字段替换.
- 替换值来自已通过 tool input schema 校验的参数.
exec模式下每个 argv 独立替换, 不拼接 shell 字符串.shell模式模板更严格, 且必须标记 dangerous 或 write.
- Tool execution 写入时机:
- 执行前写 tool_started event.
- tool handler 可以异步执行, 期间通过 event stream 暴露进度.
- 当前 agent loop 必须等待本轮所有 tool calls 完成后, 才能继续下一次模型请求或结束本轮.
- tool 完成后, 在同一事务里写最终 tool result node 和 tool_completed/tool_failed event.
- context tree 只写最终 tool result node, 不写 pending tool result node.
- 第一版不支持 tool 调用脱离当前 loop 后台继续运行.
- Tool abort:
- 用户 abort tool 时, 写 tool_result node.
is_error: true.- metadata 标记 aborted, partial, abort_reason.
- 大 partial stdout/stderr artifact 化.
- 写 aborted_tools / tool_failed event.
- run end_reason 为 aborted_tools.
- Tool 权限第一版只分:
- readonly
- write
- 文件系统写入边界:
- write tool 默认只能写
<project>/ session.cwd 下的路径. - 可通过配置增加
allowed_write_roots. /tmp/ TMPDIR 是否允许写入作为独立配置项.- 超出允许根目录的写入默认触发 permission callback; 没有 callback 时 deny.
- 所有写入目标必须先 normalize 并 resolve symlink.
- resolve 后再判断是否位于 allowed roots 下.
- 禁止通过软链逃逸写入边界.
- write tool 默认只能写
- 网络权限:
- 可能联网的工具使用
networktag 标记. - MCP server 可以整体标记 network.
- config / permission policy 可以限制 network 工具是否启用.
- config / permission policy 可以要求 network 工具每次确认.
- config / permission policy 可以配置允许的 host/domain.
- 可能联网的工具使用
- dangerous tag:
- 高风险工具或 shell command 使用
dangeroustag. - dangerous 工具必须显式用户确认.
- dangerous 工具允许 allow_for_session, 但 scope 必须足够窄.
- hook / permission policy 可以按 dangerous tag 拦截.
- 高风险工具或 shell command 使用
- 并发策略:
- 按模型输出顺序分组调度 tool calls.
- agent tools 是独立并发类别, 包括 create_sub_agent / fork_agent / dispatch_worker.
- 同一轮中相邻或可安全并行的多个 agent tools 可以并行启动 child/sub/worker agent run.
- agent tools 并发仍受 max_children_per_run、max_concurrent_children_per_session、worker pool 空闲状态和权限限制.
- 当前 agent loop 必须等待并行 agent tools 全部完成并写入最终 tool result 后, 才能继续下一次模型请求或结束本轮.
- 连续 readonly tool calls 批量并行.
- write tool call 是调度边界.
- write tool call 按模型输出顺序串行执行.
- write 前后的 readonly batch 不跨边界并行.
- 同一轮 tool call 失败后的策略:
- 并行 agent tools 默认使用 wait_all failure policy.
- 某个 agent tool 失败时, 不自动取消同组其他并行 agent tools.
- core 等待同组 agent tools 全部完成后, 分别写入成功或失败的最终 tool result.
- 父/Orchestrator 在下一次模型请求中同时看到成功和失败结果, 由模型决定是否重试、改派或取消任务.
- readonly 失败继续执行后续安全 tool calls.
- write 失败停止后续 tool calls.
- 失败结果写入
tool_result,is_error: true, 并返回模型下一轮处理.
- 模型调用未启用工具时:
- 如果 tool name 不在本轮 effective tool set, 写
tool_result is_error=true. - error type 为
tool_not_available. - 同时写 tool_failed / tool_denied event.
- 返回模型下一轮自我修正.
- 若反复发生, 由 max_turns / failed 等高层机制结束.
- 如果 tool name 不在本轮 effective tool set, 写
- 权限确认:
- readonly 自动允许.
- write / edit / run_command / declarative write / dangerous / network 工具默认询问用户.
- 敏感 read/list/search 默认询问用户.
- 支持 allow_once / allow_for_session / deny.
- 授权规则不持久化, 只在当前 session 内存保存.
- allow_for_session 按 canonical tool name + normalized target scope 记录.
- target scope 例如某个已 resolve 的目录、host/domain 或其他工具定义的资源范围.
- 不按 tool name 授权任意目标.
- deny 只拒绝本次 tool call, 不写 session deny 记忆.
code.run_command的 allow_for_session scope 至少包含 executable + cwd.- Permission prompt 由 SDK core 通过 callback 请求确认, 调用方负责展示 UI/CLI.
- main / Orchestrator / sub / worker / fork agent 的写工具权限确认都走同一个 SDK permission callback.
- 子 agent 触发的 permission request 必须包含 agent_id, run_id, parent_agent_id, parent_run_id 和 agent role, 方便调用方/UI 展示请求来源.
- 如果调用方没有提供 permission callback, 需要询问的工具默认 deny.
- Permission callback async-first:
permission_callback(request) -> Awaitable[PermissionDecision].- CLI 第一版用最小 stdin 同步输入封装 async callback, 只提供 allow_once / allow_for_session / deny 三个选择.
- Server/Web 可以挂起等待前端确认.
- Permission request 包含风险摘要和展示所需字段:
- canonical tool name.
- permission.
- tags.
- args 摘要.
- target scope.
- cwd.
- env 摘要.
- network host/domain.
- dangerous 标记.
- PreToolUse hook 结果.
- suggested decision.
- Permission decision 固定枚举:
- allow_once.
- allow_for_session.
- deny.
- Permission decision 可带 reason 和 metadata.
- Permission callback 不允许 patch tool args, 不允许直接执行工具.
- Permission callback 超时或异常时默认 deny.
- callback 失败时写 permission_failed / tool_denied event.
- callback 失败时生成
tool_result is_error=true, 返回模型下一轮处理. - permission deny 语义和 PreToolUse hook deny 一样: 写
tool_result is_error=true, 停止后续 tool calls, 返回模型.
- Tool 执行顺序固定为 PreToolUse hook -> Permission callback -> tool execution.
- Tool 执行前先跑 PreToolUse hook, hook allow 后再做 permission 判断.
- PreToolUse hook deny 后的策略:
- 不再调用 Permission callback.
- readonly deny 继续执行后续安全 tool calls.
- write deny 停止后续 tool calls.
- 被 deny 的 tool 写入
tool_result,is_error: true, 同时写 denied event.
- Tool 执行失败包装成 tool_result 交给模型处理.
This post is licensed under CC BY 4.0 by the author.