30行代码,一个循环:这就是AI Agent的核心秘密—Agent Loop

1. 前言

我们设计一个"寻宝游戏"(也就是链式依赖任务):三个文件形成线索链,大模型必须依次读取它们,才能拼出最终密码。

三个相互关联的文件内容设计如下:

step1.txt 内容如下:

复制代码
线索1:请去读取 step2.txt 文件获取下一步指示。

step2.txt 内容如下:

arduino 复制代码
线索2:做得好!密码的前半部分是 'Agent',请继续读取 step3.txt 获取后半部分。

step3.txt 内容如下:

arduino 复制代码
线索3:密码的后半部分是 'Loop'。请将密码拼接后告诉用户最终的完整密码是什么。

我们在上一篇文章中已经实现了大模型对文件内容的读取,我们现在使用上一篇实现的功能执行以下内容:

复制代码
请帮我读取 step1.txt 文件,并严格按照文件中的线索一步步寻找,直到拼凑出最终的完整密码告诉我。

结果如下:

我们看到只执行了第一步就不再执行了。

所以我们发现上一篇文章实现的功能,大模型是无法在不需人工干预的情况下自主执行多步工具的调用。

所以我们需要一个能反复调用工具、观察结果并继续决策的循环 体,这就是 Agent Loop

2. 为什么需要 Agent Loop ?

大型语言模型(LLM)在代码生成、逻辑推理等方面表现惊人。然而,它们本质上是一个"静态"的推理引擎:只能基于已有的上下文生成文本,无法主动与外部世界交互------不能执行命令、读取文件、运行测试、查看报错信息等。为了让大模型操作外部工具,OpenAI 提出了 Function Calling(工具调用)机制,我们也在上一篇文章分析和实现了这个功能。

很明显我们不但需要实现大模型调用工具 ,还需要实现一个能反复调用工具、观察结果并继续决策的循环体。

因为从前言中的例子我们可以知道在没有循环调用工具的情况下,我们只能手动将工具的输出粘贴回对话中,充当人肉中转站。这不仅低效,更违背了"自主智能体"的初衷。

而 Agent Loop 正是为了解决这一问题而应运而生:将模型与工具连接起来,让模型根据环境反馈自主决定下一步动作,直到任务完成。

那么怎么实现呢?

3. 核心概念:一个循环 + 工具

Agent Loop 的架构极其简洁:

arduino 复制代码
while 模型请求调用工具:
    执行工具调用
    将结果反馈给模型

它的运行流程则可以用下图表示:

lua 复制代码
+----------+      +-------+      +---------+
|  用户    | ---> |  LLM  | ---> |  工具   |
|  prompt  |      |       |      |  执行    |
+----------+      +---+---+      +----+----+
                     ^                |
                     |    工具结果     |
                     +----------------+
                     (循环到模型不再调用工具)

大模型每次生成响应后,我们需要检查它是否要求调用工具。如果有,就执行对应工具,将结果以"工具消息"的形式追加到对话历史中,然后再次调用大模型。大模型在新的上下文中继续推理,可能再次调用工具,也可能直接给出最终答案。

4. 代码实现

下面我们基于 Python 详细解析 Agent Loop 的实现。

4.1 环境准备

py 复制代码
import os
import json
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
# 加载环境变量(如 DEEPSEEK_API_KEY)
load_dotenv()
# ---------- 初始化客户端 ----------
# 创建 OpenAI 客户端实例,使用 DeepSeek API 密钥和基础 URL
client = OpenAI(
    api_key=os.getenv("DEEPSEEK_API_KEY"),
    base_url="https://api.deepseek.com"
)

加载环境变量,初始化 OpenAI 客户端。

4.2 工具定义

py 复制代码
# ---------- 工具定义 ----------
tools = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "读取文本文件内容。",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "要读取的文件路径"},
                    "encoding": {"type": "string", "enum": ["utf-8", "gbk"], "description": "文件编码格式"}
                },
                "required": ["path"]
            }
        }
    }
]

这里使用 OpenAI 的 function calling 格式定义了一个 read_file 工具。工具描述了名称、用途和参数,大模型会根据这些信息决定何时调用、传什么参数。

4.3 工具实现

py 复制代码
# ---------- 工具实现 ----------
class ReadFileTool:
    def execute(self, path: str, encoding: str = "utf-8") -> str:
        try:
            file_path = Path(path).expanduser()
            if not file_path.exists():
                return f"❌ 文件不存在: {path}"
            return file_path.read_text(encoding=encoding)
        except Exception as e:
            return f"❌ 读取失败: {str(e)}"

file_tool = ReadFileTool()

工具的执行逻辑被封装在 ReadFileTool 类中。它接收路径和编码,返回文件内容或错误信息。错误信息也作为正常输出返回,让模型能处理异常情况。

上面几个步骤,我们在前面的文章已经实现过了,相信大家不会陌生了。接下来才是本篇文章的重点。

4.4 核心循环:Agent Loop

py 复制代码
# -- 核心模式:一个不断调用工具的 while 循环,直到模型停止 --
def agent_loop(messages: list):
    """
    核心代理循环:
    不断地调用大模型,如果模型返回了工具调用,则执行工具并将结果发回给模型,
    直到模型不再需要调用工具并返回最终文本回复为止。
    """
    while True:
        # 调用大模型,传入历史消息、工具列表,并让模型自动决定是否调用工具
        response = client.chat.completions.create(
            model="deepseek-chat", # 使用的模型名称
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )
        msg = response.choices[0].message
        
        # 将助手的回复(可能包含工具调用,也可能是普通文本)添加到历史记录中
        messages.append(msg)
        
        # 如果模型没有调用工具,说明已完成任务,返回模型回复的文本内容
        if not msg.tool_calls:
            return msg.content

        # 否则,模型决定调用工具,遍历所有的工具调用
        for tool_call in msg.tool_calls:
            if tool_call.function.name == "read_file":
                # 解析模型生成的工具调用参数
                args = json.loads(tool_call.function.arguments)
                print(f"\033[33m🔧 调用工具: {tool_call.function.name}, 参数: {args}\033[0m")
                # 执行工具函数
                result = file_tool.execute(**args)
                # 打印工具执行结果的前200个字符
                print(f"✅ 工具执行结果:\n{result[:200]}\n")
                # 将工具执行结果作为 tool 类型的消息追加到历史记录中
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "name": tool_call.function.name,
                    "content": result
                })

