实战篇:AI Agent 后端实现细节

本节补充第四章中后端相关的实现细节,包括多模型适配、RAG 管线、生成 Pipeline、以及 JSON 容错处理。


4.1 多模型提供商的统一接入

Agent 开发中,你几乎不可能只用一个模型。不同模型擅长不同的事,而且模型厂商的定价、速率限制、区域可用性都在变。设计一个可插拔的多模型架构很有必要。

Provider 注册表模式

用 Map 做一个模型到 Provider 类的映射:

typescript 复制代码
// ai.service.ts
private providers = new Map<string, new (config: AIProviderConfig) => AIProvider>([
  ['gpt-4o',                     OpenAIProvider],
  ['claude-sonnet-4-5-20250929', ClaudeProvider],
  ['doubao-seed-2-0-pro',        DoubaoProvider],
  ['gemini-2.0-flash',           GeminiProvider],
]);

// 根据模型名获取 Provider 实例
getProvider(modelId: string): AIProvider {
  const ProviderClass = this.providers.get(modelId);
  if (!ProviderClass) throw new Error(`Unsupported model: ${modelId}`);

  const config = this.getConfigForModel(modelId);
  return new ProviderClass(config);
}

动态 API Key 路由

不同前缀的模型走不同的 API Key 和 endpoint:

typescript 复制代码
private getConfigForModel(modelId: string): AIProviderConfig {
  if (modelId.startsWith('gpt'))     return { apiKey: OPENAI_KEY, baseUrl: 'https://api.openai.com/v1' };
  if (modelId.startsWith('claude'))  return { apiKey: CLAUDE_KEY, baseUrl: 'https://api.anthropic.com' };
  if (modelId.startsWith('doubao'))  return { apiKey: DOUBAO_KEY, baseUrl: 'https://ark.cn-beijing.volces.com/api/v3' };
  if (modelId.startsWith('gemini'))  return { apiKey: GEMINI_KEY, baseUrl: 'https://generativelanguage.googleapis.com' };
}

经验教训: 国内调用 OpenAI 需要走代理,baseUrl 支持自定义很重要。Doubao 的 API 和 OpenAI 兼容,可以复用 OpenAI 的 SDK,但要注意 endpoint 不同。


4.2 生成 Pipeline:分步执行的设计

一个复杂的 Agent 任务(比如根据需求文档生成测试用例),不适合一次性扔给模型。更好的做法是拆成多个步骤,形成 Pipeline:

markdown 复制代码
需求文档
   ↓
[Step 1] RAG 上下文构建
   - 搜索相似需求
   - 搜索历史用例
   - 加载知识库
   ↓
[Step 2] 场景分解
   - 把大需求拆成 5-10 个测试场景
   - 每个场景有名称 + 关键测试点
   ↓
[Step 3] 批量生成
   - 每个场景独立调用模型
   - 并发度控制(建议 2 并发)
   - 实时进度推送(WebSocket)
   ↓
[Step 4] 结果合并 & 落库

Step 2 场景分解的关键代码

typescript 复制代码
async function decomposeScenarios(context: GenerationContext) {
  const prompt = buildDecompositionPrompt(context);

  // 调用模型拆分场景,至少返回 5 个
  let scenarios = await aiService.decomposeScenarios(prompt);

  // 不够 3 个就重试一次
  if (scenarios.length < 3) {
    scenarios = await aiService.decomposeScenarios(prompt);
  }

  // 实在不行,降级为单场景
  if (scenarios.length === 0) {
    scenarios = [{ name: '全功能测试', testPoints: ['覆盖所有功能点'] }];
  }

  return scenarios;
}

Step 3 批量生成的并发控制

typescript 复制代码
const CONCURRENCY = 2;  // 同时最多 2 个模型调用

async function batchGenerate(scenarios: TestScenario[], context: GenerationContext) {
  const results = [];
  const queue = [...scenarios];

  // 简单的并发池
  async function worker() {
    while (queue.length > 0) {
      const scenario = queue.shift();
      const result = await generateForScenario(scenario, context);
      results.push(result);
      // 推送进度
      emitProgress(results.length / scenarios.length * 100);
    }
  }

  // 启动 N 个 worker
  await Promise.all(
    Array.from({ length: CONCURRENCY }, () => worker())
  );

  return results;
}

为什么不全部并发? 大模型 API 有速率限制,并发太高会被 429 限流。2-3 并发是安全的甜点。


4.3 JSON 输出的防御性解析

这是 Agent 开发中最容易被低估的问题。大模型输出的 JSON 经常有各种格式问题:

常见的 JSON 出错类型

