去年有个读者问我:“大模型什么都能聊,可为什么让它帮我订个会议室,它就只会写个待办给我?”这个问题问得特别好,它正好戳中了当下大模型应用最核心的痛点:聊天归聊天,干活归干活。而把这两件事打通的东西,就是这两年最火、也最容易被误解的概念——AI Agent(智能体)。简单说,Agent就是让大模型不再是“嘴强王者”,而是拥有记忆、能规划、会调用工具、能自主执行任务的完整系统。这篇文章不聊虚的,我直接把Agent底层的架构拆开,把推理循环、工具调用、记忆机制这些核心模块逐个讲透,最后送你一套可以直接上手的构建路径。适合正在做LLM应用开发、准备转Agent方向的开发者,以及想搞懂Agent原理的技术产品经理。
1. Agent到底是什么:先搞清楚我们讨论的对象
1.1 从“会聊天的模型”到“能干活的智能体”
很多人以为Agent就是“接了大模型的聊天机器人”,这是最大的误解。传统的大模型应用,哪怕你给它加一千条Prompt,它的能力边界依然是“生成下一个token”。你问它“明天北京天气如何”,它回答得再像模像样,也改变不了它是靠训练数据里的记忆在“猜答案”的事实。而Agent不一样,它的定位是“能对现实世界产生影响的程序实体”。
怎么理解这个区别?我给你打一个比方。普通大模型应用就像一个只动嘴的顾问,你说什么他都能分析得头头是道,但真要他去执行,他只能告诉你“你该怎么做”。而Agent是一个手脚并用的员工,他不仅能听懂你的目标,还会自己拆解任务、查资料、调工具、校验结果,最后把活儿干完,再跟你汇报“我做了什么,结果如何”。
要实现这个“干活”的能力,一个完整的Agent系统通常至少要包含几个核心组件:大语言模型(作为推理决策的大脑)、记忆系统(短期工作记忆和长期知识存储)、工具集(能调用的外部函数或API)、规划模块(把大目标拆成子任务)以及执行循环(控制Agent不断“想一步做一步”的调度器)。后面我会逐一展开讲,这里先建立一个整体概念:Agent不是某一个模型,而是一套以模型为引擎的工程系统。
1.2 Agent与普通程序的核心差异
如果说传统软件是“输入-处理-输出”的确定性管道,Agent就是“感知-决策-行动-迭代”的循环机制。这个差异带来了两个根本变化。
第一,控制流的转移。传统程序的控制流是程序员写死的,第一步干什么、第二步干什么,是if-else和for循环决定的。Agent的控制流是模型推理出来的,它根据当前的目标和已获取的信息,动态决定下一步调什么工具、问什么问题、要不要再次尝试。这意味着同一个Agent,面对同一个任务的不同变形,走的路径可能完全不同。
第二,容错与修复逻辑变了。传统程序出错会抛异常,而Agent出错了往往会“自己想办法”。它可能在调用工具失败后重新换个方式,也可能在发现推理路径走不通时回溯重来。这种自主性带来了更强的任务泛化能力,但也带来了失控风险。很多团队踩的坑就是:开发环境里Agent表现得聪明伶俐,一上生产、喂了真实数据,就开始自己脑补工具参数,把测试库删了还洋洋得意。“越狱”指的可不是某种网络操作,而是你设计的Agent行为边界没有兜底。
所以在进入技术细节之前,我们得先统一一个观念:构建Agent的核心难点不是编一个提示词,而是设计一套边界清晰、可观测、可干预的执行体系。没有这个体系,你接到的是个定时炸弹,不是智能助手。
2. Agent的底层架构:拆穿五大核心模块
2.1 大脑:LLM的定位与选型思路
Agent的推理能力完全来自底层的LLM,所以“大脑”选得好不好,几乎决定了Agent的上限和下限。选型时我主要看三个维度:推理能力、函数调用(Function Calling)稳定性、上下文窗口长度。
先说推理能力。目前的共识是,想要Agent表现稳定,模型的推理能力必须过得去。如果模型本身就是小尺寸玩具级,它连“先查天气再决定穿什么”这种两步推理都容易断片。所以预算允许的情况下,优先选择当前第一梯队的通用模型,至少要在推理基准上有较好表现。
再重点说函数调用。这是Agent最依赖的“手和脚”。所谓函数调用,是指模型在需要获取信息或操纵外部系统时,不直接说“我要调xxx接口”,而是通过在输出中生成一段结构化指令(比如一段JSON),系统解析后执行对应函数。早期的Agent靠“让模型输出Action: search("天气”)这种文本指令来干活,现在的主流方案是让模型输出标准的函数调用对象,再由调度器执行,稳定性和安全性都有了质的提升。
上下文窗口也不能忽略。Agent在执行多步任务时,每轮都要把“系统Prompt + 历史对话 + 工具返回结果”拼在一起发给模型。窗口太短,做一两轮工具调用就爆了,所以现在社区里选型基本都要128K起步。但要注意,窗口大不等于你能随便塞,模型对长上下文的注意力集中程度会衰减,这也是后文要讲上下文压缩的直接原因。
2.2 记忆系统:短期记忆、长期记忆与工作记忆
记忆是Agent区别于普通Chatbot的核心能力之一。你想想,如果一个Agent能查资料能干活,但每轮都得用户把背景信息重新说一遍,那体验就太割裂了。
我一般把Agent的记忆分为三层来设计。
短期记忆对应的是当前会话窗口内的对话历史,本质上就是模型上下文里那份message列表。它最直接,实现也最简单,但容量有限,而且历史越久越容易稀释模型对当前目标的注意力。
长期记忆是跨会话的知识存储。它需要把历史交互中的关键信息提取、向量化并存入向量数据库。当新任务进来时,Agent通过语义检索把相关的历史片段捞回来,塞进当下上下文里。这一层靠的是Embedding模型和向量检索的配合。实操中我常用一个很朴素的方案:先跑一个内容摘要,把历史对话的关键结论提取成结构化笔记,再做向量化。比直接存原始对话的检索效果好得多。
工作记忆是Agent在解决复杂任务时临时存放中间状态的地方,有点像程序里的变量区。比如Agent正在做一个市场调研任务,它已经把三个竞品网站的信息抓下来了,这些中间结论需要有个地方存着,免得都要靠重新查。工程上常用一个简单的内存对象或者一个小型的键值存储来实现,核心是“任务完成后要清空”。
2.3 工具调用:Agent如何“长出手脚”
工具是Agent从“纸上谈兵”走向“实际执行”的关键。一个通用Agent至少要准备三类工具:信息获取类(搜索引擎、文档查询、数据库读取)、事务操作类(发送邮件、创建工单、修改配置)和计算处理类(代码解释器、数据分析、图表生成)。
工具本身实现并不难,难的是如何让模型“理解并正确使用”这些工具。我目前实践下来最好用的方案是给每个工具定义一份严格的JSON Schema(参数结构描述),把工具的名称、用途、参数类型、必填项和约束条件全部写明。为什么要这么干?因为模型是通过Prompt理解工具说明的,Schema写得越精确,模型“猜参数”的空间就越小。
举例,如果你给Agent一个“发送邮件”工具,参数里只写“收件人、主题、内容”,模型大概率会自由发挥,把一些不在验收范围内的字段塞进来。如果你把Schema定义成收件人必须是合法邮箱格式、内容不得超过1万字、必须包含签名后缀,模型的行为立刻会被约束到可控范围内。所以,好的工具定义不是“把API包一层给模型”,而是“把API包装成人话清晰、参数严格、边界分明的服务说明”。
还有一点特别关键:不是所有工具都要给Agent用。删除类、覆盖类、高成本类操作,尽量不暴露给模型自动调用,或者设置二次确认机制。Agent的自主性必须和能力边界做切割,这是工程安全的第一道闸门。
2.4 任务规划:任务分解、反思与自我纠错
复杂任务很难一步完成,Agent需要规划模块来拆解目标。目前的主流实现有两种路线:一种是单次规划(Plan-once),让模型在一开始就产出完整的子任务清单,然后逐个执行;另一种是动态规划(Re-plan),每执行一步,模型根据当前信息重新评估下一步。
我个人的建议是:从动态规划开始,配合“计划-执行-检查”的循环。听起来抽象,我来描述一个实际案例。假设你让Agent“调研一下最近三个月某赛道的新产品融资情况”,一个合格的Agent规划模块会这样运转:先用搜索工具搜“某赛道 融资 最近三个月”,拿到一堆新闻链接;这时候它不会直接汇总,而是评估信息来源的可靠度,对权威媒体和普通博客区别对待;如果发现某个关键数据缺失,它会再发起一次定向搜索补充;最后把零散信息聚合成一个结构化报告,并附上信息来源。
这个过程中最关键的能力是“反思”。所谓反思,是让模型对自己的中间结果做自检。比如Agent生成了一份摘要,系统可以再发一轮Prompt给它:“请检查上一份摘要中的数据是否都有出处,没有出处的标注为待核实”。这种反思机制能显著降低幻觉污染最终结果的概率。
2.5 执行循环:Agent运行的主控逻辑
如果说前面几个模块是零件,执行循环就是把这些零件组装起来的发动机。所有Agent框架的底层都跑着一个类似这样的主循环:接收用户请求 -> 拼接上下文 -> 调用LLM推理 -> 解析模型输出 -> 判断是“最终答案”还是“工具调用” -> 如果是工具调用则执行并返回结果 -> 将结果追加进上下文 -> 再次调用LLM……如此往复,直到模型产出最终答案或达到最大轮数。
我用Python伪代码描述一下,它其实就是这么简单的结构:
def agent_loop(user_input): messages = [system_prompt, user_input] while True: response = llm.chat(messages) # 模型推理 if response.tool_calls: # 模型要求调用工具 messages.append(response) for tool_call in response.tool_calls: tool_result = execute_tool(tool_call) messages.append(tool_result) continue elif response.is_final_answer: # 模型给出最终答案 return response.content if loop_count >= max_iterations: raise AgentStuckError("Agent达到最大轮数,疑似死循环")这个循环看着简单,实际上每个环节都藏着巨坑。比如模型输出是非法JSON怎么办?工具执行超时怎么办?模型连续五轮都在重复调用同一个工具怎么办?这些都是执行循环(调度器)要解决的问题。可以说,Agent工程化的大部分时间都耗在这个看似不起眼的循环里。
3. 关键技术原理解析:ReAct、上下文工程与结构化输出
3.1 ReAct:让模型“边想边做”的推理框架
聊Agent原理绕不开ReAct(Reasoning and Acting)。这个想法其实很朴素:让模型在处理任务时,交替输出“推理过程”和“行动指令”。推理过程是模型的“思考草稿”,让它一步步想清楚“我现在知道什么,下一步该做什么”;行动指令是它对工具的调用要求。
这和你做题时先在草稿纸上写“由条件A可知B,再由B推出C”是一个道理。有了中间推理过程,模型不只是“凭感觉跳到最后一步”,而是有了一条可追溯的思考轨迹。
我记得有一组对比实验特别能说明问题:同样是“查询A股某公司的最新财报并对比上季度收入变化”这个任务,一个普通LLM应用直接输出“我无法获取最新数据,请手动查询”,而一个ReAct模式的Agent会输出“我需要先搜索该公司财报,然后对比数据……”“搜索到2025年Q3营收为xxx,现在搜索上季度数据……”最终给出完整对比。这就是ReAct的价值:它把单次推理变成了多步推理+外部验证,模型每走一步都变得更“有根据”。
3.2 上下文工程:懂得往模型窗口里“喂什么”
你可能已经发现,Agent的每一次推理都要带上大量上下文。这个“组装上下文”的过程,我称之为上下文工程,它是Agent性能最容易被忽略的决定因素。
上下文工程的核心原则是:让模型在“恰当的时间”看到“恰到好处的信息”。比如在工具调用过程中,系统提示词里应该明确“如果你需要搜索,必须使用search_tool,不要编造数据”;知识库检索到的长文档不要一次性全塞进窗口,而是先用重排序精筛Top K段落;历史会话不能无限累积,超过一定轮数就需要做摘要压缩或丢弃次要信息。
这里有三个很实用的操作经验:
- 工具说明放在系统提示词的固定位置,保证稳定性。
- 每个工具调用返回的结果要打“标签”,例如“这是search_tool的返回结果,包含3条链接”,方便模型快速缓存。
- 对模型不熟悉的领域术语,在上下文里主动给出定义,而不是等模型来问。
3.3 结构化输出与函数调用:让模型能按剧本出牌
我在前面提过,早期Agent靠模型输出“Action: xxx”这种文本来调度。这一套显然太脆弱,模型一旦换了个措辞写成了“Action:调用搜索”,解析器就可能翻车。现代Agent普遍改用结构化输出方案,也就是让模型直接产出符合预先定义Schema的JSON对象。
举个例子,当Agent决定调用搜索工具时,它的输出可能是下面这样:
{ "thought": "需要获取当前融资信息,因此调用搜索工具", "tool_name": "web_search", "params": { "query": "AI Agent 2025 融资", "max_results": 10, "date_range": "最近3个月" } }这份JSON里的每一个字段都是预先定义好的。只要模型按Schema输出,系统就能稳定解析、稳定执行。这套方案能可靠工作的前提是模型本身经过了指令微调或支持Function Calling能力,所以选型时这个能力本身就是硬指标。
4. 从零构建一个Agent:框架选型与完整实操
4.1 框架选型:为什么我建议从“可组合”的框架开始
市面上Agent框架很多,有偏生态整合的大而全框架,也有极简到只提供循环内核的轻量框架。我的观点是,新手不要一上来就选那种“一行代码启动Agent”的大而全框架,因为封装得太黑盒了,出了问题你根本不知道是哪一环在作妖。
我更推荐从“可组合”的框架开始。所谓可组合,就是每个模块(模型调用、工具执行、记忆存储、循环控制)都暴露清晰的接口,你能看到中间每一步的状态和日志。等到你完全理解了Agent的运行机制,再迁移到大而全的平台也不迟。
我构建个人项目时用的就是一个很成熟的Python框架(这里不做商业绑定,你完全可以用任何主流Agent框架替代,核心思想一致)。它支持模型统一调用、工具自动注册、记忆插拔式接入,关键代码不超过200行就能跑通一个带搜索和计算能力的Agent。
4.2 环境准备与最简Agent实现
无论用什么框架,你的开发环境至少需要搞定这几件事:Python 3.10+、一个可调用的LLM接口(准备好API Key)、一个向量数据库(用于长期记忆,可用轻量方案代替)。下面给出一段最简可运行的Agent代码,它只实现“聊天+调用一个当前时间查询工具”,但麻雀虽小五脏俱全。
# 安装依赖:pip install agent-core from agent_core import Agent, Tool def get_current_time(format: str = "%Y-%m-%d") -> str: """获取当前日期,format参数可选。""" from datetime import datetime return datetime.now().strftime(format) # 注册工具(框架会根据函数的docstring自动生成模型的工具Schema) agent = Agent(model="your-llm-model", tools=[Tool.from_function(get_current_time)]) # 运行Agent response = agent.run("你好,今天是几月几号?帮我算一下距离元旦还有多少天。") print(response)你仔细看这段代码会发现,框架底层自动完成了三件事:把函数get_current_time描述成模型能看懂的JSON Schema;把用户提问和工具描述拼接成上下文;执行“模型推理 -> 调用工具 -> 再次推理直到最终回答”的循环。跑一次这个例子,你就能直观感受到Agent和普通Chatbot的体验差异。
4.3 给Agent接入第一个自定义工具:完整步骤拆解
上一步的玩具例子里,工具已经能用,但真实业务里你的工具往往是要读取数据库、调用内部API的。这时你必须掌握工具开发的四个关键步骤。
第一步,定义工具输入输出。先用清晰的类或JSON Schema定义参数名、类型、说明、取值范围和默认值。为什么要这么细?因为模型不是人,它看不到函数代码里的注释,所有的使用说明都必须通过Schema传达。一个不合格的Schema会让模型反复生成错误参数,白白浪费token。
第二步,处理异常与超时。工具执行不可能永远成功。调用外部API超时了,报错信息应该被捕获后以“可读文本”的形式返回给模型,并建议“请稍后重试”或“尝试其他工具”。绝不能把Python的Stack Trace直接怼给模型,那会让它陷入困惑。更可怕的是,如果你不捕获异常,整个Agent循环可能直接崩溃。
第三步,控制工具副作用。写操作工具(发消息、建订单、删数据)必须设计成“先确认再执行”的模式。我的习惯是让Agent先生成一个“预执行计划”,由人类确认后才真正落库。这一步在自动化场景里看似多余,但在生产环境中能救你命。
第四步,低成本试运行。新工具接入后,先用10~20个典型测试case跑一遍,观察模型能不能正确理解工具用途、参数传得是否准确。不要直接上生产,我见过太多Agent上线第一天就把测试环境数据搞乱的案例。
4.4 记忆持久化:让Agent拥有真正的“记性”
Agent会话一旦中断(比如服务重启),之前的记忆如果只存在内存里,就会消失。要让Agent拥有跨会话的长期记忆,需要一个持久化存储层。我的推荐方案是:向量数据库(存储可检索的语义记忆)+ 关系型数据库(存储结构化的用户偏好与任务状态)。
实际操作起来是这样的:每次对话结束后,用一个摘要链把整段对话压缩成200字以内的“要点记忆”,然后做两件事——把要点向量化存入向量库,把“用户ID、时间、任务类型、状态”等结构化字段写入关系库。下次该用户再来发起新任务时,Agent先从关系库里拉出他的历史任务状态,再从向量库里检索和当前任务相关的历史信息,一并注入上下文。
这里要提醒一个实操中常犯的错误:向量检索的结果不是越多越好。Top K取太少可能漏信息,取太多又容易把无关内容塞进上下文,干扰模型判断。我的经验是,对大多数业务场景,Top K取4~8个片段,每个片段控制在500字左右,性价比最高。这个值需要你根据具体数据分块大小反复试。
5. 常见问题与避坑指南:烧钱、失控、幻觉怎么破
5.1 上下文爆炸:Long Context并不万能
Agent每多执行一步,工具返回结果和历史记录都会让上下文变长。如果你不加任何控制,一个复杂任务可能跑到第20轮时,模型输入就已经超过窗口上限或响应变慢到不可接受。
我有几个常用的压缩策略:
- 对话修剪:超过N轮的历史记录不再全文进入上下文,而是被替换成摘要。
- 工具结果截断:超长的工具返回内容(比如整篇网页)先截断到500字再进入模型,详细全文放在向量库里按需检索。
- 暂停归档:当Agent检测到当前任务已完成,主动把整段历史归档存库,为下一轮任务清空工作记忆。
5.2 模型幻觉导致错误操作:怎么加验证关卡
Agent最让人头疼的幻觉问题,在“生成内容”场景还好,顶多胡说八道;但在“工具调用”场景,幻觉会直接造成错误操作。比如Agent本来想查A客户的合同信息,结果模型在检索条件里幻觉出了一个错的客户编号,把B客户的收发记录调了出来。
对付这个问题,我在实践中摸索出一套组合拳:
- 参数级校验:工具Schema里对枚举值、格式、取值范围做强校验,不合法的一律拒绝执行并返回错误信息。
- 结果交叉验证:对关键操作,要求模型引用“信息来源ID”而不是直接给结论。比如把某份数据塞进最终报告时,必须附带上检索来源标识。
- 人工审批兜底:高风险工具(删除、支付、发送)不直接对模型开放,而是走人工确认队列。
5.3 死循环与成本失控:预算与熔断机制
Agent的“自主性”是双刃剑。它可能会因为目标不明确,陷入“搜索-总结-发现缺少信息-再搜索”的死循环,每一轮都在消耗Token,对应着真金白银的API账单。我在搭建个人项目时,被账单“教育”过一次后,才彻底重视起熔断机制。
我自己会在系统里设置三层防护:
- 轮数限制:单个任务最多执行30步,超过即终止并返回已收集的中间结果。
- 金额限制:每次运行前估算单次任务的Token消耗上限(模型token单价 x 平均每轮token数 x 最大轮数),预算用完强制停。
- token掺水检测:如果检测到模型连续多轮输出几乎相同的工具调用参数,则判定为“卡死”,终止并请用户补充说明。
具体的成本估算公式,我常用的是:
单任务预估成本 = 平均每轮输入token数 × 模型输入单价 × 平均轮数 + 平均每轮输出token数 × 模型输出单价 × 平均轮数根据这个估算值,再乘一个3~5倍的安全系数,就是你该设置的单任务预算上限。
5.4 调试技巧:日志、追踪与可观测性建设
Agent的问题太隐蔽,因为它的决策过程是模型自己生成的,传统程序那些“打断点、看堆栈”的调试手段几乎失效。想要高效定位问题,必须给Agent建立完整的可观测体系。
我现在的做法是:每次Agent运行,都会把每个关键节点的数据以JSON Lines的格式落盘,包含以下字段:
{"step": 1, "type": "llm_call", "tokens": 1200, "messages": "系统提示词+前文摘要"} {"step": 2, "type": "tool_call", "tool": "web_search", "params": {"query": "xxx"}} {"step": 3, "type": "tool_result", "content": "返回的前200字摘要"}有了这个轨迹日志,你就能回放Agent每一步做了什么、看到了什么、基于什么信息做出的决策。很多问题一看日志就明白了:原来是工具返回的数据本身不规范,模型被错误数据误导了;或者是某轮工具返回内容过长,模型被无关信息带偏了。如果发现调试还是要靠“盲猜”,说明你的可观测体系还没到位。
5.5 常见问题速查表
| 典型现象 | 可能原因 | 快速解决办法 |
|---|---|---|
| Agent反复调用同一个工具 | 工具返回结果不满足需求,或被错误信息卡住 | 优化工具返回内容的格式,加入结构化摘要;检查是否需要在Prompt里提供备选工具 |
| 模型总是编造工具参数 | 工具Schema描述不够清晰 | 补充参数说明、给出实体示例(Few-shot) |
| 长任务跑到一半上下文爆掉 | 缺少上下文压缩机制 | 引入对话修剪、工具结果截断、摘要归档三板斧 |
| Agent生成的结果看似合理但数据完全错误 | 检索结果不相关或没有交叉验证 | 检查向量检索Top K设置,增加检索来源强制引用 |
| 响应越来越慢 | 上下文太长 | 工作记忆归档,精简历史 |
| Agent执行危险操作 | 工具暴露范围过大 | 高风险工具走人工审批,不直连模型 |
6. 我的几点实战心得与接下来值得做的事
文章写到这里,该讲的技术点都讲得差不多了,最后分享几个我踩过坑之后沉淀下来的体会。
第一,Agent的“智能”很大程度上是工程堆出来的,不是模型凭空自带的。别看模型本身能力已经很猛,但在复杂的真实任务里,它需要完善的工具描述、清晰的上下文结构、严格的校验逻辑来兜底。你在工程上付出的每一分努力,都能实打实地反映在Agent的稳定性和准确率上。别迷信“提示词调教就能让Agent变神”,那是幸存者偏差。
第二,从小处着手。很多朋友一上来就想做一个能全自动处理所有报表、自动回复所有邮件的超级Agent,结果做了一个月还在跟第一层工具调用死磕。我的建议是,先用最朴素的架构,做一个小而完整的功能闭环:比如“让Agent每天定时帮你抓取三篇指定网站的文章并生成摘要”。这一个大闭环跑通了,再横向扩展更多工具和能力,你才真正理解Agent系统该如何成长。
第三,多关注Agent的“拒绝能力”。一个优秀的Agent不应该有求必应。它应该能识别出哪些请求超出了自己的能力边界、哪些请求存在风险,然后主动向用户说明“这个操作我不建议自动执行,因为它会影响到线上数据”。把“拒绝与确认机制”设计好,你的Agent会比那些只会横冲直撞的Agent靠谱十倍。
接下来我打算把精力放到两件事上:一是多Agent协作模式下,如何设计角色分工与消息路由,让多个Agent在同一个任务上手拉手干活而不是互相打架;二是Agent的记忆系统如何与业务知识图谱结合,真正做到“越用越懂你”。这些方向目前还在业内快速演进,如果后面有了新的可落地的经验和踩坑记录,我再写文章和你详细分享。