从「用 OpenClaw」到「造你自己的 Agent」:把最佳实践搬进代码

进阶进行中Claude API昨天更新

适用场景: 会用 OpenClaw、想理解 agent 内部怎么转、进而自己造一个的开发者 前置条件: 读过 OpenClaw 系列(尤其技能篇、记忆篇、安全篇),本机能跑 Python 并装好 anthropic

目标

你会用 OpenClaw 了——装过、连过频道、写过技能、配过安全基线。这篇把视角翻过来:OpenClaw 的每一个功能模块,其实都在告诉你一个生产级 agent 需要哪些部件。我们把这份「藏在功能背后的架构清单」提炼出来,然后用 Claude API 亲手搭一个最小但完整的自建 agent。

做完你会有一个能跑 agent 循环、能调你自己的工具、有记忆、有行为边界、有日志的 Python agent 骨架。

一、OpenClaw 是一张架构清单

你在前面章节配的每样东西,都对应自建 agent 的一个部件:

OpenClaw 里的东西对应的 agent 部件自己造时你要写
Bot 自动干活agent 循环(loop)调模型 → 执行工具 → 喂回结果,直到收工
Skills / ClawHub工具接口工具的名字、参数、执行体
记忆系统 / SOUL记忆与人格什么写下来、怎么读回
安全基线 / 行动分级权限与边界高危动作的确认门
daemon status / 日志可观测性每次工具调用留痕

OpenClaw 把这五件事打包好了,你只管用。自己造,就是把这五件事一件件写出来。下面逐个搭。

二、最小 agent 循环

agent 的心脏是一个循环:模型决定调哪个工具 → 你执行 → 把结果喂回去 → 模型接着想,直到不再调工具。手写这个循环容易出错,Claude SDK 的 tool runner 直接替你跑这个循环。

python
import anthropic
from anthropic import beta_tool

client = anthropic.Anthropic()

@beta_tool
def get_time(timezone: str = "Asia/Shanghai") -> str:
    """返回指定时区的当前时间。

    Args:
        timezone: IANA 时区名,如 Asia/Shanghai。
    """
    from datetime import datetime
    from zoneinfo import ZoneInfo
    return datetime.now(ZoneInfo(timezone)).isoformat()

runner = client.beta.messages.tool_runner(
    model="claude-sonnet-5",
    max_tokens=8192,
    tools=[get_time],
    messages=[{"role": "user", "content": "现在几点了?"}],
)

for message in runner:
    for block in message.content:
        if block.type == "text":
            print(block.text)

@beta_tool 从函数签名和 docstring 自动生成工具 schema——这正是 OpenClaw 的 Skill 定义在替你做的事,现在你自己写。tool runner 自动跑完「调模型 → 执行 get_time → 把时间喂回模型」的整个来回,你不必手写循环。

三、加工具:这就是你的 Skills

一个 agent 的能力边界,就是你给它的工具集。加能力 = 加一个带 docstring 的函数:

python
@beta_tool
def query_order(order_id: str) -> str:
    """按订单号查询订单状态。当用户询问某个订单的进度、物流或金额时调用。

    Args:
        order_id: 订单号,形如 ORD-20260811-001。
    """
    # 这里换成你真实的数据库/接口调用
    return f"订单 {order_id}:已发货,预计明天送达。"

把它加进 tools=[get_time, query_order],agent 就多了一项能力。

工具的 docstring 决定模型什么时候调它。写清楚「什么情况下用这个工具」(如上面的「当用户询问……时调用」),比只写「这个工具做什么」更能让模型准确触发。

四、加记忆:把学到的写下来

OpenClaw 的记忆系统,本质是「读写一个持久文件」。自建时,给 agent 一对读写工具就是记忆:

python
import os

NOTES_DIR = "agent_notes"

@beta_tool
def write_note(title: str, content: str) -> str:
    """把一条需要长期记住的信息写进笔记,供以后的对话查阅。

    Args:
        title: 简短标题,会作为文件名。
        content: 笔记正文。
    """
    os.makedirs(NOTES_DIR, exist_ok=True)
    path = os.path.join(NOTES_DIR, f"{title}.md")
    with open(path, "w", encoding="utf-8") as f:
        f.write(f"# {title}\n\n{content}\n")
    return f"已记下:{title}"

