ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Agent工程实战:从七要素到七个决策点的完整落地指南

Agent工程实战:从七要素到七个决策点的完整落地指南 这两年聊 AI Agent 的文章基本都在解释“Agent 是什么”能拆任务、能调工具、能自己决策。可真到自己上手做工程实现你会发现概念层面的热闹撑不住代码层面的冷清——Demo 里那个会自己刷网页、写周报的 Agent挪到生产环境之后不是上下文爆掉就是工具调一半卡死再不然就是在同一个错误上反复横跳。问题不是出在大模型不够聪明而是出在“Agent 工程”这件事本身还没被结构化地讲清楚。我这篇文章想做的事就是把我自己折腾 Agent 工程这些年的总结拆成两个可以落地的观察框架一个是七要素用来回答“一个 Agent 系统里到底有哪些组成部分”另一个是七个决策点用来回答“从 Demo 走到生产环境时每一步选型该怎么取舍”。两个框架合在一起就是一条从概念到代码的完整路径。适合正在做 Agent 开发、被各种架构图和名词绕晕的工程师也适合想评估 Agent 落地成本的技术负责人。1. 七要素是拆“组件”七个决策点是过“关卡”Agent 工程的两个观察框架先说清楚为什么需要两套框架而不是一套。你去看市面上各种 Agent 架构图大多把 Agent 画成一个圆中间写“LLM”周围挂几个小方块Tools、Memory、Prompt、Action。这种图看完之后你会觉得“好像懂了”但落笔写代码时不知道从哪开始。因为架构图是“静态结构”而 Agent 最重要的是“动态行为”——模型怎么循环调用工具、怎么在失败时修正方向、记忆怎么在每一轮之间流转。七要素解决的是前者七个决策点解决的才是后者。1.1 七要素拆的是“Agent 由哪些零件组成”我习惯把 Agent 拆成七个要素顺序按从核心到外围排模型Model决策大脑负责理解目标、生成计划和执行动作。规划Planning把大目标拆成子任务、确定执行顺序的能力。工具ToolsAgent 对外部世界施加影响的接口比如搜索、发请求、操作数据库。记忆Memory短期上下文窗口和长期外部存储的统称。执行器Executor把模型的决策转成真实操作的那一层通常表现为代码里的一个循环。感知与反馈Observation Feedback读取工具返回结果、环境状态和用户输入作为下一轮决策的依据。自我改进Self-improvement从错误中总结、修正计划甚至调整 prompt 的环节。这七个要素几乎对应着 Agent 运行时的每一个环节。后面第 2 节我逐个说透。1.2 七个决策点卡的则是“具体选哪条路”要素是“必须有”决策点是“怎么选”。同样是具备七要素的系统模型用云 API 还是本地部署、工具用 Function Calling 还是 MCP、记忆用滑动窗口还是向量库……这些选择直接决定系统的成本、稳定性和复杂度。七个决策点对应从设计到上线会反复纠结的七个问题模型选型与部署形态通用模型还是专用模型云 API 还是本地上下文要多大记忆与上下文策略窗口怎么管理、过期信息怎么淘汰、长期信息怎么检索。工具接入方式Function Calling 直连还是走 MCP 协议超时和幂等怎么做单 Agent 还是多 Agent一个大脑包揽全局还是拆成多个角色分工协作。执行模式同步阻塞、异步任务、事件驱动还是批处理。可观测性与质量评估怎么判断 Agent 到底干得好不好出错之后能不能追溯。成本、延迟与并发控制Token 消耗怎么压、并发上限怎么定、模型降级怎么做。1.3 两个框架的衔接关系要素决定“你缺什么”决策点决定“你填什么”。一个典型的推演过程是这样的先按七要素盘一遍系统——模型定了、工具注册表有了、记忆模块建了、反馈链路接上了然后逐个过七个决策点——模型不够快就换供应商、工具调用不稳定就加超时重试、上下文膨胀就切记忆策略、Agent 行为不可控就上 trace 和评估集。七要素让你看得清全局七个决策点让你走得动泥潭。下面两节分别展开。2. 七个要素逐一说透大脑、手脚、记忆和反思在工程里到底是什么这一节不讲概念讲每个要素在代码里的实体和边界。你读完能拿着这张对照表去盘点自己的系统缺什么。2.1 模型要素不只是“调 API”决定能力上限和成本下限模型是 Agent 的大脑但在工程实现里模型要素包含三个子问题选哪家模型、用什么版本、怎么调用。先说选型。做 Agent 不比做聊天机器人对模型的要求不只是“会说话”更重要的是指令遵循能力和工具调用稳定性。同一个任务有的模型能准确地输出{name: search_web, arguments: {...}}有的模型会在 JSON 里多塞几句解释导致解析直接失败。因此我建议你在选型时准备一组工具调用压力测试集——100 条需要调用 2 到 3 个工具才能完成的任务测成功率、测解析率、测平均 Token 消耗。再说调用。模型要素在工程上就是一段 API 封装但这层封装很关键它决定了下游所有环节怎么对接。我的做法是只定义两种接口chat(messages, tools, ...)用于规划决策模型可能返回文本也可能返回工具调用指令。complete(prompt)用于补全、摘要、重写这类子任务。封装的内部处理好重试、超时、模型版本切换和流式输出。这层做稳了后面换供应商、换模型参数都不会伤筋动骨。2.2 规划要素Plan-and-Execute 与 ReAct 两种风格的取舍规划要素决定 Agent “怎么想”。工程上常见的思路有两种ReAct 风格模型走“思考Thought→ 行动Action→ 观察Observation”的循环走一步看一步灵活性高但 Token 消耗大长任务容易在中间跑偏。Plan-and-Execute 风格先让模型生成一个完整计划再按计划逐步执行每执行完一步可以重新审视计划。这种风格适合任务链路比较确定的场景比如“查数据→生成图表→写报告”这种三步走的流程。我现在的习惯是默认走 Plan-and-Execute但允许在执行循环里出现“计划修正”分支。也就是说模型每完成一个子步骤都可以选择“继续下一步”或“重新规划”。这样既保留了灵活性又避免了 ReAct 那种每一步都无边界发散的问题。2.3 工具要素注册表、参数校验和结果封装构成工具层工具是 Agent 的“手脚”但工具的工程实现远比“写一个函数”复杂。一个规范的工具层至少包含三部分工具注册表所有工具函数的元信息统一登记包括名称、描述、参数 JSON Schema。模型并不直接调用函数它只是看到工具的“描述”然后返回一个调用指令真正执行由系统侧完成。所以工具描述的准确性直接决定模型的调用成功率。参数校验与转换模型返回的参数是字符串要经过一层校验和类型转换才能传给真实函数。别省这一步——模型经常会把枚举值写错、把数字写成字符串。我在工程里都会加一层 Pydantic 或 JSON Schema 校验校验失败时把错误信息回传给模型让它自行修正。这个“回传修正”机制比你在系统里硬编码参数修复有效得多。结果封装工具执行完返回什么格式给模型需要统一约定。我通常返回一个结构化 JSON{success: true/false, data: ..., error: ...}。关键是千万不要把未做截断的完整结果直接塞回上下文工具返回 2 万行日志就是上下文灾难。我会自己做一层摘要只把前几条关键记录和统计摘要回填给模型。2.4 记忆要素上下文窗口是工作台向量库是档案柜很多新人有一个误解上下文越大记忆越强。实际上上下文窗口只是“工作台”模型能“看到”的内容不等于模型能“用好”的内容。窗口一大注意力分散模型反而容易出现选择困难。我的记忆分层经验是三者配合短期记忆当前对话窗口里的原始消息。窗口接近上限时触发摘要压缩。工作记忆当前任务需要持续关注的中间状态比如计划列表、已执行步骤。这部分必须以结构化数据维护而不是塞在对话历史里。长期记忆从历史对话和任务结果里抽取的结论、偏好、事实存进向量库或 KV 存储需要时检索后注入上下文。工程上有一个容易忽略的细节记忆模块应该显式区分“用户说了什么”和“系统得出了什么”。把模型自己之前的胡说八道存进长期记忆几天后它会把错误当事实用。我只存“经过工具验证的结果”或“用户明确确认的信息”模型在 ReAct 过程中的思考过程不进长期记忆。2.5 感知与反馈要素工具结果、环境状态和用户修正回路Agent 如果没有良好的反馈回路就像一个人蒙着眼睛干活。反馈要素在工程上包含三层第一层是工具返回结果上一节说的结果封装就是这层的一部分。第二层是环境状态比如页面加载状态、进程是否存活、API 限流余量。第三层是用户修正——用户说“停一下”“换一种思路”这类指令优先级最高必须能随时打断 Agent 的自主循环。这层最容易踩的坑是误以为“工具返回了内容 任务成功了”。很多工具调用失败后返回的是一段错误堆栈模型也能读但如果错误信息语义不清晰模型会把报错误判为正常结果继续往下走。所以反馈链路里要有一个明确的状态字段模型在做推理时第一眼看success而不是从一坨文本里猜。2.6 自我改进要素Agent 的进化能力藏在评估闭环里自我改进听起来很玄但工程实现其实落在一个闭环上记录失败案例 → 分析失败原因 → 调整 prompt / 工具描述 / 流程 → 回归验证。我见过最简单的自我改进机制是给 Agent 循环加一个“事后回顾”步骤任务结束后让模型用系统 prompt 反思“这次执行哪里不顺、下次遇到同类任务要怎么调整方向”把反思结果存成文本。这个做法效果有限但聊胜于无。真正有效的做法是形成一套回归评估集每发现一个失败模式就把它变成一条评估用例。下一次 Agent 行为变更后先把回归集跑一遍看有没有引入新问题。关于评估集的构建我在第 3 节的可观测性决策点里展开。3. 七个决策点一关一关过从模型选型到可观测性的取舍逻辑七要素搭好了骨架接下来的是血和肉。七个决策点是工程实现里最消耗心力的部分每个决策点我都给你一个“怎么选”的方法论。3.1 决策点一模型选型与部署形态先定“类型阈值”再选供应商模型选型这个决策很多团队是被热门榜单带着走的。我建议你建立一个自己的评估维度表至少包含四项评估维度核心关注点测试方法工具调用准确率是否能稳定返回合法、完整的调用指令拿 100 个工具用例打分指令遵循能力是否能严格按输出格式、长度、风格约束执行看格式错误率和偏离率中文上下文理解长段中文语义捕捉、多轮记忆保持用你业务领域的长文测试延迟与成本P50/P95 延迟单任务 Token 消耗真实任务压测部署形态上云 API 的优势是省事、模型迭代快缺点是延迟受网络波动影响、成本随用量线性增长。本地部署则适合数据敏感、离线、高并发场景对 GPU 和推理引擎的运维能力要求更高。我的建议是项目初期无脑选云 API先把业务逻辑跑通当单月成本或延迟指标触到阈值例如单任务 Token 成本超过毛利预期、P95 延迟超过 3 秒时再考虑换模型或加推理缓存。不要前期就自建推理集群。关于热词里常看到的“token 是什么意思”——简单说token 是模型处理文本的最小单位英文大概 4 个字符一个 token中文差不多 1 到 1.5 个字一个 token。它同时是计费单位和上下文容量单位Agent 的每一项决策思考、调工具、看结果都在消耗 token所以后面大部分优化本质上是“省 token”。3.2 决策点二记忆与上下文策略用“三层缓存”的思路管窗口上下文管理是我认为 Agent 工程里最容易被低估的一环。模型上下文是有限资源Agent 每执行一轮任务对话历史就会膨胀一轮。策略层面有三种方案可以组合滑动窗口只保留最近 N 轮对话实现最简单但会丢失早期关键信息。摘要压缩窗口快满时把早期对话重写成摘要。实现不复杂但摘要本身也占 token且压缩过程有信息损失。向量检索把历史信息切块向量化需要时按相似度召回。信息密度高但引入了相似度计算和检索失败的噪声。我的实践是“三层混合”按时间衰减把信息分为热层当前任务上下文完整保留、温层最近任务的摘要压缩保留、冷层历史事实和结论向量检索后按需注入。判断触发条件的方法是监听 token 用量和上下文占比当剩余可用上下文低于总窗口 20% 时触发压缩和归档。这个阈值可以根据模型类型微调但 20% 是一个比较稳的起点。3.3 决策点三工具接入方式Function Calling 和 MCP 之间不是单选题工具接入是新老 Agent 系统分水岭最明显的地方。早期做法是开发者手写工具描述、塞进 prompt、解析模型输出也就是 Function Calling 的原型。后来各家大厂都提供了原生 Function Calling 能力模型侧保证输出结构稳定、参数能对齐。Function Calling 的优势是成熟、调试简单、延迟低适合工具数量少比如 10 个以内、调用链路由开发者可控的场景。MCPModel Context Protocol出现后工具接入进入了“标准化”阶段。它的核心思路是把工具接入抽象成统一协议Agent 主机通过 MCP 客户端连接工具服务器工具可以提供资源、提示或能力。好处是同一套代码能对接任意支持 MCP 的服务生态意义上的互操作性强。但我不建议“All in MCP”。原因很直接MCP 的重心在于“协议统一”但当你的业务只有三五个内部 API 时走协议层反而增加了一层的调试成本、连接管理和网络开销。我的选型标准是团队自研、调用频率高、延迟敏感的内部工具用 Function Calling 直连第三方服务、需要跨系统复用、生态更新快的工具走 MCP。另外工具层无论选哪条路超时时间、重试策略、幂等设计是必须做的外部 API 调用要设定超时上限工具执行最好有任务 ID 来保证重试不产生重复副作用。3.4 决策点四单 Agent 还是多 Agent先看任务的可并行度“多 Agent 协作”这几年非常火但工程复杂度也被明显放大了。我的判断标准是三个问题任务是否需要多个不同领域的模型角色子任务之间是否有明确的收发接口是否真的需要并行处理如果你的答案是“都不是”老老实实用单 Agent。单 Agent 的可控性、可调试性和成本优势都非常明显。只有当你遇到“领域隔离 环节复杂 流量大”三重条件同时满足时才值得拆多 Agent。比如客服系统里一个 Agent 负责意图识别、一个 Agent 负责检索知识库、一个 Agent 负责生成回复这种拆法是为了隔离 prompt 和权限边界而不是为了炫技。多 Agent 架构有三个绕不开的工程问题通信协议Agent 之间传格式化 JSON 还是自然语言、编排拓扑链式、路由、还是共享黑板、状态一致性Agent 之间共享哪些状态冲突怎么仲裁。这些问题的调试成本是几何级上升的。我的建议是从单 Agent 起步当 agent 的 prompt 已经长到无法维护、或者不同步骤的模型需求冲突时再按边界逐步拆分而不是一开始就设计五角色协作图谱。3.5 决策点五执行模式决定系统是“能用”还是“扛得住”Agent 的执行模式直接决定系统的吞吐和稳定性。实体上我把它分为四种执行模式适用场景关键工程点同步阻塞用户在线等待结果如对话助手超时、流式输出、客户端重试异步任务耗时任务如批量报告生成任务队列、状态查询、回调通知事件驱动被动响应触发如监控告警处理事件订阅、幂等消费、背压控制批处理离线清洗、批量长文本处理分片、断点续跑、并发限流大多数业务不是“只有一种模式”而是在一条链上混合。比如“用户请求 → 同步阻塞生成计划 → 转为异步任务执行 → 完成后事件回调”。工程上要特别注意Agent 的循环不适合做同步长连接——一次任务可能跑几十轮工具调用单次请求耗时动辄几分钟这会拖垮 Web 服务的 worker 池。实践上我会把规划阶段做成同步快速返回“已开始”把工具执行阶段移到后台任务队列再由前端轮询或 WebSocket 推送动态。3.6 决策点六可观测性与质量评估没有度量就没有迭代Agent 和传统程序最大的差别是你没法断言“这一行代码执行完是对是错”。所以可观测性不是可选项而是 Agent 工程质量的生命线。至少要采集三类数据轨迹Trace完整记录每一次模型调用的输入、输出、工具选择、参数、返回值。注意这里的输入输出最好做脱敏和截断不然日志存储量会爆炸。指标Metric任务成功率、平均轮数、工具调用成功率、上下文压缩次数、Token 消耗分布、单任务延迟。这些数字能直接告诉你优化方向。评估Evaluation构建回归集用大模型或规则给 Agent 输出打分。评分维度可以包括任务完成度、工具调用准确性、格式合规性、用户意图满足度。这里有一个容易被忽略的事质量评估必须包含“失败样本归因”。每次任务失败不仅要记录“失败了”还要让系统自动保存失败路径的完整 trace并打上失败原因标签比如“工具超时”“参数解析失败”“规划偏离目标”。积累一批标签后你就能知道最该优先解决的问题是什么。这也是前面说的“自我改进”在工程里的真正抓手。3.7 决策点七成本、延迟与并发控制优化空间藏在模型分级里最后这个决策点直接决定项目能不能盈利。Agent 的成本控制不像普通接口因为模型的每次调用都会附带多轮工具上下文放大Token 消耗可能是指数级的。省钱的核心思路不是压价而是减少不必要的消耗Prompt 瘦身系统指令里只保留与当前任务相关的约束把通用背景知识移到工具调用里按需获取。缓存策略对相同或高度相似的用户请求命中结果缓存对工具返回内容做摘要后入缓存。模型分级意图识别、关键词抽取这类简单任务用便宜的小模型复杂规划、长文本推理才用大模型。这是成本优化空间最大的一项。并发控制上要强调的是“限流语义”不只限 API 调用频率还要限 Agent 启动的任务数否则某个用户的一个任务循环 30 轮可能瞬间打爆供应商并发配额。我会在任务队列里做信号量控制上限设置为供应商并发额度的 70%留出余量应对突发。4. 一份可以落到代码里的工程骨架从配置管理到工具注册的实现路径这一节给出一份可以直接抄作业的最小骨架。我以 Python 为例结合前面七要素和七个决策点里最关键的环节做一个能跑通的 Agent 循环。你拿去换成自己的工具集和模型参数就能用。4.1 环境准备与配置外部化项目结构上我习惯这样分agent_workspace/ ├── config/ # 配置文件禁止硬编码 ├── tools/ # 工具注册与实现 ├── memory/ # 记忆管理 ├── core/ # Agent 循环、模型封装 ├── eval/ # 回归评估集 └── app.py # 启动入口配置项至少包括模型名、基础 URL、密钥注入方式、上下文上限、单任务最大轮数、工具超时时间。密钥不要出现在代码里用环境变量或密钥管理服务注入。这一步虽然简单却是生产环境的第一道门槛。# config/settings.py from pydantic_settings import BaseSettings class Settings(BaseSettings): model_name: str qwen2.5 api_base: str https://your-endpoint api_key: str env # 从环境变量读取 context_limit: int 32000 max_agent_rounds: int 15 tool_timeout: int 10 max_concurrent_tasks: int 20 settings Settings()4.2 模型封装对外只暴露两个方法模型封装层是所有 Agent 组件的数据枢纽。我建议做两层底层是“单次调用”上层是“会话级调用”。会话级调用内部维护消息列表和上下文截断逻辑。# core/llm.py from openai import AsyncOpenAI from typing import Optional, List, Dict class LLMClient: def __init__(self, settings): self.client AsyncOpenAI( base_urlsettings.api_base, api_keysettings.api_key ) self.settings settings async def chat( self, messages: List[Dict], tools: Optional[List[Dict]] None, temperature: float 0.2, ) - Dict: 单轮对话可能返回文本或 tool_calls resp await self.client.chat.completions.create( modelself.settings.model_name, messagesmessages, toolstools, temperaturetemperature, ) return resp.choices[0].message async def complete( self, prompt: str, max_tokens: int 1024, ) - str: 用于摘要、重写等辅助任务 resp await self.client.chat.completions.create( modelself.settings.model_name, messages[{role: user, content: prompt}], max_tokensmax_tokens, temperature0.1, ) return resp.choices[0].message.content这里我把temperature默认调到 0.2因为 Agent 场景下创造性不是刚需稳定性才是。规划、工具调用这类任务用低温文案生成这类子任务可以单独调高。4.3 工具注册表装饰器 Schema 自动生成工具注册表是 Agent 手和脑的接口。我用装饰器把函数信息自动转成模型需要的 JSON Schema省去手工维护双份定义的工作也避免“函数更新了、描述忘了改”的不一致问题。# tools/registry.py import inspect, json from functools import wraps from typing import Dict, List, Callable TOOL_REGISTRY: Dict[str, Dict] {} TOOL_FUNCS: Dict[str, Callable] {} def tool(name: str, description: str): def decorator(func): # 用 inspect 自动从函数签名生成参数 schema简化示例 sig inspect.signature(func) properties {} required [] for param_name, param in sig.parameters.items(): properties[param_name] {type: string} # 真实项目用 pydantic schema if param.default is inspect.Parameter.empty: required.append(param_name) schema { type: object, properties: properties, required: required, } TOOL_REGISTRY[name] { type: function, function: {name: name, description: description, parameters: schema}, } TOOL_FUNCS[name] (func, schema) return func return decorator async def call_tool(name: str, args: Dict): 统一工具调用入口校验参数、执行、封装结果 func, schema TOOL_FUNCS[name] try: # 参数校验真实项目用 pydantic if not set(schema.get(required, [])).issubset(args.keys()): raise ValueError(f缺少必要参数: {args}) result await func(**args) return {success: True, data: result} except Exception as e: # 关键把错误信息回传给模型让模型自行调整 return {success: False, error: str(e)}4.4 Agent 主循环ReAct 简化版但加上最大轮数和放弃条件Agent 主循环是执行器的核心。这里我实现一个带“最大轮数保护”的循环模型决定调用工具就执行并回填结果模型决定结束就退出如果连续多轮没有调用工具也没有结束触发放弃逻辑。# core/agent.py from tools.registry import TOOL_REGISTRY, call_tool class AgentLoop: def __init__(self, llm, toolsNone, max_rounds15): self.llm llm self.tools tools or list(TOOL_REGISTRY.values()) self.max_rounds max_rounds async def run(self, user_query: str): messages [{role: system, content: self.system_prompt()}] messages.append({role: user, content: user_query}) for step in range(self.max_rounds): resp await self.llm.chat(messages, toolsself.tools) # 模型决定直接返回答案 if not getattr(resp, tool_calls, None): return resp.content # 模型要求调用工具逐个执行并回填 messages.append(resp) # 保留 assistant 的 tool_calls 消息 for tool_call in resp.tool_calls: tool_name tool_call.function.name tool_args json.loads(tool_call.function.arguments or {}) tool_result await call_tool(tool_name, tool_args) messages.append({ role: tool, tool_call_id: tool_call.id, content: self._format_tool_result(tool_result), }) # 达到最大轮数强制收尾 return 达到最大轮数任务未完成。 def _format_tool_result(self, result: Dict, max_chars: int 2000) - str: 对工具结果做截断防止上下文膨胀 text json.dumps(result, ensure_asciiFalse) if len(text) max_chars: # 截断并只保留摘要性字段 text text[:max_chars] ...[已截断] return text def system_prompt(self) - str: return ( 你是一个任务执行助手。你的目标是:理解用户需求使用可用工具完成任务。 如果工具调用失败请根据错误信息修正参数后重试不要编造成功结果。 如果已经收集到足够信息或遇到不可恢复的错误直接输出最终答复。 )这段代码信息量挺大我逐行解释几个关键点messages.append(resp)这一步不能省。模型返回的 tool_calls 必须原样进入下一轮消息列表否则 API 会报“多轮工具调用缺少上下文”。这是 Function Calling 最容易写错的地方。回填的tool消息必须带tool_call_id用来和上一步的调用请求对应。工具结果截断是上下文保护的基本功。我在_format_tool_result里做了字符级截断更精细的做法是按结构化字段保留枚举摘要和统计结论。失败结果也回填。这个设计刻意为之——让模型看到{success: false, error: ...}它可能自行修正参数重试这就是反馈儿子到自我改进的最短路径。4.5 Rust 做 Agent 的意义一个被低估但确实存在的高并发视角热词里出现“基于 Rust 语言 AI Agent”这里给一个工程上的客观比较。Agent 服务本质上是一个 IO 密集 网络密集系统大量等待外部 API 返回、工具结果、流式传输真正消耗 CPU 的推理逻辑全部在模型端。这种特征下Rust 的异步运行时tokio确实能提供高并发和低资源占用优势对内存占用敏感的边缘场景友好。但对大多数产品和业务团队Python/TypeScript 生态的开发效率和 AI SDK 集成便利性更实际。我的判断标准是如果你已经有 Node 或 Python 的存量服务不要为了 Agent 专门引入 Rust如果你是从零做一个高并发、资源受限、长生命周期的基础设施型 Agent 服务Rust 值得考虑尤其是需要单机扛大量 Agent 实例的场景。4.6 运行服务从同步阻塞到任务队列最小骨架里可以直接用 FastAPI 暴露同步接口但生产环境建议按第 3.5 节的决策点改成异步任务。这里给一个可以平滑迁移的中间方案# app.py from fastapi import FastAPI, BackgroundTasks from core.agent import AgentLoop app FastAPI() agent AgentLoop(llm, toolsTOOL_REGISTRY.values()) app.post(/agent/run) async def run_agent(payload: dict, background_tasks: BackgroundTasks): 快速版同步执行适合短任务 result await agent.run(payload[query]) return {result: result} app.post(/agent/submit) async def submit_agent(payload: dict, background_tasks: BackgroundTasks): 稳妥版后台任务 任务 ID 查询适合长任务 task_id ftask_{uuid4().hex[:8]} background_tasks.add_task(execute_in_background, task_id, payload[query]) return {task_id: task_id}execute_in_background里需要做任务状态存储和并发信号量限流。信号量的上限就来自第 3.7 节说的供应商并发配额。5. 从 Demo 到生产线的常见翻车现场上下文、幻觉与反馈失灵最后这节是实战中反复踩过的坑。前面是“怎么做”这里是“别这么干”全是血泪经验。5.1 上下文漂移Agent 干着干着就忘了目标任务Demo 里跑两三轮工具调用Agent 表现得像个明白人到生产环境任务链路一长它就开始创造新目标——用户让它“查天气”它查完天气顺手开始推荐穿衣搭配然后沿着这条线跑下去越跑越偏。这不是聪明的体现而是上下文漂移。根因在于对话窗口被大量工具返回和中间推理填充后原始目标的重要性被稀释了。工程对策有两个一是每轮循环都把原始目标作为不可丢失的锚点在 system prompt 里显式声明“你的根本目标是 X所有操作必须服务于此目标”并且每轮在输入里重新注入精简版任务描述二是在 Agent 循环里加入目标校验每完成一个工具调用模型必须回答一句“当前状态是否仍与目标相关”不相关就回到目标路径。我见过不少团队只用第一个对策效果会衰减两个一起用长链路稳定性才有保障。5.2 工具幻觉模型会“脑补”成功还带上下文爆炸这是生产环境最可怕的坑。模型调用某个工具工具实际返回了错误但因为错误信息里包含了一些正常字段模型就把整个结果当成成功继续往深处跑。更隐蔽的一种是工具返回空列表模型在下一步推理里却把“没有数据”描述为“数据已处理完毕”看起来像完成了任务实际什么都没干。对策在第 2.5 节已经埋了伏笔结果必须带显式状态字段且这个字段要被模型在推理时优先读取。我还会在 prompt 里加上一句“任何工具返回结果必须基于success字段判断任务状态即使工具返回了数据也要先确认数据非空。”另外如果任务结果最终依赖某个关键工具的返回需要加一道独立校验器规则代码来验证“关键信息确实拿到了”而不是完全信任模型的自我判断。上下文爆炸则发生在工具返回结果不截断的时候。有人把 5 万字的网页正文直接塞给模型然后下一轮模型输出的“理解结果”也要跟着塞进去几轮下来窗口就满了。我的习惯是工具结果回填前必须经过“结构化、摘要化、截断化”三道处理尽量把单次回填压到 2000 字符以内。5.3 反馈死循环反思机制成了无限内耗的出口给 Agent 加上“反思”和“自我改进”之后另一个坑会出现遇到失败模型不是换方案而是反复总结“我上次失败了这次我要更小心”然后又用一样的路径冲一次继续失败继续总结。这是反馈回路设计得太“心理学”的结果——反思并没有改变行动。对策是把反思从“自省”改成“定向重试策略”。不让模型泛泛地说“我要更小心”而是要求它输出具体的策略变更例如“上一次失败是参数缺少日期格式约束这一次在调用前先补充格式转换步骤”。如果模型给出的新策略与旧策略没有本质区别可以用文本相似度判断就直接终止任务避免死循环。另外限制反思次数也很重要我通常允许单任务内最多 2 次计划修正超过就放弃并转人工。5.4 没有评估集的回归测试一切优化都可能是负优化很多团队在 Agent 开发期跑得好好的一改 prompt 或升级模型就把一批原本成功的用例搞挂了。因为 Agent 的行为有随机性没有回归集就无法判断“这次改进是真的进步还是碰巧好运”。我构建回归集的方法比较朴素但要花时间维护从日志里挑出过去两周最有代表性的 50 到 100 条任务标注每个任务的“预期关键行为”比如必须调用某个工具、必须包含某个字段、最终演示必须不报错。每次改动后跑一遍比对行为的分布变化。这里的判定不一定用大模型打分——规则检查加关键步骤断言在大多数场景下已经能拦住回归。5.5 模型参数过拟合到单个供应商迁移时半身不遂最后提醒一个很多人不重视的问题项目写到一定阶段代码里到处是对某家模型输出格式的假设比如message.tool_calls[0].function.name的解析方式。一旦你要换供应商改动波及面可能是全局的。我最开始做 Agent 时也吃过这个亏。后来把模型层收敛到LLMClient.chat / complete / stream这三个接口所有下游代码不直接依赖 SDK 类型而是项目内自定义的Message / ToolCall数据结构。这样换模型时只改LLMClient内部业务层完全不动。如果你的项目已经有大面积对 SDK 类型的直接引用了尽早收敛——越晚迁移成本越高这方面的改造早晚要做。踩过这些坑之后我的做法已经变得极其保守任何 Agent 能力上线前先跑回归集再压一把上下文耗尽场景最后用 trace 日志确认关键路径没有幻觉。这套流程虽然不性感但至少不会让线上 Agent 变成大型翻车现场。如果你正准备把一个 Agent 原型推向生产我的建议是先把第 3 节的七个决策点逐条过一遍再对照第 4 节的骨架把代码补严——七个要素保证你不缺零件七个决策点保证你不走弯路剩下就是拿真实流量慢慢打磨了。
返回列表