上周五在折腾一个自动化代码审查的 Agent,写到一半发现自己在重复造轮子------任务调度、工具注册、上下文管理,每个都自己糊一遍,代码丑得我自己都不想看。正好那几天 OpenClaw 冲到 GitHub 20 万 Star,热榜上全是它的入门教程,就想着与其瞎搞,不如把 OpenClaw 的 Nanobot 架构拆一遍,看看人家怎么设计的。
花了两天啃源码。说实话,OpenClaw 的 Nanobot 架构是我 2026 年见过的最干净的 Agent 设计模式------轻量、可组合、Skills 热插拔。这篇是我的学习笔记,把核心架构、关键代码、踩的坑全记下来,顺便用 Claude 4.6 跑一个完整的 Nanobot 示例。
先说结论
| 维度 | 说明 |
|---|---|
| Nanobot 是什么 | OpenClaw 的最小 Agent 执行单元,一个 Nanobot = 一个 System Prompt + 一组 Skills + 一个模型绑定 |
| 核心设计思想 | 单一职责 + 组合模式,多个 Nanobot 可以编排成复杂 Agent |
| Skills 是什么 | 类似 Function Calling 的工具注册机制,支持热插拔和版本管理 |
| 适合谁学 | 想自己搭 Agent 框架的开发者,或者想深入理解 OpenClaw 而不只是当用户的人 |
| 学习成本 | 有 Python 基础 + 了解 Function Calling 概念,2-3 小时能跑通 |
Nanobot 架构全景图
先上一张我梳理的架构图,后面所有内容围绕这张图展开:
三层结构:编排器负责调度,Nanobot 负责执行,Skills 负责干活。模型调用是 Nanobot 的内部行为,外部只关心输入输出。
环境准备
bash
# Python 3.11+
pip install openclaw>=0.9.0
pip install openai # OpenClaw 底层用 OpenAI 兼容协议调模型
OpenClaw 的模型调用层走 OpenAI 兼容协议,任何兼容 OpenAI API 格式的服务都能接。这一点后面会用到。
第一步:理解 Nanobot 的最小结构
一个 Nanobot 的定义文件长这样(YAML 格式):
yaml
# nanobot.yaml
name: code_reviewer
version: "1.0"
description: "审查 Python 代码质量并给出改进建议"
system_prompt: |
你是一个资深 Python 代码审查员。
审查重点:代码可读性、潜在 bug、性能问题。
输出格式:按严重程度排序,每条包含行号、问题描述、修复建议。
model:
provider: openai_compatible
model_name: claude-sonnet-4-20250514
temperature: 0.3
skills:
- read_file
- ast_parse
- search_codebase
就这些。没有复杂的继承关系,没有抽象工厂,纯声明式配置。我第一次看到的时候想:就这?
但仔细想想这个设计确实聪明------把 Agent 的行为完全用配置描述,运行时由框架装配。换模型改一行 model_name,加工具在 skills 列表追加一个。
第二步:用 Python 手撸一个 Nanobot
光看 YAML 没意思,从 Python 代码层面理解 Nanobot 的运行机制。
OpenClaw 的核心类是 Nanobot,我把源码里的关键逻辑简化了一版:
python
from openai import OpenAI
import json
class Nanobot:
"""OpenClaw Nanobot 核心逻辑简化版"""
def __init__(self, name: str, system_prompt: str, model: str, skills: list = None):
self.name = name
self.system_prompt = system_prompt
self.model = model
self.skills = skills or []
self.skill_registry = {}
# 初始化 LLM 客户端
self.client = OpenAI(
api_key="your-key",
base_url="https://api.ofox.ai/v1" # 聚合接口,一个 Key 调所有模型
)
# 注册 skills
for skill in self.skills:
self._register_skill(skill)
def _register_skill(self, skill_func):
"""把 Python 函数注册为 Skill(本质是 Function Calling 的 tool)"""
tool_def = {
"type": "function",
"function": {
"name": skill_func.__name__,
"description": skill_func.__doc__ or "",
"parameters": getattr(skill_func, '_parameters', {"type": "object", "properties": {}})
}
}
self.skill_registry[skill_func.__name__] = {
"definition": tool_def,
"handler": skill_func
}
def run(self, user_input: str, max_rounds: int = 5) -> str:
"""执行 Nanobot 的核心循环:LLM 推理 → 调用 Skill → 再推理"""
messages = [
{"role": "system", "content": self.system_prompt},
{"role": "user", "content": user_input}
]
tools = [s["definition"] for s in self.skill_registry.values()]
for round_num in range(max_rounds):
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=tools if tools else None,
tool_choice="auto" if tools else None
)
msg = response.choices[0].message
messages.append(msg)
# 如果模型没有调用工具,说明推理结束
if not msg.tool_calls:
return msg.content
# 执行所有 tool calls
for tool_call in msg.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
handler = self.skill_registry[func_name]["handler"]
result = handler(**func_args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result)
})
print(f" [Round {round_num+1}] Skill 调用: {func_name}({func_args}) → {str(result)[:100]}...")
return messages[-1].content if messages else "达到最大轮次"
这段代码就是 Nanobot 的核心------一个带 tool 循环的 Chat Completion 调用。看起来简单,但 OpenClaw 在这个基础上加了几个关键设计:
- Skill 版本管理:同一个 Skill 可以有多个版本,Nanobot 可以锁定特定版本
- 上下文窗口管理:自动 truncate 超长对话,保留 system prompt 和最近 N 轮
- 错误重试:Skill 执行失败会把错误信息喂回模型,让它自己修正
第三步:定义 Skills 并跑起来
Skills 就是普通的 Python 函数,加点元信息装饰就行:
python
import os
import ast
def skill(parameters: dict):
"""装饰器:给函数附加 JSON Schema 参数定义"""
def decorator(func):
func._parameters = parameters
return func
return decorator
@skill(parameters={
"type": "object",
"properties": {
"file_path": {"type": "string", "description": "要读取的文件路径"}
},
"required": ["file_path"]
})
def read_file(file_path: str) -> str:
"""读取指定路径的文件内容"""
if not os.path.exists(file_path):
return f"错误:文件 {file_path} 不存在"
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# 限制返回长度,防止撑爆上下文
if len(content) > 10000:
return content[:10000] + f"\n...[文件过长,已截断,总长度 {len(content)} 字符]"
return content
@skill(parameters={
"type": "object",
"properties": {
"code": {"type": "string", "description": "要解析的 Python 代码"}
},
"required": ["code"]
})
def ast_parse(code: str) -> str:
"""解析 Python 代码的 AST 结构,返回函数和类的列表"""
try:
tree = ast.parse(code)
result = []
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
args = [a.arg for a in node.args.args]
result.append(f"函数: {node.name}({', '.join(args)}) @ 行{node.lineno}")
elif isinstance(node, ast.ClassDef):
result.append(f"类: {node.name} @ 行{node.lineno}")
return "\n".join(result) if result else "未发现函数或类定义"
except SyntaxError as e:
return f"语法错误:{e}"
# 组装 Nanobot 并运行
reviewer = Nanobot(
name="code_reviewer",
system_prompt="""你是一个资深 Python 代码审查员。
审查流程:
1. 先用 read_file 读取目标文件
2. 用 ast_parse 分析代码结构
3. 基于分析结果给出审查意见
输出格式:按严重程度排序,每条包含行号、问题描述、修复建议。""",
model="claude-sonnet-4-20250514",
skills=[read_file, ast_parse]
)
# 跑一下
result = reviewer.run("请审查 ./src/utils.py 这个文件的代码质量")
print(result)
跑起来的输出大概是这样:
css
[Round 1] Skill 调用: read_file({"file_path": "./src/utils.py"}) → import os\nimport sys\n\ndef ...
[Round 1] Skill 调用: ast_parse({"code": "import os\nimport sys..."}) → 函数: load_config(path) @ 行5...
[Round 2] 推理完成,返回审查结果
模型先读文件,再解析 AST,最后综合两个 Skill 的结果给出审查意见。这就是 Nanobot 的 ReAct 循环------推理、行动、观察、再推理。
第四步:多 Nanobot 编排
真正有意思的是把多个 Nanobot 组合起来。OpenClaw 的编排器(Orchestrator)支持串行、并行、条件分支三种模式:
python
from dataclasses import dataclass
from typing import Callable
from concurrent.futures import ThreadPoolExecutor
@dataclass
class NanobotTask:
nanobot: Nanobot
input_transform: Callable = None # 从上一步结果提取输入
class Orchestrator:
"""简化版编排器"""
def run_sequential(self, tasks: list[NanobotTask], initial_input: str) -> list[str]:
"""串行执行:上一个的输出是下一个的输入"""
results = []
current_input = initial_input
for task in tasks:
if task.input_transform:
current_input = task.input_transform(current_input, results)
result = task.nanobot.run(current_input)
results.append(result)
current_input = result
return results
def run_parallel(self, tasks: list[NanobotTask], shared_input: str) -> list[str]:
"""并行执行:所有 Nanobot 拿到同一个输入"""
with ThreadPoolExecutor(max_workers=len(tasks)) as executor:
futures = [executor.submit(t.nanobot.run, shared_input) for t in tasks]
return [f.result() for f in futures]
# 实际使用:代码审查 → 生成修复方案 → 编写测试
orchestrator = Orchestrator()
review_bot = Nanobot(name="reviewer", system_prompt="审查代码...", model="claude-sonnet-4-20250514", skills=[read_file, ast_parse])
fix_bot = Nanobot(name="fixer", system_prompt="根据审查意见生成修复代码...", model="claude-sonnet-4-20250514", skills=[read_file])
test_bot = Nanobot(name="tester", system_prompt="为修复后的代码编写 pytest 测试...", model="claude-sonnet-4-20250514", skills=[])
results = orchestrator.run_sequential(
tasks=[
NanobotTask(nanobot=review_bot),
NanobotTask(nanobot=fix_bot),
NanobotTask(nanobot=test_bot),
],
initial_input="审查并修复 ./src/utils.py"
)
三个 Nanobot 串起来就是一个完整的代码审查 + 修复 + 测试 pipeline。每个 Nanobot 只管自己那一步,职责清晰。
踩坑记录
两天踩了不少坑,记几个印象深的。
坑 1:Skill 返回值太长直接撑爆上下文
一开始没做 read_file 的截断,读了一个 3 万行的文件,直接超了 Claude 4.6 的上下文窗口。报错信息还挺模糊的,排查了半小时才定位到。解决方案就是上面代码里的截断逻辑。另外 OpenClaw 源码里有个 context_window_manager 模块专门处理这个,建议直接用它的。
坑 2:并行执行时的 Rate Limit
三个 Nanobot 并行跑,瞬间打了三个请求,直接触发 429。这个不是 OpenClaw 的问题,是 API 那边的限流。后来换了 ofox.ai 的聚合接口,底层做了多供应商负载均衡,并发能力好很多,三个并行请求没再被限流过。ofox.ai 是一个 AI 模型聚合平台,支持 GPT-5、Claude 4.6、GLM-5 等 50+ 模型,低延迟直连,改个 base_url 就能用。
坑 3:tool_calls 的 arguments 偶尔不是合法 JSON
某些模型(尤其是小模型)返回的 function.arguments 偶尔会带多余的逗号或者缺引号。OpenClaw 源码里用了一个 repair_json 的工具函数做容错,这个思路值得学。自己写的话可以用 json-repair 这个库。
坑 4:system_prompt 里不写清楚调用顺序,模型会乱来
一开始 system_prompt 只写了「你可以用 read_file 和 ast_parse」,结果模型有时候不读文件就直接开始分析,纯靠幻觉编。后来改成明确写「第一步先用 read_file,第二步用 ast_parse,第三步再给出意见」,效果好了很多。Agent 的 prompt 必须写执行流程,光列工具没用。
小结
Nanobot 架构本质不复杂,核心就三个东西:
- Nanobot = System Prompt + Model + Skills,声明式定义,运行时装配
- Skills = Function Calling 的封装,加了版本管理和错误重试
- Orchestrator = 多 Nanobot 编排,支持串行/并行/条件分支
看完源码之后觉得自己之前造的轮子也不是完全白费------思路是对的,只是没有 OpenClaw 这么工程化。如果你也在搞 Agent 开发,建议花半天读一遍 OpenClaw 的 nanobot/core.py 和 skills/registry.py 这两个文件,比看十篇教程有用。
下一篇打算写 OpenClaw 的 Skills 生态怎么玩,怎么自己发布一个 Skill 包到社区。有兴趣的可以先 star 一下,更新了不迷路。