问题 示例 出现频率
被 Markdown 包裹 json\n[...]\n 非常高
中文引号 "标题" 而不是 "标题" 高(中文 prompt)
尾随逗号 [{...}, {...},] 中等
截断(token 用完) [{...}, { 偶发但致命
转义错误 字符串内未转义的 " 中等

多层容错策略

typescript 复制代码
function parseModelOutput(raw: string): any[] {
  // 第 1 层:去掉 Markdown 代码块
  let cleaned = raw.replace(/```(?:json)?\n?/g, '').replace(/```/g, '').trim();

  // 第 2 层:提取 JSON 数组
  const bracketMatch = cleaned.match(/\[[\s\S]*\]/);
  if (bracketMatch) cleaned = bracketMatch[0];

  // 第 3 层:修复中文引号
  cleaned = cleaned
    .replace(/\u201c/g, '"')  // "
    .replace(/\u201d/g, '"'); // "

  // 第 4 层:移除尾随逗号
  cleaned = cleaned.replace(/,(\s*[}\]])/g, '$1');

  // 第 5 层:尝试解析
  try {
    return JSON.parse(cleaned);
  } catch (e) {
    // 第 6 层:逐对象恢复(处理截断)
    return recoverByObjects(cleaned);
  }
}

// 处理截断的 JSON ------ 尽量恢复已完成的对象
function recoverByObjects(text: string): any[] {
  const results = [];
  const objectPattern = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
  let match;

  while ((match = objectPattern.exec(text)) !== null) {
    try {
      results.push(JSON.parse(match[0]));
    } catch {
      // 跳过无法解析的片段
    }
  }

  return results;
}

真实数据: 在我们的生产环境中,大约 15% 的模型输出需要经过第 2 层以上的修复。用了 Tool Use(第三章 3.4 节)之后,这个比例降到了不到 2%。


4.4 两阶段深度分析(进阶)

对于复杂需求,单次模型调用可能分析得不够深入。一个有效的策略是两阶段调用

markdown 复制代码
阶段 1:深度思考模型(慢但深)
   - 用 Seed 2.0 Pro(思考模型)做需求深度分析
   - 输出自然语言的分析报告
        ↓
阶段 2:快速执行模型(快但准)
   - 把阶段 1 的分析报告 + 原始需求一起给执行模型
   - 用 Seed 1.5 Pro(快速模型)做场景分解
typescript 复制代码
async function twoStageDecompose(context: GenerationContext) {
  // 阶段 1:深度分析(思考模型)
  const analysisPrompt = '你是资深测试架构师,请对需求进行深度分析...';
  const analysis = await callThinkingModel(analysisPrompt, context);

  // 阶段 2:把分析结果注入上下文
  const enrichedContext = {
    ...context,
    requirements: [
      ...context.requirements,
      `\n---\n## 专家深度分析结果\n\n${analysis}`
    ]
  };

  // 用快速模型做场景分解
  return await decomposeScenarios(enrichedContext);
}

什么时候用两阶段? 需求文档超过 2000 字、涉及多个功能模块、或业务逻辑复杂时。简单需求直接一阶段就够了。


4.5 模型路由策略

不同的步骤适合不同的模型:

typescript 复制代码
// model-router.ts
function getModelForStep(step: string, userSelectedModel: string): string {
  // 思考类模型(Seed 2.0)用于分析,但生成用快速模型
  if (isSeed2Model(userSelectedModel)) {
    switch (step) {
      case 'analysis':    return userSelectedModel;          // 深度分析用思考模型
      case 'decompose':   return FAST_GENERATION_MODEL;      // 场景分解用快速模型
      case 'generate':    return FAST_GENERATION_MODEL;      // 用例生成用快速模型
    }
  }

  // 其他模型全程使用同一个
  return userSelectedModel;
}

为什么要路由? 思考模型(如 Seed 2.0、o1)很擅长分析但生成速度慢、成本高。把"想"和"做"分开,既保证质量又控制成本。


本节小结

实践 核心收获
多模型架构 Map 注册表 + 动态 Key 路由,加新模型只需一行
Pipeline 模式 拆步骤 > 一次性调用,每步可独立优化和重试
JSON 容错 至少 5 层防御,Tool Use 能大幅减少出错率
两阶段分析 "想"和"做"分开,深度模型分析 + 快速模型执行
模型路由 不同步骤用不同模型,平衡质量和成本
相关推荐
量子位3 小时前
卡帕西开源Agent自进化训练框架,5分钟一轮实验,48h内揽星9.5k
aigc·agent
DigitalOcean4 小时前
如何在云端运行Kimi K2.5:从配置到部署全攻略
llm·aigc
架构技术专栏4 小时前
OpenClaw 个人 AI 助手本地部署与配置权威指南
aigc·openai·ai编程
yes的练级攻略4 小时前
装了 OpenClaw 后,信用卡被盗刷了...
aigc·ai编程
沸点小助手6 小时前
「AI 编程搭子真香or翻车」沸点获奖名单公示|本周互动话题上新🎊
aigc·agent·ai编程
AI攻城狮7 小时前
OpenFang 给我的一个提醒:AI Agent 真正难的不是自主,而是治理
人工智能·云原生·aigc
树獭叔叔9 小时前
OpenClaw Tools 与 Skills 系统深度解析
后端·aigc·openai
树獭叔叔9 小时前
OpenClaw Memory 系统深度解析:从文件到向量的完整实现
后端·aigc·openai
荼锦10 小时前
Agentic 设计模式详解 - 提示词链 (Prompt Chaining)
aigc·agent·ai编程