拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从零搭建AI工程:Agent循环、提示工程与稳定性实战

1. 为什么我决定从零搭建AI工程而不是直接套编排框架大概半年前我手头堆了一堆 AI 相关的工具和框架LangChain、AutoGPT、各种 Agent 平台还有前段时间社区里反复讨论的 harness engineering 思路。工具的清单越来越长但真正落到业务上的时候我发现自己陷入了一个很尴尬的处境——单个模型调用的效果已经不错可一旦组合成工程就到处漏水。你可能会问市面上现成的编排框架那么多为什么要从零开始我当时的判断很简单现成的框架解决的是通用连接问题而我遇到的是特定场景下的可靠性问题这两件事根本不在一层。框架帮你把 A 模型的输出接到 B 模型的输入但如果你连A 模型的输出到什么程度算合格都没办法定义接得再顺也是把垃圾从一个管道倒进另一个管道。另外还有一个很现实的因素——黑盒叠加黑盒出了问题根本没法排查。大模型本身的输出就有随机性这是第一层不确定框架在你不知道的地方做了 prompt 拼接、记忆压缩、工具调用重试这是第二层不确定。当两层不确定性叠在一起你拿到一个错误结果完全不知道是模型的问题、提示词的问题、还是框架内部逻辑的问题。与其在一个失控的系统里猜不如从最底层把每一个环节都变成自己看得懂的代码。所以我把这个项目起名叫 ai-engineering-from-scratch。这里的from scratch不是说要从头训练一个大模型而是说在应用层把 AI 能力当作一个被严谨对待的工程组件来搭建——自己设计提示词的信息结构自己写 Agent 的循环自己定义多 Agent 协作的协议自己建立可观测性和质量评估。这篇文章就是我在这个过程中的完整复盘包括设计思路、踩坑记录和可以直接拿走的代码骨架。这个项目适合谁我建议这几类人重点看已经用 API 调通了一些零散功能但想让 AI 真正稳定地跑在业务链路里的开发者被 LangChain 这类框架的黑盒魔法困扰过想搞清楚底层机制的人对 prompt engineering 有一定了解但觉得背模板不够用、想理解第一性原理的人下面的内容不会涉及任何需要付费的工具核心思路也不绑定具体的模型厂商你可以根据自己的实际情况套用。2. 提示工程的第一性原理从背咒语到设计信息结构2.1 你给的上下文决定了模型的能力上限很多人把 prompt engineering 理解成给 AI 写一套神奇的咒语——似乎只要把网上流传的某某大师提示词复制进去模型就会开窍。我在项目初期也是这么干的直到有一次对比实验让我彻底改变看法。同一个任务我用网上流传的资深专家身份提示词跑出来的结果和用我自己写的一份朴素但结构清晰的指令跑出来的结果在准确率上几乎没有差别。差别出现在哪里呢出现在模型的输出的稳定性上——结构清晰的指令十次输出有九次能保持一致花哨的提示词十次输出有三次会跑偏。后来我理解了背后的原因大模型本质上是一个在巨大文本语料上训练出来的下一个 token 预测器。它不是真正理解了一段中文指令的深意而是在你给出的上文约束下计算最可能的续写路径。这就意味着提示词里每一个字都在改变模型后续输出的概率分布。你给的信息结构越清晰模型在续写时面对的不确定性就越小输出自然更稳定。我自己总结了一套比较实用的上下文设计公式你可以直接抄作业上下文模块作用我的经验角色设定框定语言风格和知识视角不要只写你是专家要写你是具备 10 年经验的 Java 后端工程师熟悉 JVM 调优任务边界明确能做什么、不能做什么只处理用户输入的订单查询类问题其余问题一律返回 UNSUPPORTED输出格式约束响应结构指定 JSON Schema 或 Markdown 模板比口头说请用 JSON 输出稳定得多示例注入给模型一个正确的续写方向1-2 个高质量 few-shot 示例比大段规则描述更有效约束条件划出安全区和禁区回答不超过 200 字禁止编造不在上下文中出现的信息不确定时输出 UNKNOWN2.2 结构化输出是工程化的第一步如果说提示词设计第一个要解决的问题是怎么让模型说人话那第二个问题就是怎么让模型说结构化的人话。工程化的本质是接口化而大模型输出文本天然是弱类型的。我最初犯的错误是让模型直接输出自然语言然后在代码里用正则去解析关键信息——这个方案在 demo 阶段看起来很顺利一到真实流量下就崩了因为模型的表达方式千奇百怪今天说价格为 100 元明天说100 块钱后天说总价是 100 元整。我后来的做法是让模型输出 JSON并且在提示词里直接给出完整的 JSON Schema 示例。比如我在一个信息抽取任务中的提示词片段是这样的请从以下用户反馈中抽取结构化信息严格按照示例格式输出 JSON不要输出任何多余内容。 示例输出格式 {sentiment: negative, issue_category: shipping_delay, confidence: 0.92, key_phrases: [还没收到, 快递太慢]} 用户反馈{{user_input}}这段提示词的核心不是让模型用 JSON 输出而是通过一个完整的示例把JSON 长什么样直接喂给模型让它在这个结构里续写。实测下来配合模型的 JSON mode大多数主流 API 都支持十次调用里九次以上能拿到一个可直接json.loads()的合法结果。这也引出一个很重要的工程决策不要把解析逻辑写在代码里而是把解析约束写在提示词里。模型负责生成符合结构的内容代码负责安全的解析——职责分离出了问题也容易定位。2.3 采样参数是提示词的另一半我在项目早期几乎完全忽略了采样参数对工程稳定性的影响直到有一次一个任务在测试环境跑得很好上线后却开始频繁输出随机内容。排查了很久才发现不是模型变了是我忘了设置temperature——默认参数下模型选择可能性的倾向太强导致同样的问题给出了多种多样的答案。我的经验是如果你用 AI 做的是确定性优先的任务信息抽取、分类、意图识别、格式化转换temperature可以直接压到 0 或者接近 0如果你做的是创意生成类任务文案撰写、头脑风暴temperature可以放到 0.7 到 0.9。另外还有两个参数值得关注top_p控制模型从概率前多少的 token 里采样。和temperature有一定的重叠作用我习惯固定一个不调只调另一个避免两个参数一起抖造成不可预期的结果。max_tokens不设上限的后果是模型有时候会在输出完结构化内容后又来一段废话。我建议根据任务类型估算正常输出长度然后给一个稍高的上限既能防止失控也能控制成本。这里有个容易被忽略的细节temperature0并不意味着输出一定完全确定。我在项目中遇到过几次在temperature0下依然输出不一致的情况原因是 GPU 计算的浮点不确定性或者并行采样引起的微小差异。所以永远不要在代码里假设同样的输入一定得到同样的输出任何 AI 输出都必须走校验那一关。3. 让 AI 从回答工具升级为工作单元Agent 循环的拆解与实现3.1 Agent 不是魔法是一个显式化的循环社区里对 AI Agent 的讨论很多但你会发现很多人其实说不清楚 Agent 到底是什么。有人觉得能调用工具就是 Agent有人觉得能自主规划就是 Agent。我的理解更朴素Agent 是把从目标到行动再到结果这个过程显式化成一个循环让模型在循环的每一步都有机会反思和修正。传统的单次调用模式是这样的你问一个问题模型给你一个答案任务结束。这种模式的瓶颈在于模型在一个回合里既要理解问题又要规划步骤还要把步骤执行完——信息量太大模型的长程规划能力又有限稍复杂的任务就崩。Agent 循环的核心思路是把这个过程拆开模型先思考在思考结果里声明我下一步需要调用什么工具然后外部系统执行工具拿到结果再把这个结果返回给模型模型再继续思考下一步。用代码来表达是这样的def run_agent(task, max_steps10, verboseFalse): messages [{role: system, content: SYSTEM_PROMPT}] messages.append({role: user, content: task}) for step in range(max_steps): # 模型产出思考结果可能包含调用工具的意图 response llm_call( messagesmessages, toolsTOOL_SCHEMAS, # 工具的函数签名描述 tool_choiceauto, temperature0.2 ) messages.append(response) # 如果没有工具调用意图说明 Agent 认为任务完成了 tool_calls response.get(tool_calls) if not tool_calls: return response[content] # 外部系统真实执行工具结果返回给模型继续循环 for tool_call in tool_calls: result execute_tool(tool_call) messages.append({ role: tool, tool_call_id: tool_call[id], content: result }) if verbose: print(f[step {step1}] called {tool_call[function][name]}) return MAX_STEPS_REACHED如果把代码拉平看这个循环一点也不神奇——它就是模型在思考和行动之间来回切换的对话记录。但它解决了单次调用的最大问题模型不再需要在一句话里完成从规划到执行的全过程它可以在每步执行后看到真实世界的反馈再决定下一步怎么走。3.2 工具调用的边界条件给模型配知道自己的能力的清单Agent 循环能不能稳定工作很大程度上取决于你给模型的工具清单设计得怎么样。我见过很多人直接把十几个函数一股脑丢进工具列表结果模型经常选错工具或者在一个工具上绕圈。这里的原因也好理解——模型对每个工具都只有一个名字和描述的文本它是在用文字理解工具的用途而不是像人一样看过工具源码。我的几个实操建议工具数量控制一个 Agent 在一段循环里需要关注的工具建议不超过 5 个。工具太多模型的选择空间太大出错的概率指数上升。如果业务确实需要很多工具拆成多个 Agent 各管一摊。工具描述必须说明边界不要只写搜索函数要写在内部知识库中搜索产品文档仅支持精确匹配标题输入参数为文档 ID 列表返回值最多 5 条。描述直接决定模型的调用准确度。一个工具只做一件事我最开始设计了一个综合查询工具能查订单、查商品、查用户信息当时觉得方便结果模型经常搞不清楚该传什么参数。拆成三个独立工具后准确率立刻就上去了。工具调用的本质是把模型的文本世界和真实系统的物理世界之间的桥梁搭好桥梁上的每一处指示牌描述越清晰车模型就越不容易走错路。3.3 思维链的价值与局限什么时候该让它先想再说现在很多开发者习惯在提示词里加一句请一步步思考这就是思维链的通俗版。思维链确实能在不少任务上提升效果但我在实际项目里发现它有两个容易忽略的副作用第一思维链会显著增加输出 token 的数量直接拉高成本。一次推理任务如果让模型把每一步想清楚的推导都写出来输出长度可能增加 30%-50%而且在长文本场景下还要预留足够的输出窗口间接影响上下文容量。第二思维链暴露了模型的推理中间态。在某些需要严格保密策略判断的场景比如信贷审批、风控决策把推理过程明文输出是不可接受的。而且一旦推理中间态出现错误信息在后续处理中反而会干扰最终决策。我的取舍策略是简单的分类、抽取任务不做思维链直接强制输出结构化结果复杂的多步推理任务比如代码调试、数据分析才让模型先在内部推理所有推理步骤都放在一个受控的字段里。具体做法是在输出格式里增加一个hidden字段{ reasoning: 内部推理过程不直接对外展示, result: { category: bug_fix, files_changed: [server.py], suggestion: 在数据库连接池关闭后增加重试机制 } }这样模型有了思考空间同时最终输出依然是可控的结构化格式。你可以根据自己的需求决定是否输出reasoning字段——第一次跑通系统时建议完整输出方便排查问题稳定运行后再在接口层把它过滤掉。4. 多 Agent 协作与工作流编排我踩过的三个深坑4.1 上下文污染邻居的消息被塞进了我的窗口项目进入多 Agent 阶段后我遇到的第一个大坑是上下文污染。当时我让一个 Agent 负责收集材料再把收集到的结果交给另一个 Agent 做总结输出。听着很合理对吧但实际上我把 Agent A 的完整对话历史包括它的思考过程、中间试探、以及无关紧要的自我纠错全部塞进了 Agent B 的上下文窗口。结果 Agent B 开始继承 Agent A 的思考习惯甚至把 Agent A 中间的错误假设当成事实来使用。有一次我在日志里发现 Agent B 输出了一个 Agent A 在第三步生成的中间猜测值而那个值后来已经被 Agent A 自己纠正过了——信息不是简单的拼接而是上下文窗口内所有文本共同影响输出分布被纠正过的错误信息依然在对话里依然会影响模型。这个坑的解法是串行协作时只传递最终结果不传递过程历史。Agent A 的执行过程是它的私有空间Agent B 只需要知道Agent A 得到了什么结论、置信度是多少、数据来源是什么。如果你需要 Agent B 了解执行轨迹交给日志系统不要交给上下文窗口。4.2 重复劳动每个 Agent 都在做别人的事第二个坑更有意思。我原本设计了一个调度者-执行者-校验者的三角色结构想法很美但在实际跑起来的时候执行者经常自作聪明地做校验者的工作——它会在输出里附带一句经检查以上结果没有问题。而校验者也不甘示弱它开始重新执行一遍任务去验证结果而不是基于执行者的产物做检查。这听起来像是模型的主动性强但放在工程里就是灾难。重复劳动不仅增加 token 成本和延迟更关键的是模糊了责任边界——一旦最终结果出错你根本不知道是哪一个环节出的问题。解决办法是想清楚了角色的信息差。三个 Agent 看到的信息刻意做得不一样调度者用户的目标 全局任务清单执行者被分配的子任务 明确的产出模板校验者执行者的产出 校验规则列表 只检查不重做的硬约束提示词里我加了这样一句硬约束实测效果明显你不需要重新执行任何任务你只负责根据校验规则对给定的产出进行检查输出 PASS 或 FAIL 以及原因。任何超出校验规则的内容都不允许出现在你的回复中。一个角色只保留完成本职所需的最小信息量这是多 Agent 协作里我学到最重要的一课。4.3 校验缺失没有检查环节AI 就是批量生产幻觉的流水线我在早期版本里其实没有设计独立的校验环节。当时的想法是模型在循环过程中已经设置了反思机制理论上会把错误的输出自己纠回来。但后来发现这种假设有个致命漏洞——模型在反思时和它犯错时是同一个脑子反思很难跳出自己犯错的路径依赖。就像一个人写代码写了半天检查不出 bug旁边人看一眼就发现了因为你在自己习惯性的思维方式里看不到自己的盲区。后来的架构里我加了一个独立校验器它和任务执行器共享模型的调用方式但提示词里不包含任何任务背景只包含校验规则和输入产物。这个校验器有三大类检查项结构检查各项字段是否齐全、格式是否符合预期JSON Schema。事实一致性检查输出中是否有与输入上下文明显矛盾的陈述特别是数字、日期、引用。安全低位检查是否包含明显超出任务边界的输出比如询问用户隐私、尝试执行高危操作。如果校验器返回 FAIL执行器会根据校验意见重新生成一次。这个重新生成的机制也很讲究——不是让执行器看一遍校验意见就重新跑而是把校验意见作为明确的迭代指令让它知道自己哪一步出了问题。这个机制跑起来之后我的项目的端到端成功率提高了大概两成。多花的那点 token 成本换来的是稳定性的指数级提升这笔账非常划算。5. 可观测性与质量评估AI 工程能上生产的前提条件5.1 每一轮 Agent 交互都要有行车记录仪AI 工程和传统软件的运维有个明显的差异传统软件的日志是正常-异常的二元状态记录而 AI 工程的日志更像是一个深思的过程记录。我在项目一开始就觉得如果不记录模型每一次的输入、输出、工具调用结果和校验结论那整个系统就是一台没有行车记录仪的车——出了问题只能靠蒙。我的线上日志格式是按一次完整的 Agent 运行为单位来组织的{ run_id: a3f9c1d2-7e4b-4f6a-9e2d-8b7c6a5d4e3f, task_id: order_inquiry_001, agent_chain: [dispatcher, executor, validator], steps: [ { agent: dispatcher, input: 用户询问订单状态, output: 分配订单查询任务给 executor, tool_calls: [], latency_ms: 523, tokens_used: 218 }, { agent: executor, input: 查询订单号 OD20240115, output: 返回订单状态已发货物流单号 SF123456, tool_calls: [query_order], latency_ms: 812, tokens_used: 305 } ], final_output: 您的订单已于 1 月 20 日从仓库发出物流单号 SF123456预计 3 天内送达。, validator_result: PASS }这套日志格式里有三个我比较庆幸的设计。第一个是run_id一次用户请求对应的完整链路可以全局追踪试想一下如果多个 Agent 并发运行没有这个关联键日志根本没法串起来。第二个是 token 和使用成本的记录AI 应用和传统应用不一样每次交互都要花钱没有成本意识很容易月底收到一张天价账单。第三个是validator_result把校验结论存下来方便做回归分析——你可以回溯那些校验通过但用户仍然抱怨的 case进一步优化校验规则。5.2 质量评估没有度量就无法迭代我见过不少 AI 项目的迭代方式是感觉不行了调一下提示词试试这种方式的效率太低了。要让 AI 工程持续改进必须把质量的评估量化。但量化不是让你给每个输出打个主观分数而是定义一套可重复、可归因的评估流程。我给自己的项目设计了四个维度维度度量方式目标值结构合规率输出能被目标 Schema 正确解析的比例≥99%事实准确率抽样人工标注输出与标准答案的一致性≥90%端到端成功率完整任务链路成功完成的比例≥85%平均延迟从用户请求到最终输出的耗时P95可接受范围根据业务定这四个维度在迭代中会互相牵制。比如把延迟优化上去可能牺牲了事实准确率把事实准确率拉上去可能又拖慢了端到端成功率。所以我每周都会拉出这几个指标做一次对比用数据决定下一轮优化方向而不是拍脑袋。5.3 评估集的构建先把标准答案找出来可能有人会问输出质量好还是坏标准答案从哪来确实很多 AI 任务没有一个唯一正确的答案但在一个具体的业务场景里你是可以建立一个参考标准的。我在项目中做的是从真实用户请求里抽样 100 条自己人工写一份这条请求最理想的处理结果应该是什么然后让 AI 去执行这 100 条测试用例计算命中率。这个方式的一个小技巧是不要只看最终答案是否一致还要看中间步骤是否合理。比如一条比较两个方案优劣的任务如果模型连比较的维度和权重都没抓对即使最终结论碰巧和标准答案一致也不算好结果。把评估维度拆细一点你才能知道该改哪里。评估集还有一个迭代机制每当线上出现一个用户反馈不好但指标却通过的案例我就会把这条用例加进评估集作为回归测试的一部分。这样评估集会越来越贴近真实业务需求系统的质量也会跟着明显提升。6. 工程化落地的最终拼图轻量化、复用与成本控制6.1 把 AI 能力封装成功能开关而不是定制脚本当初做 from scratch 的另一个动机是希望自己搭的这套东西能像乐高积木一样复用而不是为了一个任务临时写死一套流程。所以在项目后期我把整个体系抽象成了三个可以独立替换的层级。第一层是模型层所有对话都走同一个封装好的LLMClient换模型厂商时只需要改配置完全不用动业务代码。第二层是能力层每个 Agent 不再是一个 Python 脚本而是一个带输入输出协议、带质量自检逻辑的独立服务单元可以被不同的上游任务复用。第三层是流程层Dispatcher 根据任务类型选择组合哪些 Agent这个选择逻辑本身是一份可配置的 YAML 而不是硬编码的 if-else。# workflow_config.yaml 示例 workflows: order_inquiry: description: 订单查询处理流程 stages: - agent: intent_classifier config: model: gpt-4o-mini temperature: 0 - agent: order_query_executor config: model: gpt-4o-mini temperature: 0 - agent: response_formatter config: model: gpt-4o temperature: 0.3 fallback: agent: human_handoff为什么把流程做成配置而不是代码因为在实际迭代中我发现每次调整 Agent 组合方式都要重新部署代码太蠢了。业务方说我想让这个任务先经过一轮敏感信息过滤再进入分类器如果流程写死在代码里一次简单的变更就要走整套发布流程如果流程是配置只需要改一行 YAML 即可。而且把配置和代码分开后非技术同事也能在一定程度上参与流程调整。6.2 模型选型大模型干小活小模型干杂活成本控制是我在 from scratch 项目中最深刻地感受到工程和 Demo 的区别的地方。一个 Demo 级 AI 应用你直接选能力最强的大模型就完事了跑一次几毛钱也不心疼。但一旦进入生产环境每天几千次调用大模型和小模型的成本差距会呈几何级放大。我的选型原则很简单简单任务用便宜的小模型复杂任务才让大模型上。一个订单分类和意图识别任务用 mini 级别的模型完全够用而复杂的 Agent 调度、策略判断才需要旗舰模型出手。听起来很理所当然对吧但很多项目在一开始就把所有任务都丢给同一个最强模型等账单出来才知道肉痛。还有一个没那么明显的好处小模型在特定任务上的表现不一定比大模型差。这里有个经验规律是——任务越聚焦边界清晰、输入输出格式固定小模型通过良好 prompt 设计就越能接近大模型的效果任务越开放开放域对话、长程推理、创造力大模型优势越明显。所以我会针对不同类型的任务分别测试几个候选模型的准确率和成本用数据做选型决策。6.3 一步步跑通的路径我的最小可行闭环很多读者可能会觉得上面讲得太多自己想要一个可以直接照做的最小路径。这里我给一个从零开始快速验证 AI 工程闭环的五步方案选一个高价值的单任务比如客服工单自动分类不要一上来就想着做全链路助理单任务容易度量也容易快速见效。手工构造 50-100 条测试集包含典型场景和边界情况标注好期望的输出结构。基于自身的需求设计提示词应用我第二部分的信息结构公式跑通最小调用用测试集量一下准确率基线。接一个真实的工具调用哪怕只是查询一个静态数据库把 Agent 循环跑起来验证调用-入-入-出的闭环。建立日志和评估机制把每一步的输入输出记下来定时跑一次测试集看指标在提示词调整后的变化。这个五步路径的精髓是尽快让整个体系跑起来然后让数据驱动你迭代。我第一次跑通这个闭环花了大概两天之后所有的改进都有数据支撑效率比之前凭感觉调提示词高出一大截。7. 最后说点自己的体会如果让我给后来者一句最大的忠告那就是别把 AI 工程想得太玄也别把现成框架想得太神。模型本身的能力已经被行业验证了真正的差异化在于你怎么设计信息结构、怎么构建 Agent 循环、怎么建立反馈和评估机制。这些能力不依赖于某个特定工具而来源于你对业务逻辑和模型特性的双重理解。我踩过的最深的坑是在项目一开始过度迷恋用最前沿的框架 最多参数的工具能一步到位。事实是一个从零写出来、只有两三百行的 Agent 循环加上精心设计的提示词和校验环节稳定性和可维护性完胜那些叠了一堆抽象层的复杂系统。做 AI 工程的正确姿势是先把最小核心跑通然后让每一层都成为你能完全掌控的、透明的、可度量的存在。这套思路的扩展空间也很大——我在最近的工作中已经尝试把多个工作流组合成一个更复杂的业务系统底层的 Agent 单元全部复用之前搭好的那批。稳定的地基一旦打好上面盖多少层楼都不慌。希望这篇记录能帮你少走我走过的那些弯路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门