项目简介

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:

  1. 结构化

    : 便于解析和处理

  2. 可靠性

    : 减少 LLM 输出的不确定性

  3. 可控性

    : 明确区分思考和行动

  4. 调试友好

    : 容易看出 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: 复杂任务可以通过以下方式处理:

  1. 增加 MAX_STEPS

    : 允许更多推理步骤

  2. 添加更多技能

    : 分解任务为多个技能

  3. 优化系统提示

    : 引导 LLM 更好地推理

示例:计算 (10 + 20) * 3

需要:

  • add_numbers

     技能

  • multiply_numbers

     技能

Agent 会自动分解为两步:

  1. 先计算 10 + 20 = 30

  2. 再计算 30 * 3 = 90

Q7: 如何提高准确性?

A:

  1. 使用更强的模型

    : GPT-4 > GPT-3.5

  2. 降低 temperature

    : 从 0.7 降到 0.1(已设置)

  3. 优化系统提示

    : 提供更清晰的指令和示例

  4. 增加错误处理

    : 在技能中验证参数

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)    # 记忆

关键要点

  1. LLM 提供推理能力

     - 基于历史决策

  2. 循环实现持续推理

     - 思考→行动→观察

  3. 技能扩展 Agent 能力

     - 工具/函数

  4. 观察驱动下一步

     - 根据结果调整

  5. JSON 约束输出

     - 可靠性和可控性

下一步

理解了 Simple Agent,你可以:

  • ✅ 添加更多技能
  • ✅ 优化推理逻辑
  • ✅ 集成外部系统
  • ✅ 构建实际应用
  • ✅ 探索 Ultimate Agent

最后的话

Agent 不是魔法,就是:

一个循环 + 一个 JSON 解析 + 几个函数调用

理解了这个,你就掌握了 AI Agent 的本质。🎓



Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