
1. 从 OpenClaw 看 AI Native Agent 的架构分水岭OpenClaw 这类项目最近在开发者圈子里讨论度很高它本质上是一个能深度融入聊天软件、自主完成复杂任务的 AI Agent。很多人第一次看到它的能力清单会以为背后藏着一个庞大的工程体系但真正拆开看核心其实只有三件事Agent 调度、Skills 扩展、统一 API 通道。这三件事决定了你的智能体是能跑还是能长。我先把结论放在前面OpenClaw 类架构的关键不在于你写了多少工具函数而在于你有没有把能力扩展这件事从代码层下放到运行时。传统做法是开发者写死一个工具AI 才能用AI Native 的做法是给 AI 一个执行环境和一份技能描述它自己决定怎么调、调什么、什么时候调。这个差别听起来抽象落到代码上就是 config.toml 里一个 skills 目录的配置差异。这篇文章面向三类人一是想理解 OpenClaw 架构设计思路的开发者二是正在用 Agent 框架但感觉越写越重的工程师三是想用统一 API 通道支撑多工具协作、又不想自己维护一堆 Key 的实践者。我会给出可复制的 config.toml 骨架、TaoToken 接入配置并演示一次完整的 Skills 注册与调用验证。你跟着做能跑出一个最小可用的 AI Native Agent 骨架。先说清楚一个概念区分这是后面所有配置的基础。Tools 是框架层提供的、用 Python 或 Node.js 写死在代码库里的静态能力每次扩展都要人类提交 PR。Skills 是纯文本描述或 AI 现场生成的动态脚本AI 自己决定何时创建、如何修改不需要进人类代码库。OpenClaw 的架构实验里有一个很极端的做法不写任何业务代码只约定一个启动协议让 Agent 自己写 startup.sh 去拉取消息、自己决定用什么方式回复。这个思路的价值在于它把框架该做什么和AI 该做什么的边界重新划了一遍。那么复刻一只 OpenClaw最小架构需要哪些模块我的拆解是四层第一层是 Agent Loop负责接收输入、规划、调用、返回第二层是 Skills Registry负责技能的注册、发现和版本管理第三层是统一 API 通道负责把不同模型的请求收敛到一个入口第四层是执行沙箱负责给 AI 一个能跑 bash、能读写文件的受控环境。这四层里第三层最容易被忽视但它恰恰是多工具协作能不能跑通的关键。因为当你的 Agent 同时要调 Claude、GPT、本地模型时如果每个模型一套 Key、一套 Base URL、一套鉴权逻辑你的 config 会迅速膨胀到不可维护。TaoToken 在这里的角色就是第三层的统一入口。它提供兼容 OpenAI 风格的 API 通道你只需要一个 Key、一个 Base URL就能在 Agent 里切换不同模型而不需要改业务代码。下面我会从环境准备开始一步步把配置写出来。2. TaoToken 前置准备与统一 API 通道配置在写 config.toml 之前先把统一 API 通道这件事落地。很多人的 Agent 项目后期难维护根源就是模型接入层没有抽象好。你一开始只用一个模型Key 写在环境变量里Base URL 写死在代码里看起来没问题。等到你要加第二个模型做 fallback或者要给不同 Skill 分配不同模型时就会发现到处都要改。TaoToken 的接入方式是把模型调用收敛到一个兼容 OpenAI 协议的端点。你需要准备的东西很少一个 API Key一个 Base URL以及你要用的 Model ID。Base URL 是https://taotoken.net/api注意这个地址不带任何查询参数直接作为 OpenAI 客户端的 base_url 使用。API Key 在控制台的 API Keys 页面创建创建后只显示一次记得立刻保存到环境变量或密钥管理工具里。我建议你把 Key 放在环境变量里而不是写进 config.toml。原因很简单config.toml 大概率会进版本控制Key 写进去等于泄露。正确的做法是 config.toml 里引用环境变量名实际值通过 shell 或容器注入。下面是一个环境变量准备的示例你可以直接复制到.env文件或 shell 配置里export TAOTOKEN_API_KEYsk-你的实际Key export TAOTOKEN_BASE_URLhttps://taotoken.net/api export TAOTOKEN_MODEL_IDclaude-sonnet-4-20250514这里 Model ID 我填的是 Claude 系列因为 OpenClaw 类 Agent 在代码生成和长上下文规划上对模型能力要求较高。你可以根据实际场景换成其他模型TaoToken 的通道支持在请求里指定 model 字段所以切换模型不需要改 Base URL只需要改 model 参数。这一点对 Agent 架构很重要你的 Skills Registry 里可以给不同 Skill 标注不同的推荐模型运行时由调度器决定用哪个而底层通道始终是同一个。接下来验证通道是否通。在写完整 Agent 之前先用一个最小请求确认 Key 和 Base URL 没问题。用 curl 发一个 chat completions 请求curl -s https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: $TAOTOKEN_MODEL_ID, messages: [{role: user, content: 回复 OK 两个字母}], max_tokens: 16 }如果返回的 JSON 里有choices[0].message.content且内容是 OK说明通道正常。如果返回 401说明 Key 有问题如果返回 model not found说明 Model ID 写错了。这两个错误后面排障章节会详细讲。这里有一个容易被忽略的点OpenAI 兼容协议里 base_url 的写法。有些客户端要求 base_url 包含/v1有些要求不包含。TaoToken 的 Base URL 是https://taotoken.net/api在 OpenAI Python SDK 里这样初始化from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api, ) resp client.chat.completions.create( modelos.environ[TAOTOKEN_MODEL_ID], messages[{role: user, content: ping}], ) print(resp.choices[0].message.content)注意 SDK 会自动在 base_url 后面拼/v1/chat/completions所以你不要手动在 base_url 里再加/v1否则会变成/api/v1/v1/chat/completions直接 404。这个坑我在多个项目里都见过配置的时候留意一下。统一通道配好之后你的 Agent 就有了一个稳定的模型调用入口。接下来才是架构的主体Agent 调度和 Skills 扩展。这两块我会用 config.toml 骨架来承载因为配置文件比散落在代码里的常量更容易审查和迁移。3. 可复制的 config.toml 骨架与 Skills 注册现在进入架构主体。我设计的 config.toml 分成四个区块agent、api、skills、sandbox。每个区块对应前面说的四层架构。先给完整骨架再逐段解释。# config.toml - OpenClaw 类 AI Native Agent 最小骨架 [agent] name openclaw-mini max_iterations 12 planning_model claude-sonnet-4-20250514 execution_model claude-sonnet-4-20250514 system_prompt_path ./prompts/agent_system.md workspace ./workspace [api] provider taotoken base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY default_model claude-sonnet-4-20250514 timeout_seconds 120 max_retries 3 [skills] registry_dir ./skills auto_reload true allow_runtime_creation true allowed_languages [python, bash] [skills.entries.web_search] enabled true description 根据关键词检索网页并返回摘要 entrypoint web_search.py model_override [skills.entries.file_ops] enabled true description 读写工作区内的文件 entrypoint file_ops.py model_override [sandbox] type local workdir ./workspace allow_network true allowed_commands [python, bash, curl, ls, cat, grep]逐段说明。[agent]区块里max_iterations控制 Agent Loop 的最大轮数防止死循环烧 token。planning_model和execution_model分开配置是因为规划阶段和工具执行阶段对模型能力要求不同你可以给规划用强模型、执行用快模型成本会降不少。workspace是 Agent 的工作目录所有文件读写都限制在这个目录内。[api]区块是统一通道的配置。provider标记为 taotokenbase_url固定为https://taotoken.net/apiapi_key_env指向环境变量名而不是 Key 本身。default_model是兜底模型当 Skill 没有指定 model_override 时用它。max_retries设 3 次因为 Agent 场景下网络抖动比单次对话更常见重试能显著提升成功率。[skills]区块是 Skills Registry 的核心。registry_dir指向技能目录auto_reload开启后你往目录里丢新技能文件Agent 下一轮就能发现不需要重启。allow_runtime_creation是 AI Native 的关键开关允许 AI 在运行时自己创建新技能脚本。这个开关打开后AI 遇到没有现成技能的任务会自己写一个脚本放进 registry_dir下次同类任务就直接复用。allowed_languages限制 AI 能写什么语言的脚本生产环境建议只开 python 和 bash不要开任意语言。[skills.entries.*]是显式注册的技能。每个技能有enabled、description、entrypoint、model_override四个字段。description很重要Agent 调度时靠它判断该不该用这个技能所以描述要写清楚输入输出。model_override留空表示用 default_model如果某个技能需要特定模型在这里覆盖。[sandbox]区块定义执行环境。type local表示本地执行生产环境建议换成容器。allowed_commands是白名单只允许列出的命令执行。这里有个安全原则白名单要尽量窄不要图省事写[*]否则 AI 生成的脚本可能执行危险操作。配置写好后目录结构应该是这样openclaw-mini/ ├── config.toml ├── prompts/ │ └── agent_system.md ├── skills/ │ ├── web_search.py │ └── file_ops.py └── workspace/agent_system.md是系统提示词告诉 Agent 它的角色、可用技能、输出格式。一个最小版本你是一个 AI Native Agent。你可以使用 skills 目录下的技能完成任务。 每次需要调用技能时输出 JSON{skill: 技能名, args: {...}} 如果现有技能无法完成任务你可以创建新技能脚本写入 skills 目录。 所有文件操作限制在 workspace 目录内。这个提示词的关键是最后两句允许创建新技能、限制操作范围。前者是 AI Native 的能力来源后者是安全边界。两者缺一不可。到这里config.toml 骨架和目录结构就齐了。接下来演示一次完整的 Skills 注册与调用验证把静态配置跑成动态行为。4. 一次 Skills 注册与调用验证的完整过程这一节我带你跑通一次从技能注册到实际调用的完整链路。目标是验证三件事技能能被 Agent 发现、Agent 能正确选择技能、技能执行结果能回到 Agent Loop。先写一个最小技能skills/web_search.py。为了不依赖外部搜索 API我用一个模拟实现重点是展示技能的结构约定# skills/web_search.py import json import sys def run(args: dict) - dict: query args.get(query, ) # 实际项目中这里调用搜索 API # 这里返回模拟结果验证链路 return { query: query, results: [ {title: f关于 {query} 的结果一, url: https://example.com/1}, {title: f关于 {query} 的结果二, url: https://example.com/2}, ], } if __name__ __main__: payload json.loads(sys.stdin.read()) result run(payload) print(json.dumps(result, ensure_asciiFalse))技能的结构约定是从 stdin 读 JSON 参数向 stdout 写 JSON 结果。这个约定让技能和 Agent 之间解耦技能可以用任何语言写只要遵守这个 IO 协议。file_ops.py同理实现 read 和 write 两个动作。技能写好后Agent 怎么发现它在auto_reload true的情况下Agent 每轮开始时会扫描registry_dir把每个.py文件的文件名和[skills.entries.*]里的 description 关联起来。如果某个技能文件存在但没有在 config.toml 里显式注册Agent 会读取文件头部的 docstring 作为描述。所以更规范的做法是在技能文件顶部加 docstring web_search: 根据关键词检索网页并返回摘要。 参数: {query: 搜索词} 返回: {query: str, results: [{title: str, url: str}]} 现在启动 Agent 主循环。一个最小实现# agent.py import json import os import subprocess from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api, ) def load_skills(skills_dir): skills {} for fname in os.listdir(skills_dir): if fname.endswith(.py): name fname[:-3] skills[name] os.path.join(skills_dir, fname) return skills def call_skill(path, args): proc subprocess.run( [python, path], inputjson.dumps(args), capture_outputTrue, textTrue, timeout30, ) if proc.returncode ! 0: return {error: proc.stderr} return json.loads(proc.stdout) def agent_loop(user_input, skills, max_iter12): messages [ {role: system, content: open(prompts/agent_system.md).read()}, {role: user, content: user_input}, ] for i in range(max_iter): resp client.chat.completions.create( modelos.environ[TAOTOKEN_MODEL_ID], messagesmessages, ) content resp.choices[0].message.content messages.append({role: assistant, content: content}) try: action json.loads(content) except json.JSONDecodeError: return content skill_name action.get(skill) if skill_name not in skills: messages.append({role: user, content: f技能 {skill_name} 不存在}) continue result call_skill(skills[skill_name], action.get(args, {})) messages.append({role: user, content: json.dumps(result, ensure_asciiFalse)}) return 达到最大迭代次数 if __name__ __main__: skills load_skills(./skills) print(agent_loop(帮我搜索一下 OpenClaw 架构设计, skills))运行python agent.py你会看到 Agent 先输出一个 JSON 指定调用 web_search主循环解析后执行技能把结果塞回 messages再请求模型生成最终回复。整个过程里模型只负责决策技能负责执行通道负责通信三层职责清晰。验证成功的标志是终端打印出包含关于 OpenClaw 架构设计的结果一的最终回复。如果卡在某一步看下一节的排障对照。这里有一个实测经验max_iterations不要设太大12 轮足够覆盖绝大多数任务。设太大反而容易让 Agent 在失败时反复重试同一个技能烧 token 还不出结果。另外技能执行一定要加 timeout我设的是 30 秒防止某个技能卡死拖垮整个循环。5. 常见报错排查401、local proxy failed 与 reading choices配置跑起来之后报错是难免的。这一节我把最常见的几类错误和排查路径列出来你对照着看。第一类401 Unauthorized。这个错误几乎都是 Key 的问题。排查顺序是先确认环境变量有没有真正注入在 shell 里执行echo $TAOTOKEN_API_KEY如果输出为空说明 export 没生效或者在新开的终端里没加载。再确认 Key 有没有多余空格或换行从控制台复制时容易带上尾部空白。最后确认 Key 有没有过期或被删除去控制台的 API Keys 页面核对。如果 curl 直接请求也返回 401那就是 Key 本身的问题重新创建一个。第二类local proxy failed 或 connection refused。这个错误通常出现在你本地有代理设置、但代理没有运行的情况下。排查方法是检查环境变量HTTP_PROXY和HTTPS_PROXY如果设置了但代理服务没开请求会直接失败。临时解决是unset HTTP_PROXY HTTPS_PROXY然后重试。如果你确实需要走代理确保代理服务在运行且端口正确。注意这里说的是本地开发环境的网络配置问题不是让你去搭什么特殊通道只是排查环境变量残留。第三类reading choices 或 KeyError choices。这个错误说明你拿到的响应里没有 choices 字段通常是响应体是一个错误对象而不是正常 completion。排查方法是把原始响应打印出来resp client.chat.completions.create(...) print(resp.model_dump_json(indent2))如果看到error字段里面的 message 会告诉你具体原因。常见的有 model not foundModel ID 写错、context length exceeded上下文超长、rate limit限流。Model ID 一定要和控制台里列出的完全一致大小写和日期后缀都不能错。第四类OAuth 相关错误。如果你用的是 Claude Code 或类似工具可能会遇到 OAuth token 过期的问题。这类工具通常有自己的鉴权流程和 API Key 是两套机制。排查方法是确认你用的是 API Key 模式而不是 OAuth 模式在工具的配置里把鉴权方式切到 API KeyBase URL 填https://taotoken.net/apiKey 填环境变量。如果工具同时支持两种模式优先用 API Key因为它的排查路径更短。第五类技能执行超时或返回非 JSON。这个错误不在 API 层在技能层。排查方法是单独跑技能脚本echo {query: test} | python skills/web_search.py如果脚本本身报错先修脚本。如果脚本正常但 Agent 调用失败检查call_skill里的 timeout 设置和 stdin 传参格式。技能脚本必须从 stdin 读、向 stdout 写任何 print 调试信息都会污染 JSON 输出调试信息要写到 stderr。为了让你更快定位我把这几类错误和对应动作整理成对照报错关键词最可能原因第一步动作401 UnauthorizedKey 未注入或失效echo 环境变量核对控制台local proxy failed代理环境变量残留unset HTTP_PROXY HTTPS_PROXYreading choices响应是错误对象打印原始响应看 error.messagemodel not foundModel ID 不匹配核对控制台模型列表OAuth token expired鉴权模式用错切换到 API Key 模式技能返回非 JSON脚本有 print 污染调试信息改写到 stderr排查的核心思路是分层定位先确认通道通不通curl 直连再确认 SDK 配置对不对打印原始响应最后确认技能本身跑不跑得通单独执行脚本。三层都过了Agent Loop 就没有理由失败。6. 用统一通道支撑多工具协作的下一步走到这里你已经有了一个能跑的最小 AI Native Agentconfig.toml 定义了架构骨架TaoToken 统一通道解决了模型接入Skills Registry 解决了能力扩展Agent Loop 把三者串起来。接下来我想聊聊这套架构在真实项目里怎么继续演进。第一个方向是技能的市场化。当allow_runtime_creation true打开后AI 会自己往 skills 目录里写脚本。这些脚本积累多了就形成了一个技能库。你可以给技能加版本号、加依赖声明、加测试用例让它从AI 随手写的脚本变成可复用的能力单元。这一步的关键是给技能文件加元数据头比如# version: 1.0、# requires: requestsAgent 在加载时解析这些元数据就能做依赖检查和版本管理。第二个方向是调度策略的细化。现在的 Agent Loop 是单模型串行规划模型和执行模型分开配置但用的是同一个通道。你可以进一步做模型路由根据技能类型选模型搜索类技能用快模型代码生成类技能用强模型。TaoToken 的通道支持在请求里指定 model所以路由逻辑只需要在call_skill之前加一层判断不需要改通道配置。第三个方向是沙箱的强化。type local只适合开发阶段生产环境要换成容器沙箱。容器沙箱的配置思路是把[sandbox]区块扩展成镜像地址、资源限制、网络策略三部分。技能执行时在容器里跑Agent 主循环在宿主机跑两者通过标准 IO 通信。这样即使 AI 生成的脚本有问题也影响不到宿主机。第四个方向是可观测性。Agent 的决策链路比普通应用长得多一次任务可能经过十几轮模型调用和技能执行。你需要记录每一轮的输入输出、耗时、token 消耗才能定位性能瓶颈。最简单的做法是在agent_loop里加结构化日志每轮记录iteration、skill_name、duration_ms、tokens_used。这些数据积累起来你就能看出哪些技能调用频繁、哪些模型响应慢、哪些任务容易失败。如果你想把 Coding Plan 用在长期编码任务上或者想用模型对话快速验证不同模型在 Agent 场景下的表现可以按下面的路径走需要创建和管理 Key 的去 API Keys 页面需要查接入细节的看接入文档想直接对话验证模型的用模型对话长期跑编码和 Agent 任务的看 Coding Plan。这几个入口覆盖了从验证到生产的完整链路。最后说一个我踩过的坑不要一开始就把架构设计得太复杂。我见过有人上来就搞多 Agent 协作、搞向量记忆、搞复杂路由结果连一个技能都调不通。正确的顺序是先跑通单 Agent 单技能再逐步加技能、加模型、加沙箱。OpenClaw 的架构实验之所以有价值恰恰是因为它做了极致的减法把框架该做的事压到最少把 AI 能做的事放到最大。你复刻的时候也遵循这个原则先让最小闭环跑起来再谈扩展。