☰
大模型Agent入门:核心原理、ReAct循环与实战避坑指南
2026/10/16 6:11:35 网站建设 项目流程

1. 别被“智能体”三个字吓住——它到底是什么

1.1 用一个叫外卖的例子说清楚Agent

很多刚接触大模型的小白程序员,一听到“Agent(智能体)”这个词,第一反应是:这玩意儿是不是跟科幻电影里的AI一样,能自己思考、自己决策?然后就开始发怵。

其实你完全可以把Agent理解成一个“会自己动手办事的大模型打工仔”。

我给你打个比方。以前你用大模型,就像是打电话给一个知识渊博的顾问,你问一句,他答一句。你问“今天北京天气怎么样”,他只能凭训练时的记忆回答你,或者干脆告诉你“我无法获取实时信息”。他嘴上很厉害,但手和脚是废的。

现在有了Agent,你相当于雇了一个“全能助理”。你告诉他:“帮我查一下明天北京天气,如果下雨就提醒我带伞,顺便把这条提醒同步到我手机日历里。”这个助理会自己拆解任务——先调用天气查询工具,看到“明天下雨”的结果,再触发“写提醒”的另一个工具,最后把结果反馈给你。整个过程,你只需要下指令,中间的执行路径他自动规划。

这就是大模型智能体的核心价值:从“只会说”到“会干活”。

那它跟普通的大模型应用有什么区别?不是说接一个API、让程序调用大模型返回一段话就叫Agent。一个真正的Agent,至少要具备四个东西:一个能理解任务的大脑(大模型)、一份能记住上下文的记忆、一双能调用外部工具的手、以及一套循环执行任务的机制。这四个零件缺一个,它都只能算“增强版聊天机器人”,算不上智能体。

1.2 Agent和普通API调用的本质差异

我见过不少新手写代码,觉得Agent不过就是“多调几次API”而已。这个理解方向对,但远远不够。

普通API调用是“单次问答”:请求发过去,响应返回,结束。流程是固定的,没有中间状态,模型说错了也没机会纠正。

Agent的核心是“循环决策”:模型不是只回答一次,而是会被放进一个循环里,每轮都由模型自己决定“下一步该做什么”——是继续找资料、调用某个工具、还是已经收集够信息可以给出最终答案。这个过程类似于你自己做事:接到任务、查资料、动手操作、看结果、不行再换方法、直到搞定。

一句话概括:普通API调用是“一问一答”,Agent是“目标驱动下的自主规划与执行”。

这个差异带来的直接变化是:你需要写代码处理的不再只是一次请求,而是一个循环里多次往返的过程。每次循环里,模型输出一段特殊结构的文本,告诉程序“我要调用某个工具、传什么参数”,程序执行工具后把结果返回给模型,模型基于这个结果决定下一步。这个“模型决策 → 程序执行 → 结果回传 → 模型再决策”的过程,会反复多轮,直到模型认为任务完成了。

理解这件事,对新手来说比理解“思维链”之类的高深概念重要得多,因为所有Agent框架的底层,说白了都是这样一个循环。

1.3 什么样的小项目适合拿Agent练手

不是所有需求都适合硬上Agent。如果只是“把一段用户输入做分类”这种单次任务,直接调大模型API就够了,套上Agent反而又贵又慢。

我推荐新手用下面这几类需求入门:

  • 信息查询类:查天气、查航班、查汇率,需要对接实时数据。
  • 内容编排类:把一堆零散素材整理成结构化文档,中间需要调用几次不同处理逻辑。
  • 简单任务代办类:比如自动写会议纪要并发送邮件、定时抓取网页内容生成摘要。

我自己的第一个练手Demo是“自动查天气并生成出行提醒”。选它的原因很简单:工具边界清晰、返回结果容易验证、失败模式也直观。你在练手的时候,也尽量挑这种“一眼就能看出模型有没有做对”的项目,而不是一上来就做那种涉及多轮对话、记忆模糊的复杂助手。

等你跑通了一个简单的,再往里面加记忆、加多工具协同,每一步都在原有框架上做增量,心态会稳很多。

