首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >规避 Demo 陷阱,构建可投产的 Agent 系统

规避 Demo 陷阱,构建可投产的 Agent 系统

原创
作者头像
ctrl加滚轮
发布2026-07-24 13:49:44
发布2026-07-24 13:49:44
20
举报

规避 Demo 陷阱,构建可投产的 Agent 系统

前言:Demo 与生产之间,隔着一道 "确定性" 的鸿沟

过去一年,我评审了超过 50 个 Agent 项目,发现几乎所有的 Demo 都看起来完美,但一旦进入生产环境就暴露出本质问题:

Demo 阶段表现

生产阶段现实

"我的 Agent 能调用 20 个工具!"

工具调用准确率只有 60%,经常选错

"多轮对话很流畅!"

对话超过 3 轮就开始"失忆"

"它能自主决策!"

决策路径不可控,无法审计

"推理很深入!"

单次推理耗时 10 秒+,用户等不了

核心判断:Demo 展示的是 Agent 的 "上限" ,而生产系统需要保证的是 "下限" 。可投产 Agent 的核心不是"更聪明",而是更可控、更可预测、更可观测


第一部分:重新定义 Agent——去掉"魔法",增加"工程"

1.1 生产级 Agent 的五个核心特征

特征

说明

反模式(Demo 陷阱)

确定性边界

Agent 只在明确定义的"围栏"内自主

让 Agent 自由探索所有可能性

可中断性

任何步骤都可暂停、回滚、人工接管

一旦启动就必须跑到结束

可观测性

每一步决策都有迹可循、可审计

只记录最终结果

成本可预测

单次调用的成本上限可控制

不设 Token 限制

失败优雅降级

出错时能降级到确定性规则

出错就直接报错

1.2 Agent 成熟度模型

帮助你和团队评估当前系统处于哪个阶段:

代码语言:javascript
复制
Level 0: 无 Agent(纯规则引擎)
  └── 完全确定,无 AI 介入

Level 1: 单轮 LLM 调用(ChatBot)
  └── 有 AI 回复,无工具调用,无状态

Level 2: 固定流程 Agent(Workflow + LLM)  ← 大部分生产系统在此
  └── 流程由代码控制,某些节点用 LLM 填充

Level 3: 动态决策 Agent(ReAct / Toolformer)
  └── Agent 自主选择工具和决策路径  ← 本文重点

Level 4: 自演进 Agent(持续学习)
  └── 能从历史交互中自动优化行为

现实建议:90% 的业务场景应该停留在 Level 2。只有当你确信需要 Level 3 时,再考虑下面的复杂架构。


第二部分:可投产 Agent 架构设计

2.1 生产级 Agent 整体架构

2.2 核心设计模式:Plan-Execute-Verify(PEV)

ReAct 模式(推理-行动-观察)适合 Demo,但生产环境下不确定性太高。我们采用 PEV 模式

阶段

职责

确定性程度

Plan

生成执行计划(JSON结构),不执行

可校验、可审批

Execute

按计划逐布执行,每一步记录状态

可监控、可中断

Verify

验证执行结果,决定是否继续/重试/降级

可量化、可追溯

代码语言:javascript
复制
# PEV 模式核心实现
class PlanExecuteVerifyAgent:
    def __init__(self, planner, executor, verifier, llm):
        self.planner = planner
        self.executor = executor
        self.verifier = verifier
        self.llm = llm
        
    def process(self, user_request: str) -> AgentResponse:
        # ===== PLAN 阶段 =====
        plan = self.planner.generate_plan(user_request)
        # plan 是结构化的 JSON,例如:
        # {
        #   "steps": [
        #     {"id": 1, "tool": "search_docs", "params": {...}},
        #     {"id": 2, "tool": "query_db", "params": {...}}
        #   ],
        #   "estimated_steps": 3,
        #   "estimated_tokens": 2000
        # }
        
        # 计划校验:是否在允许范围内?
        if not self.is_plan_valid(plan):
            return self.fallback_response("请求过于复杂,请简化或联系人工")
        
        # 计划审批(高风险操作)
        if self.requires_approval(plan):
            approval = self.request_approval(plan)
            if not approval.granted:
                return self.fallback_response("操作需要审批,请等待或联系管理员")
        
        # ===== EXECUTE 阶段 =====
        execution_state = ExecutionState()
        for step in plan['steps']:
            # 执行前检查:是否超预算?
            if self.budget_exceeded(execution_state):
                return self.fallback_response("执行预算超限,已停止")
            
            # 执行步骤
            result = self.executor.execute(step)
            execution_state.add_result(step['id'], result)
            
            # 检查是否需要提前终止(例如已找到答案)
            if self.should_early_stop(execution_state):
                break
        
        # ===== VERIFY 阶段 =====
        verification = self.verifier.verify(execution_state, user_request)
        
        if verification.passed:
            # 生成最终响应
            return self.generate_response(execution_state, verification)
        else:
            # 验证失败:尝试重试或降级
            return self.handle_verification_failure(
                execution_state, 
                verification, 
                user_request
            )

