前几天有位读者来找我,说去某电商大厂面 AI Agent 方向,二面被面试官追着问了四十多分钟,回来整个人都是懵的。他把面试官的问题列表发给我,我快速扫了一遍,第一反应是:这些问题其实都不难,难的是他一直在用“LangChain 怎么调”的思路回答,而面试官真正想听的是“Agent 为什么需要这么设计”的底层逻辑。这件事让我很想把整个拆解过程完整写下来,从小白的视角讲清楚大模型 Agent 的核心设计,顺便把实战里那些面试官不会写在 JD 上的坑也一起说了。这篇你可以当面试笔记看,也可以当动手做 Agent 的参考手册用,内容偏实战,不堆术语。
要说清楚这件事,得先从那场追问本身开始。面试官的问题看起来散,实际上每一条都锚定在 Agent 设计的某个关键节点上,把这些节点拼起来,就是一张完整的 Agent 骨架图。
1. 面试官的连番追问,问的到底是什么
1.1 还原一次高压追问现场
那场面试我没有全程旁听,但通过读者复述,基本能还原出当时的追问链。第一轮很常规,让你介绍自己做的 Agent 项目。从第二轮开始,节奏就变了。
面试官的问题大体是这么几个:
- 你说你做了个 Agent,它和普通的聊天机器人到底有什么区别?边界在哪?
- 你的 Agent 是怎么拆解用户目标的?如果目标拆错了,后面怎么发现,怎么修正?
- 你用了 ReAct 范式,那 ReAct 到底在解决什么问题?如果第一轮规划出来的 Plan 执行到一半失效了,你的 Agent 会怎么办?
- 工具调用失败之后,你是直接重试、换一个工具,还是干脆重新规划?有没有想过重试几次就该停?
- 你的记忆存在哪里?是文件、数据库还是向量库?上下文窗口满了以后,历史信息怎么处理?
- 如果你要拆两个 Agent 协作,A 的结果要给 B 用,你会怎么设计它们之间的通信?走消息队列、共享存储,还是单纯让一个 Agent 去调另一个的接口?
- 用户说“帮我订个会议室,预算 300 元以内”,这个任务的完成标准由谁来定义?你的 Agent 怎么知道自己做成功了?
说句实话,这些问题如果没真正做过 Agent,光靠背 Prompt 工程或者熟读某框架的文档,是接不住的。因为每一条都在挑战同一个东西:你脑子里有没有一套完整的 Agent 设计框架,还是只是把别人的 Demo 跑通了而已。
1.2 追问背后真正想验证的东西
这几轮追问看似东一榔头西一棒子,组合起来其实指向五个能力维度:目标拆解能力、工具调用边界、记忆管理、错误恢复路径,以及任务完成标准的定义。单个问题都能在文档里找到对应模块,但串成追问链的时候,考察的就是你有没有把它们当成一个闭环系统来理解。
我后来帮那位读者重新梳理时,给他的第一句话是:不要把这些模块当成“功能点”去背,要把它们当成一个循环里的环节。Agent 运行的本质是一个“感知-规划-行动-反思”的循环,核心设计就是把这个循环的每个环节都做出明确的边界和兜底策略。面试官追问“失效了怎么办”“失败了怎么办”“满了怎么办”,其实就是在逼你把边界画出来。
想通这一点之后,再看那些问题就顺了。下面我按小白能理解的方式,把 Agent 的核心设计拆成四块,也就是那场面试里反复出现的四个主题。
2. 小白也能看懂的Agent核心设计:规划、记忆、工具与反思
2.1 规划:模型不是回答者,而是路径设计者
很多人对 Agent 的第一个误解,是把 Agent 当成一个更聪明的问答机器人。实际上,普通聊天机器人拿到问题后直接生成答案,Agent 拿到目标后要先生成一条路径。
规划模块干的事,就是把“帮我订个会议室,预算 300 元以内”这样的大目标,拆成“搜索可用会议室”“核对预算”“检查参会人时间冲突”“提交预订”“向用户确认”这几个子步骤。这个过程在学术圈有各种名字,CoT、ReAct、Self-Ask、Plan-and-Solve,听上去很多,内核其实是同一件事:让模型把最终答案的推导过程显式地写出来,而不是一步到位。
面试官追问“目标拆错了怎么办”指向的正是规划的容错设计。我的建议是不要假设模型第一次拆解一定正确,而是在实现里预留两条路:一条是执行中发现前置条件不满足时,让模型重新读取当前状态并重新规划,另一条是设置最大规划轮次,拆了三轮还走不通,就停下来问用户,而不是自己硬编。
这里有一个多数教程不会强调的细节:规划不是只在任务开头做一次,而是在每个关键节点都做一次轻量判断。你可以把规划想象成用手机地图导航,出发前规划一次全程路线,但走到一半发现前方封路,导航会基于当前位置重新规划后半程。Agent 的规划模块最好也是这种“全局规划 + 局部重规划”的结构,全局定方向,局部保灵活。
2.2 记忆:短期工作区与长期档案库
面试官问“你的记忆存在哪”时,很多人的第一反应是向量数据库。这个答案不能说错,但没说到根上。
Agent 的记忆至少分两层。短期记忆就是上下文窗口里正在处理的信息,相当于你办公桌上摊开的资料,正在用,占地方。长期记忆则是任务结束后仍需要保留的知识、历史决策、用户偏好,相当于你身后的档案柜,不常翻,但需要时能在里面找到东西。向量库只是长期记忆的一种载体,不是记忆本身。
上下文窗口满了怎么办?这几乎是每个 Agent 项目早晚会遇到的问题。处理手段常见的有三种:截断最旧的消息、把中间轮次的对话压缩成摘要、只保留与当前目标相关的关键历史。我个人在实际项目中偏向“摘要+关键信息抽取”的组合,因为纯截断会把一些重要的中间状态丢掉,而摘要能保留语义,关键信息抽取能保留精确数值,比如会议室的 ID、预订的单号。
还有一个容易忽略的点:记忆要区分“任务相关”和“用户相关”。任务相关的记忆随着任务结束可以清理,用户相关的偏好则需要持久化。如果这两类混在同一个存储里,时间长了会出现一个很尴尬的现象——Agent 在做一件完全无关的新任务时,突然调出三个月前的某个偏好,毫无来由地改变了当前行为。
2.3 工具层:Function Calling 的本质是结构化输出
工具层是很多小白最容易误解的部分。有人以为 Agent 调用工具,是大模型自己发起了 HTTP 请求,或者大模型自己执行了代码。实际上不是这样。以目前主流实现的 Function Calling 为例,大模型做的只是根据对话内容,输出一个结构化的调用意图,真正去调接口、查数据库、发邮件的,是外围的业务代码。
打个比方,工具调用就像你请了一个助理,助理不会亲自去厨房炒菜,但他会写一张精确的工单递给厨师,写明菜品、口味、忌口。Function Calling 里那个 JSON 结构就是这张工单,模型只需要把“炒一盘少油少盐的青椒肉丝”翻译成{"dish": "青椒肉丝", "oil": "少", "salt": "少"},剩下的事由后厨完成。
所以工具设计的核心不是模型,而是工单规范。我踩过的坑是,工具参数描述写得含糊,模型就会频繁地传错参数。后来我给每个参数都加了 example,并且把可枚举值直接写在描述里,调用成功率肉眼可见地涨了一截。
另外一个让很多人意外的事实是:工具返回结果也需要结构化。如果你让一个工具返回一整个 HTML 页面,模型再聪明,也很难从中准确提取“预订成功”和“订单号 12345”。比较好的做法是让工具统一返回简洁的 JSON 字段,比如{"status": "success", "booking_id": "12345"},模型拿到之后不需要二次解析就能继续下一步。
2.4 反思循环:让 Agent 拥有自我修正能力
第四块设计是反思,这也是“Agent 的灵魂”最集中的体现。一个没有反思环节的 Agent,本质上是一条直线:用户提需求,模型调工具,工具返回结果,模型给最终答案。这套流程应付简单任务没问题,但稍微复杂一点的场景,比如工具返回“会议室不存在”或者“预算超了”,如果 Agent 没有反思机制,就会卡死在原地,或者把错误结果直接抛给用户。
反思模块的位置通常在一次行动之后、下一次规划之前。它做的事情很简单:让模型看一眼“刚才这步操作的结果是否符合预期”,然后决定是继续、调整、还是终止。很多框架里有 Self-Refine、Critique 之类的说法,原理都差不多。
实际落地的时候,反思不能只靠模型自觉。我会在 Prompt 里要求模型在每个工具调用后先输出一段简短判断,比如“结果包含有效预订 ID,可以继续”或者“预算不足,需要搜索其他时段”,再决定下一步动作。这样做的另一个好处是,整个执行过程有了一条清晰的可读轨迹,排查问题的时候能知道 Agent 到底在哪个环节做了错误判断。
2.5 把四个模块串成一个循环
把这四块串起来,就是 Agent 运行时的最小闭环:接收目标 → 拆解规划 → 按需调用工具 → 查看结果 → 反思是否达成子目标 → 进入下一步或修正计划 → 全部完成后输出最终答案。看起来不复杂,但它解释了面试里的大多数问题。
用伪代码表示就是这样:
def run_agent(goal, tools, max_steps=5): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": goal}, ] for step in range(max_steps): response = model.chat(messages=messages, tools=tools) if response.tool_calls: for call in response.tool_calls: tool_result = execute_tool(call) # 真正的业务代码在这里执行 messages.append(format_tool_result(call, tool_result)) # 结果写回上下文 reflect = model.chat(messages=messages) # 反思这一步要不要继续 if should_stop(reflect): break else: return response.content return generate_final_summary(messages)这段代码里最容易被忽略的是max_steps。它像是一个安全阀,防止 Agent 陷入“调用工具-失败-反思-再调用-再失败”的无限循环。很多生产事故的根源,就是忘了加这个上限。
框架层面的东西讲完,接下来落到实战。面试官问了很多“为什么”,但你手上得有一个真正跑通的 Agent 项目,才答得踏实。
3. 从0到1搭一个可面试、可实战的Agent:实操复盘
3.1 别急着上框架,先写一个最简循环
现在 Agent 框架很多,功能也很全。但我的建议是,第一次做 Agent 不要一上来就套框架,先自己写一个 50 行左右的最简循环,把“模型生成-工具执行-结果回填”这三步跑通。框架帮你封装的是细节,但也把很多本该理解的机制藏了起来。自己写一遍,你对 Agent 运行机制的理解会扎实很多。
上面那段伪代码,加上一个真实的模型接口和两三个真实的工具函数,就能变成第一个可运行的 Demo。我当时用的组合很简单:一个大模型 API,配合 Python 写两个纯函数工具,一个负责查天气,一个负责算日期。目标也简单:“帮我看看后天适不适合户外跑步。”这个任务不复杂,但完整覆盖了工具调度、结果回填、最终回答三个核心环节。
第一版跑通之后,再去看 LangChain 这类框架,你会发现自己能看懂它每一层在做什么,而不是对着抽象类瞎猜。这个认知对面试尤其重要,因为面试官一问“你框架底层发生了什么”,你就能用自己的代码经历回答出来。
3.2 真正需要调的参数不是只有 temperature
很多人聊 Agent 调参,第一反应是 temperature。这个参数确实影响随机性,但在 Agent 场景里,更值得关注的是另外几个参数。
第一个是max_steps或类似的最大迭代次数,它决定 Agent 最多能执行多少轮工具调用。设太大会增加成本和延迟,设太小复杂任务完不成。我常用的经验值是:简单任务 3 到 5,复杂任务 8 到 10,超过 10 轮还没结束的任务,大概率是规划出了问题,不如直接终止。
第二个是工具选择的控制方式。很多模型接口支持tool_choice参数,可以设为自动、必选某个工具、或者禁止调用工具。在需要强制某个环节走工具的场景里,这个参数比在 Prompt 里反复强调有效得多。
第三个是返回结果的输出格式。我会给 Agent 的最终回答定义一个 JSON Schema,要求模型按固定结构返回,比如{"success": true, "booking_id": "12345", "error": null}。这样下游系统可以直接解析,不需要处理一堆自由文本。
下面是我早期项目里用过的一组参数配置,供参考:
| 参数 | 配置值 | 设置原因 |
|---|---|---|
| temperature | 0.2 | 降低随机性,让工具调用更稳 |
| max_steps | 8 | 复杂任务留足余地,同时防死循环 |
| tool_choice | auto | 让模型根据任务自主决定何时调工具 |
| response_format | json_schema | 保证最终输出可解析 |
| timeout | 30s | 工具调用超过 30 秒直接判失败,走兜底逻辑 |
还有个细节是上下文长度管理。工具返回结果如果又长又杂,会快速消耗上下文预算。我通常在把工具结果写回 messages 之前做一个轻量压缩,只保留关键字段,这样同等窗口能用更多轮次。
3.3 从“能跑”到“可控”:加三层约束
Demo 能跑是个分水岭,接下来真正花时间的是让它变得可控。可控我理解成三层约束。
第一层是格式约束。所有工具的输入和输出都用严格的结构化定义,模型输出的调用参数要经过校验,参数类型不对就要求模型重新生成,而不是直接执行。第二层是业务规则约束。比如订会议室场景里,预算 300 元以内的要求应该在代码层强制校验,不能指望模型每次都记得,即使模型把预算算错了,工具执行前也会被校验逻辑拦下来。第三层是流程约束。关键决策点加人工确认,比如下单、删除、支付这类不可逆操作,Agent 只能生成待确认指令,由用户确认后再执行。
这三层约束加完,Agent 才从“玩具”变成“工具”。面试官问“怎么保证 Agent 不乱来”,你可以直接把这套分层约束讲给他听,这比一句“我 Prompt 写得很详细”有说服力得多。
3.4 面试里怎么讲 Agent 项目才算加分
项目讲得好不好,直接影响面试结果。我给那位读者的建议是,不要在自我介绍阶段罗列“我用了 LangChain、用了向量库、用了 ReAct”,而是用一个完整的故事线把项目串起来。
可以按这个顺序讲:业务背景是什么,为什么需要 Agent 而不是普通接口;Agent 的闭环怎么设计的,四个模块分别负责什么;你具体遇到了什么问题,又是怎么定位、怎么解决的;最终效果如何,用什么指标衡量。面试官追问到哪一层,你都能往下接,这才是项目展示的完整姿态。
我当时给他示范了一个讲法:项目一开始最痛的是工具调用不稳定,模型经常传错参数,后来把所有工具的参数定义加上了约束和示例,错误率降低了一半;后来又发现上下文窗口不够用,就做了历史摘要压缩。像这样带着问题讲,每个技术点都有场景支撑,面试官自然觉得你是真的做过,而不是背了题。
4. 追问深水区:稳定、评估与多Agent协作
4.1 稳定性靠三层防线,不靠模型自觉
讲到稳定性,一定要把前面说的三层约束再深化一层。模型本质上是一个概率系统,它可能这次调用这个工具,下次同样的输入选了另一个工具。稳定性设计的目的,就是让这种概率波动不影响整体流程的正确性。
第一道防线是输入输出校验。工具输入参数在代码层面做严格的类型和范围检查,输出结果也统一结构。第二道防线是业务规则引擎。所有硬性规则,比如金额限制、日期合法性、库存数量,都写在 Agent 之外,由普通代码执行校验,模型负责生成意图,规则负责放行或拒绝。第三道防线是熔断降级。设置最大轮次、最大延迟、最大成本三个硬指标,一旦触发就停止运行,输出一个兜底话术,比如“当前任务太复杂,需要人工介入”。
这三道防线缺一不可。只靠 Prompt 约束,模型表现不稳定;只靠代码兜底,Agent 的灵活性又会被锁死。两者结合,才是生产级 Agent 该有的样子。
4.2 效果评估:用回归集和数据说话
Agent 没有标准答案,这让评估变得很棘手。但面试官问“你效果怎么衡量”的时候,你不能只说“我觉得还挺好”。正确的应对方法是建立一套可复用的回归测试集。
我会把测试集分成三类:正常路径、边界路径、失败路径。正常路径是用户按预期方式提需求,Agent 应该完整走通;边界路径是预算刚好等于 300、会议室刚好满员这类情况,Agent 要做合理判断;失败路径是工具持续报错、用户中途改需求,Agent 要能优雅地终止或转人工。
每一类准备 10 到 15 条用例,每次改完代码都跑一遍回归集,记录任务成功率、平均轮次、工具调用失败率、单任务成本这几个指标。数据积累到一定量,你就能很客观地说出“改动前成功率 80%,改动后成功率 92%”,这种表述在面试里是碾压级的。
4.3 RAG 是 Agent 的记忆增强,而不是 Agent 本身
很多人把 RAG 和 Agent 混为一谈,面试里这是一个很容易暴露认知深度的话题。RAG 的本质是给模型提供外部资料检索能力,它解决的是“模型不知道”的问题;Agent 解决的是“模型不会做”的问题,后者需要规划、工具和反思。
在 Agent 系统里,RAG 通常作为记忆模块的一部分存在。比如用户问到公司内部政策,Agent 决定调用一个“知识库检索工具”,这个工具内部走向量召回和重排,再把最相关的几段文本作为上下文返回给模型。这个过程中,RAG 是被 Agent 调用的一个工具,而 Agent 本身仍然负责判断什么时候检索、检索结果是否可信、检索不到时怎么办。
搞清楚这个边界,回答面试题“RAG 和 Agent 的区别”时,就能给出比较完整的视角:RAG 是被动检索,Agent 是主动决策;RAG 增强单次回答的质量,Agent 负责多轮目标的达成。
4.4 多Agent协作:该拆才拆,别为架构而架构
多 Agent 是面试爱聊的话题,也是容易聊飘的话题。一上来就规划五个 Agent 协作,但问你它们之间怎么同步状态、怎么避免冲突、怎么保证最终结果一致,又答不上来,这种情况很常见。
我的建议是先从单 Agent 开始,单 Agent 实在撑不住了再拆。什么情况需要拆?上下文窗口实在不够用,或者任务需要强权限隔离,比如一个 Agent 只能查数据,另一个 Agent 才有权限写数据,或者任务天然分成几个可以并行处理的阶段。只有这些时候,拆多 Agent 才有意义。
拆的时候也要控制粒度。我在实际项目中比较推荐“一主多辅”的模式:一个主控 Agent 负责任务理解、拆解、汇总,多个子 Agent 各负责一个子任务,结果返回给主控。子 Agent 之间尽量不直接通信,所有的中间状态通过主控统一管理。这样能避免多 Agent 之间消息乱飞、状态难以追踪的问题。
至于通信方式,别想得太复杂。前期用共享的任务队列就够了,A 的结果写到队列,B 从队列取任务和输入。等到需要实时协作或者复杂编排时,再去研究消息总线和事件机制。
4.5 成本与体验的取舍:每多一步,都是钱
Agent 的调用成本比普通聊天高得多,因为一次任务需要多轮模型调用。我算过一笔账:一个简单任务,拆解 1 次、工具调用前后各调 1 次、反思 1 次、最终汇总 1 次,总共至少 5 次模型调用。如果任务复杂,十几二十次也很正常。
成本控制有几个常用手段。一是能一次调用完成的任务,不要拆成多轮。有些问题不需要工具,直接回答就行,没必要为了“像个 Agent”而强行走工具。二是中间结果压缩,把工具返回的长文本摘要成关键信息,能大幅减少后续轮次的 token 消耗。三是设置预算上限,任务累计消耗超过预设值就自动终止,避免失控。
面试官问“Agent 和普通 API 有什么区别”的时候,你可以很自然地带上这层成本视角,让他知道你不仅懂设计,还懂工程化落地。
5. 常见问题排查与避坑技巧实录
5.1 高频问题速查表
把实战中高频出现的问题整理成了一张速查表,做项目或者面试复盘时都可以直接对照。
| 问题现象 | 常见原因 | 排查方法 |
|---|---|---|
| Agent 陷入死循环 | 没有设置最大轮次,或工具结果未写回上下文 | 设 max_steps 硬上限,检查工具结果是否拼回 messages |
| 工具参数频繁传错 | 参数描述模糊,缺少枚举和示例 | 给 schema 添加 example,用 tool_choice 锁定关键工具 |
| 模型一直不调用工具 | Prompt 未说明工具使用时机,模型不知道何时该调 | 在系统提示词中明确触发条件,必要时设 tool_choice |
| 上下文窗口很快耗尽 | 工具返回过长的原始文本 | 对工具结果做摘要,只保留结构化关键字段 |
| 执行中途计划失效 | 全局规划一次到底,缺少重规划机制 | 增加局部重规划步骤,每轮检查前置条件是否成立 |
| 最终结果无法解析 | 输出自由文本,结构不稳定 | 强制使用 JSON Schema 输出,并在代码层校验 |
5.2 三条独家经验
第一条经验,工具返回一定要设计成“模型友好”的结构。不要返回用户看的格式化页面,模型需要的是字段清晰的 JSON。这件事我踩过不止一次,每次都是“看起来没问题,一跑就出乱子”,后来把所有工具返回统一成 JSON,问题一下子就少了。
第二条经验,给 Agent 一个“主动放弃”的选项。不是所有任务都能完成,与其让模型在最后一步编造一个结论,不如在 Prompt 里明确告诉它:当关键信息缺失、多次尝试失败时,直接输出{"success": false, "reason": "..."}。这本质上是给不确定性留了出口。
第三条经验,测试集里必须有失败用例。很多人只测正常路径,测试样例全是“一问一答一成功”,看起来效果很好,一上真实数据就崩。我在回归集里专门放了 30% 的失败路径用例,比如用户需求前后矛盾、工具返回错误码、中间断网等,通过这些用例,Agent 的错误处理能力才真正长出来。
5.3 面试里 30 秒讲清一个排查案例
在面试中讲排查经验,最忌讳的就是绕了半天说不到重点。我习惯用“背景-现象-假设-排查-修复-验证”这个浓缩结构,30 秒内讲完一个完整故事。
举个例子:背景是 Agent 在线预订功能上线一周后出现间歇性失败;现象是用户反馈订不上会议室,日志里显示工具调用超时;假设是某些会议室 ID 不存在导致查询接口报错,也可能是网络抖动;排查是加了工具调用日志后发现失败集中在少数几个会议室;修复是在工具执行前增加 ID 校验,失败时让模型重新搜索;验证是回归测试通过,线上失败率从 7% 降到 0.5%。
这样讲,每句话都有信息量,面试官不需要追问就能理解你的思路。这套结构现在也被我用在团队内部复盘里,效果比写长篇周报好得多。
最后说点个人体感。Agent 面试追问到最后,面试官真正在意的不是你会不会某个框架,也不是你背了多少概念,而是你面对一个开放式任务时,能不能把它拆成可验证的步骤,并且每一步都画清楚边界和兜底。我自己最早做 Agent 的时候,也觉得只要把模型接上、工具配上,就能自然跑通。后来在上下文爆炸、工具参数错乱、计划失效这些坑里反复滚过几轮,才慢慢体会到,把规划、记忆、工具、反思四个模块的边界划清楚,再补上约束和评估,才是 Agent 真正的灵魂所在。希望这篇复盘能帮你在下一次面试或项目启动时,少走我走过的弯路。