摘要:OpenClaw、Claude Code等Agent被过度神化?本质上就是一套模型+记忆+工具+技能的工程系统。本文用不到200行代码,从零搭建一个具备持续对话、上下文记忆、系统人设、工具调用和渐进式Skill加载能力的Agent。全程带可运行代码示例和原理讲解,适合想深入理解Agent底层机制的开发者。

目录

  1. 破除神话:Agent的本质
  2. 第一步:搭建Agent大脑
  3. 第二步:实现持续对话与上下文记忆
  4. 第三步:赋予Agent人格(系统提示词)
  5. 第四步:工具调用——让Agent长出“手脚”
  6. 第五步:Skill系统与渐进式披露
  7. 完整Agent代码结构总结
  8. 参考资源

1. 破除神话:Agent的本质

网上对OpenClaw、Claude Code等Agent的吹嘘往往过度神话。本质上,它们就是功能更复杂、形式更花哨的智能体。Agent的核心组成不外乎:

组件 作用
大语言模型(LLM) 大脑,负责理解和生成
循环交互 实现多轮对话
上下文记忆 记住历史对话
系统提示词 赋予人格与行为规范
工具调用 执行外部操作(命令行、搜索等)
技能(Skill) 可扩展的专业能力包

本文将用不到200行代码,完整实现上述所有能力。代码基于Anthropic格式(兼容OpenAI),可运行在Python环境。

2. 第一步:搭建Agent大脑

最基础的Agent只需要调用大语言模型API。以Anthropic为例:

import anthropic

client = anthropic.Anthropic(
    api_key="your-api-key",
    base_url="your-base-url"  # 可选
)

response = client.messages.create(
    model="claude-3-opus-20240229",
    messages=[{"role": "user", "content": "你好"}],
    max_tokens=1000
)
print(response.content[0].text)

这段代码完成了一次模型调用,约20行。但程序运行完就结束了,无法持续对话。

3. 第二步:实现持续对话与上下文记忆

将调用逻辑嵌入while循环,并维护history列表保存对话历史。

history = []

while True:
    user_input = input("你: ")
    if user_input.lower() == "exit":
        break
    
    history.append({"role": "user", "content": user_input})
    
    response = client.messages.create(
        model="claude-3-opus-20240229",
        messages=history,
        max_tokens=1000
    )
    
    assistant_msg = response.content[0].text
    print(f"Agent: {assistant_msg}")
    history.append({"role": "assistant", "content": assistant_msg})

原理:每轮将用户输入和AI回复追加到history,下次调用时传入全部历史,模型就有了“记忆”。

效果验证:问“1+1等于几”→答“2”;再问“再加一呢”→答“3”。模型记住了上文。

4. 第三步:赋予Agent人格(系统提示词)

系统提示词(system prompt)用于设定角色、行为规范,在每次调用时固定传入。

SYSTEM_PROMPT = """
你是大内太监总管李公公,侍奉皇上多年,忠心耿耿。
你的语调要谦卑,尊称用户为“皇上”。
每次回复前缀必须加上“奉天承运,皇帝诏曰:”。
"""

response = client.messages.create(
    model="claude-3-opus-20240229",
    system=SYSTEM_PROMPT,
    messages=history,
    max_tokens=1000
)

运行效果:问“你是谁”,回复“奉天承运,皇帝诏曰:回皇上,奴才是伺候您二十年的李公公…”

5. 第四步:工具调用——让Agent长出“手脚”

仅靠文本生成无法执行实际操作。定义工具数组,让Agent能调用命令行。

tools = [
    {
        "name": "run_command",
        "description": "执行命令行命令并返回结果",
        "input_schema": {
            "type": "object",
            "properties": {
                "command": {"type": "string", "description": "要执行的命令"}
            },
            "required": ["command"]
        }
    }
]

处理工具调用的完整流程

import subprocess

def call_tool(tool_name, tool_input):
    if tool_name == "run_command":
        result = subprocess.run(
            tool_input["command"],
            shell=True,
            capture_output=True,
            text=True
        )
        return result.stdout or result.stderr
    return None

# 在对话循环中
response = client.messages.create(
    model="claude-3-opus-20240229",
    system=SYSTEM_PROMPT,
    messages=history,
    tools=tools,
    tool_choice="auto"
)

while response.stop_reason == "tool_use":
    tool_use = next(block for block in response.content if block.type == "tool_use")
    tool_name = tool_use.name
    tool_input = tool_use.input
    
    result = call_tool(tool_name, tool_input)
    
    history.append({
        "role": "user",
        "content": [{
            "type": "tool_result",
            "tool_use_id": tool_use.id,
            "content": result
        }]
    })
    
    response = client.messages.create(
        model="claude-3-opus-20240229",
        system=SYSTEM_PROMPT,
        messages=history,
        tools=tools,
        tool_choice="auto"
    )

# 最终文本回答
final_answer = response.content[0].text
print(final_answer)

