很多朋友学完第一课和第二课之后,会有一种“AI也不过如此”的错觉——聊聊天、写写文案、生成几张图,好像已经摸到天花板了。但真正进入AI Agent(智能体)和AI编程实战的人,很快就发现事情没那么简单:模型经常不按套路出牌,任务一复杂就翻车,工具调用失败了你还不知道为什么。这恰恰是第三课要解决的核心问题:从“会聊天”到“会交付”。
这一课,我不打算给你贴大段的概念定义,而是把AI大模型基础理论里最影响实战的几块掰开讲清楚,然后带着你从零搭一个能查资料、能算数据、能干活的小Agent,再聊多AI协作和并发场景下的工程化思路。适合的人有两类:一类是刚学完AI入门课、想往AI编程走但不知道从哪下手的同学;另一类是已经在业务里用AI提效,但总觉得自己在用“人工智障”,想搞明白背后原理的开发者。
1. 从对话到交付:第三课先想明白Agent和你谁说了算
很多人对AI Agent的第一反应是“这玩意儿能自动干活”。这个说法没错,但它掩盖了一个关键问题:Agent到底是什么?我的定义很朴素——Agent是一个“能自己做决定、自己动手、自己根据结果调整下一步”的AI系统。它和大模型对话的本质区别,不是功能变多了,而是控制权发生了转移。
你和大模型聊天,每一轮都是你在主导:你问,它答,你继续问,它继续答。Agent不一样,你只需要给它一个目标,剩下的过程它来规划、它来尝试、它来修正。听起来很爽对吧?但代价是,你要学会“把话说清楚”,并且懂得设边界。我见过太多人搭Agent失败,不是模型不行,而是他在System Prompt里根本没说清楚:这个Agent的职责范围是什么、有哪些工具可以用、什么情况下必须停下来问人。
这里有个很重要的认知:大模型的强项是“生成”,不是“记忆”,更不是“执行”。它本质上是一个极度擅长预测下一句话的系统,你给它合理的上下文,它就能输出合理的内容。但它不记你的会话历史(除非你手动塞给它)、不会主动去查数据库(除非你给它工具)、也不能保证每一步都严谨(它经常跳步和瞎编)。Agent存在的意义,就是把这些弱点全部用工程手段补上——用外部存储补记忆,用工具调用补执行,用任务规划补严谨。
那为什么第三课才讲这个?因为第一课你学了模型是什么,第二课你学了怎么写提示词,但这些都是“对话范式”。到了Agent,你需要换一套思维:提示词不再是“把话问好”,而是“把任务定义清楚”。这是两种完全不同的写作方式。对话范式里,你写的是问题;Agent范式里,你写的是章程、边界、工具清单和纠错策略。
我见过最快的入门路径是这样的:先别急着用复杂的框架,自己用API写一个最小的Agent循环——给模型配两个工具,让它反复“思考-调用-观察-再思考”,跑通了再去学LangGraph、Dify这些工具。为什么?因为框架会把很多细节藏起来,藏起来的东西一旦出问题,你根本不知道去哪排查。自己写一遍循环,你对“模型输出和工具调用之间到底怎么连接”会有肌肉记忆式的理解。
2. 又一次大模型基础理论:token、上下文与幻觉的真相
既然要搞Agent,大模型基础理论里有些东西就得重新复习一遍——但不是为了考试,是为了让你在选参数、排故障的时候有据可依。
2.1 token不是“字数”,是成本也是能力边界
所有模型都按token计费,但很多人对token的理解停留在“大概等于英文字母或半个汉字”。这在对话场景无所谓,Agent场景就麻烦了:Agent会多轮循环、每轮都要把工具返回结果拼进上下文,token消耗是指数级上升的。我做过一个真实的文本分析Agent,让它处理一篇3000字的文章并输出摘要,中间它调用了4次工具,最后一算,总token是文章本身字数的6倍。
所以你在搭Agent之前,一定先搞清楚三件事:模型的上下文窗口到底有多大、你的业务单次会话大概需要多少token、成本上限是多少。上下文窗口不是“越大越好”的,窗口越大,模型的注意力越容易被稀释。实测下来,很多任务在长上下文下反而不如短上下文稳定,因为无关信息太多,模型容易“迷失重点”。能通过RAG(检索增强生成)缩小范围的,就不要把整份文档塞进去。
2.2 temperature到底该怎么设
temperature这个参数,通俗讲是“回答的随机程度”。0到1之间,越大越有创造性,越小越确定。很多人从头到尾用默认值,这是不对的。
在Agent场景里,大部分环节你希望模型“稳”而不是“飘”——调工具、解析参数、做数据提取,这些任务一旦发散就会出错。我会把工具调用和数据处理节点设成0到0.2;只有需要在最后生成文案、写总结、做头脑风暴的节点,才调到0.7以上。这个经验属于谁用谁知道的那种,不调,你后面会被各种莫名其妙的输出逼疯。
2.3 幻觉是bug吗?不,是特性
大模型会产生幻觉,也就是一本正经地胡说八道。很多人把这当模型缺陷,实际上这是“预测下一个词”机制的必然结果。模型不知道“事实”,它只知道“哪个词接在这儿更像人话”。所以它回答一个问题,不是去“查证”,而是去“生成一个看起来合理的答案”。当这个答案恰好在训练数据里匹配到了真实信息,它就显得聪明;匹配不到,它就编一个。
理解了这一点,你就能理解为什么Agent必须配备工具和知识库:模型的角色是“推理”,不是“记忆”。凡是涉及实时信息、私有数据、精确计算的,一律通过工具调用去拿结果,而不是让模型凭记忆生成。RAG、API查询、数据库读取,都是为了把“记忆”外包出去,只留“推理”给模型。
我在实操中还有个教训:低temperature也不能完全消除幻觉,尤其是模型被要求回答超出它知识范畴的问题时。所以Agent的System Prompt里要明确写一句:“当你不确定时,明确回答不确定,并使用工具查询;如果工具仍未返回可靠结果,如实告知用户。”这行字不值钱,但能省下大量后期救火的时间。
3. Agent的工作哲学:ReAct循环里的想、做、看
现在进入本期最核心的话题:Agent是怎么“想事情”的。几乎所有主流Agent架构,底层都是ReAct范式——Reasoning(推理)加Acting(行动)交替进行。理解这一个模式,你就理解了Agent设计的半壁江山。
3.1 ReAct到底是怎么转起来的
我给你画个简单的循环,不用看论文也能懂:
- 用户给一个目标。
- Agent先生成一段“思考”——分析当前目标、判断下一步该做什么。
- Agent调用一个工具——可能是搜索、算数、查数据库。
- 系统把工具返回的结果拼接到上下文里。
- Agent再生成一段“思考”——评估工具结果,决定下一步是继续调用工具,还是给用户产出最终答案。
- 循环,直到结束。
用生活类比的话,这就像一个实习生做调研:他先想“我需要哪些资料”,然后去翻资料,看到资料后想“这些够不够”,不够就继续找,够了就写报告。ReAct就是把这个过程程序化。
这就是Agent和“单次对话”的差别。你让普通大模型“帮我写一篇市场分析”,它直接生成——全程没有查证,内容全靠训练记忆。但Agent版本的“帮我写市场分析”,会先去搜索行业报告、查几个数据源、交叉验证一下再动笔。谁更靠谱,不用我多说了吧。
3.2 Function Calling:让模型学会使唤工具
模型本身不能执行代码、不能发请求,它只能输出文字。所以Agent里有一层非常关键的机制叫Function Calling——模型在需要外部能力时,不是直接给答案,而是输出一段结构化的调用请求,比如:
{ "name": "web_search", "arguments": { "query": "2026年新能源汽车市场销量数据" } }我以OpenAI风格API为例,整个Agent循环的代码骨架长这样:
import json from openai import OpenAI client = OpenAI() def web_search(query): # 这里接入搜索服务 return f"搜索到与{query}相关的结果若干,最重要的是……" def get_current_time(): import datetime return datetime.datetime.now().isoformat() tools = [ { "type": "function", "function": { "name": "web_search", "description": "搜索最新的网络信息", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "搜索关键词"} }, "required": ["query"] } } }, { "type": "function", "function": { "name": "get_current_time", "description": "获取当前时间", "parameters": {"type": "object", "properties": {}} } } ] messages = [{"role": "user", "content": "帮我查一下今天天气怎么样,顺便告诉我现在几点了"}] finished = False while not finished: response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools, ) message = response.choices[0].message messages.append(message) if message.tool_calls: for tool_call in message.tool_calls: fn_name = tool_call.function.name args = json.loads(tool_call.function.arguments) if fn_name == "web_search": result = web_search(args["query"]) elif fn_name == "get_current_time": result = get_current_time() else: result = "未支持的函数" messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) else: finished = True print(message.content)这个循环就是Agent的心脏。你仔细看会发现,模型本身不会去执行工具,它只是“提议”调用工具和参数,真正执行的是你的Python代码。这就是Agent的工程本质:你写的程序负责执行,模型负责决策。
在实际做AI编程的时候,我会用像Fitten Code这样的AI插件去辅助写这类胶水代码——你只要把上面的循环描述清楚,IDE里的AI能直接生成差不多能用的版本,你自己再根据业务微调。这不丢人,反而是AI Native研发范式下的正确习惯:把重复代码交给AI,把判断留给人类。
3.3 System Prompt:Agent的螺丝钉式约束
很多人在Agent翻车之后才发现,问题出在System Prompt上。System Prompt不是普通提示词,它是给Agent设定的“职业规范”。写System Prompt有四个必须覆盖的内容:
- 角色与边界:你的Agent是谁,什么能做、什么绝对不能做。
- 工具使用规则:什么情况下必须调用工具,什么情况下禁止调用。
- 输出格式:最终答案要什么结构,比如必须是JSON或MD格式。
- 兜底策略:遇到不确定情况怎么处理,是继续尝试还是回退给用户。
我自己的System Prompt模板大概是这样的:
你是一个智能助理,负责帮用户查询资料、分析数据并给出建议。 规则: - 凡涉及实时信息、精确计算、数据查询,必须先调用工具,禁止凭记忆回答。 - 每次工具调用后,先分析工具结果,再决定下一步动作。 - 如果工具返回为空或结果不可靠,明确告知用户"暂未查到可靠信息",不要尝试编造。 - 最终输出使用Markdown格式,包含"结论"和"依据"两个小节。 - 当用户要求明显超出你的能力范围时,礼貌说明并提供替代建议。别小看这段文字。我在调试Agent时,有超过一半的诡异行为都是“规则没写全”导致的。模型是个“遵纪守法的好员工”,你给了什么规则,它就按什么规则走,但你没写的规则,它就自由发挥——自由发挥通常是翻车的前奏。
4. 动手做一个能查资料、算数据、发消息的小Agent
纯粹讲原理容易飘,接下来我带你动手搭一个能跑的最小Agent。别急,我们不用那些重型框架,就用一个Python脚本,主线逻辑就是第三节那个ReAct循环。等到你把这个循环跑通了,再决定要不要换框架。
4.1 需求定义
我以“会议室预订助手”为例,这个场景很短小,但足够覆盖Agent的大多数核心动作:
- 输入:用户说“帮我查一下明天下午三点的会议室是否空闲,如果空闲就订一个两小时的,同时把这个消息发给项目群。”
- 能力:一个查空闲时间的方法、一个预订会议室的方法、一个发消息的方法。
- 输出:用户能感知到“查询-预订-通知”三个动作依次完成,并收到确认信息。
4.2 prompt与工具定义
工具部分直接复用第三节的函数格式,在这里我补一下System Prompt的设计:
你是会议室预订助手,职责是帮助用户查询和预订会议室。 规则: - 查空闲必须调用check_room工具,预订必须调用book_room工具,发通知必须调用send_message工具。 - 在预订之前必须已经完成空闲查询,并且结果是"空闲"。 - 如果查询结果为空或不满足用户需求,直接如实告知,不要擅自更改条件。 - 每完成一个动作,用一句话向用户确认。你看,每一条都在约束“决策顺序”。因为Agent最大的风险不是不会干活,而是乱干活——比如用户还没确认,它就直接订了。这一类“自作主张”的问题是Agent落地时最常遇到的,靠模型自我反省是靠不住的,必须在规则里写死。
4.3 完整Demo代码
这里给出一个精简版,核心只是循环里的工具分派逻辑,完整代码已经够你照着敲了:
import json from openai import OpenAI client = OpenAI() # 模拟会议室数据 rooms = {"A202": [("09:00", "12:00"), ("14:00", "16:00")], "B305": [("10:00", "18:00")]} def check_room(room_id, date): if room_id in rooms: return f"{room_id}在{date}的名额:{rooms[room_id]}" return "未找到该会议室" def book_room(room_id, date, start, end): return f"{room_id}在{date}的{start}-{end}已预订成功" def send_message(channel, content): return f"已发送消息到{channel}:{content}" tools = [...省略具体定义,结构同上...] def agent_run(user_input): messages = [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ] for _ in range(10): resp = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools ) msg = resp.choices[0].message messages.append(msg) if not msg.tool_calls: return msg.content for tc in msg.tool_calls: args = json.loads(tc.function.arguments) if tc.function.name == "check_room": result = check_room(**args) elif tc.function.name == "book_room": result = book_room(**args) elif tc.function.name == "send_message": result = send_message(**args) messages.append({"role": "tool", "tool_call_id": tc.id, "content": result}, ) return "已达最大执行轮数,停止。" print(agent_run("帮我查一下B305明天下午的会议室,如果空闲就订两点到四点的"))我把这一步单独拎出来讲是因为:Agent的第一个版本文档里不写原理,只写“沿着这个骨架改业务函数就行”。很多人学AI编程最大的障碍不是不会写代码,而是不知道第一行应该放哪。上面这段就是你的第一行。
跑完这个Demo,你至少会直接遇到三个问题:模型有时候不按约定调用工具而是直接回答(跳步)、工具参数格式偶尔不匹配(幻觉参数)、循环次数设置太短导致任务中途停止。这三个问题太典型了,我会在第六节逐个踩一遍给排查思路。
4.4 低代码平台值不值得学
搭建Agent完全不写代码可行吗?可行。Dify、Coze这类低代码平台,把节点拖一拖就能构建一个Agent。我的建议是:可以用来做MVP验证业务价值,但生产环境最终要落到代码。原因是平台有边界,一旦需求超出平台的预设节点,你要么被迫加非标准方式,要么换框架重写。而做AI编程的核心竞争力不只是把Agent跑通,而是知道它为什么能跑通,以及出了问题去哪修。For初学者,我的路径是:先用低代码平台跑通整个业务的逻辑流程,然后用代码平台(比如LangGraph或原生API)重写核心的、需要定制的部分。两边一对比,你对“Agent到底是什么”的理解会生根。
5. 从单兵到小队:多AI协作怎么编排、怎么扛并发
当你的Agent越来越复杂,你就会遇到两个新问题:一个Agent搞不定,需要多个角色协作;同时用的人多了,性能扛不住。这两个问题往往是同时出现的,所以我放在一起讲。
5.1 多Agent协作的三种经典编排模式
多AI协作不是“多开几个Agent聊天”,而是有目的性地分工。我实践下来最常用的有三种模式,直接给结论:
第一种:流水线模式(Pipeline)。任务天然有先后顺序,比如“先分析文档,再写摘要,最后翻译成英文”。每个Agent只负责一步,前一个的输出是后一个的输入。这种模式最简单、最稳定,就是链路长的时候延迟高。
第二种:扇出聚合模式(Fan-out / Fan-in)。一个任务可以横向切成多块,比如“分析全国各省的销售数据”,拆成几十个子任务,每个子任务一个Agent并行处理,最后再汇总。这种模式下,并发多,效率高,但是要注意子任务的输出格式必须严格对齐,否则聚合的时候会五花八门。
第三种:路由器模式(Router)。一手用户请求进来,由一个“调度Agent”判断该交给哪个下游Agent——是客服Agent、技术Agent,还是数据分析Agent。这种模式最接近真实业务,但调度Agent本身成了新的瓶颈,它的判断准确率直接决定整个系统体验。
模式的选择没有标准答案,取决于你的任务可不可以被切分,以及切分后是否容易合并。
5.2 Agent怎么扛并发:从请求到任务队列
热词里有一个问题特别实在:AI Agent怎么扛并发。单机把Agent循环跑一百次,本质上就是一百次串行,超过一定量级就要上工程手段。
我的核心结论:Agent的循环本身是CPU密集加IO密集的混合体,你不可能靠加线程解决所有问题,真正的解法是把任务加入队列,用一批Worker消费。
整体架构我一般这么设计:
- 用户请求进来,先入库或进消息队列(Redis或RabbitMQ)。
- 一批Worker进程从队列里拉任务,每个Worker内部跑Agent循环。
- 循环中的每次模型调用都要做超时控制——模型接口卡住了不能等它一辈子。
- 任务状态要持久化:running、success、failed,都要记录在案。
- 失败任务要有重试机制,但是重试次数要设上限,且重试要在退避(Backoff)之后进行,否则接口被打满更难恢复。
这里面最容易忽略的是速率限制。各家模型API都有每分钟请求数限制,你的并发一上来,最先爆的就是这个。所以代码里必须加限流器——我用的是令牌桶思路,一个Worker在调用模型前先取令牌,取不到就等一下。这一个小改动,能让你的Agent在高并发下的稳定性提高好几个档次。
下面是我常用的并发改造思路表:
| 阶段 | 问题 | 解法 |
|---|---|---|
| 大量任务涌入 | 请求打到模型API,触发限流 | 引入消息队列削峰填谷 |
| 单任务执行太久 | Agent一个循环卡在模型调用上 | 每次调用设置合理的超时时间 |
| Worker崩溃 | 进行到一半的任务丢失 | 任务状态落库,启动时扫描未完成 |
| 重复执行 | 重试导致重复预订等副作用 | 给任务和工具调用加幂等键 |
| 上下文过大 | 多轮循环后token超限 | 对工具结果做截断和摘要压缩 |
5.3 多Agent与真实硬件扩展:一个不起眼但值得知道的点
热词里有个“openclaw+ros为你的ai代理”,看起来像是Agent和真实硬件设备交互的方向。其实思路是一样的:如果Agent要操作的不是软件工具而是ROS机器人,那工具调用的目标就从“函数”变成了“接口指令”,本质上还是“模型决策-函数执行-结果回传”的循环。一旦Agent开始操作实体设备,可靠性要求会急剧上升——软件服务出错了可以重试,机器人动作出错了是会出事故的。所以这类场景一定要加“人工确认”节点,凡是高风险动作,Agent只能发起请求,真正执行必须有人拍板。
这是我在多AI协作上踩过的最大的坑:光顾着让Agent干活,忘了在关键节点上留人类干预的闸门。等你想起来的时候,往往已经造成了不可逆的副作用。
6. 实测中踩过的Agent坑:从上下文膨胀到模型跳步
最后这部分,我把自己在真实项目里踩过的坑挨个说一遍。每一个说出来都是“这也能错?”,但每一个我都实际花钱买过教训。
6.1 模型跳步:明明有工具,它偏要自己猜
现象:System Prompt里写明了“查数据必须调用查询工具”,但模型在部分轮次直接绕过工具,凭训练记忆输出数据。排查后发现,模型在“觉得差不多能答”的时候就会偷懒——这是它的本能。修复方式有两个:第一,把System Prompt改成“除非工具返回了结果,否则不要相信你的记忆”;第二,在代码循环里强制校验:如果某类关键动作没有对应的tool_call记录,直接让模型重来一轮。
6.2 上下文膨胀:Agent跑着跑着就“失忆”了
Agent每轮循环都把工具返回塞进上下文,等跑到七八轮时,上下文里全是历史结果,最早的指令被淹没了。你明明让它做三步任务,它做完第二步就停了。这个问题最适合用“截断摘要”解决:每一轮结束后,把之前的消息列表压缩成一个“过程摘要”,只保留关键事实和已完成的动作,下一轮重新拼接。一句话:别让Agent背着全部历史跑,要让它背着“要点”跑。
6.3 工具返回格式不和:模型读不懂自己的调用结果
有时候工具返回了结构化的JSON,但模型会执着地把它当纯文本解读,导致后续解析全部乱掉。解决方案有两种:一种是让工具返回“模型友好格式”,比如拼接好的自然语言摘要;另一种是在system prompt里强制指定“工具返回内容必须按JSON解析”。我更推荐第一种,因为模型对自然语言的容错度远高于强制JSON解析。
6.4 死循环与无限调用:成本失控的开端
最要命的一个坑:Agent在某个问题上反复调用同一个工具,返回结果都一样,但它就是不死心,一直循环。如果你不设最大循环次数,这个循环能直到token耗尽为止。我的血泪经验是:循环上限不是保护任务成功率,是保护你的钱包。10轮是默认值,20轮是极限,再高说明Agent的规划逻辑有缺陷,你调的是System Prompt而不是把上限调高。
6.5 遗忘测试:Agent重构了,旧功能坏了
多Agent协作搞起来以后,团队经常只测新路径,老路径慢慢失效了也不知道。我现在维护Agent项目一定会建一个评估集:把过去用户反馈过的最典型的20个任务写进去,每次改Prompt、换模型、加工具,全量跑一遍评估集,看有没有退化。这个习惯和做软件的回归测试是一个道理,但Agent项目里更容易被忽略,因为“对话式交互”给人感觉好像不容易回归——实际恰恰相反,模型或提示词一换,行为完全可能大变样。
6.6 真实项目里的一个完整排查链路
给你还原一次真实的排错过程。当时用户反馈“查会议室的时候,有时候不会发送确认消息”。我排查的时候没有急着改Prompt,而是翻日志,发现同一个任务有两个分支路径:
- 路径A:先查会议室,返回空闲,继续预订,发送消息。正常。
- 路径B:先查会议室,返回空闲,模型在下一步居然没有发起预订调用,而是直接返回了一句“会议室空闲,请问需要预订吗?”
问题定位:这其实不是功能坏了,而是模型“决策保守化”了。系统上下文里某个历史样本可能让它觉得“擅自预订会惹用户不满”,所以它在可做可不做时选择了不做。修复方式也直白——在System Prompt里加一条硬性规则:“对于用户明确要求的完整任务,不得在中间步骤停下来询问确认,除非遇到异常。”跑完评估集,重启,恢复正常。
类似这样的问题是Agent项目里最耗时间的部分。排查思路就一条:先复现,再看日志,再看模型到底在哪个环节改变了决策,最后针对性地修改Prompt或工具的定义。不要一上来就“加一句更严格的提示词”来解决问题,那样很容易按下葫芦浮起瓢。
最后分享一个我自己调整Agent的小习惯:每加一个工具或改一次Prompt,我都会把模型的完整运行日志保存下来,简单统计一下每轮的目的。跑得久了,你会形成一种直觉——看到Agent的输出路径,你基本能猜出它在哪一步会翻车。这种直觉不是来自算法理论,而是来自反复试验和数据积累。AI这个领域变化快,但Agent的底层逻辑——决策、执行、观察、纠错——在可预见的未来不会过时,掌握好这套思路,你换什么模型、用什么框架都不慌。