上述代码便是 Agent Loop 的核心实现,主要功能如下:

  • 循环调用大模型,每次传入最新的消息列表和工具定义。
  • 将模型的响应(msg)追加到历史。
  • 检查 tool_calls:如果没有,返回 msg.content 结束循环。
  • 如果有工具调用,遍历每个调用,解析参数,执行对应工具,并将结果以 tool 角色消息加入历史(包含 tool_call_id 以便关联)。
  • 循环继续,大模型看到工具结果,会继续分析并决定下一步。

4.5 交互终端

py 复制代码
if __name__ == "__main__":
    # 初始化历史消息列表,包含系统提示词,系统提示词,用于指导助手的行为
    history = [
        {"role": "system", "content": "你是一个文件读取助手,必要时可以调用工具帮助用户读取文件内容。"}
    ]
    # 启动交互式终端会话
    while True:
        try:
            # 获取用户输入
            query = input("\033[36m用户 >> \033[0m")
        except (EOFError, KeyboardInterrupt):
            # 处理 Ctrl+D 或 Ctrl+C 退出的情况
            break
        # 处理正常退出的输入
        if query.strip().lower() in ("q", "exit", "退出"):
            break
        
        # 将用户输入追加到历史记录中
        history.append({"role": "user", "content": query})
        # 启动代理循环进行对话
        final_answer = agent_loop(history)
        
        # 打印助手给出的最终答案
        if final_answer:
            print(f"\033[32m助手: {final_answer}\033[0m\n")

交互终端主循环并支持多轮对话,每次用户输入后都调用 agent_loop,并打印大模型的最终答案。整个程序就是一个可在终端交互的自主文件读取助手。

5. 链式依赖任务测试

我们在在终端启动程序后,输入以下内容:

复制代码
请帮我读取 step1.txt 文件,并严格按照文件中的线索一步步寻找,直到拼凑出最终的完整密码告诉我。

执行结果如下:

我们可以看到整个过程是完全自主的,大模型根据每次工具返回的线索,动态规划下一步,直到任务完成,这就是 AI Agent 的核心功能 --- Agent Loop。

6. Agent Loop 工作原理总结

一个典型的 Agent Loop 包含以下步骤:

  1. 用户输入:用户的消息作为第一条消息加入历史中。

  2. 调用模型:将完整对话历史(包括系统提示)和工具定义发送给 LLM。

  3. 解析响应

    • 如果模型没有调用工具(tool_calls 为空),则返回最终内容,循环结束。
    • 如果模型调用了工具,则执行对应工具。
  4. 执行工具:遍历所有工具调用,运行对应的工具函数,收集结果。

  5. 反馈结果 :将每个工具的结果包装成 tool 角色的消息,追加到历史中。

  6. 回到步骤2:模型在新的上下文中再次推理。

这个过程一直持续,直到大模型认为任务完成,不再请求工具。这就是自主智能体的最小闭环

7. 总结

我们用不到30行代码,实现了将大模型的推理能力与外部工具的执行能力无缝连接,使大模型能够根据环境反馈自主决策,完成多步复杂的任务。这就是 AI 智能体的最小核心模式 ------ Agent Loop

无论是简单的文件读取,还是复杂的代码编写、代码运行,其背后都是同一个逻辑原理:模型 → 工具 → 结果 → 模型 。理解并掌握这个逻辑原理,你就掌握了智能体设计的钥匙。在此基础上,你可以添加策略、记忆、多智能体协作等高级特性,但核心永远不会变------Agent Loop

一个工具 + 一个循环 = 一个智能体,就是智能体的核心秘密。

我是 Cobyte,欢迎添加 v: icobyte,学习交流 AI 全栈。

相关推荐
漂流瓶jz4 小时前
总结CSS组件化演进之路:命名规范/CSS Modules/CSS in JS/原子化CSS
前端·javascript·css
踩着两条虫5 小时前
「AI + 低代码」的可视化设计器
开发语言·前端·低代码·设计模式·架构
Jagger_5 小时前
项目上线忙碌结束之后,为什么总想找点事做?
前端
Spider Cat 蜘蛛猫5 小时前
Springboot SSO系统设计文档
java·spring boot·后端
GalenZhang8885 小时前
OpenClaw 配置多个飞书账号实战指南
前端·chrome·飞书·openclaw
冬奇Lab5 小时前
理发师会被 AI 取代吗?这可能是 AI 时代最有意思的一个社会学问题
人工智能·aigc
我是宝库6 小时前
英文专业论文,可以用维普AIGC检测查AI率吗?
人工智能·aigc·英文论文·论文查重·turnitin系统·turnitin·维普aigc检测
大拿爱科技6 小时前
低清视频修复怎么接入批处理?AI画质增强流程拆解
人工智能·自动化·aigc·音视频
zyk_computer6 小时前
AI 时代,或许 Rust 比 Python 更合适
人工智能·后端·python·ai·rust·ai编程·vibe coding
萌新小码农‍6 小时前
python装饰器
开发语言·前端·python