源码拆解|工具系统与子 agent;如何把 nanobot 改造成你的 AI 产品

这是系列文章的第 4 篇。
前面我拆解了 nanobot 的技术架构,它如何实现 Agent Loop,长期记忆,以及集成 Skills。

现在,我会重点拆它的工具系统(tools)与子 agent(subagent)。
随后,我会讨论如何把它当成可读的最小内核,融入到我们的生产级 AI 产品中。
我想用两个视角把它说清楚。
-
nanobot 已经做对了什么,这些你应该保留
-
你必须补齐什么,不补齐,上生产很容易翻车
最后,给出一份 12 个改造点的落地清单。
PART 01|工具系统,ToolRegistry + JSON Schema 是扩展的底座
nanobot 的工具系统更像一个最小的 function calling runtime。
-
每个工具是一个 Tool 子类
-
用 JSON Schema 定义参数
-
ToolRegistry 统一注册与执行
在 nanobot/agent/tools/base.py 里,工具 schema 的出口是 to_schema()。
def to_schema(self) -> dict[str, Any]:return {"type": "function","function": {"name": self.name,"description": self.description,"parameters": self.parameters,}}
在 nanobot/agent/tools/registry.py 里,关键在两件事。
-
参数校验(validate_params)
-
捕获异常,返回字符串错误,不让主 loop 直接崩
errors = tool.validate_params(params)if errors:return f"Error: Invalid parameters for tool '{name}': " + "; ".join(errors)return await tool.execute(**params)
这套结构很适合嵌进产品。
你可以用同样的 registry 接入自己的工具,无论是数据库查询、工单系统、企业知识库,还是内部 API。
PART 02|提供默认工具,刚刚够用
在 nanobot/agent/loop.py 的 _register_default_tools() 里,nanobot 默认注册了一组通用生产力工具。
-
文件工具,read_file / write_file / edit_file / list_dir
-
命令工具,exec
-
网络工具,web_search / web_fetch
-
消息工具,message
-
子 agent,spawn
-
定时任务,cron(可选,取决于是否启用 cron service)
这组工具满足了一个 agent 内核最基础的闭环,能看、能写、能跑、能查、能通知、能拆分任务。
如果你做产品,应该保留这种克制。工具多不是优势,工具的边界清晰才是优势。
PART 03|工具的安全边界,restrict_to_workspace 只是起点
nanobot 已经做了一些安全默认值。
比如文件工具在 nanobot/agent/tools/filesystem.py 里通过 allowed_dir 做路径限制。
resolved = Path(path).expanduser().resolve()if allowed_dir and not str(resolved).startswith(str(allowed_dir.resolve())):raise PermissionError(f"Path {path} is outside allowed directory {allowed_dir}")
再比如 exec 工具在 nanobot/agent/tools/shell.py 里有 deny patterns(禁止 rm -rf 等危险命令)。
self.deny_patterns = [r"\brm\s+-[rf]{1,2}\b", # rm -r, rm -rf, rm -frr"\bdel\s+/[fq]\b", # del /f, del /qr"\brmdir\s+/s\b", # rmdir /sr"\b(format|mkfs|diskpart)\b", # disk operationsr"\bdd\s+if=", # ddr">\s*/dev/sd", # write to diskr"\b(shutdown|reboot|poweroff)\b", # system powerr":\(\)\s*\{.*\};\s*:", # fork bomb]
这些都很有用。但如果你要上生产,我更愿意把它理解成最低配防线,不是最终答案。
生产里你需要更硬的隔离。
容器 sandbox、受控文件系统、网络 egress 控制、命令 allowlist、审计日志、权限审批(approval),一个都少不了。
PART 04|spawn 子 agent,把复杂任务拆成后台任务
nanobot 的 subagent 机制值得一提。
在 nanobot/agent/tools/spawn.py 里,spawn 工具只是把一个 task 丢给 SubagentManager。
return await self._manager.spawn(task=task,label=label,origin_channel=self._origin_channel,origin_chat_id=self._origin_chat_id,)
真正的关键在 nanobot/agent/subagent.py。
-
subagent 运行在后台 asyncio.create_task
-
subagent 自己也会调用模型 + 工具,但工具集被刻意限制
-
subagent 不能直接发消息给用户,只能把结果注入成 system message 交回主 agent
它的回传设计很巧妙。
msg = InboundMessage(channel="system",sender_id="subagent",chat_id=f"{origin['channel']}:{origin['chat_id']}",content=announce_content,)await self.bus.publish_inbound(msg)
主 agent 收到 system message 后,会在 _process_system_message()里解析 chat_id,把结果路由回原对话。
这套机制的价值在于,它没有引入复杂的分布式队列,也没有强行实现并行推理。它只是把任务放到后台去做,同时还能回传、能追溯。
PART 05|MCP,把外部工具生态变成原生工具
nanobot v0.1.4 里加入 MCP 支持,信号很明确,扩展正在协议化。
在 nanobot/agent/tools/mcp.py 里,nanobot 会把 MCP server 的 tool 包装成一个本地 Tool。
self._name = f"mcp_{server_name}_{tool_def.name}"
然后在 AgentLoop 启动时做懒连接(lazy connect)。
if self._mcp_connected or not self._mcp_servers:returnawait connect_mcp_servers(self._mcp_servers, self.tools, self._mcp_stack)
这对产品化很关键。你不用把所有工具都写进代码库,可以通过协议接入更多服务能力。
但同样,生产里你要非常小心。MCP 工具相当于外部执行面,权限、隔离、审计、供应链安全,一个都不能少。
PART 06|如何做生产级改造
以下为 12 个改造点,可供参考。
1)多租户隔离,workspace、sessions、memory 全部按 tenant 切分
现在默认 workspace 是一个目录,session_key 是 `channel:chat_id`。产品化后要加 tenant 维度,否则会出现跨用户数据污染。
2)权限系统,工具调用必须过 policy
把 ToolRegistry.execute() 前面加一道策略层。哪些用户能用 exec,能不能 web_fetch,能不能读某个目录,都别靠 prompt 去赌。
不要指望 prompt 约束。
3)审批流(approval),高风险工具一律需要确认
企业里最现实的形态很常见,模型提议(propose),人确认(approve),系统执行(execute)。
exec、写文件、对外发消息,都应该可审批。
4)更硬的 sandbox,容器隔离 + 网络 egress 控制
deny_patterns 更像文档级安全,sandbox 才是系统级安全。
5)观测与审计,每次 LLM 调用、每次 tool 调用都要可追溯
建议做 structured logging,request_id、session_id、tool_name、duration、tokens、cost。
没有审计的 agent,在企业里不可用。
6)成本预算,把 max_iterations 变成多维预算
迭代上限只是其一。你还需要 token budget、web 调用次数、exec 次数、外部 API 次数、总 cost cap。
7)幂等与去重,渠道消息必须处理重复投递
真实渠道会重试、会乱序。你需要 message_id 去重与“至少一次投递”语义下的幂等处理。
8)并发与取消,任何长任务都必须可取消
用户关闭窗口、撤回消息、系统升级,都应该能 cancel 正在跑的 tool 和子任务。
9)记忆治理,PII 脱敏 + schema 化 + 冲突处理
MEMORY.md 很灵活,但企业里更需要结构化字段、脱敏规则、版本控制(避免并发覆盖)。
10)技能治理,技能市场必须有签名/白名单/审核
技能是给模型看的说明书,也是潜在的供应链入口。生产里要做治理。
11)回退与降级,provider 不稳定时要自动切换
LiteLLM 把多 provider 接入简化了,但产品里你还需要 fallback 策略,比如超时、限流、错误码处理。
12)测试与回归,给工具系统和关键链路补自动化测试
至少把这些变成可测试项。ContextBuilder、ToolRegistry 参数校验、session 持久化、memory consolidation 的 JSON 解析、exec guard。
PART 07|我建议的落地路径
具体怎么开始呢?我的建议是三步走。
-
先做隔离与权限,workspace、tenant、tool policy、审批流
-
再做观测与预算,trace、审计、成本护栏
-
最后做扩展与生态,MCP、技能市场、业务 connector
你会发现一个规律,先把边界做硬,后面你接的工具多了,系统还是会很稳健。
至此,nanobot 源码拆解系列文章写完了。
我们可以看到,nanobot 并没有替你把所有功能都做完。它的价值在于用极简的代码,把一套 agent 系统的关键骨架写清楚了,loop、tools、memory、skills、subagents、bus。
剩下的事,交给你,把它变成你的产品的一部分。
夜雨聆风