2. Agent的四个核心零件:大脑、记忆、手脚、任务节拍器

2.1 大脑:大模型如何决定下一步干什么

Agent的“大脑”就是大模型本身,但和普通聊天不同,Agent场景下给模型的指令(Prompt)要特殊得多。

你要在系统提示词里,把“决策规则”交代清楚。我见过新手犯的最多的错误,就是把系统提示词写得像跟人聊天一样:“你是一个智能助理,请帮助用户解决问题。”这不够。

Agent场景下的系统提示词,需要告诉模型三件事:

第一,你是谁、目标是什么。比如“你是一个出行助手,目标是根据用户请求,查询天气并生成提醒建议。”

第二,你身边有哪些工具,分别怎么用。这是你的“手脚清单”。模型不知道它能调用什么,全凭你告诉它。工具的名字、功能、需要什么参数、参数格式是什么,都要写明白,否则模型会用错工具或者传错参数。

第三,循环规则。也就是告诉模型:“当你需要工具执行的时候,按特定格式输出;当你认为已经可以回答用户时,输出最终答复,不要继续调用工具。”这条规则决定了模型什么时候停止,非常重要。很多Agent失控死循环,就是因为这条规则没写清楚。

另外,模型本身的推理能力也要纳入选型考虑。你可以试试看,同一个任务,小参数模型和大参数模型的表现差别非常大。小模型经常不知道该调哪个工具,或者调完之后不知道该怎么收尾;大模型在遵循指令和规划路径上明显更稳。如果条件允许,新手入门阶段建议选择当前主流水平的通用模型,等你把流程跑通了再尝试换更轻量的模型做优化。

2.2 记忆:上下文窗口之外的“外挂硬盘”

大模型本身有上下文窗口限制,这就是它的“短期记忆”,比如几万token之内的事它能记住,超过这个长度,最早的内容就会被挤掉。

但在Agent执行任务的时候,往往需要跨多个步骤记住一些关键信息。比如你让Agent“帮我查三个城市的天气,然后选一个天气最好的推荐给我”。在查询过程中,前两个城市的查询结果如果不记录下来,等模型查第三个城市时,它可能已经把前面结果忘了。

更关键的是,当你把上一轮对话历史也塞进上下文作为“记忆”时,随着对话轮次增加,token消耗会越来越大,费用和延迟都会快速上升。

所以在实际项目里,通常需要一套“记忆管理方案”。最简单的办法是:对历史消息做截断,只保留最近几轮;进阶一点的做法是把重要信息抽取出来存成摘要,在需要时注入系统提示词;再进阶就是接入向量数据库做长期记忆检索。新手不用一上来就搞神经网络级别的记忆,先学会“控制消息长度、用好摘要缓存”,已经能解决绝大部分问题了。

我见过一个很有意思的类比:Agent的记忆就像你手机的内存和硬盘。上下文窗口是“内存”,会话结束就清空;而向量库或者存档文件是“硬盘”,下次重启还能读回来。你在设计Agent时要想清楚:哪些信息只需要存在内存里用一轮,哪些信息要写成文件,下回见面还能用。

2.3 手脚:工具调用与Function Calling原理

Agent之所以能“干活”,是因为它能调用外部工具。这里的“工具”范围很广:可以是一个查天气的API、一段执行Python代码的函数、一个读写数据库的操作,甚至可以是另一个模型。

让大模型学会调用工具,主流的方式有两种。

一种是让模型以特定文本格式输出调用指令,程序解析后执行。典型例子就是ReAct模式,模型会输出一段“Thought: ... Action: 工具名[...] Action Input: ...”这样的文本,你的代码拿到这段文本,用正则或者字符串解析,提取出工具名和参数,然后调用对应函数。这种方式好处是不依赖特定接口,任何大模型都能用,只要它能按格式输出文本。

