从零到一:AI Agent 开发入门完全指南

12 阅读11分钟

本文基于一个真实的 AI 测试用例生成平台的开发经验,帮助"听过 AI 但没写过 Agent"的开发者快速入门。


一、Agent 到底是什么?

1.1 一句话定义

Agent = LLM + 工具 + 自主决策循环

普通的 AI API 调用是"一问一答"——你给一段 prompt,它返回一段文本。而 Agent 是一个能自主规划、调用工具、根据结果继续推理的程序。

打个比方:

  • API 调用 像一个只会接电话回答问题的客服
  • Agent 像一个能自己查系统、填工单、打电话协调的项目经理

1.2 Agent 的核心能力

用户需求
   ↓
┌──────────────────┐
│   LLM(大脑)      │ ← 理解意图、规划步骤
│                    │
│  ┌──────────────┐ │
│  │  工具调用      │ │ ← 搜索数据库、调用 API、读写文件
│  └──────────────┘ │
│  ┌──────────────┐ │
│  │  记忆/上下文   │ │ ← 记住历史对话、参考过往经验
│  └──────────────┘ │
│  ┌──────────────┐ │
│  │  自主循环      │ │ ← 检查结果、决定下一步、自我纠错
│  └──────────────┘ │
└──────────────────┘
   ↓
输出结果

1.3 2026 年,为什么现在要学?

三个趋势让 Agent 开发从"研究课题"变成了"工程实践":

  1. 模型能力跃升:Claude 4.5/4.6、GPT-5 系列的推理能力已经足够支撑复杂的多步任务
  2. 工具生态成熟:MCP(Model Context Protocol)让模型连接外部工具有了统一标准
  3. 开发工具链完善:Claude Code、Cursor 等 AI-native 开发工具让开发 Agent 本身也变得高效

一个真实案例:我们用 Agent 技术构建了一个测试用例自动生成平台。输入产品需求文档,它会:

  1. 自动理解需求内容
  2. 从历史数据库中检索相似需求和测试用例(RAG)
  3. 拆解为多个测试场景
  4. 为每个场景批量生成测试用例
  5. 自动进行质量审查

原来一个测试工程师需要 2-3 天完成的工作,现在 5 分钟就能生成初稿。

1.4 Agent 的典型应用场景

场景传统方式Agent 方式
测试用例编写人工逐条编写需求文档 → 自动生成 + 人工审查
代码审查人工逐行检查Agent 自动扫描 + 定位问题 + 建议修复
客服系统关键词匹配理解意图 → 查知识库 → 组织回答
数据分析手动写 SQL自然语言描述 → 生成 SQL → 执行 → 可视化

二、技术栈选型:做减法

入门阶段最大的坑是"技术选型焦虑"。这一章帮你做减法:先跑通,再优化。

2.1 LLM API —— 别纠结,先跑通一个

入门推荐:选一个能用的就行。

模型优势适合场景接入难度
Claude 4.5 Sonnet推理强、中文好复杂逻辑、代码生成中(需代理或 API Key)
GPT-4o生态最大、教程最多通用场景
Doubao 2.0 Pro国内直连、性价比高中文场景、合规要求

关键建议:从第一天就做好多模型适配。不是说一开始要接多个模型,而是代码架构要支持切换。

我们项目中的做法——定义统一的 Provider 接口:

// 所有 AI 模型都实现这个接口
interface AIProvider {
  name: string;

  // 核心能力:给定上下文,生成结果
  generateTestCases(
    context: GenerationContext,
    onProgress?: (progress: number) => void,
  ): Promise<GeneratedTestCase[]>;

  // 通用能力:解析内容
  parseContent(systemPrompt: string, content: string): Promise<string>;
}

然后每个模型实现自己的 Provider:

// Claude Provider
class ClaudeProvider extends BaseAIProvider {
  name = 'claude';
  // ... 具体实现
}

// Doubao Provider
class DoubaoProvider extends BaseAIProvider {
  name = 'doubao';
  // ... 具体实现
}

这样切换模型只需要改一行配置,不需要改业务代码。

2.2 向量数据库 —— pgvector 一把梭

入门推荐:pgvector(PostgreSQL 扩展)