@beta_tool
def read_notes() -> str:
    """列出所有已记下的笔记标题与内容。开始任务前先读一遍。"""
    if not os.path.isdir(NOTES_DIR):
        return "还没有任何笔记。"
    return "\n\n".join(
        open(os.path.join(NOTES_DIR, f), encoding="utf-8").read()
        for f in sorted(os.listdir(NOTES_DIR))
    )

一条信息一个文件、标题即摘要——这套约定和 OpenClaw 记忆篇里那套是一回事,只是现在文件由你的工具写。

五、加边界:在工具里设门禁

安全篇讲过行动分级。自建 agent 里,红黄线光写在提示词里是软约束——真正靠得住的做法是在工具执行体里硬拦。高危工具执行前先要人工确认:

python
@beta_tool
def delete_file(path: str) -> str:
    """删除一个文件。高危操作,执行前必须人工确认。

    Args:
        path: 要删除的文件路径。
    """
    confirm = input(f"⚠️ agent 想删除 {path},允许吗?(yes/no) ")
    if confirm.strip().lower() != "yes":
        return "用户拒绝了这次删除。"
    os.remove(path)
    return f"已删除 {path}"

模型请求删除时,tool runner 会自动调这个函数,而门禁就在函数里——用户说 no,agent 拿到的是「被拒绝」的结果,它会换思路而不是硬删。这就把 SOUL.md 的黄线从软约束搬成了代码里的硬约束,正好接上上一篇「别让 agent 自作聪明」的结论。

六、加可观测:每次调用留痕

OpenClaw 的日志让你事后能查 agent 到底干了什么。自己造,在工具里加一行结构化日志就够起步:

python
import logging, json

logging.basicConfig(filename="agent.log", level=logging.INFO)

@beta_tool
def send_email(to: str, subject: str, body: str) -> str:
    """发送一封邮件。"""
    logging.info(json.dumps({"tool": "send_email", "to": to, "subject": subject}))
    # 这里换成真实发信;同样建议给它套上第五步的确认门禁
    return f"已发送给 {to}"

每个工具入口 log 一行,你就有了一条可回溯的行为轨迹——排查「它为什么做了这件事」时,这条轨迹比模型的解释可靠得多。

把五件事拼起来

循环(二)+ 工具(三)+ 记忆(四)+ 边界(五)+ 日志(六),凑齐就是一个迷你版的 OpenClaw——只不过每个部件都在你手里,可以按你的需要改。这也解释了 OpenClaw 的价值:它把这五件事调好了默认值打包给你;自己造,是拿回全部控制权,代价是这五件事往后都得自己维护。

从这里往哪走

  • 主循环成本:agent 循环是多轮工具调用、成本敏感的典型场景,用 Sonnet 跑主循环、把最难的子任务用工具触发时路由到 Opus,参考本站「多模型路由降本」那篇;
  • 跨会话状态 / 定时任务:需要 agent 跨轮次记住目标、断点续跑时,去找长运行 agent 的状态内核方案;
  • 上线前自查:给 agent 做一次对齐、业务 KPI、运营风险的体检,比出事后补救便宜得多。

常见问题

tool runner 是 beta 吗? 是,它是 anthropic SDK 的 beta helper,通过 client.beta.messages.tool_runner 使用。如果你想完全掌控循环(自定义传输、特殊的重试逻辑),可以手写 while stop_reason == "tool_use" 的循环,但大多数场景 tool runner 就够了。

自己造比用 OpenClaw 强在哪? 强在完全掌控——工具集、记忆格式、边界逻辑、部署环境全是你的。代价是这五件事都得自己维护。OpenClaw 是「电池全含」,自建是「按需装配」,按你的控制欲和维护精力选。

该用哪个模型跑循环? agent 循环是 Sonnet 的主场(多轮工具调用、对成本敏感);把最难的判断留给 Opus。别一上来所有请求都用旗舰模型——那和第一板斧讲的道理一样,是在烧钱。

从「用 OpenClaw」到「造你自己的 Agent」:把最佳实践搬进代码 | 资讯狗 | Zixungou