S01-Agent最小循环教程
第一部分:提出问题:原生大模型只能聊天,无法变成自主干活的Agent
1. 现存现象(原生LLM的天然短板)
大语言模型(LLM)本质是文本生成器,只会根据输入续写文字,天生无法自主落地实操任务,做不到4件关键操作:
- 不能自主打开文件、执行终端/程序命令;
- 无法查看工具运行后的报错、返回数据;
- 不会主动触发外部工具调用;
- 拿不到工具执行结果,没办法根据真实结果继续调整思路、推进剩余任务。
2. 我们的目标需求
把「只会打字聊天的大模型」升级为智能体Agent:收到用户复杂指令后,自主判断何时调用工具、执行工具、依据真实运行结果迭代思考,分步完成完整任务。
3. 核心待解决问题
缺少一套中间调度逻辑,打通链路:模型思考输出→工具真实执行→结果反馈回模型→新一轮思考,这是本章节要解决的唯一核心。
关键结论前置:没有循环,就没有真正可用的Agent。
第二部分:分析问题:拆解Agent无法落地的底层逻辑与基础概念
1. 问题根源:缺失闭环数据流
如果没有代码搭建循环:
模型输出工具调用指令后,指令不会被真实执行;即便工具运行出数据,运行结果只打印在控制台,无法写入上下文传给模型 。
模型永远看不到现实世界的真实反馈,每一轮推理都是孤立的,没办法基于上一步结果做下一步决策,永远停留在单次文字生成。
2. 4个核心名词通俗翻译(新手必记)
| 专业名词 | 大白话解释 |
|---|---|
| Loop(循环) | 不是程序死循环;只要任务没做完,固定重复一套执行步骤,持续迭代 |
| Turn(一轮) | 单次完整运行周期:消息入模型→获取回复→按需调用工具→结果存档→准备下一轮 |
| tool_result(工具结果) | 不只是终端日志,必须存入对话历史,成为模型下一轮的输入素材 |
| state(运行状态) | 循环全程随身带的数据;最小版本=历史消息列表+运行轮次+继续循环的理由 |
3. Agent核心心智流程图(简化版)
用户提问消息 → 送入LLM做推理
├─分支1:模型直接输出最终答案 → 任务结束,循环终止
└─分支2:模型决定调用工具 → 程序执行工具 → 生成tool_result → 结果写入消息历史 → 带着新数据开启新一轮模型调用
灵魂关键点:工具结果必须回写到消息历史,缺这一步,Agent闭环直接断裂。
4. 三类关键数据结构的设计目的(为什么必须这么设计)
(1)Message消息结构体
格式:{"role": "user/assistant", "content": "内容"}
作用:存储全量对话上下文,是模型每一轮推理的原始输入 ,≠前端聊天框展示文案。
坑点:只保存用户消息、丢弃助手回复 → 上下文断层,模型忘记上一轮自己的操作。
(2)Tool Result Block工具结果块
json
{"type": "tool_result","tool_use_id":"对应工具ID","content":"工具输出内容"}
作用:依靠tool_use_id绑定「本次结果」和「对应那次工具调用」,避免模型分不清哪条结果对应哪次指令。
(3)LoopState循环状态集合
python
state = {"messages": [], "turn_count": 1, "transition_reason": None}
作用:收拢所有循环变量,不用零散定义全局变量;transition_reason标记本轮继续循环的原因,入门只用tool_result(刚跑完工具,需要回传结果继续思考)。
第三部分:解决问题:落地最小可用Agent循环(代码分步+避坑+迭代规划)
1. 单轮Turn分步实现(5步最小流程)
步骤1:初始化消息,存入用户原始提问
python
messages = [{"role": "user", "content": 用户输入的查询语句}]
步骤2:调用大模型接口
携带系统提示词(SYSTEM)、工具清单(TOOLS)、全量历史消息,向模型发起请求。
步骤3:强制追加助手回复到messages(新手最高频漏写)
模型返回的assistant内容必须存入消息列表,保存本轮模型思考,否则上下文断裂。
python
messages.append({"role": "assistant", "content": response.content})
步骤4:识别并执行工具
遍历模型返回内容,筛选tool_use类型(工具调用指令),逐个运行对应工具,收集全部输出结果。
步骤5:工具结果打包回写消息
把所有tool_result以user角色追加进messages,完成单轮闭环,开启下一轮循环。
2. 完整封装:最小agent_loop主循环函数
python
def agent_loop(state):
while True:
# 1. 携带状态里的上下文调用模型
response = client.messages.create(model=MODEL,system=SYSTEM,messages=state["messages"],tools=TOOLS,max_tokens=8000)
# 2. 保存本轮助手回复到消息历史
state["messages"].append({"role": "assistant", "content": response.content})
# 3. 模型不调用工具,直接结束循环
if response.stop_reason != "tool_use":
state["transition_reason"] = None
return
# 4. 执行所有工具调用,收集结果
results = []
for block in response.content:
if block.type == "tool_use":
output = run_tool(block) # 执行工具
results.append({"type": "tool_result","tool_use_id": block.id,"content": output})
# 5. 工具结果写入消息,更新状态,进入下一轮
state["messages"].append({"role": "user", "content": results})
state["turn_count"] += 1
state["transition_reason"] = "tool_result"
逻辑说明 :while True维持循环;stop_reason="tool_use"是入门简化终止条件,只有模型要调用工具时才继续循环。
3. 简化设计说明:为什么入门只用stop_reason做终止判断?
初学阶段优先目标:跑通完整闭环链路 ,不纠结复杂边界。只用单一终止条件可以聚焦三件核心:
① 助手回复写入历史;② tool_result写入历史;③ 循环持续迭代。
复杂终止、异常判断、多分支续行逻辑,放到后续章节补充。
4. 初学者5大致命踩坑清单
- 工具结果只打印控制台,不写入messages:模型看不到真实运行数据,无法迭代;
- 只存用户消息,丢弃assistant回复:上下文断层,模型丢失自身上一轮思考记录;
- tool_result不绑定tool_use_id:模型无法匹配结果与工具调用,指令和结果错乱;
- 入门就堆砌流式输出、异常重试、权限管控:基础闭环逻辑被复杂功能掩盖,学不会核心;
- 错把messages当成聊天展示数据:messages是Agent的工作记录本、模型的输入上下文,不是前端展示文案。
5. 后续迭代拓展(本章节是Agent底座)
S01只完成最小闭环,后续s系列章节在当前循环基础上叠加能力:
- s02:接入工具路由管理
- s03:新增任务规划状态
- s06:上下文自动压缩
- s07:工具权限校验
- s11:工具报错自动恢复
6. 最终一句话总结(牢记Agent本质)
Agent Loop(智能体循环)= 把模型空想的工具指令落地成真实结果,再将结果回喂模型,驱动下一轮持续推理。
基于DeepSeek API|3阶入门Agent实战案例(沿用前文Agent循环架构|新手可直接复制运行)
前置准备:
- 安装依赖:
pip install openai(DeepSeek 兼容OpenAI SDK调用格式)- 前往DeepSeek官网获取API_KEY,替换代码内
DEEPSEEK_API_KEY- 接口地址:
base_url="https://api.deepseek.com",模型固定:model="deepseek-chat"
统一代码架构:完全复用上一教程agent_loop循环逻辑,3个案例工具逐个升级,难度循序渐进
公共前置配置(所有案例共用头部代码,放在文件最上方)
python
from openai import OpenAI
# 1、DeepSeek全局配置
DEEPSEEK_API_KEY = "你的DeepSeek密钥"
client = OpenAI(
api_key=DEEPSEEK_API_KEY,
base_url="https://api.deepseek.com"
)
MODEL = "deepseek-chat"
SYSTEM = "你是具备工具调用能力的AI智能体,遇到数学计算、时间查询、文件操作需求必须调用对应工具,禁止自己编造结果。工具执行完成拿到结果后再汇总答案。"
案例1:Agent自带四则计算器工具【入门:单工具、纯数值运算】
提出问题
用户输入数学计算题(如(25+75)*8/2),大模型容易计算出错;需要让AI自主调用计算器工具,通过程序精准运算,不能AI心算。
分析问题
- 模型无法精准做复杂四则运算,依赖自身推理容易出错;
- 设计1个
calculator工具,接收计算公式字符串,代码运行求值; - 沿用Agent循环:用户提问→模型判断要调用计算器→执行代码计算→结果回填消息→模型整理答案结束循环。
解决问题:完整可运行代码
python
# ==========案例1:计算器工具定义==========
def run_tool(tool_info):
"""工具分发函数,匹配工具名称执行对应逻辑"""
if tool_info.name == "calculator":
expr = tool_info.parameters["expr"]
try:
res = eval(expr)
return f"算式{expr}的计算结果:{res}"
except Exception as e:
return f"计算失败,错误:{str(e)}"
# 工具描述:告诉模型可用的工具
TOOLS = [
{
"type": "function",
"function": {
"name": "calculator",
"description": "用于四则数学运算,传入数学表达式字符串,返回计算结果",
"parameters": {
"type": "object",
"properties": {
"expr": {"type": "string", "description": "数学计算公式,例如:(12+38)*5"}
},
"required": ["expr"]
}
}
}
]
# 智能体主循环(和教程原版逻辑完全一致)
def agent_loop(user_input):
state = {
"messages": [{"role": "user", "content": user_input}],
"turn_count": 1,
"transition_reason": None
}
while True:
resp = client.chat.completions.create(
model=MODEL,
messages=state["messages"],
tools=TOOLS
)
choice = resp.choices[0]
msg = choice.message
# 保存模型本轮回答到上下文
state["messages"].append(msg.model_dump(exclude_none=True))
# 没有工具调用,循环结束,输出答案
if not msg.tool_calls:
print("【最终回答】", msg.content)
return
# 执行所有工具
tool_returns = []
for tool_call in msg.tool_calls:
tool_res = run_tool(tool_call.function)
tool_returns.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_res
})
# 工具结果写入消息,开启下一轮思考
state["messages"].extend(tool_returns)
state["turn_count"] += 1
# 测试运行
if __name__ == "__main__":
agent_loop("帮我计算 (125+375)*12/5 - 99")
运行说明
模型识别数学公式→调用calculator→Python代码运算→结果回传给DeepSeek→AI整理结果输出。
案例2:系统时间查询工具【进阶:调用Python内置库获取真实环境数据】
提出问题
大模型知识库时间固定,不知道本机当前年月日时分,无法回答「现在几点、今天日期」;需要调用Python datetime获取本机真实时间。
分析问题
- LLM没有实时系统时间,不能凭空生成当前时间;
- 新增
get_current_time工具,使用datetime获取系统时间; - 循环逻辑不变,仅修改TOOLS和run_tool,实现时间查询。
解决问题:完整可运行代码
python
from datetime import datetime
# ==========案例2:时间查询工具==========
def run_tool(tool_info):
if tool_info.name == "get_current_time":
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return f"当前系统时间:{now}"
TOOLS = [
{
"type": "function",
"function": {
"name": "get_current_time",
"description": "获取服务器/本机当前的年月日、时分秒实时时间,无入参",
"parameters": {"type": "object", "properties": {}, "required": []}
}
}
]
# agent_loop函数和案例1完全一模一样,无需改动直接复用
def agent_loop(user_input):
state = {
"messages": [{"role": "user", "content": user_input}],
"turn_count": 1,
"transition_reason": None
}
while True:
resp = client.chat.completions.create(
model=MODEL,
messages=state["messages"],
tools=TOOLS
)
choice = resp.choices[0]
msg = choice.message
state["messages"].append(msg.model_dump(exclude_none=True))
if not msg.tool_calls:
print("【最终回答】", msg.content)
return
tool_returns = []
for tool_call in msg.tool_calls:
tool_res = run_tool(tool_call.function)
tool_returns.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_res
})
state["messages"].extend(tool_returns)
state["turn_count"] += 1
# 测试
if __name__ == "__main__":
agent_loop("现在是什么日期和具体时间?")
案例3:本地TXT文件读写工具【实战:贴近真实办公,多参数工具】
提出问题
用户需要AI帮忙把一段话保存到本地txt、读取已有文件内容;模型不能直接操作电脑文件,必须通过文件读写工具实现。
分析问题
- LLM无法直接操作系统文件,需要封装
write_file(写入)、read_file(读取)两个工具; - 工具入参包含文件名、文本内容,实现多参数传入;
- Agent自动判断用户需求是读还是写,自主选择对应工具。
解决问题:完整可运行代码
python
# ==========案例3:文件读写双工具==========
def run_tool(tool_info):
if tool_info.name == "write_file":
# 解析入参
file_name = tool_info.parameters["file_name"]
content = tool_info.parameters["content"]
try:
with open(file_name, "w", encoding="utf-8") as f:
f.write(content)
return f"成功:内容已写入{file_name}"
except Exception as e:
return f"写入失败:{str(e)}"
elif tool_info.name == "read_file":
file_name = tool_info.parameters["file_name"]
try:
with open(file_name, "r", encoding="utf-8") as f:
text = f.read()
return f"文件{file_name}内容:\n{text}"
except Exception as e:
return f"读取失败:{str(e)}"
TOOLS = [
{
"type": "function",
"function": {
"name": "write_file",
"description": "将文本内容写入本地txt文件",
"parameters": {
"type": "object",
"properties": {
"file_name": {"type": "string", "description": "文件名,如note.txt"},
"content": {"type": "string", "description": "需要保存的文本"}
},
"required": ["file_name", "content"]
}
}
},
{
"type": "function",
"name": "read_file",
"description": "读取本地txt文件的全部内容",
"parameters": {
"type": "object",
"properties": {"file_name": {"type": "string", "description": "待读取的文件名"}},
"required": ["file_name"]
}
}
]
# agent_loop代码复用,不用修改
def agent_loop(user_input):
state = {
"messages": [{"role": "user", "content": user_input}],
"turn_count": 1,
"transition_reason": None
}
while True:
resp = client.chat.completions.create(
model=MODEL,
messages=state["messages"],
tools=TOOLS
)
choice = resp.choices[0]
msg = choice.message
state["messages"].append(msg.model_dump(exclude_none=True))
if not msg.tool_calls:
print("【最终回答】", msg.content)
return
tool_returns = []
for tool_call in msg.tool_calls:
tool_res = run_tool(tool_call.function)
tool_returns.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": tool_res
})
state["messages"].extend(tool_returns)
state["turn_count"] += 1
# 测试:先写入再读取
if __name__ == "__main__":
# 任务1:写入文件
agent_loop("把'DeepSeek Agent入门学习笔记:智能体循环=思考→调用工具→结果回传'保存到note.txt")
# 任务2:读取文件
agent_loop("读取note.txt里面的内容")
新手练习拓展指引
- 练手修改1:在案例1新增开方、取余工具;
- 练手修改2:案例2新增查询星期几的逻辑;
- 练手修改3:案例3新增追加写入(append)工具;
- 核心记忆点 :所有Agent万变不离其宗------模型思考→工具执行→结果入上下文→下一轮思考,和前文教程闭环逻辑保持一致。