agent教程S01-Agent 最小循环教程整理

S01-Agent最小循环教程

第一部分:提出问题:原生大模型只能聊天,无法变成自主干活的Agent

1. 现存现象(原生LLM的天然短板)

大语言模型(LLM)本质是文本生成器,只会根据输入续写文字,天生无法自主落地实操任务,做不到4件关键操作:

  1. 不能自主打开文件、执行终端/程序命令;
  2. 无法查看工具运行后的报错、返回数据;
  3. 不会主动触发外部工具调用;
  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大致命踩坑清单

  1. 工具结果只打印控制台,不写入messages:模型看不到真实运行数据,无法迭代;
  2. 只存用户消息,丢弃assistant回复:上下文断层,模型丢失自身上一轮思考记录;
  3. tool_result不绑定tool_use_id:模型无法匹配结果与工具调用,指令和结果错乱;
  4. 入门就堆砌流式输出、异常重试、权限管控:基础闭环逻辑被复杂功能掩盖,学不会核心;
  5. 错把messages当成聊天展示数据:messages是Agent的工作记录本、模型的输入上下文,不是前端展示文案。

5. 后续迭代拓展(本章节是Agent底座)

S01只完成最小闭环,后续s系列章节在当前循环基础上叠加能力:

  • s02:接入工具路由管理
  • s03:新增任务规划状态
  • s06:上下文自动压缩
  • s07:工具权限校验
  • s11:工具报错自动恢复

6. 最终一句话总结(牢记Agent本质)

Agent Loop(智能体循环)= 把模型空想的工具指令落地成真实结果,再将结果回喂模型,驱动下一轮持续推理。

基于DeepSeek API|3阶入门Agent实战案例(沿用前文Agent循环架构|新手可直接复制运行)

前置准备:

  1. 安装依赖:pip install openai(DeepSeek 兼容OpenAI SDK调用格式)
  2. 前往DeepSeek官网获取API_KEY,替换代码内DEEPSEEK_API_KEY
  3. 接口地址: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. 模型无法精准做复杂四则运算,依赖自身推理容易出错;
  2. 设计1个calculator工具,接收计算公式字符串,代码运行求值;
  3. 沿用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获取本机真实时间。

分析问题

  1. LLM没有实时系统时间,不能凭空生成当前时间;
  2. 新增get_current_time工具,使用datetime获取系统时间;
  3. 循环逻辑不变,仅修改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、读取已有文件内容;模型不能直接操作电脑文件,必须通过文件读写工具实现。

分析问题

  1. LLM无法直接操作系统文件,需要封装write_file(写入)、read_file(读取)两个工具;
  2. 工具入参包含文件名、文本内容,实现多参数传入;
  3. 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:在案例1新增开方、取余工具;
  2. 练手修改2:案例2新增查询星期几的逻辑;
  3. 练手修改3:案例3新增追加写入(append)工具;
  4. 核心记忆点 :所有Agent万变不离其宗------模型思考→工具执行→结果入上下文→下一轮思考,和前文教程闭环逻辑保持一致。
相关推荐
Flynt16 分钟前
这个日增 1400 星的 E2E 框架说"第二次跑不调模型",我改了按钮文案试了试
agent·ai编程·测试
白帽攻防录20 分钟前
SRC 挖洞:Certighost AD CS 域控制器伪造深度复盘,CVE-2026-54121 一个普通域用户怎么拿走整个企业域
网络·安全·web安全·网络安全·域安全
青春不朽51225 分钟前
requests 超时、重试、SSL 报错?9 个高频问题与一整张排错对照表
网络·网络协议·ssl
酣大智31 分钟前
H3C MSR3600(Comware V7)接口限速完整操作步骤
网络·mqc
一技安身35 分钟前
VMware Workstation 25H2 创建兼容ESXi8.0虚拟机教程
服务器
硅基手札40 分钟前
【Linux 内核专栏 13】快IO层
linux·服务器·嵌入式硬件
镜舟科技42 分钟前
从 StarRocks 到 MIP:数据平台的下一步是“理解”
starrocks·sql·ai·prompt·agent·context·mip
vx-程序开发1 小时前
【计算机毕设】51300童装超市管理系统的设计与实现
java·vue.js·spring boot·源码·课程设计·毕设·大作业
XLYcmy1 小时前
PDF 论文处理器 — 改进方向与建议文档
python·pdf·llm·agent·dify·rag·harness
Pointer Pursuit1 小时前
Linux调试器 ——gdb/cgdb
linux·运维·服务器