从AI对话Demo到Agent平台:关键路径与最小实现
一个能对话、能查天气、能调两三个API的AI Demo我大概一天就能写出来。但你把它拿给团队或者客户用马上就会撞上一堵墙它只能在我电脑上跑换个场景就得改代码大模型输出稍微偏一点整条链路就跟着乱套而且你根本说不清一个多轮对话到底是在哪一步开始走歪的。这篇文章不聊怎么写一个Agent Demo——写Demo这件事本身没什么门槛。我想聊的是更麻烦的那一段怎么把一个看起来不错的AI对话Demo一步一步长成一个能被业务持续使用、能迭代、能出问题又能被快速定位的Agent平台。不管你是刚接触Agent开发的初学者还是已经做出Demo但不知道怎么往下走的团队这篇都值得你花十分钟读完不会有太多虚的全是实操里磨出来的东西。1. 从一个Demo讲起它到底证明了多少东西1.1 Demo和平台之间那条看不见的线很多人觉得Demo做得越炫距离产品就越近。其实不是。Demo和平台之间隔着的不是代码量而是对不确定性的容忍度。AI对话Demo本质上是一个单机验证程序你写一段Prompt把用户的提问塞进去调用模型接口拿到回复再显示出来。这个流程跑通了你验证的是大模型能不能理解这个场景、能不能给出大概可用的结果仅此而已。但Agent平台解决的是另一类问题大模型能不能在真实环境里稳定地、可重复地、可追溯地完成任务。真实环境意味着有多用户、有权限、有并发、有失败重试、有成本上限、有版本迭代。这些东西Demo阶段完全可以不管但一旦平台要上线哪一个漏掉都是事故。我自己见过太多次这样的场景项目组兴奋地演示完一个Agent Demo老板直接问这个能接到我们的系统上吗 然后就没有然后了。不是产品没有价值而是Demo的架构压根没有为被接入做准备。所以我习惯把Demo和平台的差异整理成一张表每次立项先过一遍看自己到底缺在哪维度AI对话Demo可演进的Agent平台运行方式本地脚本或单机进程服务化支持多用户并发Prompt管理写死在代码里独立配置可灰度可回滚上下文处理简单拼接历史消息有预算控制、压缩、检索、持久化工具调用固定写死几个函数动态注册、可热插拔、带权限校验可观测性print语句、截图结构化日志、会话Trace、成本统计稳定性靠运气和提示词有超时、重试、兜底、护栏规则迭代方式改代码重启进程Agent版本、Skill版本、模型版本可独立演进效果验证人工看几条对话回归评测集自动跑量化对比把这张表填完你基本就知道自己手里的Demo离平台还差多少个模块了。1.2 三句让你从Demo兴奋回到现实的话在我的经验里一个Demo做得再漂亮也扛不住三个来自业务方和团队的追问。第一个问题这个能上到我们的系统里吗 这问的是工程化能力。你的代码里有没有鉴权能不能做横向扩容模型密钥是不是写死在配置里工具函数能不能脱离当前进程单独部署第二个问题如果大模型回答错了怎么办 这问的是容错能力。Agent拿着错误信息继续往下走会不会把业务流程带偏工具调用失败了是重试还是放弃有没有人能干预和打断第三个问题你怎么知道它刚才为什么这么做 这问的是可观测能力。Agent执行了一个五步任务每一步为什么选这个工具、传了什么参数、模型消耗了多少Token有没有完整记录没有这些线上出问题你根本无从下手。这三个问题基本决定了你接下来的工作方向。我见过很多团队在Demo阶段花了大把精力调Prompt、做花哨的交互结果一上真实业务全部精力都耗在为什么又报错和这个结果是怎么来的上面。所以标题里说的可演进的Agent平台关键不在Agent多聪明而在它能不能被稳定地接入、控制、追踪、迭代。2. Agent平台的核心能力到底要把什么做出来2.1 先分清对话和Agent如果把AI对话和Agent混为一谈后面的架构大概率会跑偏。对话模型的目标是生成更像人话的回复它的输出是文本。写一个对话Demo你只需要负责把上下文拼好、调用模型、把回复展示出来就行。Agent的目标是完成一个需要多步骤的真实任务它的输出是一系列决策和动作。比如帮我查一下合同里有没有关于违约金的条款有的话顺手整理成摘要——这件事需要大模型理解任务、决定调用哪个检索工具、拿到结果后再判断是否需要追问还是直接生成答案。目前最主流的Agent执行范式就是ReAct循环Thought思考下一步该做什么→ Action调用工具→ Observation观察工具返回结果→ 再思考 → 直到输出Final Answer。本质上Agent就是一个让模型反复做决策-执行-观察的循环结构。生活里类比一下对话模型像是一个很能聊的朋友你跟他说什么他都能接但他不会真帮你办事。Agent更像一个能帮你跑腿的助理他会先确认你要什么然后自己决定先去银行还是先去邮局事情没办完还会自己调整方案。Demo到平台的跨越就是从能聊到能办事的跨越。2.2 我理解的Agent平台五层结构做Agent平台之前脑子里一定要有一张分层的地图不然写着写着就会变成一个大杂烩。我习惯把平台拆成五层第一层是接入层。统一封装模型调用接口不管底层用的是开源模型还是商业API上层只面对一个标准接口。这一层还负责模型路由比如简单的查询走便宜的小模型复杂推理才调用大参数模型。第二层是能力层。能力层是Tool和Skill的集合。Tool是原子能力比如查询工单发送邮件调用某个内部API通常就是一个函数加一份JSON Schema描述。Skill是能力的组合把多个工具、一段专用提示词、参数校验逻辑打包成一个可复用的业务技能。第三层是决策层。决策层跑Agent循环负责决定下一步调用哪个能力、参数是什么、要不要停下来问人。单Agent负责简单任务复杂任务可以拆给多个子Agent协作这种场景我们叫Multi-Agent编排。第四层是记忆层。短期记忆是上下文窗口里的对话记录长期记忆是跨会话存下来的用户画像、历史结论、业务知识一般用向量数据库做检索。没有记忆层的Agent就像一个每次都失忆的实习生永远记不住五分钟前自己干过什么。第五层是治理层。治理层是平台真正的底气评测集、日志追踪、成本统计、权限控制、版本管理、灰度发布全都归这一层管。很多团队把Agent接上线后又退回Demo就是因为治理层没跟上线上跑得心里没底。这五层不要求一次性做全但设计时脑子里要有这张图每个模块知道自己属于哪一层后期才不会变成一锅粥。2.3 新手必看Skill和Agent的分工边界在Agent平台的讨论里Skill和Agent的概念被混用得最厉害。热搜里一堆人问skill和agent区别其实这两个东西边界很清楚。Skill是能力包它是静态的描述的是我能做什么、需要什么输入、什么场景下适合用。比如一个合同审查Skill它绑定了文档解析工具、条款检索工具、一份审查提示词还有一套输出格式校验。它自己不会主动跑起来它只是一套打包好的能力。Agent是决策体它是动态的负责决定现在要不要用某个Skill、用完之后下一步干什么、如果失败是重试还是换方案。同一个Agent可以按任务需求在多个Skill之间切换。我常用一个比喻Skill是工具箱里的电钻Agent是装修师傅。电钻本身很能干但它不知道自己该在哪面墙上打孔。装修师傅看了现场决定用几号钻头、先打哪个位置、打完孔之后下一步做什么。师傅会犯错但师傅能根据现场反馈调整方案——这就是Agent的决策价值。工程上怎么切分Skill尽量做成配置化一份YAML描述清楚名字、用途、绑定的工具、提示词模板、运行参数。Agent的代码则保持相对通用不要把所有业务规则都写死在Agent的循环里否则换个业务场景又得改一遍Agent。很多项目死在把Skill写得像Agent、把Agent写得像Skill模块边界一旦模糊后续任何改动都牵一发动全身。3. 从Demo演进到平台的关键路径3.1 第一步把对话链路和业务逻辑解耦Demo时代的典型代码是接收用户消息拼上下文、调用模型、把结果直接返回。问题在于模型的决策过程和业务动作完全搅在一起你没法单独升级任何一边。我说过很多次平台化的第一步不是引入什么高级框架而是把接口语义从对话改成任务。让上层调用方提交一个明确的任务描述平台负责拆解、执行和回传结果。一个比较推荐的接口设计是这样{ task: 查一下工单WO-2024-001的状态如果还在处理中提醒负责人明天中午前更新进展, session_id: session_001, user_id: user_123 }平台执行完返回的不应该只是一段话而是最终结果动作轨迹{ status: success, result: 工单当前为处理中状态已向负责人发送提醒, actions: [ { tool: query_work_order, input: {work_order_id: WO-2024-001} }, { tool: send_reminder, input: {owner: zhangsan, deadline: 明天12:00} } ] }把动作记录和自然语言结果分开返回上层的业务系统可以拿动作记录做审计、做UI展示甚至直接触发其他业务流程。这个改动成本不大但会把架构从一个会聊天的接口推向一个能办事的平台。3.2 第二步把能力沉淀成Skill当你能跑通任务→规划→工具调用→结果之后下一步就是沉淀Skill。这一步的核心目的是把业务专家经验从代码里剥离出来让运营和产品同学也能参与配置和优化。一个标准的Skill配置文件我一般包含这些部分name和description这个Skill叫什么、什么场景下该用、什么场景下不该用绑定的工具列表这个Skill需要依赖哪些原子能力prompt模板引导模型在这个场景下的行为规则和输出格式输入输出校验入参的JSON Schema、出参的格式约束运行参数使用哪个模型、温度是多少、最大步数是多少用一个工单场景举例子配置大概长这样name: work_order_agent description: 处理工单查询、状态更新与责任人指派的场景 model: gpt-4o-mini temperature: 0.2 max_steps: 8 prompt: | 你是工单处理助理。你可以查询工单详情、更新工单状态、指派责任人。 如果用户提供的信息不完整先追问不要猜测。 如果工具调用连续失败两次直接告诉用户当前无法办理并说明原因。 tools: - query_work_order - update_work_order_status - assign_owner schema: input: type: object required: [task] output: type: object required: [result, actions]这段配置里的重点在prompt里那句如果工具调用连续失败两次直接告诉用户当前无法办理——这是很多人会漏掉的东西。没有兜底话术的Agent会在失败时反复重试把Token烧光还不给用户一个交代。Skill写好后把它注册进平台的Skill仓库。模型升级、提示词调整都只需要发一个新版本不需要发布代码。这就是可演进的第一步。3.3 第三步给Agent装上决策、记忆和人工介入Skill解决了会做什么Agent和记忆层解决怎么决定做什么。决策机制就是ReAct循环这部分在第四章我会贴一段可以跑的伪代码。这里我想重点谈谈停下来的能力。一个负责任的Agent不能只知道闷头执行。涉及以下情况时必须暂停下来把决定权交还给用户要执行的操作不可逆比如删除数据、发正式邮件、提交报销要花的成本超过预设阈值比如某个工具调用会触发高额的第三方服务上下文信息不够但Agent无法自行补齐连续多次尝试都失败需要用户重新给方向Human-in-the-loop人在回路不是偶尔用一下的功能它是Agent平台里必须内置的机制。常见做法是让Agent输出一个waiting_for_user_confirmation的状态平台收到这个状态后挂起任务等用户确认或补充信息再继续执行。记忆层同样重要。平台至少要有两级记忆短期记忆跟着会话走长期记忆放进向量库。比如一个用户上周说过我司用的ERP是金蝶如果系统记得这件事下次他再问帮我看看库存接口怎么对接Agent就能结合前面提到的ERP信息给出更精准的答复。没有长期记忆的Agent每次都从零开始体验非常断层。3.4 第四步让平台能放心演进平台和Demo最根本的区别是你敢不敢改它。一个能让你放心演进的平台至少要具备四件事评测、版本、灰度、观测。评测是安全网。把你关心的典型场景写成回归集比如工单查询准确率退款流程完成率敏感话题拒答率每次改完提示词或者换模型先自动跑一遍回归。没有评测集你根本不知道改动是变好了还是变坏了。版本管理要覆盖三个对象模型版本、Prompt版本、Skill版本。模型升级不能直接全量上必须先跑评测再灰度。灰度发布要做到按比例或按用户维度切流量。比如先让5%的真实请求走新模型跑两天看错误率和耗时没问题再逐步放大。观测是整张逃生图。每次Agent执行都要把过程记录下来模型返回了哪些思考、调用了哪个工具、传入什么参数、工具返回什么结果、每一步耗时和Token消耗。这样线上出了问题你能从头到尾重放一遍执行过程而不是对着黑盒猜。我在实操中发现社区里现在有大量个人Agent项目和开源框架它们很多都是很好的起点Demo研究它们的编排和工具定义方式能省不少时间。但注意别指望直接把别人的东西拿过来当平台用框架能解决通用能力解决不了你那套业务规则和评测体系。平台永远是自己长出来的。4. 实操一个最小但完整的Agent运行时4.1 极简Agent循环核心逻辑就一个while第四节上干货。虽然大厂框架很多但我建议每个想深入Agent开发的人都手写过一个最小循环这样你才知道框架里那些参数到底在控制什么。这个循环的逻辑非常朴素每次调用模型时带上工具定义让模型要么输出工具调用指令要么输出最终答案。如果输出了工具调用指令就本地执行工具把结果作为Observation塞回上下文再让模型继续决策。import json def run_agent(task, tools, llm, max_steps10): messages [ {role: system, content: 你是一个任务执行Agent。}, {role: user, content: task} ] step 0 while step max_steps: step 1 # 让模型决定调用工具 or 输出最终答案 response llm.chat(messagesmessages, toolstools) msg response.message # 没有 tool_calls说明模型给出了最终答案 if not getattr(msg, tool_calls, None): return msg.content, step # 执行工具调用 messages.append(msg) for call in msg.tool_calls: result execute_tool(call.function.name, call.function.arguments) messages.append({ role: tool, tool_call_id: call.id, content: json.dumps(result, ensure_asciiFalse) }) # 这里可以根据业务需要判断是否要停下来等人工确认 if should_ask_human(messages): return WAITING_FOR_CONFIRMATION, step return MAX_STEPS_EXCEEDED, step这段代码的每个细节都有用意。max_steps是护栏防止Agent死循环把Token烧光tool_call_id用来把工具返回和对应的调用请求对上这是OpenAI兼容接口的硬性要求execute_tool是本地函数分发生产环境会换成服务发现机制should_ask_human是人工介入的检查点。整个循环对应平台五层结构里的决策层和能力层是最核心的跑腿中枢。4.2 工具与Skill的接入规则描述即真相工具能否被正确调用一半功劳在代码一半功劳在描述。模型是完全按照工具的name和description来决策的描述写得模糊再好的模型也会选错工具。一个标准的工具定义长这样{ type: function, function: { name: query_work_order, description: 根据工单ID查询工单的处理状态、责任人、当前备注。当用户提到工单、单号、报修编号时使用该工具。, parameters: { type: object, properties: { work_order_id: { type: string, description: 工单ID格式如WO-2024-001 } }, required: [work_order_id] } } }注意description里说明了什么时候用和输入格式比一干巴巴的查询工单要好用得多。工具返回的数据尽量返回结构化JSON不要让工具返回一大段给人看的文案。模型解析结构化数据远比解析大段文本稳定而且能省Token。我在项目里给团队的硬性要求是工具只返回关键字段描述性长文本一律截断。Skill的接入方式是配置化注册在3.2节的YAML基础上平台启动时扫描Skill目录解析配置并注册到工具列表里。这样新增一个业务能力产品同事写个配置就能上线开发不用改一行代码。4.3 可观测性每一次思考都要有接缝Agent的核心问题是不可预测性。不可预测的东西如果还没有过程记录那线上出了事故就只能干瞪眼。所以可观测性不是后期加分项是从第一行Demo代码就该埋的基建。我的做法是给每一步执行输出一个结构化日志包含下列信息字段示例session_idsession_001step_index3thought模型思考用户要求查工单需要先调用查询工具action动作{tool: query_work_order, input: {id: WO-2024-001}}action_result动作结果{status: processing, owner: 张三}modelgpt-4o-miniprompt_tokens2350completion_tokens168latency_ms324errornull这些字段攒起来每一个Agent会话天然就是一个可以完整重放的过程记录。排查问题的时候直接定位到具体step看模型当时想了什么、做了什么、工具返回了什么问题基本一眼就能看出来。想把这个做好的一个建议提前把日志规范定好Demo阶段就按这个标准打印后面接平台时你的历史数据也能用。我见过太多项目Demo阶段只管print等做平台时再补日志那叫一个痛苦以前的错误case全丢了没法回溯。4.4 从Demo到MVP我建议按这个节奏走实战中从Demo到最小可用平台我一般按四周推进节奏大概是这样周期目标关键产出第1周服务化改造把本地脚本改为HTTP服务接入统一模型层支持多模型切换第2-3周能力沉淀建立工具注册表沉淀2-3个业务Skill接入基础记忆功能第4周治理基座接入评测集、结构化日志、基础灰度开关和成本统计第5周起真实业务打磨跑真实流量收集失败case反哺Prompt和Skill迭代这个计划的核心逻辑是前两周解决能用后两周解决敢用第五周以后解决好用。别一上来就做多Agent编排、复杂记忆网络先把最小闭环跑稳再一步步加能力。5. 常见问题与排查技巧实录5.1 agent execution terminated due to error. 到底为什么这个是很多Agent开发者的噩梦用过LangChain的人基本都遇到过。这个报错看起来很笼统根因其实就那么几种。第一种是Agent达到了max_iterations或max_steps上限循环被强制中止。这种情况通常是你设置的步数太小或者模型在反复调用同一个工具没有进展。第二种是工具调用过程中抛出了未捕获的异常框架直接终止了整个执行链。比如工具函数里写了一个KeyError模型根本不知道发生了什么。第三种是模型连续输出的内容格式不对比如工具调用参数不是合法的JSON或者解析出了没有注册的工具名。我的排查思路固定三步先翻日志看最后一步模型输出的是什么、有没有tool_calls再看中间步骤有没有重复调用同一工具的迹象最后检查工具函数自身的异常有没有被try-except兜住。这三个方向基本能覆盖90%的情况。解决问题时除了修工具代码别忘了在系统提示词里加一句如果同一个操作连续失败两次停止尝试向用户说明情况并询问是否换一种方式。这一句能在很多场景下把死循环变成优雅收场。5.2 上下文窗口不够用三个方案按序选Agent跑久了上下文一定会膨胀。对话历史在涨工具返回结果也在涨哪天突然报token超限很正常。我的优先级排序是这样的先上摘要压缩。把早期的对话历史定时压缩成一两句话的摘要替换掉完整历史能立刻解决大部分问题。具体做法是单独调用一次模型把已有对话做总结然后作为一条system message放回去。如果摘要压缩还扛不住上向量检索。把历史对话拆成片段、embedding后存入向量库每次任务只检索与当前问题最相关的三五段历史塞回上下文。这个方案更适合需要跨长时间跨度记忆的场景比如用户一周前说过的偏好。最后限制工具返回长度。很多token是被工具返回的大段JSON吃掉的。修改工具让它只返回摘要字段或状态字段原始详情放到另一个查询接口里。一个查询工单的工具返回处理中、负责人张三没必要把整个工单时间线全倒出来。预算上也留一个参考整个上下文里系统提示词和建议示例控制在20%以内用户任务和对话历史占40%工具定义和工具返回占40%。如果工具定义太长可以考虑哪些工具不是每个任务都需要动态加载。5.3 工具调用反反复复、输出不稳定怎么治还有一类高频问题Agent老是在工具调用上打转而给不出最终答案或者同一个问题跑三次三个结果。工具调用反复打转先看工具描述是不是有歧义。比如两个工具都能查看工单模型分不清该用哪个就会随机抽。我建议把工具描述里的使用场景写明确甚至加上当用户提到……时使用如果没有……请勿使用这种排除性描述。输出不稳定第一件事把temperature调低通常降到0.2以内会有明显改善。第二件事检查提示词里是否给出了输出格式的强约束比如让模型必须按JSON格式输出并给出一个few-shot样例。第三件事检查模型选型同一个场景小参数模型和大参数模型的稳定性差距非常大如果业务容错低别为了省钱牺牲效果。另外给工具调用加一个观测字段比如在action_result里带上调用时间、耗时、第几次尝试。这样能在数据层面看到是不是同一个工具反复失败比靠肉眼和感觉判断高效得多。5.4 从Demo到平台的避坑速查表最后把我在多个项目里踩过的坑整理成一张速查表每一条都是拿真实代价换来的阶段最容易踩的坑应对方式Demo转平台模型密钥写死在代码里立即改用环境变量或密钥管理服务Tool接入工具返回大段文本导致Token爆炸工具只返回结构化关键字段Skill管理Skill之间职责重叠提示词互相覆盖先画边界再写配置每个Skill功能唯一Agent循环忘记设置最大步数和兜底话术必须配置max_steps 失败放弃逻辑评测阶段只测回答像不像人改成任务完成率和关键动作正确率灰度切换一次性全量切换新模型按用户或百分百灰度盯错误率和耗时记忆管理长期记忆写入脏数据写向量库前加一层清洗和去重人工介入把所有操作都交给Agent自动执行涉及不可逆操作强制等待用户确认这张表我每次带队做Agent平台都会发给成员当checklist。项目一旦过了Demo阶段这些坑几乎必踩提前写进规范里能省几个通宵。从我自己的实践来看把一个Demo做成平台最重要的不是选什么框架而是愿意在不确定性和失败上下多少功夫。Agent再好用也一定会出错平台的意义不是让错误消失而是让错误可以被控制、被追溯、被修复。这个系列先写到这里下一篇我准备重点聊多Agent编排和成本治理这两块也是我自己最近在折腾的方向到时候接着把这些实操细节掰开揉碎了讲。