做一个simple-agent
LangChain 有 3000+ 个文件,抽象层级太多。许多人把 Agent 描述得很复杂,提到"认知架构"、"自主决策引擎"等术语。parse_json_response() - 解析 JSON。(GPT-4, Claude, DeepSeek 等)技能就是普通的 Python 函数,通过字典注册即可。理解了这个,你就掌握了 AI Agent 的本质。是一个极简的 AI Agent 实现,用不
项目简介
Simple Agent 是一个极简的 AI Agent 实现,用不到 315 行代码展示了 Agent 的核心原理。
核心公式
Agent = LLM + Loop + Skill
- LLM (Large Language Model)
: 大语言模型提供推理能力
- Loop
: 循环执行"思考→行动→观察"
- Skill
: 工具/技能扩展 Agent 的能力
为什么做 Simple Agent?
✅ 极简设计 - 5个文件,300行代码,清晰易懂
✅ 核心聚焦 - 展示 Agent 本质,没有复杂抽象
✅ 实战导向 - 可运行的完整示例
✅ 易于扩展 - 添加新技能只需几行代码
✅ 框架无关 - 只依赖标准 API,兼容所有 LLM
技术栈
- Python 3.7+
- httpx
- HTTP 客户端
- python-dotenv
- 环境变量管理
- 任何支持 `/chat/completions` 的 LLM API
(GPT-4, Claude, DeepSeek 等)
核心原理
Agent 的本质
许多人把 Agent 描述得很复杂,提到"认知架构"、"自主决策引擎"等术语。但实际上,Agent 的核心机制非常简单:
# 这就是 Agent 的全部秘密!
while 未完成:
# 1. LLM 推理下一步
action = LLM.think(history)
# 2. 执行动作
if action == "answer":
return answer
elif action == "use_skill":
result = execute_skill()
# 3. 观察结果加入历史 (关键!)
history.append(result)
ReAct 循环
Simple Agent 基于 ReAct (Reasoning + Acting) 模式:
用户输入
↓
┌─────────────────────┐
│ 1. Reasoning │ ← LLM 基于历史推理
│ (思考) │
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 2. Acting │ ← 执行技能/返回答案
│ (行动) │
└──────────┬──────────┘
↓
┌─────────────────────┐
│ 3. Observation │ ← 观察结果
│ (观察) │
└──────────┬──────────┘
↓
加入历史 → 回到步骤1
关键设计
1. JSON 约束输出
强制 LLM 返回结构化 JSON:
{
"thought": "我的思考过程",
"action": "use_skill",
"skill_name": "add_numbers",
"skill_args": {"a": 25, "b": 37}
}
好处:
- 可靠解析
- 减少不确定性
- 明确区分思考和行动
2. 观察驱动
# 关键!将观察结果注入历史
history.append({
"role": "user",
"content": f"技能执行结果: {result}"
})
这样 LLM 才能基于结果决定下一步,而不是盲目执行预定计划。
3. 技能注册
SKILLS = {
"add_numbers": {
"function": add_numbers,
"description": "计算两个数字的和"
}
}
技能就是普通的 Python 函数,通过字典注册即可。
项目结构
simple-agent/
├── config.py # 配置管理 (~15行)
│ └── API_KEY, API_BASE_URL, MODEL_NAME, MAX_STEPS
│
├── skills.py # 技能定义 (~60行)
│ ├── add_numbers() # 加法技能
│ ├── SKILLS{} # 技能注册表
│ ├── get_skills_description() # 获取技能描述
│ └── call_skill() # 调用技能
│
├── llm.py # LLM 接口 (~70行)
│ ├── call_llm() # 调用大语言模型
│ └── parse_json_response() # 解析 JSON 响应
│
├── agent.py # 核心引擎 (~120行)
│ └── SimpleAgent
│ ├── _build_system_prompt() # 构建系统提示词
│ ├── run() # 主循环 (核心!)
│ └── clear_history() # 清空历史
│
├── main.py # 主程序入口 (~50行)
│ └── main() # 交互式命令行
│
├── .env.example # 环境变量示例
├── requirements.txt # 依赖包
├── .gitignore # Git 忽略文件
├── README.md # 项目说明
└── GUIDE.md # 本文件
总代码量: ~315 行(含注释)
代码详解
1. agent.py - 核心引擎
这是整个项目的核心,实现了 ReAct 循环。
关键方法:run()
def run(self, user_query: str) -> str:
"""Agent 的主循环"""
# 添加用户消息
self.history.append({"role": "user", "content": user_query})
# 开始循环
for step in range(config.MAX_STEPS):
# 1. 构建消息 = 系统提示 + 历史
messages = [
{"role": "system", "content": self.system_prompt}
] + self.history
# 2. 调用 LLM 推理
llm_response = call_llm(messages)
# 3. 解析 JSON
action = parse_json_response(llm_response)
# 4. 执行动作
if action["action"] == "answer":
return action["answer"] # 完成!
elif action["action"] == "use_skill":
# 调用技能
result = call_skill(
action["skill_name"],
**action["skill_args"]
)
# 5. 将观察结果加入历史 (关键!)
self.history.append({"role": "assistant", "content": llm_response})
self.history.append({
"role": "user",
"content": f"技能执行结果: {result}"
})
# 继续下一轮循环...
return"已达到最大步数"
核心思想:
- 每次循环都调用 LLM 决策
- 执行动作后,将结果加入历史
- LLM 基于完整历史决定下一步
系统提示词
def _build_system_prompt(self) -> str:
return f"""你是一个 AI Agent,可以使用技能来完成任务。
## 可用技能
{get_skills_description()}
## 响应格式
你必须用 JSON 格式回复:
{{
"thought": "你的思考过程",
"action": "use_skill" 或 "answer",
"skill_name": "技能名称",
"skill_args": {{"参数": "值"}},
"answer": "最终答案"
}}
## 示例
步骤1: {{"thought": "...", "action": "use_skill", ...}}
步骤2: {{"thought": "...", "action": "answer", ...}}
"""
2. llm.py - LLM 接口
call_llm() - 调用大语言模型
def call_llm(messages: List[Dict[str, str]]) -> str:
"""调用 LLM API"""
url = f"{config.API_BASE_URL}/chat/completions"
payload = {
"model": config.MODEL_NAME,
"messages": messages,
"temperature": 0.1, # 降低随机性
"response_format": {"type": "json_object"} # 强制 JSON!
}
response = httpx.post(url, json=payload, headers=headers)
return response.json()["choices"][0]["message"]["content"]
关键点:
response_format强制 LLM 输出 JSON
temperature=0.1让输出更确定性
-
使用标准的
/chat/completions接口
parse_json_response() - 解析 JSON
def parse_json_response(text: str) -> dict:
"""容错的 JSON 解析"""
try:
return json.loads(text.strip())
except:
# 尝试提取 JSON 部分
match = re.search(r'\{.*\}', text, re.DOTALL)
if match:
return json.loads(match.group(0))
raise ValueError("无法解析 JSON")
3. skills.py - 技能系统
定义技能
def add_numbers(a: float, b: float) -> dict:
"""加法技能"""
try:
result = float(a) + float(b)
return {
"success": True,
"result": result,
"message": f"{a} + {b} = {result}"
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
设计原则:
- 技能就是普通函数
- 返回结构化结果(dict)
- 包含成功/失败标志
注册技能
SKILLS = {
"add_numbers": {
"function": add_numbers,
"description": "计算两个数字的和。参数: a, b"
}
}
调用技能
def call_skill(skill_name: str, **kwargs) -> dict:
"""动态调用技能"""
if skill_name not in SKILLS:
return {"success": False, "error": "技能不存在"}
skill_func = SKILLS[skill_name]["function"]
return skill_func(**kwargs)
4. config.py - 配置管理
import os
from dotenv import load_dotenv
load_dotenv()
# API 配置
API_KEY = os.getenv("API_KEY", "")
API_BASE_URL = os.getenv("API_BASE_URL", "https://api.openai.com/v1")
MODEL_NAME = os.getenv("MODEL_NAME", "gpt-4o-mini")
# Agent 配置
MAX_STEPS = 10 # 最大推理步数
5. main.py - 程序入口
def main():
agent = SimpleAgent()
whileTrue:
user_input = input(">>> ").strip()
if user_input == "quit":
break
if user_input == "clear":
agent.clear_history()
continue
# 运行 Agent
answer = agent.run(user_input)
print(f"📋 总结: {answer}")
如何扩展
添加新技能(超简单!)
步骤 1: 定义技能函数
编辑 skills.py,添加新函数:
def subtract_numbers(a: float, b: float) -> dict:
"""减法技能"""
try:
result = float(a) - float(b)
return {
"success": True,
"result": result,
"message": f"{a} - {b} = {result}"
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
步骤 2: 注册技能
在 SKILLS 字典中添加:
SKILLS = {
"add_numbers": {
"function": add_numbers,
"description": "计算两个数字的和。参数: a (数字), b (数字)"
},
"subtract_numbers": { # 新增!
"function": subtract_numbers,
"description": "计算两个数字的差。参数: a (数字), b (数字)"
}
}
步骤 3: 重启程序
python3 main.py
就这么简单! Agent 会自动学会使用新技能。
更多技能示例
乘法技能
def multiply_numbers(a: float, b: float) -> dict:
"""乘法技能"""
return {
"success": True,
"result": float(a) * float(b),
"message": f"{a} × {b} = {float(a) * float(b)}"
}
SKILLS["multiply_numbers"] = {
"function": multiply_numbers,
"description": "计算两个数字的乘积。参数: a, b"
}
获取当前时间
from datetime import datetime
def get_current_time() -> dict:
"""获取当前时间"""
now = datetime.now()
return {
"success": True,
"time": now.strftime("%Y-%m-%d %H:%M:%S"),
"message": f"当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}"
}
SKILLS["get_current_time"] = {
"function": get_current_time,
"description": "获取当前日期和时间。无需参数。"
}
搜索网页(示例)
import httpx
def search_web(query: str) -> dict:
"""搜索网页"""
try:
# 这里可以接入真实的搜索 API
return {
"success": True,
"results": f"搜索 '{query}' 的结果...",
"message": "搜索完成"
}
except Exception as e:
return {"success": False, "error": str(e)}
SKILLS["search_web"] = {
"function": search_web,
"description": "在网络上搜索信息。参数: query (搜索关键词)"
}
切换 LLM 模型
只需编辑 .env 文件:
# 使用 GPT-4
MODEL_NAME=gpt-4
# 使用 DeepSeek (便宜)
API_BASE_URL=https://api.deepseek.com/v1
MODEL_NAME=deepseek-chat
# 使用 Claude
API_BASE_URL=https://api.anthropic.com/v1
MODEL_NAME=claude-sonnet-4-5
无需修改代码!
设计亮点
1. 极简架构
|
对比项 |
Simple Agent |
LangChain |
|---|---|---|
|
文件数 |
5 个 |
3000+ 个 |
|
代码行数 |
~315 行 |
100,000+ 行 |
|
依赖包 |
2 个 |
50+ 个 |
|
学习曲线 |
1 小时 |
数周 |
优势:
- ✅ 代码清晰,易于理解
- ✅ 没有过度抽象
- ✅ 便于调试和定制
- ✅ 适合学习和原型开发
2. JSON 约束
强制 LLM 输出 JSON 格式:
payload = {
"response_format": {"type": "json_object"}
}
好处:
- 可靠性
: 避免解析失败
- 结构化
: 易于处理
- 可控性
: 减少 LLM 的不确定性
3. 观察驱动的推理
# 错误做法:直接返回结果
result = call_skill()
return result # ❌ 没有进一步推理
# 正确做法:将结果注入历史
result = call_skill()
history.append({"role": "user", "content": f"结果: {result}"})
# LLM 基于结果决定下一步 ✅
这是 ReAct 的核心:根据观察调整行动。
4. 技能即函数
# 技能就是普通的 Python 函数
def add_numbers(a, b):
return {"result": a + b}
# 通过字典注册
SKILLS["add_numbers"] = {
"function": add_numbers,
"description": "..."
}
优势:
- 简单直观
- 易于测试
- 无需学习复杂的框架
5. 兼容所有 LLM
只要支持 /chat/completions 接口即可:
- OpenAI (GPT-4, GPT-3.5)
- Anthropic (Claude)
- DeepSeek
- Moonshot
- 本地模型 (Ollama, vLLM 等)
无需修改代码!
快速开始
第一步:安装依赖
cd simple-agent
pip3 install -r requirements.txt
依赖包:
httpx- HTTP 客户端
python-dotenv- 环境变量管理
第二步:配置 API
2.1 复制配置文件
cp .env.example .env
2.2 编辑配置文件
nano .env # 或使用你喜欢的编辑器
2.3 填入配置
选项 A: 使用 OpenAI
API_KEY=sk-your-openai-api-key
API_BASE_URL=https://api.openai.com/v1
MODEL_NAME=gpt-4o-mini
选项 B: 使用 DeepSeek (便宜!)
API_KEY=sk-your-deepseek-key
API_BASE_URL=https://api.deepseek.com/v1
MODEL_NAME=deepseek-chat
选项 C: 使用 ollama
API_KEY=ollama
API_BASE_URL=http://localhost:11434/v1
MODEL_NAME=qwen2.5:7b
选项 D: 使用其他兼容 OpenAI 格式的 API
只要支持 /chat/completions 接口即可。
第三步:运行 Agent
python3 main.py
你会看到:
╔══════════════════════════════════════════════════════════╗
║ ║
║ 🤖 Simple AI Agent ║
║ ║
║ Agent = LLM + Loop + Skill ║
║ ║
╚══════════════════════════════════════════════════════════╝
命令:
clear - 清空对话历史
quit - 退出程序
可用技能:
- add_numbers: 计算两个数的和
示例问题:
- 计算 25 + 37
- 88 加 22 等于多少
- 把 100 和 200 加起来
============================================================
>>>
第四步:尝试问题
>>> 计算 25 + 37
>>> 88 加 22 等于多少
>>> 把 100 和 200 加起来
运行示例
示例 1: 简单加法
============================================================
>>> 计算 88 + 22
============================================================
🤖 用户: 计算 88 + 22
============================================================
📍 步骤 1
💭 正在思考...
💡 思考: 用户要计算加法,我应该使用 add_numbers 技能
⚙️ 调用技能: add_numbers
参数: {"a": 88, "b": 22}
结果: {"success": true, "result": 110.0, "message": "88 + 22 = 110.0"}
📍 步骤 2
💭 正在思考...
💡 思考: 技能返回了结果,我可以给出答案了
✅ 最终答案: 88 + 22 = 110
============================================================
📋 总结: 88 + 22 = 110
============================================================
示例 2: 自然语言
============================================================
>>> 把 25 和 37 加起来
============================================================
🤖 用户: 把 25 和 37 加起来
============================================================
📍 步骤 1
💭 正在思考...
💡 思考: 用户想要计算 25 + 37,我需要使用 add_numbers 技能
⚙️ 调用技能: add_numbers
参数: {"a": 25, "b": 37}
结果: {"success": true, "result": 62.0, "message": "25 + 37 = 62.0"}
📍 步骤 2
💭 正在思考...
💡 思考: 已经得到结果,可以回答用户了
✅ 最终答案: 25 + 37 = 62
============================================================
📋 总结: 25 + 37 = 62
============================================================
内置命令
>>> clear # 清空对话历史
✅ 历史已清空
>>> quit # 退出程序
👋 再见!
常见问题
Q1: 为什么不用 LangChain?
A: LangChain 有 3000+ 个文件,抽象层级太多。本项目证明 Agent 的核心机制非常简单,不需要庞大的框架。
理解了原理,你可以:
- 根据需求定制功能
- 避免框架的限制
- 更容易调试问题
Q2: 这个 Agent 能做什么?
A: 目前只能做加法,但你理解了原理,可以添加任何技能:
- ✅ 数学运算(加减乘除、开方、阶乘)
- ✅ 文本处理(翻译、摘要、分析)
- ✅ 网络搜索(Google、Wikipedia)
- ✅ 文件操作(读写、搜索)
- ✅ 数据库查询(SQL、NoSQL)
- ✅ API 调用(天气、股票、新闻)
- ✅ 代码执行(Python、JavaScript)
- ✅ …
关键:技能就是函数,你想做什么就写什么函数。
Q3: 如何防止 Agent 死循环?
A: 设置了 MAX_STEPS = 10,最多执行 10 步。
你可以在 config.py 中调整:
MAX_STEPS = 20 # 增加到 20 步
Q4: 为什么要强制 JSON 输出?
A:
- 结构化
: 便于解析和处理
- 可靠性
: 减少 LLM 输出的不确定性
- 可控性
: 明确区分思考和行动
- 调试友好
: 容易看出 LLM 的决策过程
Q5: 支持哪些 LLM?
A: 支持任何提供 /chat/completions 接口的 LLM:
|
LLM |
配置 |
|---|---|
|
OpenAI GPT-4 |
API_BASE_URL=https://api.openai.com/v1 |
|
DeepSeek |
API_BASE_URL=https://api.deepseek.com/v1 |
|
Claude |
API_BASE_URL=https://api.anthropic.com/v1 |
|
Moonshot |
API_BASE_URL=https://api.moonshot.cn/v1 |
|
Ollama (本地) |
API_BASE_URL=http://localhost:11434/v1 |
Q6: 如何处理复杂任务?
A: 复杂任务可以通过以下方式处理:
- 增加 MAX_STEPS
: 允许更多推理步骤
- 添加更多技能
: 分解任务为多个技能
- 优化系统提示
: 引导 LLM 更好地推理
示例:计算 (10 + 20) * 3
需要:
add_numbers技能
multiply_numbers技能
Agent 会自动分解为两步:
-
先计算
10 + 20 = 30 -
再计算
30 * 3 = 90
Q7: 如何提高准确性?
A:
- 使用更强的模型
: GPT-4 > GPT-3.5
- 降低 temperature
: 从 0.7 降到 0.1(已设置)
- 优化系统提示
: 提供更清晰的指令和示例
- 增加错误处理
: 在技能中验证参数
Q8: 如何保存对话历史?
A: 当前历史保存在内存中。要持久化,可以:
import json
# 保存历史
def save_history(agent, filename="history.json"):
with open(filename, 'w') as f:
json.dump(agent.history, f, ensure_ascii=False, indent=2)
# 加载历史
def load_history(agent, filename="history.json"):
with open(filename, 'r') as f:
agent.history = json.load(f)
Q9: 能否并行执行多个技能?
A: 当前是顺序执行。要并行执行,可以使用 asyncio:
import asyncio
async def call_skill_async(skill_name, **kwargs):
# 异步执行技能
...
# 并行调用多个技能
results = await asyncio.gather(
call_skill_async("skill1"),
call_skill_async("skill2")
)
Q10: 如何监控 Agent 的执行?
A: 项目已经包含详细的日志输出:
print(f"💡 思考: {thought}")
print(f"⚙️ 调用技能: {skill_name}")
print(f" 结果: {result}")
要保存日志到文件:
import sys
# 重定向输出到文件
with open("agent.log", "w") as f:
sys.stdout = f
agent.run(query)
推荐学习资源
- ReAct 论文
: ReAct: Synergizing Reasoning and Acting in Language Models
- OpenAI API 文档
: https://platform.openai.com/docs/api-reference
- Python asyncio 教程
: 用于实现异步技能
项目对比
Simple Agent vs Ultimate Agent
|
特性 |
Simple Agent |
Ultimate Agent |
|---|---|---|
| 代码量 |
~315 行 |
~2000 行 |
| 文件数 |
5 个核心文件 |
15+ 文件 |
| 技能系统 |
函数注册 |
Markdown 文档 + 插件 |
| 安全机制 |
无 |
三层防御 |
| 命令执行 |
无 |
subprocess 执行 |
| 热重载 |
需重启 |
支持热重载 |
| 适用场景 |
学习/原型 |
生产环境 |
| 学习曲线 |
平缓 |
中等 |
选择建议:
- 学习原理 → Simple Agent
- 生产部署 → Ultimate Agent
总结
核心价值
这个项目证明了一个核心观点:
Agent 的本质非常简单!
去掉炫技的包装,Agent 就是:
while True:
action = LLM(history) # 推理
result = execute(action) # 执行
history.append(result) # 记忆
关键要点
- LLM 提供推理能力
- 基于历史决策
- 循环实现持续推理
- 思考→行动→观察
- 技能扩展 Agent 能力
- 工具/函数
- 观察驱动下一步
- 根据结果调整
- JSON 约束输出
- 可靠性和可控性
下一步
理解了 Simple Agent,你可以:
- ✅ 添加更多技能
- ✅ 优化推理逻辑
- ✅ 集成外部系统
- ✅ 构建实际应用
- ✅ 探索 Ultimate Agent
最后的话
Agent 不是魔法,就是:
一个循环 + 一个 JSON 解析 + 几个函数调用
理解了这个,你就掌握了 AI Agent 的本质。🎓
更多推荐

所有评论(0)