另一种是利用大模型服务商提供的Function Calling(函数调用)能力。你在请求时把你的工具定义成一份结构化JSON(包括函数名、描述、参数类型等)传给模型接口,模型如果觉得需要调用某个工具,会在返回值里带上一个结构化的函数调用字段,而不是自然语言。你的程序只需要检查这个字段,有就直接执行,没有再考虑最终输出。这种方式解析更稳定,也不容易出格式错误,是目前生产环境里的主流选择。

选择哪种方式,取决于你对接的模型接口是否支持Function Calling。支持的话优先用结构化方式;不支持也别慌,ReAct文本解析照样能跑通。

无论哪种方式,有一个原则必须记住:工具的真实执行权和模型之间是隔离的。模型只是“提议”要调用某个工具、给出参数,真正执行工具的是你自己的代码。这一层隔离是安全护栏,你可以在代码里加上校验逻辑,比如“模型提议删除文件,但这个动作不在允许列表里,那就拒绝执行”。新手一定不要图省事让模型去执行任意代码,否则一旦受到恶意提示词注入,后果很麻烦。

2.4 任务循环:ReAct模式与最大迭代限制

ReAct这个词,是从“Reasoning(推理)”和“Acting(行动)”两个词各取一段拼出来的。它的思路非常像人类解决问题的过程:看到问题 → 想一下该用什么方法 → 动手做 → 看结果 → 根据结果继续想下一步。

把这句话翻译成Agent的执行循环,就是下面这个流程:

  1. 把用户请求、系统提示词、历史记录拼成消息序列,发给模型。
  2. 模型返回两种可能:要么是最终答案,要么是一条“工具调用提议”。
  3. 如果是工具调用提议,程序解析出工具名和参数,执行对应的函数。
  4. 把工具执行结果作为一条“观察(Observation)”消息拼接回去,再次发给模型。
  5. 重复第2步到第4步,直到模型给出最终答案,或者达到最大迭代次数。

这个循环是Agent系统的发动机。你必须给它装上一个“上限保险丝”——也就是最大迭代次数。我习惯设为5到8次,跑完后如果还没给出最终答案,就走兜底逻辑:返回超时提示、收集已经拿到的信息,或者把最后一次决策连同错误信息发给模型再问一次。

我看到很多新手第一次跑Agent时陷入死循环,就是因为忘了设置这个上限。模型在工具结果不够明确时会反复尝试,一轮一轮地调用下去,token烧得飞快,后台日志刷屏,程序就是不退出。设置最大迭代限制,就像是给这辆加速的车装上刹车片,从机制上杜绝失控。

3. 从零跑通第一个Agent:一个“自动查天气并生成出行提醒”的Demo

3.1 环境准备与模型接口接入

我建议你直接用Python来跑这个Demo,因为生态最成熟,后面接工具库也方便。不需要任何云环境,本地装一个Python 3.10以上版本就够了。

你的电脑上需要准备两样东西:一个能访问的大模型API密钥,以及一个支持标准对话补全接口的开发库。很多大模型服务商都提供了兼容主流风格的SDK,你不需要在这上面纠结,随便选一家支持的,把API Key配置到环境变量里就行。

我强烈建议所有源码里都不要硬编码API Key,而是用环境变量去读。顺手把.gitignore里加上.env文件,避免哪天脑子一热把密钥传上代码仓库,这种翻车事故我真的见过好多次。

接下来建一个虚拟环境,然后装依赖。依赖不需要多,一个发HTTP请求的库就够了。如果你用的服务商官方库比较顺手,用官方库也行。下面以最简单的请求库为例演示思路:

python -m venv .venv source .venv/bin/activate pip install requests

把模型接口封成一个最基础的函数,负责输入一个消息列表、返回模型回复文本。这个函数后续会被Agent主循环反复调用。

3.2 定义一个工具集:让Agent有“手”可用

这个Demo我设计了两个工具,很简单但能说明问题:

  • 查询天气:根据城市名和日期,返回当地天气状况和温度区间。
  • 发送出行提醒:把一段提醒文本写入本地文本文件,模拟“推送到手机”。

为什么选这两个?因为它们的输入输出都非常结构化,你很容易判断模型有没有正确调用。如果模型把城市名都传错了,那你一眼就能发现。