很多教程会让你单独部署 Milvus、Pinecone、Qdrant 等专用向量数据库。入门阶段完全不需要。

理由:

  • 你的业务数据(用户、订单、需求)本来就在 PostgreSQL 里
  • pgvector 直接在 PostgreSQL 中添加向量列,不需要额外维护一个数据库
  • 对于 10 万条以下的数据量,pgvector 的性能完全够用
-- 就这么简单:给现有表加一个向量列
ALTER TABLE "Requirement" ADD COLUMN embedding vector(1024);

-- 创建索引加速检索
CREATE INDEX ON "Requirement"
  USING ivfflat (embedding vector_cosine_ops)
  WITH (lists = 100);

在 Prisma(Node.js ORM)中使用:

// 语义搜索:找到与查询最相似的需求
const results = await prisma.$queryRawUnsafe(
  `SELECT id, title, 1 - (embedding <=> $1::vector) as similarity
   FROM "Requirement"
   WHERE embedding IS NOT NULL
     AND 1 - (embedding <=> $1::vector) >= $2
   ORDER BY embedding <=> $1::vector
   LIMIT $3`,
  vectorStr,    // 查询文本的向量
  0.65,         // 相似度阈值
  10,           // 返回数量
);

什么时候该换专用向量库?

  • 数据量超过 100 万条
  • 需要复杂的过滤 + 向量检索组合
  • 需要分布式部署

2.3 Agent 框架 —— LangChain 太重?先用原生 SDK

入门推荐:直接用模型 SDK,不用框架。

LangChain/LangGraph 是很好的工具,但对入门者来说:

  • 概念太多(Chain、Agent、Tool、Memory、Callback...)
  • 抽象层太厚,出了问题很难调试
  • 学框架的时间可能比学 Agent 本身还长

更好的路径:先用原生 SDK 理解原理,遇到痛点再引入框架。

// 原生 SDK 调用就够了
const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': apiKey,
    'anthropic-version': '2023-06-01',
  },
  body: JSON.stringify({
    model: 'claude-sonnet-4-5-20250929',
    max_tokens: 4096,
    system: systemPrompt,
    messages: [{ role: 'user', content: userMessage }],
  }),
});

我们的项目到现在都没用 LangChain,全部是原生 SDK + 自定义 Pipeline。反而更灵活:

Pipeline 流程:
analyzeReqs → decompose → batchGenerate → validate → aggregate
(分析需求)   (拆解场景)  (批量生成)    (验证格式)  (汇总结果)

2.4 最小技术栈总结

┌─────────────────────────────────────┐
         你的 Agent 应用              
├─────────────────────────────────────┤
  后端: NestJS / Express / FastAPI   
  前端: Vue / React / 随便           
  数据库: PostgreSQL + pgvector      
  AI: 任选一个模型 SDK               
  部署: Docker                       
└─────────────────────────────────────┘

一句话:PostgreSQL 一把梭,模型 SDK 直连,框架以后再说。


三、核心概念扫盲

这一章用通俗语言解释 Agent 开发中必须理解的 4 个核心概念。

3.1 Embedding —— 把文字变成数字

一句话解释: Embedding 是把一段文字转换成一组数字(向量),使得意思相近的文字对应的数字也相近。

为什么需要?

数据库不理解语义。如果用户搜索"扫码跳转小程序",传统的关键词搜索:

  • ✅ 能找到标题包含"扫码"的文档
  • ❌ 找不到标题是"二维码识别与小程序唤起"的文档(意思一样,但用词不同)

Embedding 的做法:

  1. 把"扫码跳转小程序"转成向量 [0.12, -0.34, 0.56, ...]
  2. 把数据库中所有文档都转成向量
  3. 计算向量之间的距离(余弦相似度)
  4. 距离最近的就是最相关的