2.3 计划生成器(Planner)的设计要点

关键洞察:Planner 是 Agent 中唯一需要调用大模型的地方。其他组件应该是确定性的。

代码语言:javascript
复制
class Planner:
    def __init__(self, llm, tool_registry, plan_schema):
        self.llm = llm
        self.tool_registry = tool_registry
        self.plan_schema = plan_schema  # JSON Schema
        
    def generate_plan(self, user_request: str) -> dict:
        # 1. 获取可用工具列表(动态过滤,只给相关的)
        available_tools = self.tool_registry.get_tools_for_scenario(user_request)
        
        # 2. 构建结构化的 Prompt
        prompt = f"""
        你是一个任务规划专家。请根据用户请求生成执行计划。
        
        用户请求:{user_request}
        
        可用工具:
        {self.format_tools(available_tools)}
        
        输出格式必须符合以下 JSON Schema:
        {json.dumps(self.plan_schema, indent=2)}
        
        注意:
        1. 每一步必须使用列出的工具,不要发明新工具
        2. 步骤数不超过 5 步
        3. 如果请求需要的信息不明确,在第一步设置为 "ask_clarification"
        """
        
        # 3. 调用 LLM 生成计划
        response = self.llm.invoke(
            prompt,
            response_format="json",
            temperature=0.1  # 低温度保证确定性
        )
        
        # 4. 用 JSON Schema 严格校验
        plan = json.loads(response)
        jsonschema.validate(plan, self.plan_schema)
        
        return plan

第三部分:工具系统的工业级设计

3.1 工具注册中心(可发现、可校验)

Demo 阶段的工具通常是硬编码的,生产环境需要动态注册和发现。

代码语言:javascript
复制
// 工具定义(带 Schema)
@Data
@Builder
public class ToolDefinition {
    private String name;                    // 唯一标识
    private String description;             // 人类可读描述
    private Map<String, ParameterSchema> parameters;  // 参数 Schema
    private Set<String> requiredPermissions; // 所需权限
    private boolean isDestructive;          // 是否具有破坏性
    private int maxRetries;                 // 最大重试次数
    private long timeoutMs;                 // 超时时间
    private String fallbackTool;            // 降级时使用的备选工具
}

// 工具注册中心
@Component
public class ToolRegistry {
    private final Map<String, ToolDefinition> definitions = new ConcurrentHashMap<>();
    private final Map<String, ToolExecutor> executors = new ConcurrentHashMap<>();
    
    public void register(ToolDefinition def, ToolExecutor executor) {
        // 校验:工具名称不能重复
        if (definitions.containsKey(def.getName())) {
            throw new DuplicateToolException("工具已存在: " + def.getName());
        }
        
        // 校验:参数 Schema 必须有效
        validateSchema(def.getParameters());
        
        definitions.put(def.getName(), def);
        executors.put(def.getName(), executor);
        
        log.info("工具注册成功: {} (destructive: {})", 
            def.getName(), def.isDestructive());
    }
    
    public List<ToolDefinition> getAvailableTools(Set<String> permissions) {
        return definitions.values().stream()
            .filter(def -> permissions.containsAll(def.getRequiredPermissions()))
            .collect(Collectors.toList());
    }
    
    public ToolDefinition getDefinition(String name) {
        return definitions.get(name);
    }
}

3.2 工具执行的"三板斧":超时、重试、熔断

