配套代码:simpleAgent 项目
agentloop/01_while_loop.py(手写 AgentLoop)
一、一个关键转变:从"问一句"到"干一件事"
用 ChatGPT 时我们是"一问一答":问一句,它答一句。
但真实工作不是这样------"把收容所的小猫领养出去"这个任务,需要一连串动作:
- 第 1 步:先看看收容所有哪些宠物? (工具:列出目录)
- 第 2 步:找到猫咪小花,把它移走 (工具:移动文件)
- 第 3 步:再确认一下现在剩下什么 (工具:列出目录)
- 第 4 步:向用户汇报结果 (输出总结)
AI 要完成这类任务,不能只答一句话,它需要反复地"思考 → 动手 → 看结果 → 再思考" 。
这个循环,就是 AgentLoop(Agent 主循环) 。

一句话:AgentLoop = 反复执行"模型提建议 + 程序执行(ToolCall) + 结果回传",直到模型认为任务完成。
二、为什么"工具执行权"必须在我们手里?
这是 Agent 设计最重要的安全原则:
-
模型只会说:"我想调用 move_file 工具,参数是 inbox/猫咪小花.txt → archive/"
-
真正移动文件的是我们的代码
-
我们可以在执行前做任何检查(路径是否合法、权限是否足够、要不要人工确认)
模型输出文字:"我要调 move_file(猫咪小花.txt, archive)"
│
▼
代码:校验 → 执行 → 返回结果
│
▼
结果放回历史 → 模型看到结果 → 决定下一步
这也是为什么 Agent 的能力边界 = 我们给了它多少工具。给它的工具越多、越安全,
它能干的事越多,但风险也越大------工具就是 Agent 的手。
三、AgentLoop 的 4 步本质(手写版,一眼看懂)
参考代码:agentloop/01_while_loop.py,核心只有这几步:
python
for turn in range(1, 8): # ① 限定最多 8 轮,防死循环
response = llm.invoke(messages) # ② 让模型思考(基于全部历史)
messages.append(response) # 模型的话放回历史
if not response.tool_calls: # ③ 模型没要求调工具?
print(response.content) # 那就是最终回答,结束
break
tool_call = response.tool_calls[0] # ④ 模型要求调工具:
result = tool_map[tool_call["name"]].invoke(tool_call["args"]) # 执行!
messages.append(ToolMessage( # 结果放回历史(带 tool_call_id)
content=str(result),
tool_call_id=tool_call["id"],
)) # → 回到 ②,模型继续思考
这 4 步就是所有 Agent 框架的底层原语。
后面学 LangGraph,你会发现它只是把这 4 步"画"成了一张开源的图。
三个必须记住的细节
- 历史要全量回传:每一轮都要把完整对话历史发给模型,模型才能"记得"前面做过什么。
- tool_call_id 要对上号:工具结果必须带模型给的 id,模型才知道"这个结果回应的是哪个调用"。
- 必须有最大轮数 :模型可能陷入死循环(反复调同一个工具),
for range(8)就是保险丝。
四、核心概念:ReAct(推理 + 行动)
Agent 边思考边行动的经典范式叫 ReAct(Reasoning + Acting):
- Reasoning(推理):模型观察当前状态,决定"下一步该干嘛"
- Acting(行动):调用一个工具,观察结果,再推理下一步
txt
Thought: 我需要先看看收容所有哪些宠物
Action: list_files("inbox")
Observation: 有猫咪小花、狗狗旺财、仓鼠球球
Thought: 找到了猫咪小花,现在领养它
Action: move_file("inbox/猫咪小花.txt", "archive/")
Observation: 移动成功
Thought: 任务完成,可以汇报了
Answer: 已领养猫咪小花......
我们的手写循环就是 ReAct 的代码化。模型输出里的"Thought / Action / Observation"不一定是显式文字------现在的大模型通过结构化的 tool_calls 字段直接表达"我要行动",而观察结果由我们回传。
五、动手体验(10 分钟)
bash
cd ~/simpleAgent
source .venv/bin/activate
# 配置 .env 里的 API_KEY(DeepSeek 注册送额度)
cd agentloop
python3 01_while_loop.py
运行后你会亲眼看到每一轮的过程:
txt
--- 第 1 轮:模型思考 ---
模型决定调用工具:list_files({"path": "inbox"})
工具返回 = - inbox/猫咪小花.txt ...
--- 第 2 轮:模型思考 ---
模型决定调用工具:move_file({"source": "inbox/猫咪小花.txt", "target": "archive/"})
工具返回 = 已移动 ...
--- 第 3 轮:模型思考 ---
✅ 最终回答:已完成领养......
推荐练习:
- 把任务改成"领养仓鼠球球"------观察模型怎么自己调整工具参数
- 在代码里故意去掉
ToolMessage回传------看模型会"失忆" - 把
range(1, 8)改成range(1, 2)------看模型来不及完成任务
六、小结
| 概念 | 一句话 |
|---|---|
| AgentLoop | 反复"思考 → 执行工具 → 回传结果"直到任务完成的循环 |
| ReAct | 推理 + 行动交替的经典范式 |
| tool_calls | 模型表达"想调工具"的结构化字段(name / args / id) |
| 执行权 | 永远在我们代码手里,模型只提议 |
| 最大轮数 | 防止死循环的保险丝 |