从核心原理到完整实现,百行代码手搓AI Agent教程
摘要:OpenClaw、Claude Code等Agent被过度神化?本质上就是一套模型+记忆+工具+技能的工程系统。本文用不到200行代码,从零搭建一个具备持续对话、上下文记忆、系统人设、工具调用和渐进式Skill加载能力的Agent。全程带可运行代码示例和原理讲解,适合想深入理解Agent底层机制的开发者。
目录
- 破除神话:Agent的本质
- 第一步:搭建Agent大脑
- 第二步:实现持续对话与上下文记忆
- 第三步:赋予Agent人格(系统提示词)
- 第四步:工具调用——让Agent长出“手脚”
- 第五步:Skill系统与渐进式披露
- 完整Agent代码结构总结
- 参考资源
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_reason为tool_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
工作流程:
- 模型通过技能描述知道有
web_search技能。 - 用户要求“搜索热门新闻”,模型决定调用
load_skill("web_search")。 - 获取完整技能内容(包含具体搜索方法)后,模型再调用真正的搜索工具。
- 最终完成搜索任务并回答。
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知识、工具教程,需要将视频中的讲解内容快速整理成笔记或提取关键代码片段,也可以试试这个工具,无广,亲测好用!
如有问题欢迎评论区交流。如果本文对你有帮助,点赞、收藏、转发支持~
更多推荐



所有评论(0)