代码语言:javascript
复制
class ToolExecutor:
    def __init__(self, registry, circuit_breaker):
        self.registry = registry
        self.cb = circuit_breaker
        
    def execute(self, tool_name: str, params: dict) -> ToolResult:
        tool_def = self.registry.get_definition(tool_name)
        if not tool_def:
            return ToolResult.error(f"未知工具: {tool_name}")
        
        # 1. 参数校验
        try:
            validate_params(params, tool_def.parameters)
        except ValidationError as e:
            return ToolResult.error(f"参数错误: {e}")
        
        # 2. 使用熔断器包裹执行
        @self.cb(tool_name)
        def _execute():
            executor = self.registry.get_executor(tool_name)
            return executor(params)
        
        # 3. 重试逻辑(带退避)
        retries = 0
        while retries <= tool_def.maxRetries:
            try:
                result = _execute()
                return self.wrap_result(result)
            except (TimeoutError, CircuitBreakerOpenError) as e:
                # 不重试:超时和熔断说明系统有问题
                return self.handle_tool_error(tool_def, e)
            except Exception as e:
                # 可重试的错误
                retries += 1
                if retries > tool_def.maxRetries:
                    # 尝试降级
                    return self.fallback(tool_def, params, e)
                time.sleep(2 ** retries * 0.1)  # 指数退避
        
        return ToolResult.error("所有重试均失败")
    
    def fallback(self, tool_def, params, error):
        if tool_def.fallbackTool:
            # 调用备选工具
            return self.execute(tool_def.fallbackTool, params)
        else:
            # 返回错误信息,让上层决定怎么处理
            return ToolResult.fallback(f"工具 {tool_def.name} 不可用: {error}")

3.3 工具沙箱(安全隔离)

在生产环境中,Agent 调用的工具绝不能直接操作生产数据。必须通过沙箱层

代码语言:javascript
复制
# 工具沙箱配置
sandbox:
  # 读操作:允许直接访问(有缓存)
  read_operations:
    - search_docs
    - get_user_info
    - query_metrics
  
  # 写操作:必须经过审批队列
  write_operations:
    - update_ticket
    - send_notification
    - create_incident
  
  # 危险操作:只能在测试环境执行
  dangerous_operations:
    - delete_data
    - restart_service
    - modify_config
  
  # 隔离策略
  isolation:
    network: "restricted"  # 只能访问特定内网
    data: "readonly"       # 写操作需审批
    resources: "limited"   # CPU/内存限制

第四部分:记忆系统的工程化

4.1 三层记忆架构

Demo 中的记忆通常是单层缓存,生产需要分层设计:

层级

名称

存储

生命周期

典型内容

L1

会话记忆

Redis(TTL 30min)

单次对话

最近 10 轮对话,用户偏好

L2

用户记忆

关系数据库

跨会话

用户历史问题、习惯、角色

L3

场景记忆

向量库

长期

相似场景的处理经验

代码语言:javascript
复制
class MemorySystem:
    def __init__(self, redis_client, db_session, vector_store):
        self.redis = redis_client
        self.db = db_session
        self.vector_store = vector_store
        
    def get_context(self, session_id: str, user_id: str) -> MemoryContext:
        # L1:会话记忆(最近20轮)
        session_key = f"session:{session_id}"
        recent_history = self.redis.lrange(session_key, -20, -1)
        
        # L2:用户记忆
        user_profile = self.db.query(UserProfile).filter_by(user_id=user_id).first()
        
        # L3:场景记忆(只检索与当前问题相关的)
        current_query = recent_history[-1]['content'] if recent_history else ""
        similar_experiences = self.vector_store.search(
            query=current_query,
            top_k=3,
            filter={"user_id": user_id}
        )
        
        # 压缩:确保总 Token 数不超标
        context = self.compress([
            *recent_history[-5:],  # 最近5轮必须保留
            self.summarize(user_profile.history) if user_profile else None,
            *similar_experiences
        ], max_tokens=4000)
        
        return MemoryContext(context)
    
    def summarize(self, long_history: list) -> str:
        """用 LLM 压缩长历史"""
        if len(long_history) < 30:
            return ""
        
        # 调用 LLM 生成摘要
        summary = self.llm.invoke(f"请总结以下对话的关键信息:{long_history}")
        return f"[历史摘要] {summary}"

4.2 记忆的"遗忘"策略

不要让记忆无限膨胀。设计明确的遗忘策略:

代码语言:javascript
复制
class MemoryManager:
    def forget_strategy(self, memory_type: str, content: dict) -> bool:
        """决定是否遗忘某条记忆"""
        if memory_type == "session":
            # 会话记忆:30分钟无活动后过期(由Redis TTL保证)
            return True  # Redis 自动处理
        
        if memory_type == "user_preference":
            # 用户偏好:超过30天未使用则遗忘
            last_used = content.get('last_used')
            if last_used and (datetime.now() - last_used).days > 30:
                return True
        
        if memory_type == "task_completion":
            # 任务完成记录:保留3个月用于分析
            completed_at = content.get('completed_at')
            if completed_at and (datetime.now() - completed_at).days > 90:
                return True
        
        return False