工具的本质就是一个函数加一份描述文档。在代码里,我是这样组织的:

def get_weather(city: str, date: str) -> str: # 这里接入真实天气API会返回实时数据 # 演示用伪数据模拟返回结果 if city == "杭州": return "多云转晴,气温18~24℃,风力3级,空气质量良。" if city == "深圳": return "阵雨,气温22~28℃,南风2级,出门建议带伞。" return f"{city} {date} 天气暂时无法查询,请稍后重试。" def send_reminder(text: str) -> str: # 模拟写入提醒文件 with open("reminder.txt", "a", encoding="utf-8") as f: f.write(f"[提醒] {text}\n") return "提醒已成功写入。"

每个工具函数旁边,还要准备一份给模型看的说明,里面写清楚:工具名、干什么用、参数有哪些、每个参数的含义和格式。这份说明会被放进系统提示词里。你千万别嫌这些描述啰嗦,后面你就知道,工具描述写得越细,模型调用越准。

3.3 实现ReAct主循环:读状态、选动作、执行、观察

这是一个简化但完整的ReAct主循环,希望你逐行看明白它的逻辑:

import json import time TOOLS_DESC = """ 你拥有以下工具可供调用: 1. get_weather:查询指定城市在指定日期的天气情况。参数:city(城市名,字符串),date(日期,格式YYYY-MM-DD,字符串)。 2. send_reminder:写入一条出行提醒。参数:text(提醒内容,字符串)。 如果返回结果说明某个动作执行失败,你可以换一种方式重试,但最多尝试3次。 当你已经拿到完成任务所需的全部信息,请直接输出最终答案,不要再调用任何工具。 """ def run_agent(user_request: str, max_steps: int = 6) -> str: messages = [ {"role": "system", "content": TOOLS_DESC}, {"role": "user", "content": user_request} ] for step in range(max_steps): # 调用模型,拿到回复 reply_text = call_model(messages) action_match = extract_action(reply_text) if action_match is None: # 模型没有输出工具调用指令,视为最终答案 return reply_text # 解析Action和Action Input action_name = action_match.group(1) action_input_raw = action_match.group(2) # 执行工具 observation = execute_tool(action_name, action_input_raw) # 把模型这一轮输出和工具观察结果都追加到消息序列 messages.append({"role": "assistant", "content": reply_text}) messages.append({"role": "user", "content": f"Observation: {observation}"}) # 给接口一点缓冲时间,避免触发限流 time.sleep(0.3) return "任务处理超时,未能完成,请简化请求后重试。"

这里有个很重要但我差点忽略的细节:每一步循环之后,我会把模型的上一轮回复和工具观察结果都追加到消息序列里。这样才能让模型在下一轮看到“我之前调了什么工具,结果是什么”。如果你只把Observation加进去而漏掉了模型的那条工具调用文本,模型就失去了上下文连贯性。

3.4 完整代码与运行效果

把上面各部分拼起来,再补上两个辅助函数——一个负责从模型回复中解析Action,一个负责分发执行工具——就是一个能跑的Demo了。

解析Action的时候,我用了一个简单的正则匹配,匹配“Action: 工具名”和“Action Input: 参数”这样的文本结构:

import re def extract_action(text): # 匹配 Action: set 变量名 pattern = r"Action:\s*(\w+).*?Action Input:\s*(.+)" return re.search(pattern, text, re.DOTALL) def execute_tool(name, raw_input): if name == "get_weather": # 期望输入是 json 格式,如 {"city": "杭州", "date": "2026-05-20"} try: params = json.loads(raw_input) return get_weather(params["city"], params["date"]) except Exception as e: return f"参数解析失败:{e}" if name == "send_reminder": try: params = json.loads(raw_input) return send_reminder(params["text"]) except Exception as e: return f"参数解析失败:{e}" return f"未知工具:{name}"

在系统提示词里,我需要明确告诉模型按这个格式输出。比如下面这段:

请严格按照以下格式输出你的决策过程: Thought: 描述你在想什么 Action: 工具名 Action Input: 工具需要的参数(严格使用JSON格式)

