Agent 产品化的项目复盘:从技术 Demo 到可交付产品的十个关键决策

📅 2026/7/26 18:20:39
Agent 产品化的项目复盘:从技术 Demo 到可交付产品的十个关键决策
Agent 产品化的项目复盘从技术 Demo 到可交付产品的十个关键决策一、引言做技术的人容易陷入一个误区把 Demo 跑通就当成了项目完成。去年参与过一个 Agent 产品的完整交付周期让我深刻认识到从一段能跑的 Python 脚本到客户愿意付费的产品中间隔着的不是代码量而是十个需要反复权衡的工程决策。这篇文章是对那个项目的复盘不谈算法创新只谈落地过程中的真实取舍。项目背景不算复杂为一家中型企业的客服部门构建一个智能工单分派 Agent。它需要理解用户意图、匹配历史相似工单、推荐处理方案给人工坐席。技术栈上我们用 LangChain 做 Agent 编排FastAPI 做服务层前端一个轻量 Vue3 面板做坐席交互。听起来很标准的 LLM 应用但真正扎进去才发现Demo 和产品的差异不在模型能力上而在工程基础设施。下面按照决策的先后顺序逐一复盘。二、十个关键决策的复盘决策一Agent 架构选型——ReAct 还是 Plan-and-ExecuteDemo 阶段用 ReAct 模式跑得很流畅推理-行动循环能处理大多数单步查询。但上了真实业务数据后问题暴露了一个工单分派可能需要查客户历史记录、查产品知识库、查 SLA 规则、生成推荐——这四步如果在单一 ReAct 循环里串行延迟直接飙到 15 秒以上。最终选了 Plan-and-Execute ReAct 的混合模式先用一个轻量模型做计划分解把复杂任务拆成子任务列表再用 ReAct 执行每个子任务中间结果通过共享上下文传递。# 计划生成阶段的 Prompt 模板 PLAN_PROMPT 你是一个工单分派计划生成器。给定以下工单信息请将处理流程分解为步骤列表。 每个步骤必须包含步骤名称、所需工具、预期输出。 工单内容{ticket_content} 客户历史{customer_context} 请严格按 JSON 格式输出 [ {step: 意图识别, tool: intent_classifier, output: intent_label}, {step: 历史匹配, tool: similarity_search, output: top5_tickets}, ... ] 这个决策的核心收益不是准确率提升而是可观测性。计划步骤拆开后每一步的延迟、成功率、异常都可以独立监控出问题时能快速定位到具体阶段。决策二Prompt 管理——代码内嵌还是配置化Demo 时所有 Prompt 硬编码在代码里改一个字就要重新部署。产品化后 Prompt 迭代频率远超代码变更频率而且不同客户可能有不同的 Prompt 变体。方案是建一个 Prompt 管理中心用 YAML 文件管理 Prompt 模板支持变量注入和版本控制。# prompts/ticket_assignment/v2.1.yaml version: 2.1 system: | 你是一个{domain}领域的工单分派助手。 当前角色{agent_role} 处理规则 {rules} **重要**如果置信度低于0.6需要标记为需人工确认。 user_template: | 工单ID{ticket_id} 客户等级{customer_level} 问题描述{description} 请完成以下步骤 1. 识别问题类别 2. 匹配历史相似工单至少3条 3. 推荐处理方案配合一个简单的 Python 加载器import yaml from pathlib import Path from string import Template class PromptLoader: def __init__(self, base_path: str prompts): self.base Path(base_path) self._cache {} def load(self, name: str, version: str latest) - dict: cache_key f{name}:{version} if cache_key not in self._cache: file_path self.base / name / f{version}.yaml if not file_path.exists(): raise FileNotFoundError(fPrompt {name}:{version} not found) with open(file_path) as f: self._cache[cache_key] yaml.safe_load(f) return self._cache[cache_key] def render(self, name: str, variables: dict, version: str latest) - tuple[str, str]: prompt self.load(name, version) system Template(prompt[system]).safe_substitute(variables) user Template(prompt[user_template]).safe_substitute(variables) return system, user决策三工具调用的可靠性保障Agent 的灵魂是工具调用但也是出错最多的地方。知识库检索接口偶尔超时、数据库连接池耗尽、第三方 API 限流——这些在 Demo 阶段一个 try-catch 就能兜底但产品里必须建立系统性的容错机制。核心策略三板斧超时控制 重试退避 降级兜底。import asyncio from typing import Optional, Callable, Any from functools import wraps class ToolExecutor: def __init__( self, timeout: float 10.0, max_retries: int 2, base_delay: float 1.0 ): self.timeout timeout self.max_retries max_retries self.base_delay base_delay async def execute( self, tool_fn: Callable, args: tuple (), kwargs: dict None, fallback: Any None ) - dict: kwargs kwargs or {} last_error None for attempt in range(self.max_retries 1): try: result await asyncio.wait_for( tool_fn(*args, **kwargs), timeoutself.timeout ) return {status: success, data: result, attempts: attempt 1} except asyncio.TimeoutError: last_error timeout except Exception as e: last_error str(e) if attempt self.max_retries: delay self.base_delay * (2 ** attempt) await asyncio.sleep(delay) return { status: fallback, data: fallback, error: last_error, attempts: self.max_retries 1 }决策四上下文窗口管理Agent 对话越长上下文窗口消耗越大不仅成本上升推理质量也会下降。真实场景中一个工单处理可能涉及 10 轮以上的交互。采用滑动窗口 摘要压缩策略保留最近 5 轮完整对话更早的交互自动压缩为摘要关键信息客户ID、工单号、分类结果进入结构化状态from dataclasses import dataclass, field from typing import List dataclass class ConversationState: ticket_id: str customer_id: str intent: str unknown confidence: float 0.0 resolved_step_ids: List[str] field(default_factorylist) key_findings: List[str] field(default_factorylist) class ContextManager: MAX_RECENT_TURNS 5 def __init__(self, summarizer): self.summarizer summarizer self.state ConversationState() self.history [] async def add_turn(self, role: str, content: str) - None: self.history.append({role: role, content: content}) if len(self.history) self.MAX_RECENT_TURNS * 2: old_turns self.history[:-self.MAX_RECENT_TURNS * 2] summary await self.summarizer.summarize(old_turns) self.history [{role: system, content: f历史摘要{summary}}] \ self.history[-self.MAX_RECENT_TURNS * 2:] def build_context(self) - List[dict]: state_desc f当前状态{self.state.__dict__} return [ {role: system, content: state_desc}, *self.history ]决策五评估体系Demo 阶段靠感觉判断 Agent 好不好用产品化后必须有量化指标。建立了三层评估体系工具级每次工具调用的成功率、P50/P95 延迟任务级工单分类准确率、方案推荐命中率业务级人工坐席采纳率、处理时长缩减比例每一层对应不同的告警阈值和优化策略。决策六到十简表考虑到篇幅剩余五个决策以要点形式复盘决策核心问题最终方案关键教训六、多租户隔离不同客户的数据和配置隔离数据库级隔离 Prompt 模板级别隔离不要试图用代码逻辑区分租户七、流式响应长任务必须有进度反馈SSE 推送步骤进度前端状态机驱动WebSocket 太重SSE 够用且运维简单八、成本控制LLM API 费用增长不可控分级模型策略规划用小模型、执行用大模型90% 的任务可以用小模型处理九、权限与安全Agent 可以执行哪些操作白名单机制工具调用前校验权限范围永远不要给 Agent 写数据库的权限十、部署与运维如何做到零停机更新蓝绿部署 Prompt 热加载Prompt 变更不需要重启服务三、架构总览整个系统的最终架构如下四、关键踩坑记录坑一ReAct 的幻觉循环。早期版本里 Agent 有时会陷入调用工具→得到空结果→再次调用同一工具的死循环。解法是加最大步数限制和一个简单的去重检测——连续两次相同工具调用直接中断返回兜底方案。坑二Prompt 版本管理混乱。多人协作时经常发生 A 改了 Prompt、B 不知道、线上出现不一致。后来强制要求所有 Prompt 变更走 PR 流程并且部署脚本自动比对线上版本与仓库版本的差异。坑三成本失控的恐慌。上线第一周GPT-4 API 费用超出预期 3 倍。排查后发现是某些边缘 Case 触发了过长的 Agent 推理链。通过加 Token 消耗预算上限和模型降级策略成本回归到可控范围。五、结语从 Demo 到产品的路本质上是在可靠性、成本、体验三者之间找平衡。技术方案没有绝对的对错关键在于在特定场景和资源约束下做出合适的取舍。十个决策复盘下来最核心的一点是越早建立可观测性和评估体系越能避免凭感觉做决策。数据会告诉你哪里该投入、哪里可以妥协。Agent 产品化不是技术的终点而是工程化的起点——这一课写在这个项目的每一个深夜调试和每一次线上事故里。本文基于真实项目经验撰写技术栈版本Python 3.12 / FastAPI 0.110 / LangChain 0.1 / Vue 3.4