第五部分:可观测性——Agent 的"黑匣子"

5.1 全链路追踪(必选)

生产 Agent 必须记录每一次决策的完整链路:

代码语言:javascript
复制
@Slf4j
@Component
public class AgentTracer {
    
    private static final String TRACE_ID_HEADER = "X-Trace-Id";
    
    public TracingContext startTrace(String sessionId, String userInput) {
        String traceId = UUID.randomUUID().toString();
        TracingContext ctx = TracingContext.builder()
            .traceId(traceId)
            .sessionId(sessionId)
            .startTime(Instant.now())
            .userInput(userInput)
            .build();
        
        // 结构化日志
        log.info("AGENT_TRACE_START | traceId={} | sessionId={} | input={}",
            traceId, sessionId, truncate(userInput, 100));
        
        return ctx;
    }
    
    public void logDecision(TracingContext ctx, String step, String decision, 
                            Map<String, Object> metadata) {
        log.info("AGENT_TRACE_STEP | traceId={} | step={} | decision={} | meta={}",
            ctx.getTraceId(), step, decision, toJson(metadata));
        
        // 同时写入专门的追踪存储(如Elasticsearch)
        traceRepository.save(TraceEntry.builder()
            .traceId(ctx.getTraceId())
            .timestamp(Instant.now())
            .step(step)
            .decision(decision)
            .metadata(metadata)
            .build()
        );
    }
    
    public void logToolCall(TracingContext ctx, String toolName, 
                            Map<String, Object> params, ToolResult result) {
        log.info("AGENT_TOOL_CALL | traceId={} | tool={} | params={} | result={} | latency={}ms",
            ctx.getTraceId(), toolName, toJson(params), 
            result.isSuccess() ? "success" : "fail",
            result.getLatency());
        
        // 审计日志(合规要求,长期存储)
        auditLog.toolCall(ctx.getTraceId(), toolName, params, result);
    }
}

5.2 关键监控指标

构建专用的 Agent 监控仪表盘:

