具体的代码工程点击这里跳转至GitHub:
从零理解 ReAct Agent:一个可运行的极简 AI Agent 教学项目
下面是内容是上述GitHub的ReadMe的复制,你可以直接打开上述的GitHub查看:
这是一个面向初学者的、尽量少依赖的 ReAct Agent 示例项目。它用最基础的 Python 标准库和少量第三方库,演示了一个 AI Agent 是如何:
- 接收用户问题
- 让大模型分步思考
- 调用工具获取外部信息
- 把工具结果反馈给模型
- 最终给出答案
如果你想学习的不是“如何接一个现成 Agent 框架”,而是“一个 Agent 的核心闭环到底怎么自己写出来”,这个项目就是为这个目标准备的。
- 基于经典
ReAct思路:Thought -> Action -> Observation - 使用本地
Ollama作为模型后端,便于离线或低成本实验 - 工具系统足够简单,便于看懂和扩展
- 支持流式打印模型输出
- 代码量小,适合逐文件学习
- 注释偏教学风格,适合初学者边跑边读
当前内置了 5 个工具:
Calculator:计算数学表达式Search:做网页搜索,返回标题、摘要和 URLReadWeb:读取网页正文文本GetWeather:获取指定城市天气GetCurrentTime:获取当前日期和时间
因此它比较适合演示这几类问题:
- 需要查询事实的问题
- 需要多步搜索再阅读网页的问题
- 需要计算的问题
- 需要时间或天气等外部信息的问题
这个项目的核心不是“让模型一口气直接回答”,而是让模型按固定格式一轮一轮地工作:
- 模型先输出一条
Thought - 模型再输出一条
Action - Python 程序解析这条
Action - 程序调用对应工具,得到
Observation - 程序把
Observation追加回历史 - 模型继续下一轮,直到输出
Finish[...]
也就是说,真正的“Agent”不是单独某个模型,而是这三个部分的组合:
LLM:负责推理与决策Tools:负责与外部世界交互Loop:负责约束格式、执行动作、反馈结果
.
├── main.py # 项目入口:交互模式 / demo 模式
├── llm_client.py # 对 Ollama API 的轻量封装,负责流式读取模型输出
├── react_agent.py # ReAct 主循环:解析 Thought/Action,执行工具,回填 Observation
├── tools.py # 工具注册、工具实现、网页读取和文本清洗
├── requirements.txt # 第三方依赖
└── README.md
建议阅读顺序:
main.pyreact_agent.pytools.pyllm_client.py
这样更容易先看懂主流程,再看细节。
- Python 3.9+
- 本地已安装并启动
Ollama - 本地已有可用模型
项目默认使用的模型名是 gemma4:26b。如果你的本地 Ollama 没有这个模型,可以通过环境变量覆盖。
git clone <your-repo-url>
cd reactpip install -r requirements.txt当前依赖非常少,主要是:
ddgs:给Search工具做网页搜索
如果你还没有安装 Ollama,请先参考 Ollama 官网。
启动服务后,确保下面这个地址可访问:
http://localhost:11434
你可以使用你本地已经安装好的任意兼容聊天接口的模型。
例如,如果你想使用代码当前默认值对应的模型:
ollama pull gemma4:26b如果你想改用别的模型,比如注释里也提到过的 qwen3.5:9b,可以这样:
ollama pull qwen3.5:9b
export OLLAMA_MODEL=qwen3.5:9bpython main.py启动后你可以直接输入问题,例如:
今天是几号?星期几?北京现在天气怎么样?地球的半径大约是多少公里?它的表面积大约是多少平方公里?
退出方式:
- 输入
quit - 输入
exit - 输入
q
python main.py --demo它会自动运行几道预设问题,适合第一次观察 Agent 的执行过程。
运行时,终端通常会出现类似这样的结构:
============================================================
问题: 爱因斯坦和牛顿,谁出生得更早?早了多少年?
============================================================
--- Step 1/10 ---
Thought: 我需要先查询两人的出生年份。
Action: Search[爱因斯坦 牛顿 出生年份]
Observation: ...
--- Step 2/10 ---
Thought: 我已经得到候选信息,还需要确认并计算差值。
Action: Calculator[1643 - 1879]
Observation: -236
--- Step 3/10 ---
Thought: 我已经有足够信息,可以给出最终答案。
Action: Finish[牛顿出生得更早,约早了236年。]
这就是 ReAct 闭环的完整工作过程。
demo效果展示:
作用:组装所有模块,然后启动交互循环。
它主要做三件事:
- 创建
LLMClient - 创建默认工具集
- 创建
ReActAgent
作用:把消息发给 Ollama,并流式读取返回结果。
关键点:
- 请求
Ollama /api/chat - 开启
stream=True - 按行读取返回的 JSON chunk
- 从每个 chunk 中提取
thinking和content - 实时打印,并把最终正文拼接起来返回
这个文件主要帮助你理解:模型流式输出是如何被 Python 一边接收一边处理的。
作用:实现 ReAct Agent 的主循环。
这里是项目最核心的部分。run(question) 大致流程如下:
- 构建系统提示词和当前轮 user prompt
- 调用模型
- 从模型输出中解析
Thought和Action - 判断是否为
Finish[...] - 如果不是,就执行对应工具
- 把
Observation写回历史 - 继续下一轮
为了提高稳定性,这里还做了几件很重要的事:
- 检查
Action格式是否合法 - 防止模型连续输出非法格式
- 防止模型重复调用本质相同的动作
- 限制最大步数,避免无限循环
作用:定义工具注册中心,以及各个具体工具的实现。
这里你可以学到两个很重要的工程点:
- 如何设计一个简单但清晰的工具接口
- 如何把网页搜索、网页读取、文本清洗等能力独立封装
ToolExecutor 的职责很简单:
- 注册工具
- 根据工具名分发执行
- 生成给 LLM 看的工具描述文本
很多 Agent 框架一上来就会出现很多概念:
- memory
- planner
- graph
- node
- router
- callback
- middleware
- state machine
这些概念并不是没用,但对初学者来说,很容易在还没搞懂“Agent 最小闭环”的时候,就被框架细节淹没。
这个项目刻意保持简洁,目的就是让你先真正理解下面这件事:
Agent 的本质不是“神秘框架”,而是“模型 + 工具 + 受控循环”。
当你把这个最小闭环真正看懂,再去学更复杂的框架,理解会快很多。
因为这是最容易看懂的 ReAct 实现方式。
模型输出自由文本,程序再用规则把它解析成结构化动作。
这不是最强壮的工业方案,但非常适合教学。
如果不做这件事,模型可能会不断重复:
Search[奥巴马 出生年份]Search[奥巴马 出生年份]Search[奥巴马 出生年份]
这在真实运行中非常常见。
所以代码会把工具名和输入做归一化,识别“本质重复”的动作。
因为大模型并不总能可靠地收敛。
没有步数上限时,Agent 可能会一直循环下去。
这是一个教学项目,所以它是“清晰优先”,不是“生产可用优先”。
当前局限包括:
Action解析依赖固定文本格式- 搜索结果依赖外部网络环境,可能超时
- 网页正文提取是启发式做法,不是完整阅读器
- 没有持久化 memory
- 没有 tool schema / function calling
- 没有多 Agent 协作
- 没有测试覆盖和完善的容错体系
这些局限并不影响它作为一个很好的学习样例。
你可以尝试继续做这些练习:
- 给
Search增加重试和备用搜索源 - 给
ReadWeb增加更稳的正文抽取 - 改造
Action解析逻辑,让它更鲁棒 - 给工具系统增加参数校验
- 把日志输出改成结构化日志
- 增加单元测试
- 改造成支持 JSON schema / function calling 的版本
- 增加记忆模块或对话历史压缩
这些都很适合作为“从教学版走向工程版”的下一步。
如果你是第一次接触 Agent,建议按下面顺序学习:
- 先运行
python main.py --demo - 观察终端里每一步的
Thought / Action / Observation - 阅读
main.py - 阅读
react_agent.py的run()主循环 - 阅读
ToolExecutor和各个工具实现 - 最后再看
llm_client.py的流式读取细节
这样你会更容易建立整体心智模型。
Search 工具依赖外部网络和搜索后端。如果当前网络环境较慢、受限或目标搜索引擎超时,就可能返回:
Search 失败: Request timed out ...
这通常不是 Agent 主循环的问题,而是外部网络请求失败。
大模型并不是严格编译器。即使 prompt 已经要求输出:
Thought: ...
Action: ...
它仍然可能偶尔偏离格式。
这也是 react_agent.py 中要做解析失败纠偏和非法 Action 检测的原因。
因为这个仓库的目标是教学:帮助你先理解 Agent 的底层闭环,而不是先学习框架 API。
这个仓库特别适合以下读者:
- 想从零理解 Agent 工作原理的人
- 会一点 Python,但还没自己写过 Agent 的人
- 想看一个“小而完整”的 ReAct 教学实现的人
- 想把 Agent 原理讲给别人听的老师或学习者
如果你准备开源到 GitHub,建议补充一个明确的开源许可证,例如:
MITApache-2.0
如果这个项目对你理解 AI Agent 有帮助,欢迎你继续自己扩展它。
最推荐的学习方式不是“看完就结束”,而是边看边改:
- 自己加一个工具
- 自己改一个 prompt
- 自己处理一个异常
- 自己让它回答一种新问题
当你能亲手改动这些地方时,你对 Agent 的理解才会真正扎实起来。