跑一下,输入“帮我查一下杭州明天有没有雨,如果有就生成一条带伞提醒”,你能在日志里看到模型一轮一轮地调用get_weather,拿到“多云转晴”后判断不需要send_reminder,直接输出最终答案。如果换成深圳,你就能看到它先查询天气,发现是阵雨,再调用send_reminder写入提醒,最后给出总结。这就是你想看到的Agent行为模式。

一个运行示例(伪日志):

Step 1: Thought:用户想知道杭州明天是否有雨,我需要先查询杭州明天的天气。 Step 1: Action: get_weather | Action Input: {"city": "杭州", "date": "2026-05-20"} Step 1: Observation: 多云转晴,气温18~24℃,风力3级,空气质量良。 Step 2: Thought:杭州明天没有雨,不需要生成带伞提醒,我可以直接回答用户。 Step 2: 最终答案:杭州明天(5月20日)多云转晴,气温18~24℃,风力3级,空气质量良。没有降雨,不需要带伞。

整个代码控制在150行以内,非常轻量。但你已经实现了一个完整的“观察-决策-行动”闭环。把这段代码吃透,后面你去看再复杂的Agent框架源码,会发现本质就是加强版的这个循环。

4. 新手最容易踩的六个坑和排查思路

4.1 模型“自说自话”:幻觉与工具结果冲突

我跑第一个Agent时遇到最诡异的现象是:模型明明调用了get_weather工具,Observation返回“深圳阵雨”,但最后最终答案里却写“深圳多云,天气不错,无需带伞”。

这就属于典型的大模型幻觉——它没有认真读Observation,而是凭自己训练时的记忆“脑补”了一个错误天气。

遇到这种情况,先不要慌,排查思路有两条:

第一,检查Observation在消息里的位置和格式。有时候你会把Observation放在一个不显眼的位置,或者用了奇怪的格式,模型没把它当成硬性事实来读。你可以试试在Observation前面加一句“以下工具执行结果是真实事实,必须作为决策依据:”,强调它的优先级。

第二,考虑换一个遵循指令更强的模型。幻觉率在不同模型之间差异很大,同一个Demo,换成更强的新模型之后,这类现象会明显减少。

第三,也是最后的兜底方案:在代码里做事实校验。比如如果工具返回“阵雨”而最终答案里同时出现“无雨”,直接判为失败重试。

4.2 工具参数反复填错:给大模型的工具描述写得不够细

新手最常见的问题:工具描述写得过于简短,模型不知道参数到底该传什么。

我见过某位同学的工具描述只有一句:“查询天气:输入城市名”。结果模型在调用时,把“明天”“杭州天气”整个字符串当成了城市名传进去。

解决这个问题没有捷径,就是把参数描述写到“连一个完全没接触过你这个系统的人也能看懂”的程度。我现在的写法是:

get_weather 参数说明: - city:城市中文名,如“北京”“杭州”。不要包含“市”字,不要包含日期信息。 - date:日期,格式为YYYY-MM-DD,如2026-05-20。今天是2026-05-19,明天是2026-05-20。不要传“明天”这样的相对说法。

你可能会觉得啰嗦,但事实就是:模型非常依赖这种边界约束。描述里每多一句澄清,模型的错误率就往下掉一截。

另外,如果你用的是Function Calling接口,工具参数是结构化JSON Schema,那你可以在Schema里加enum枚举、pattern正则约束,效果比纯文本描述更好。

4.3 死循环与失控:迭代上限与护栏设计

这个问题我前面提过,但在实际排障里出现频率实在太高,值得单独再展开。

死循环的典型表现是:模型反复调用同一个工具、拿到结果后又调用一次,整个过程明显没有进展。我见过最夸张的一次,模型在8轮循环里连续查了8次同一个城市的天气,每次结果都一样,但还是不停手。

排查时先看日志里模型每一轮的Thought。如果是“我确认一下”“我再核实一下”这种反复同样的话,说明模型对工具结果不信任,或者你的提示词没告诉它“Observation就是最终事实,不需要重复验证”。

