核心机制解析:从 Function Calling 到 Skill 与 exec 的演进)
1. 从 Function Calling 到 SkillOpenClaw 到底解决了什么问题如果你用过 OpenAI 的 Function Calling大概率经历过这种场景为了让模型能查个天气、读个文件你得先写一大段 JSON Schema把参数名、类型、必填项、枚举值全部钉死。模型能做的事情完全被你定义的接口边界框住。一旦用户问了一个你没预定义参数的问题模型只能干瞪眼或者硬塞一个不匹配的参数进去然后你的后端代码报错。OpenClaw社区里常叫“龙虾”的核心机制恰恰是在这个基础上往前走了一步。它没有抛弃 Function Calling而是把 Function Calling 降级成了底层能力在上面加了一层用 Markdown 写的 Skill技能定义。你可以理解为Function Calling 是螺丝刀、扳手这些原子工具而 Skill 是一本写给模型看的操作手册告诉它“遇到什么场景该拿哪些工具按什么顺序用”。这个区别听起来抽象落到实际开发里差别很大。传统 Function Calling 是代码驱动——模型只负责填参数执行逻辑全在你的 Python 或 JS 里写死。OpenClaw 的 Skill 是模型驱动——模型读完 Markdown 说明书后自己决定调用哪个底层工具比如 exec 执行命令、file_read 读文件甚至能在遇到报错时根据说明书里的提示自己重试。适合谁看这篇如果你已经写过 Function Calling 的 demo但觉得每次加一个新能力都要改代码、重新部署很烦或者你在做 Agent 类产品想让模型处理更开放的任务那 OpenClaw 这套 Skill exec 的组合值得你花时间理解。下面我会从机制对比讲到可复制的 Skill 定义再到 exec 调用的验证步骤最后把常见的报错排查一遍。2. TaoToken 前置准备让 OpenClaw 的 Skill 跑起来需要什么OpenClaw 本身是一个 Agent 框架它要调用大模型来“读”你的 Skill 说明书然后决定怎么执行。所以第一步不是写 Skill而是先把模型接入配好。这里我用 TaoToken 作为模型接入层来演示因为它兼容 OpenAI 的接口格式OpenClaw 底层走 Function Calling 时能直接对接。你需要准备三样东西Base URL、API Key、Model ID。这三个是任何 OpenAI 兼容接口的标配缺一不可。很多人在配 OpenClaw 时卡住就是因为只填了 Key 没填 Base URL或者 Model ID 写成了带前缀的别名。先拿 API Key。打开 TaoToken 的 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi-keys创建一个新 Key。注意创建后立刻复制页面刷新后就看不到了。这个 Key 的格式通常是sk-开头的一串字符。Base URL 用https://taotoken.net/api注意结尾不要带斜杠也不要自己加/v1OpenClaw 或 OpenAI SDK 会自动拼接路径。Model ID 根据你实际要用的模型填比如gpt-4o、claude-3-5-sonnet这类。如果你不确定有哪些可用可以去模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentchat先试一下能正常对话说明模型可用。配好这三个之后OpenClaw 的配置文件里通常长这样以环境变量方式为例export OPENAI_API_KEYsk-你的key export OPENAI_BASE_URLhttps://taotoken.net/api export OPENCLAW_MODELgpt-4o如果你用的是 Claude Code 或者 Cline 这类工具来辅助开发 OpenClaw 的 Skill它们的配置逻辑是一样的。Claude Code 的 settings.json 里需要写全三件套{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: claude-3-5-sonnet } }注意 Claude Code 用的是ANTHROPIC_BASE_URL而不是OPENAI_BASE_URL但地址是同一个。这个坑我见过不少人踩配了半天发现变量名写错了。3. 可复制的 Skill 定义用 Markdown 写一份 GitHub 操作手册OpenClaw 的 Skill 本质上是一个 Markdown 文件放在项目的skills/目录下。文件名通常就是技能名比如github.md。模型在启动时会读取这个目录下所有 Markdown 文件的内容作为系统提示的一部分。下面是一份可以直接复制使用的 GitHub Skill 定义。我把它拆成几个部分技能描述、可用工具、操作步骤、错误处理。# GitHub 操作技能 ## 描述 你可以使用 gh 命令行工具来操作 GitHub。当用户要求查看 PR、合并分支、创建 issue 时使用本技能。 ## 可用工具 - exec: 执行 shell 命令 - file_read: 读取本地文件 - file_write: 写入本地文件 ## 操作步骤 ### 查看 PR 列表 1. 执行 gh pr list --state open 2. 解析输出提取 PR 编号、标题、作者 3. 用表格形式返回给用户 ### 合并 PR 1. 先执行 gh pr view 编号 确认 PR 状态 2. 如果状态是 open 且没有冲突执行 gh pr merge 编号 --squash 3. 如果报错提示有冲突返回冲突文件列表不要强行合并 ### 创建 Issue 1. 执行 gh issue create --title 标题 --body 内容 2. 返回创建的 issue 链接 ## 错误处理 - 如果 gh 命令不存在提示用户安装 GitHub CLI - 如果报错 not logged in提示用户执行 gh auth login - 如果报错 rate limit等待 60 秒后重试一次 - 任何命令执行失败先读取 stderr 输出再决定是否重试这份 Markdown 里没有一行 JSON Schema。模型读到的是一段自然语言描述它自己理解“查看 PR 列表”该怎么做。底层它还是会调用 exec 这个 Function Calling 工具但调用什么命令、加什么参数是模型根据 Markdown 里的步骤自己决定的。对比一下传统 Function Calling 的写法你要定义get_pr_list、merge_pr、create_issue三个函数每个都要写 parameters schema。而 Skill 方式下你只暴露一个exec工具剩下的交给 Markdown 说明书。这就是“用自然语言编程”指挥 Function Calling 的意思。写 Skill 有几个实用技巧。第一步骤要具体到命令级别不要写“调用 GitHub API”要写gh pr list。第二错误处理要写清楚什么错该重试、什么错该放弃。第三工具列表要明确告诉模型它有哪些原子能力可用。第四Markdown 里的标题层级要清晰模型对##和###的解析很敏感。4. 验证 exec 调用从请求到成功结果的完整链路Skill 写好了怎么验证模型真的会按说明书去调用 exec我建议用一个最小可复现的请求来测。下面这段 Python 代码模拟 OpenClaw 的调用逻辑你可以直接跑。import os from openai import OpenAI client OpenAI( api_keyos.environ[OPENAI_API_KEY], base_urlos.environ[OPENAI_BASE_URL] ) # 读取 Skill 文件内容 with open(skills/github.md, r) as f: skill_content f.read() # 定义底层 exec 工具Function Calling 格式 tools [ { type: function, function: { name: exec, description: 执行 shell 命令并返回输出, parameters: { type: object, properties: { command: { type: string, description: 要执行的命令 } }, required: [command] } } } ] response client.chat.completions.create( modelos.environ[OPENCLAW_MODEL], messages[ {role: system, content: f你可以使用以下技能\n\n{skill_content}}, {role: user, content: 帮我看看现在有哪些打开的 PR} ], toolstools, tool_choiceauto ) print(response.choices[0].message)跑这段代码你会看到模型返回的tool_calls里function.name是execarguments里是{command: gh pr list --state open}。这说明模型读懂了 Markdown 里的“执行gh pr list --state open”这一步并且自己把它翻译成了 exec 调用。接下来你要做的是真正执行这个命令把结果塞回对话让模型继续处理。这一步是 OpenClaw 框架帮你做的但理解它有助于排查问题import subprocess import json tool_call response.choices[0].message.tool_calls[0] args json.loads(tool_call.function.arguments) result subprocess.run(args[command], shellTrue, capture_outputTrue, textTrue) # 把执行结果返回给模型 follow_up client.chat.completions.create( modelos.environ[OPENCLAW_MODEL], messages[ {role: system, content: f你可以使用以下技能\n\n{skill_content}}, {role: user, content: 帮我看看现在有哪些打开的 PR}, response.choices[0].message, { role: tool, tool_call_id: tool_call.id, content: result.stdout or result.stderr } ], toolstools ) print(follow_up.choices[0].message.content)如果一切正常最后你会看到模型把gh pr list的输出整理成了表格返回给用户。这就是从 Skill 定义到 exec 调用再到结果整理的完整链路。实测下来模型对 Markdown 里步骤的遵循程度跟你的描述清晰度直接相关。如果你写“查看 PR”模型可能调用gh pr list也可能调用gh api。如果你写“执行gh pr list --state open”模型基本不会跑偏。所以 Skill 写得越具体exec 调用的准确率越高。5. 常见报错排查401、local proxy failed、reading choices 怎么解配 OpenClaw TaoToken 的过程中有几个报错出现频率特别高。我按实际遇到的顺序列一下你对照着排查。401 Unauthorized。这个最常见原因通常是 Key 没填对或者 Base URL 写错了。先检查OPENAI_API_KEY是不是完整的sk-开头字符串有没有多余空格。再检查OPENAI_BASE_URL是不是https://taotoken.net/api注意不要写成https://taotoken.net/api/v1有些 SDK 会自动加/v1你手动加了就变成/v1/v1直接 404 或 401。如果你用的是 Claude Code检查变量名是不是ANTHROPIC_API_KEY和ANTHROPIC_BASE_URL别跟 OpenAI 的混了。local proxy failed。这个报错通常出现在你本地开了某些网络工具但配置没生效或者端口冲突。OpenClaw 底层走 HTTP 请求如果系统代理设置指向了一个不存在的端口就会报这个。排查方法是先关掉所有代理设置用curl https://taotoken.net/api直接测一下能不能通。如果 curl 能通但 OpenClaw 报错检查 OpenClaw 的配置文件里有没有硬编码的 proxy 设置。reading choices 报错。完整报错通常是Error reading choices: ...或者choices is undefined。这说明模型返回的响应结构跟你代码里解析的不一致。常见原因是 Base URL 配错了请求打到了非 OpenAI 兼容的接口上返回了 HTML 而不是 JSON。另一个原因是 Model ID 写错了接口返回了错误信息但你的代码直接去读choices[0]就报 undefined。排查方法是在请求后先打印完整 response看看返回的 JSON 结构对不对。OAuth 相关报错。如果你用 Claude Code 接入可能会遇到OAuth token expired或invalid_grant。这是因为 Claude Code 默认走 OAuth 登录流程但用 API Key 接入时不需要 OAuth。你需要在 settings.json 里明确配置ANTHROPIC_API_KEY并且确保没有残留的 OAuth token 文件。删掉~/.claude/下的 token 缓存重新用 API Key 方式登录。exec 调用返回空结果。模型调用了 exec但result.stdout是空的。先确认命令本身在终端里能跑通比如gh pr list需要你先gh auth login。如果命令没问题但 OpenClaw 里跑不出来检查 OpenClaw 执行命令时的工作目录是不是你预期的目录。有些框架默认在项目根目录执行而你的gh配置在用户目录下就会找不到。Skill 没被加载。模型完全无视你的 Markdown直接瞎调工具。检查 Skill 文件是不是放在 OpenClaw 配置的 skills 目录下文件名是不是.md结尾。有些框架要求 Skill 文件必须有特定的 frontmatter比如---\nname: github\n---缺了就不加载。另外检查文件编码UTF-8 不带 BOM有些框架对 BOM 头敏感。6. 继续深入把 Skill 和 exec 用顺手的几个建议Skill 机制最大的好处是迭代快。你想给 Agent 加一个新能力不用改代码、不用重新部署写一个 Markdown 文件丢进 skills 目录就行。模型下次启动就会读到。这对于快速试错特别友好。但要注意Skill 不是越多越好。每个 Skill 都会占用系统提示的 tokenSkill 太多会挤占对话上下文模型反而容易混淆。我的做法是按业务域拆分比如github.md、file-ops.md、web-search.md每个文件聚焦一个场景。不要把所有能力塞进一个巨大的 Markdown 里。exec 工具是把双刃剑。它给了模型极大的灵活性但也意味着模型可以执行任意 shell 命令。在生产环境里你需要在 exec 外面包一层白名单或者沙箱限制可执行的命令范围。OpenClaw 本身提供了一些权限控制配置建议至少把rm -rf、curl | bash这类危险命令拦掉。如果你想让模型长期跑编码或 Agent 任务可以考虑用 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding-plan它在长上下文和工具调用稳定性上做了优化适合 OpenClaw 这种需要多轮 exec 调用的场景。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc里面有各语言 SDK 的配置示例配的时候对照着看能少踩很多坑。最后说一个我自己的经验写 Skill 的时候把模型当成一个刚入职的实习生。你给实习生的操作手册越具体、越有例子、越说明什么情况该怎么办他上手就越快。Markdown 里的每一步都写清楚命令和预期输出模型执行 exec 的准确率会明显提升。反过来如果你只写“处理 GitHub 相关操作”模型就只能靠猜结果往往不是你想要的。