关键点

  • stop_reasontool_use表示模型要求调用工具。
  • 执行真实函数后将结果以tool_result类型追加到历史中。
  • 再次调用模型,让模型基于工具结果生成最终回答。
  • 模型可能连续调用多个工具,所以使用while循环处理。

6. 第五步:Skill系统与渐进式披露

Skill是Agent持续进化的关键。若将所有技能文档一次性塞入上下文,会导致上下文窗口爆满、模型产生幻觉。解决方案:渐进式披露——只加载技能名称和描述,按需加载完整内容。

6.1 技能目录结构

skills/
  web_search/
    skill.md      # 包含完整执行流程
  create_skill/
    skill.md

6.2 技能加载器

import os

SKILLS_DIR = "./skills"

def load_skills_manifest():
    """返回 {技能名: 描述} 的字典"""
    manifest = {}
    for skill_name in os.listdir(SKILLS_DIR):
        skill_path = os.path.join(SKILLS_DIR, skill_name, "skill.md")
        if os.path.exists(skill_path):
            with open(skill_path, 'r', encoding='utf-8') as f:
                # 简单读取第一行作为描述(实际可解析YAML frontmatter)
                desc = f.readline().strip()
            manifest[skill_name] = desc
    return manifest

def load_full_skill(skill_name):
    """加载完整技能内容"""
    path = os.path.join(SKILLS_DIR, skill_name, "skill.md")
    with open(path, 'r', encoding='utf-8') as f:
        return f.read()

6.3 将技能描述注入系统提示

manifest = load_skills_manifest()
skill_desc = "\n".join([f"- {name}: {desc}" for name, desc in manifest.items()])

SYSTEM_PROMPT = f"""
你是AI助手。你有以下技能可用:
{skill_desc}
当需要某个技能的详细操作流程时,调用load_skill工具传入技能名,我会告诉你完整内容。
"""

6.4 定义加载技能的工具

tools.append({
    "name": "load_skill",
    "description": "加载指定技能的完整内容(当需要该技能的具体操作步骤时调用)",
    "input_schema": {
        "type": "object",
        "properties": {"skill_name": {"type": "string"}},
        "required": ["skill_name"]
    }
})

call_tool函数中增加分支:

def call_tool(tool_name, tool_input):
    if tool_name == "run_command":
        # ... 同上
    elif tool_name == "load_skill":
        return load_full_skill(tool_input["skill_name"])
    return None

工作流程

  1. 模型通过技能描述知道有web_search技能。
  2. 用户要求“搜索热门新闻”,模型决定调用load_skill("web_search")
  3. 获取完整技能内容(包含具体搜索方法)后,模型再调用真正的搜索工具。
  4. 最终完成搜索任务并回答。

7. 完整Agent代码结构总结

功能模块 代码行数(参考) 关键实现
基础模型调用 ~20行 SDK + API
循环+记忆 ~15行 while循环 + history列表
系统提示词 ~5行 system参数
工具调用(单个工具) ~40行 tools定义 + while循环处理tool_use
Skill加载器 ~25行 manifest + 按需读取
最终整合 ~100-200行 将所有模块嵌套

💡核心代码骨架

import anthropic, subprocess, os

client = anthropic.Anthropic(api_key="...")
history = []
SYSTEM_PROMPT = "..."  # 含技能描述
tools = [...]          # run_command, load_skill等

while True:
    user_input = input("> ")
    if user_input == "exit": break
    history.append({"role": "user", "content": user_input})
    
    response = client.messages.create(
        model="claude-3-opus-20240229",
        system=SYSTEM_PROMPT,
        messages=history,
        tools=tools,
        tool_choice="auto"
    )
    
    while response.stop_reason == "tool_use":
        tool_use = next(b for b in response.content if b.type == "tool_use")
        result = call_tool(tool_use.name, tool_use.input)
        history.append({
            "role": "user",
            "content": [{"type": "tool_result", "tool_use_id": tool_use.id, "content": result}]
        })
        response = client.messages.create(...)
    
    final = response.content[0].text
    print(final)
    history.append({"role": "assistant", "content": final})

与原理解析的对应关系

  • 外层while保证持续对话
  • history实现记忆
  • system参数赋予人格
  • tools + 内层while处理工具调用循环
  • load_skill + 技能目录实现渐进式披露

与Claude Code、Codex等工程化智能体相比,我们搭建的是最小原型。但亲手实现一遍,对Agent的理解会远超只用现成产品。

8. 参考资源

  • 本文基于B站视频@小单说《从零实现自己的agent第二期:百行代码从零手搓agent》撰写,Ai好记辅助整理。如果你正在通过视频学习Ai知识、工具教程,需要将视频中的讲解内容快速整理成笔记或提取关键代码片段,也可以试试这个工具,无广,亲测好用!

如有问题欢迎评论区交流。如果本文对你有帮助,点赞、收藏、转发支持~

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