// 调用 Embedding API 把文字变成向量
async generateEmbedding(text: string): Promise<number[]> {
  const response = await fetch(embeddingEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${apiKey}`,
    },
    body: JSON.stringify({
      model: 'doubao-embedding-vision-250615',
      input: [{ type: 'text', text: text }],
      dimensions: 1024,
    }),
  });

  const data = await response.json();
  return data.data.embedding;  // [0.12, -0.34, 0.56, ...] 1024维向量
}

常见坑:文本长度限制

Embedding 模型有 token 上限(通常 8000 字符左右)。超过长度怎么办?

// 最简单的处理:截断
const MAX_TEXT_LENGTH = 8000;
const truncatedText = text.length > MAX_TEXT_LENGTH
  ? text.substring(0, MAX_TEXT_LENGTH)
  : text;

更好的方案是分块(chunking):把长文档拆成多个小块,分别生成 embedding。但入门阶段截断就够用。

3.2 RAG —— 让 AI 有记忆、有依据

RAG = Retrieval-Augmented Generation(检索增强生成)

一句话解释: 先从数据库里搜到相关资料,再把资料和用户问题一起喂给 AI,让 AI 基于真实数据回答。

为什么需要?

LLM 有两个致命问题:

  1. 知识过时:训练数据有截止日期,不知道你公司内部的文档
  2. 幻觉:不知道的东西会编造

RAG 的解决思路:

用户输入: "扫码跳转小程序的测试用例"Step 1: 检索(Retrieval)
   → 用 Embedding 在数据库中搜索相似的历史需求和测试用例
   → 找到: "小程序二维码唤起功能需求"(相似度 92%)
   → 找到: "微信扫码跳转测试用例"(相似度 87%)
         ↓
Step 2: 增强(Augmented)
   → 把检索结果塞进 prompt:
   "参考以下历史资料,生成新的测试用例:
    [历史需求1]...
    [历史用例1]..."Step 3: 生成(Generation)
   → AI 基于真实资料生成测试用例,而不是凭空编造

我们项目中的 RAG 实现:

// 1. 检索相似需求(语义搜索)
const similarRequirements = await embeddingService.searchSimilar(
  'Requirement',           // 在需求表中搜索
  currentRequirement.text, // 当前需求文本
  { limit: 5, threshold: 0.65, projectId }
);

// 2. 检索相似测试用例(few-shot 示例)
const historicalTestCases = await embeddingService.searchSimilar(
  'TestCase',
  currentRequirement.text,
  { limit: 3, threshold: 0.7, projectId }
);

// 3. 把检索结果注入到生成上下文中
const context: GenerationContext = {
  projectName: '我的项目',
  requirements: [currentRequirement.text],
  similarRequirements,    // RAG: 相似需求,帮助理解业务背景
  historicalTestCases,    // RAG: 历史用例,作为 few-shot 示例
};

// 4. AI 基于上下文生成
const testCases = await aiService.generateTestCases(model, context);

RAG 的关键参数:

参数含义建议值
threshold相似度阈值,低于此值不返回0.60 ~ 0.75
limit最多返回几条3 ~ 10
dimensions向量维度1024(平衡精度和性能)

3.3 Prompt Engineering —— 和 AI 说人话的艺术

一句话解释: 设计好的指令(Prompt),让 AI 准确理解你要什么、按什么格式输出。

三个实战技巧:

技巧 1:结构化 Prompt

不要写一大段自然语言,用 Markdown 结构组织:

const prompt = `你是一个专业的测试用例生成专家。

项目名称: ${projectName}

## 🎯 生成规则
${rulePrompt}

## 📋 需求信息
<requirements>
${requirementText}
</requirements>

## 📚 相似需求参考(RAG 检索)
${similarRequirements}

## 📋 历史用例参考(few-shot 示例)
${historicalTestCases}

## 输出要求
⚠️ 严格要求:只输出 JSON 数组,不要任何其他文字!
直接以 [ 开头,以 ] 结尾。
`;

技巧 2:XML 标签防注入

用 XML 标签包裹用户输入,防止用户内容被模型当作指令:

// 用 <requirements> 标签包裹,AI 会把里面的内容当作"数据"而不是"指令"
prompt += `<requirements>
${userProvidedRequirement}
</requirements>`;

技巧 3:输出约束要反复强调

AI 模型有"话痨"倾向,经常在 JSON 前后加解释文字。解决办法:

prompt += `## 输出要求
⚠️ 严格要求:只输出 JSON 数组,不要任何其他文字!
- 不要写"好的"、"以下是"等开场白
- 不要写任何解释、总结或补充说明
- 直接以 [ 开头,以 ] 结尾`;

3.4 Tool Use —— 让 AI 调用你的函数

一句话解释: 告诉 AI "你有哪些工具可以用",AI 会在需要时主动调用。

这是 Agent 区别于普通 AI 调用的核心能力。以 Claude 为例:

const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  max_tokens: 1024,
  tools: [
    {
      name: 'search_requirements',
      description: '搜索历史需求文档',
      input_schema: {
        type: 'object',
        properties: {
          query: { type: 'string', description: '搜索关键词' },
          limit: { type: 'number', description: '返回数量' },
        },
        required: ['query'],
      },
    },
  ],
  messages: [
    { role: 'user', content: '帮我找一下和扫码功能相关的历史需求' }
  ],
});

// AI 会返回 tool_use 类型的响应,告诉你它想调用哪个工具
// { type: 'tool_use', name: 'search_requirements', input: { query: '扫码功能' } }

MCP(Model Context Protocol) 是 2025 年 Anthropic 推出的工具连接标准,让任何 AI 模型都能用统一的方式连接外部工具。你可以把它理解为"AI 的 USB 接口"。


四、第一个 Agent 项目实战

以"测试用例自动生成"为例,从零搭建一个完整的 Agent 应用。

4.1 项目架构

我们采用 Monorepo 架构,前后端在一个仓库里管理:

ai-test-platform/
├── apps/
│   ├── server/           # NestJS 后端
│   │   ├── src/modules/
│   │   │   ├── ai/       # AI 模型层(多 Provider)
│   │   │   ├── embedding/ # 向量检索层
│   │   │   ├── generation/# 生成 Pipeline
│   │   │   └── ...
│   │   └── prisma/
│   │       └── schema.prisma  # 数据库定义
│   └── web/              # Vue 3 前端
│       └── src/views/
├── packages/
│   └── types/            # 共享类型定义
├── pnpm-workspace.yaml
└── turbo.json

为什么用 Monorepo?

  • 前后端共享类型定义,不用手动同步
  • 一个命令启动所有服务
  • 统一的代码审查和版本管理

4.2 核心流程:Pipeline 模式

我们没有用 LangChain,而是自己设计了一个简单的 Pipeline:

┌──────────┐    ┌──────────┐    ┌──────────────┐    ┌──────────┐    ┌──────────┐
│ 分析需求  │ →  │ 拆解场景  │ →  │ 批量生成用例  │ →  │ 验证格式  │ →  │ 汇总结果  │
│ analyzeReqs│   │decompose │   │batchGenerate │   │ validate │   │aggregate │
└──────────┘    └──────────┘    └──────────────┘    └──────────┘    └──────────┘
     5%              8%              12~88%              88%            95%
// Pipeline 定义:每个步骤是一个纯函数
const PIPELINE_STEPS = [
  { name: 'analyzeReqs',   step: analyzeReqsStep,   progress: 5 },
  { name: 'decompose',     step: decomposeStep,      progress: 8 },
  { name: 'batchGenerate', step: batchGenerateStep,   progress: 12 },
  { name: 'validateFmt',   step: validateStep,        progress: 88 },
  { name: 'aggregate',     step: aggregateStep,       progress: 95 },
];

// 执行器:依次运行每个步骤
async function runPipeline(state, ctx) {
  for (const { name, step, progress } of PIPELINE_STEPS) {
    // 检查任务是否被用户取消
    const task = await ctx.prisma.generationTask.findUnique({
      where: { id: state.taskId },
    });
    if (task?.status === 'cancelled') return state;

    // 更新进度
    await ctx.updateStatus(state.taskId, 'generating', progress);

    // 执行步骤
    state = await step(state, ctx);
  }
  return state;
}

为什么用 Pipeline 而不是简单的一次调用?

  1. 拆分复杂度:一次让 AI 生成 100 条用例,质量很差;拆成 10 个场景各生成 10 条,质量好很多
  2. 可中断恢复:每个步骤之间可以保存状态,用户可以随时取消
  3. 进度可见:每个步骤更新进度,用户能看到进展
  4. 独立调试:某个步骤出错了,可以单独调试和重试

4.3 处理 AI 输出的"脏数据"

这是实战中最花时间的部分。 AI 返回的 JSON 经常有问题:

// AI 经常犯的错误:
// 1. 在 JSON 前加"好的,以下是测试用例:"
// 2. 用中文引号 "" 代替英文引号 ""
// 3. JSON 末尾多一个逗号
// 4. 输出被截断(token 限制)

// 我们的处理策略:多层容错
function parseResponse(response: string): TestCase[] {
  // Step 1: 提取 JSON 内容(忽略前后的废话)
  const firstBracket = response.indexOf('[');
  const lastBracket = response.lastIndexOf(']');
  let jsonContent = response.substring(firstBracket, lastBracket + 1);

  // Step 2: 尝试直接解析
  try { return JSON.parse(jsonContent); } catch {}

  // Step 3: 修复中文引号
  jsonContent = fixChineseQuotes(jsonContent);
  try { return JSON.parse(jsonContent); } catch {}

  // Step 4: 修复尾部逗号
  jsonContent = jsonContent.replace(/,\s*([\]}])/g, '$1');
  try { return JSON.parse(jsonContent); } catch {}

  // Step 5: 逐对象提取(处理截断的 JSON)
  const cases = [];
  const objectRegex = /\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g;
  let match;
  while ((match = objectRegex.exec(jsonContent)) !== null) {
    try {
      const obj = JSON.parse(match[0]);
      if (obj.title) cases.push(obj);
    } catch {}
  }

  return cases;
}

关键经验:永远不要相信 AI 的输出格式是完美的。 多层容错是必须的。

4.4 多模型适配的实战经验

不同模型的输出质量差异很大。我们的数据:

模型同一需求生成量覆盖度格式正确率
Claude 4.5 Sonnet60+ 条98%
Doubao 2.0 Pro25~30 条90%
GPT-4o40+ 条95%

应对策略:

  1. 统一接口:所有模型实现同一个 AIProvider 接口
  2. 配置化:模型选择、API Key 全部存数据库,运行时切换
  3. 容错增强:JSON 解析做多层 fallback,适配不同模型的输出习惯
// 模型路由:根据任务类型选择最合适的模型
class AIService {
  private providers = new Map([
    ['claude-sonnet-4-5-20250929', ClaudeProvider],
    ['doubao-seed-2-0-pro-260215', DoubaoProvider],
    ['gpt-4o', OpenAIProvider],
    ['gemini-2.0-flash', GeminiProvider],
  ]);

  async getProvider(model: string): Promise<AIProvider> {
    const ProviderClass = this.providers.get(model);
    const config = await this.getProviderConfig(model);
    return new ProviderClass(config);
  }
}

五、测试与调试

Agent 应用和传统应用的测试方法有本质区别。

5.1 Agent 的不确定性问题

传统应用:相同输入 → 相同输出(确定性) Agent 应用:相同输入 → 不同输出(非确定性)

这意味着你不能用传统的单元测试断言精确值。

5.2 Agent 测试的三个层次

层次 1:接口级测试(能跑通)

# 最基础:API 能调通,不报错
curl -X POST http://localhost:3000/api/generation/tasks \
  -H 'Content-Type: application/json' \
  -d '{"projectId": "xxx", "requirementIds": ["yyy"], "model": "claude-sonnet-4-5-20250929"}'

层次 2:输出格式测试(格式对)

// 验证 AI 输出的格式是否符合预期
function validateTestCase(tc: any): boolean {
  return (
    typeof tc.title === 'string' &&
    tc.title.length > 0 &&
    Array.isArray(tc.steps) &&
    tc.steps.length > 0 &&
    ['P0', 'P1', 'P2', 'P3'].includes(tc.priority)
  );
}

层次 3:质量评估(内容好)

用另一个 AI 来评估生成质量:

// 用思考模型审查生成的测试用例
const reviewReport = await aiService.reviewTestCases(
  'claude-sonnet-4-5-20250929',  // 审查模型
  context,                       // 原始需求
  generatedTestCases,            // 生成的用例
  scenarios,                     // 拆解的场景
);

// 审查报告包含:
// - 覆盖度分析
// - 冗余检测
// - 质量评分(1-10)
// - 补充建议

5.3 调试技巧

  1. 记录完整 Prompt:把发给 AI 的完整 prompt 保存到日志,出问题时可以复现
  2. 保存原始响应:AI 返回的原始文本要完整保存,方便排查解析问题
  3. 分步调试:Pipeline 模式让你可以只运行某一个步骤
// 日志记录关键信息
logger.log(`[生成用例] 模型: ${model}`);
logger.log(`[生成用例] Prompt 长度: ${prompt.length}`);
logger.log(`[生成用例] 响应长度: ${response.length}`);
logger.log(`[生成用例] 解析结果: ${testCases.length} 条`);
logger.log(`[性能] 总耗时: ${elapsed}s`);

六、常见坑 & 最佳实践

坑 1:Token 限制导致输出截断

现象: AI 生成到一半突然停了,JSON 不完整。

原因: 模型有 max_tokens 限制,输出超过限制会被截断。

解决:

  • 拆分任务(Pipeline 模式,每次只生成一个场景的用例)
  • 设置合理的 max_tokens
  • JSON 解析做截断容错(逐对象提取)

坑 2:中文引号破坏 JSON

现象: JSON.parse() 报错。

原因: AI 在 JSON 字符串值中使用了中文引号 "" 而不是英文引号 ""

解决: 解析前替换中文引号:

cleaned = response
  .replace(/\u201c/g, '"')   // " → "
  .replace(/\u201d/g, '"')   // " → "
  .replace(/\u2018/g, "'")   // ' → '
  .replace(/\u2019/g, "'");  // ' → '

坑 3:Embedding 长文本截断丢失关键信息

现象: 很长的需求文档,语义搜索结果不准。

原因: Embedding 模型有 token 限制(通常 ~8000 字符),超过的部分直接被截断,后半部分的信息丢失。

解决:

  • 短期:截断前做摘要,保留关键信息
  • 长期:实现分块策略(chunking),每块独立生成 embedding

坑 4:模型"话太多"

现象: 让 AI 只输出 JSON,它偏要加一段"好的,根据您的需求..."。

解决:

  • Prompt 中反复强调输出要求
  • 用正则提取 [...] 之间的内容
  • 在 prompt 末尾加"直接以 [ 开头"

坑 5:API 超时和重试

现象: AI API 偶尔超时或返回 5xx。

解决: 指数退避重试:

async fetchWithRetry(url, options, retries = 3) {
  for (let attempt = 1; attempt <= retries; attempt++) {
    try {
      const response = await fetchWithTimeout(url, options, 60000);
      if (response.status >= 500 && attempt < retries) {
        await delay(1000 * Math.pow(2, attempt - 1)); // 1s, 2s, 4s
        continue;
      }
      return response;
    } catch (error) {
      if (attempt < retries) {
        await delay(1000 * Math.pow(2, attempt - 1));
      } else {
        throw error;
      }
    }
  }
}

坑 6:依赖安装到错误位置(Monorepo)

现象: pnpm add xxx 后依赖安装了但代码中 import 报错。

原因: 在 monorepo 根目录直接 pnpm add,依赖装到了根目录而不是子项目。

解决: 永远用 --filter

pnpm --filter server add @nestjs/websockets
pnpm --filter web add naive-ui

总结

Agent 开发的核心路径:

1. 选一个模型 SDK,调通 API          ← 30 分钟
2. 搭建基础项目架构(后端 + 数据库)    ← 1 天
3. 实现 Embedding + pgvector 语义搜索  ← 1 天
4. 构建 RAG 流程(检索 → 增强 → 生成) ← 2 天
5. 设计 Pipeline + 多层容错          ← 2 天
6. 前端界面 + 进度展示               ← 2 天

总共 1~2 周,你就能拥有一个可用的 Agent 应用。

关键心法:

  • 先跑通再优化:不要在技术选型上纠结太久
  • Pipeline > 单次调用:拆分任务,分步执行
  • 永远不信任 AI 输出:多层容错是必须的
  • RAG 是性价比最高的增强方式:让 AI 基于你的数据回答

本文基于一个真实的 AI 测试平台项目的开发经验编写。 如果觉得有帮助,欢迎点赞、收藏、关注专栏。