还有一个更隐蔽的原因:模型不知道任务什么时候算“完成”。你的系统提示词里如果只写了“调用工具完成任务”,没有明确“收集齐所有必需信息后直接给出最终回答”,模型就会一直处于“干更多事”的状态。一定要写一句类似“当所有必要信息都已获得且用户原始请求已得到满足时,输出最终答案,不再调用工具”这样的话。

线上环境的Agent一定要有超时监控和最大循环限制。宁可让任务超时返回一个“解题失败”的透明结果,也不要在循环里白白烧掉几百块钱的token。

4.4 上下文越跑越长:记忆压缩与裁剪策略

只要你的Agent交互超过5轮,你就会发现消息列表越来越长,每次请求的token消耗越来越大,响应越来越慢。

这里我有一个非常实用的习惯:只保留系统提示词、用户最初请求、最近两轮模型回复和Observation,以及一份动态更新的摘要。

实现摘要的方式很简单——每处理完一个子任务,让模型用一两句话总结“目前已完成什么、当前进度如何”,然后把这个总结拼接到系统提示词的“任务进度”区域里。

比如用户让你查三个城市的天气,每查完一个,就追加一条进度:“已查询北京(晴),待查询上海、广州。”这样即使最早的原始结果被从上下文里裁剪掉了,模型依然能知道整体进度。用这个方案,一个多轮任务的token消耗可以控制在单纯堆历史的几十分之一。

4.5 调用费用失控:Token成本如何控制在个位数

新手常常忽略Agent的token消耗速度。一个看起来简单的查询任务,因为循环里的多轮往返,实际消耗可能是你预想的5到10倍。

想控制成本,我的建议是:

  • 给循环设上限,不仅防死循环,也防费用失控。
  • 精简系统提示词,工具描述只留关键信息,不要一大段一大段地堆无关文字。
  • 小任务用轻量模型,只有复杂推理时才切到更强的模型。比如简单的工具调用和文本解析用小模型,而任务规划阶段用大模型。
  • 把工具调用结果压缩再回填,不要返回几百行原始日志,需要什么信息就提取什么,尽量让Observation短小精悍。

我自己在跑Demo的时候,设置了单任务最大消耗统计。每次任务完成后,打一条日志记录“本轮调用了几次模型、总计消耗了多少token”。如果发现某个任务动辄调用十几次,我就会回头审视,是不是工具描述不清晰导致模型反复试探,或者规划做得太碎,本来一次能查完的事被拆成了三次。

4.6 调试方法:从“黑盒”到“白盒”的日志渗透

Agent是一个循环系统,天然比普通API调用难调试。因为问题可能出在提示词、工具解析、参数传递、循环设计任何一个环节。

我被坑过几次之后,养成了一个“让Agent说人话”的调试习惯:在每轮循环里,把模型输出的Thought也一起打出来。很多人只关注Action对不对,但Thought才是模型做决策的心路历程。看到Thought,你就能一眼判断它是不是走偏了。

第二个习惯是把每轮发往模型的完整消息序列导出来存档。当模型某次表现异常时,我可以复盘:“这轮上下文里到底有什么内容,导致它做出了错误决策。”排查Agent问题时,这条信息往往比代码本身更有用。

第三个习惯是“最小化复现”。遇到问题后,把任务拆小,试试只调一个工具,看看能不能正常走完;不行就再缩短到只输出Action、不接工具。通过二分定位,很快就能锁定问题是在模型规划、工具执行,还是消息拼接。

以上这些经验,都是我一个个Debug和线上问题喂出来的。你第一次写Agent时,遇到问题不要慌,照着这几个方向去排查,大部分坑都能绕过去。

我个人在实际操作中的体会是:Agent入门其实没有多深的门槛,核心是理解“循环 + 工具 + 记忆”这个铁三角,然后亲手跑通一个小闭环。先让它查一个天气、写一个提醒,再做带记忆的会话式助手,再挑战一次接入多工具的小系统,每一步都能叠加新的认知。你不需要一开始就掌握所有高层框架,把一个最简单的ReAct循环看透,后面再学什么都快。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询