代码语言:javascript
复制
# Prometheus 指标定义
metrics:
  - name: agent_requests_total
    labels: [scenario, model, outcome]
    type: counter
    
  - name: agent_latency_seconds
    labels: [scenario, phase]  # plan/execute/verify
    type: histogram
    buckets: [0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
    
  - name: agent_tool_calls_total
    labels: [tool_name, success]
    type: counter
    
  - name: agent_plan_steps_count
    labels: [scenario]
    type: histogram
    buckets: [1, 2, 3, 5, 8]
    
  - name: agent_budget_exceeded_total
    labels: [reason]  # token/time/step
    type: counter
    
  - name: agent_human_escalation_total
    labels: [reason]  # confidence/verification/user_request
    type: counter

5.3 可复现的调试环境

生产问题最难的就是复现。建立"回放"机制:

代码语言:javascript
复制
class AgentReplay:
    def __init__(self, trace_repository):
        self.trace_repo = trace_repository
        
    def replay_trace(self, trace_id: str, stop_at_step: int = None):
        """
        重放某次完整执行过程,用于调试
        """
        trace = self.trace_repo.get(trace_id)
        
        # 使用 Mock 工具替换真实工具(避免副作用)
        with self.use_mock_tools():
            # 从相同的输入开始
            agent = Agent()
            for step in trace.steps[:stop_at_step]:
                # 在每个决策点打印当前状态
                print(f"Step {step.index}: {step.decision}")
                print(f"  State: {step.state}")
                print(f"  LLM Input: {step.prompt}")
                print(f"  LLM Output: {step.response}")
                print("-" * 50)
            
            # 从断点继续执行
            if stop_at_step:
                remaining = trace.steps[stop_at_step:]
                agent.continue_from(remaining)

第六部分:成本与性能优化

6.1 Token 预算控制

代码语言:javascript
复制
class TokenBudgetController:
    def __init__(self, limits: dict):
        self.max_per_request = limits.get('per_request', 8000)  # 单次请求
        self.max_per_session = limits.get('per_session', 50000) # 单次会话
        self.warning_threshold = 0.8
        
    def check_budget(self, session_id: str, current_tokens: int):
        # 1. 获取会话已消耗
        session_usage = redis.get(f"token_usage:{session_id}") or 0
        
        # 2. 检查是否超标
        if current_tokens > self.max_per_request:
            raise TokenBudgetExceeded(f"单次请求Token超限: {current_tokens}")
        
        if session_usage + current_tokens > self.max_per_session:
            raise TokenBudgetExceeded(f"会话Token超限,已用 {session_usage}")
        
        # 3. 预警
        if session_usage / self.max_per_session > self.warning_threshold:
            logger.warning(f"会话 {session_id} Token使用已达 {session_usage/self.max_per_session:.0%}")

6.2 性能优化清单

问题

原因

优化方案

首次响应慢

加载所有工具定义(含Prompt)

按场景懒加载,预热

推理延迟高

模型输入太长

语义压缩,滑动窗口

工具调用串行

工具间无依赖关系

分析依赖图,并行执行

重复计算

同一工具多次调用

结果缓存(带TTL)


第七部分:生产就绪检查清单

在上线前,逐项核对以下内容:

🔐 安全与治理

  • □ 所有工具调用都有权限校验
  • □ 敏感数据(PII)在日志和存储中已脱敏
  • □ 高风险操作(写/删)需二次确认或人工审批
  • □ 有完整的审计日志,至少保留 180 天
  • □ 输入输出都有内容安全过滤(注入攻击、有害内容)

📊 可观测性

  • □ 每次请求有唯一 Trace ID,全链路可追溯
  • □ 关键指标已接入 Prometheus/Grafana
  • □ 已配置告警规则(错误率、延迟、成本)
  • □ 有结构化日志,支持快速检索
  • □ 有"回放"能力,便于生产问题复现

🔄 容错与降级

  • □ 每个工具调用都有超时设置
  • □ 关键工具有备选/降级方案
  • □ 失败时有明确的错误码和用户提示
  • □ 有"kill switch"能紧急关闭 Agent 功能
  • □ 会话超时后能自动清理

⚡ 性能与成本

  • □ 单次请求的 Token 上限已配置
  • □ 会话成本预算已设置
  • □ 高频查询已启用缓存
  • □ 已进行过压力测试,满足 QPS 要求
  • □ 预算告警已配置(如月成本超过预测 120%)

🧪 测试与验证

  • □ Golden Set 评估准确率 > 85%
  • □ 边界案例(输入过长、恶意输入)已测试
  • □ 有 A/B 测试方案,能灰度验证新版本
  • □ 用户反馈收集机制已就绪

结语:从"炫技"到"可靠"

可投产的 Agent 系统,本质上是一套"用 AI 赋能的高可靠分布式系统"。它的核心不是 LLM 的能力上限,而是:

  1. 计划的确定性:不依赖 LLM 控制流程,用代码控制流程
  2. 执行的可靠性:超时、重试、熔断、降级一个都不能少
  3. 结果的可验证性:每个输出都要经过检查和校验
  4. 行为的可观测性:每一笔决策都有迹可循
  5. 成本的可知性:预算控制不是事后统计,而是事中拦截

当你的 Agent 系统能让业务方说出:"虽然它不是每次都完美,但我知道它什么时候会出错,而且出错的影响是可控的"——你就真正走出了 Demo 陷阱。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 规避 Demo 陷阱,构建可投产的 Agent 系统
    • 前言:Demo 与生产之间,隔着一道 "确定性" 的鸿沟
    • 第一部分:重新定义 Agent——去掉"魔法",增加"工程"
      • 1.1 生产级 Agent 的五个核心特征
      • 1.2 Agent 成熟度模型
    • 第二部分:可投产 Agent 架构设计
      • 2.1 生产级 Agent 整体架构
      • 2.2 核心设计模式:Plan-Execute-Verify(PEV)
      • 2.3 计划生成器(Planner)的设计要点
    • 第三部分:工具系统的工业级设计
      • 3.1 工具注册中心(可发现、可校验)
      • 3.2 工具执行的"三板斧":超时、重试、熔断
      • 3.3 工具沙箱(安全隔离)
    • 第四部分:记忆系统的工程化
      • 4.1 三层记忆架构
      • 4.2 记忆的"遗忘"策略
    • 第五部分:可观测性——Agent 的"黑匣子"
      • 5.1 全链路追踪(必选)
      • 5.2 关键监控指标
      • 5.3 可复现的调试环境
    • 第六部分:成本与性能优化
      • 6.1 Token 预算控制
      • 6.2 性能优化清单
    • 第七部分:生产就绪检查清单
      • 在上线前,逐项核对以下内容:
    • 结语:从"炫技"到"可靠"
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档