核心概念扫盲:四个你必须搞懂的 AI 术语

11 阅读7分钟

本章目标:用最通俗的语言,讲清楚 Agent 开发中绕不开的四个核心概念。不需要你有机器学习背景,只要你写过 CRUD,就能看懂。


3.1 Embedding —— 把文字变成"坐标"

一句话解释

Embedding 就是把一段文字转换成一组数字(向量),让计算机能用数学距离来衡量两段文字的语义相似度

为什么需要它?

假设你的数据库里有 500 条历史需求,用户输入了一条新需求"扫码跳转小程序"。你想找出以前做过的类似需求作为参考。

传统关键词搜索的问题:

  • "扫码跳转小程序" vs "扫描二维码打开微信小程序" → 关键词不完全匹配,搜不到
  • "扫码跳转小程序" vs "小程序码生成" → 关键词部分匹配,但语义完全不同,搜出来是噪音

Embedding 的解法: 把两段文字都转成向量,计算向量之间的距离。语义相近的文字,向量距离就近。

"扫码跳转小程序"        → [0.12, -0.34, 0.56, ...]  (1536维向量)
"扫描二维码打开微信小程序" → [0.13, -0.33, 0.55, ...]  ← 距离很近!语义相似
"小程序码生成"          → [0.78, 0.12, -0.45, ...]   ← 距离很远,语义不同

实际怎么用?

调用大模型厂商的 Embedding API,传入文本,返回向量。以下是一个真实的调用示例:

// 调用 Embedding API 生成向量
async function generateEmbedding(text: string): Promise<number[]> {
  // 关键:文本要做截断,大部分 Embedding 模型有 token 上限
  const MAX_TEXT_LENGTH = 8000;
  const truncatedText = text.length > MAX_TEXT_LENGTH
    ? text.substring(0, MAX_TEXT_LENGTH)
    : text;

  const response = await fetch(embeddingEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`
    },
    body: JSON.stringify({
      model: 'doubao-embedding',       // 或 text-embedding-3-small (OpenAI)
      input: [truncatedText]
    })
  });

  const data = await response.json();
  return data.data[0].embedding;        // 返回 number[] 向量
}

向量存到哪? 存到 PostgreSQL 的 vector 类型字段(需要装 pgvector 扩展):

-- 启用 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;

-- 给表加向量字段
ALTER TABLE requirements ADD COLUMN embedding vector(1536);

-- 相似度查询(核心 SQL)
SELECT id, title,
       1 - (embedding <=> $1::vector) AS similarity
FROM requirements
WHERE 1 - (embedding <=> $1::vector) >= 0.65  -- 相似度阈值
ORDER BY similarity DESC
LIMIT 10;

<=> 是 pgvector 的余弦距离运算符。1 - 距离 = 相似度,值越大越相似。

入门要记住的三件事

  1. Embedding 有长度限制 —— 大部分模型支持 512~8192 tokens,超长文本要截断
  2. 不同模型的向量不互通 —— OpenAI 生成的向量不能和 Doubao 的做比较
  3. 相似度阈值需要调 —— 0.65 是个不错的起点,但要根据实际数据测试

3.2 RAG —— 让模型"开卷考试"

一句话解释

RAG(Retrieval-Augmented Generation,检索增强生成)就是在调用大模型之前,先从数据库里检索相关资料,塞进 prompt 里一起发给模型。让模型带着"参考资料"回答问题,而不是纯靠记忆。

为什么需要它?

大模型有两个天然缺陷:

  1. 知识截止 —— 训练数据有截止日期,不知道你公司的业务逻辑
  2. 幻觉 —— 不知道的事情会瞎编,而且编得很像真的

RAG 的解法很直接:你不知道没关系,我把资料给你,你照着资料回答

一个完整的 RAG 流程

用户输入 "扫码跳转小程序" 的需求
          ↓
   ① 生成用户输入的 Embedding
          ↓
   ② 在向量数据库中搜索相似内容
      ├── 找到相似需求:"微信扫码跳转"(相似度 0.82)
      ├── 找到历史用例:"扫码功能测试用例"(相似度 0.75)
      └── 找到知识库页面:"小程序跳转规范文档"
          ↓
   ③ 把检索结果 + 用户输入 组装成 Prompt
          ↓
   ④ 发给大模型生成回答

在代码里长什么样?

// 语义搜索:输入文本 → 返回相似内容
async function semanticSearch(queryText: string) {
  // 1. 把查询文本转成向量
  const queryEmbedding = await generateEmbedding(queryText);

  // 2. 搜索相似的需求
  const similarRequirements = await findSimilarRequirements(queryEmbedding, {
    threshold: 0.6,   // 相似度阈值
    limit: 5          // 最多返回5条
  });

  // 3. 搜索相似的历史测试用例
  const historicalTestCases = await findSimilarTestCases(queryEmbedding, {
    threshold: 0.65,
    limit: 3
  });

  return { similarRequirements, historicalTestCases };
}

然后把搜索结果注入到 prompt 里:

function buildPromptWithRAG(userInput: string, ragResults) {
  let prompt = `你是一个专业的测试用例生成专家。\n`;

  // 注入相似需求(标注"仅供参考",防止模型照抄)
  if (ragResults.similarRequirements.length > 0) {
    prompt += `\n## 相似需求参考(仅供理解上下文,不要直接复制)\n`;
    for (const req of ragResults.similarRequirements) {
      prompt += `- ${req.title}(相似度: ${(req.similarity * 100).toFixed(0)}%)\n`;
      prompt += `  ${req.description}\n`;
    }
  }

  // 注入历史用例(作为风格参考)
  if (ragResults.historicalTestCases.length > 0) {
    prompt += `\n## 历史测试用例参考(参考风格和粒度,不要复制内容)\n`;
    for (const tc of ragResults.historicalTestCases) {
      prompt += `- ${tc.title}\n  步骤: ${tc.steps}\n`;
    }
  }

  prompt += `\n## 当前需求\n${userInput}\n`;
  prompt += `\n请基于以上信息生成测试用例。`;

  return prompt;
}

入门要记住的三件事

  1. RAG 不是万能的 —— 检索质量直接决定生成质量,垃圾进垃圾出
  2. "仅供参考"很重要 —— 一定要在 prompt 里告诉模型参考资料是参考,不是让它照抄
  3. 先跑通,再优化 —— 一开始用最简单的"全文截断 + 余弦相似度"就够了,别一上来就搞复杂的分块策略

3.3 Prompt Engineering —— 怎么"说人话"让模型干活

一句话解释

Prompt Engineering 就是精心设计发给大模型的指令文本,让它的输出更准确、更稳定、更符合你的预期。

为什么这很重要?

同样的任务,不同的 prompt 写法,输出质量天差地别:

❌ 差的 prompt:"帮我写测试用例"
→ 模型不知道写什么功能的、什么格式的、什么粒度的用例

✅ 好的 prompt:
"你是一个专业的测试用例生成专家。
项目名称: 电商小程序
## 生成规则
- 测试用例必须包含前置条件、操作步骤、预期结果
- 优先级分为 P0/P1/P2 三个级别
- 必须覆盖正常流程、异常流程、边界条件

## 当前需求
用户扫描商品二维码,跳转到对应的小程序商品详情页

请生成测试用例,输出 JSON 格式。"

实战中的 Prompt 结构

一个生产级的 prompt 通常包含这些层次:

┌─────────────────────────────────────┐
│  ① 角色定义                          │  "你是一个专业的测试用例生成专家"
├─────────────────────────────────────┤
│  ② 规则约束(最重要,放最前面)        │  用户选择的生成规则
├─────────────────────────────────────┤
│  ③ RAG 上下文                        │  相似需求、历史用例、知识库
├─────────────────────────────────────┤
│  ④ 用户输入                          │  当前要处理的需求
├─────────────────────────────────────┤
│  ⑤ 输出格式要求                      │  JSON schema、字段说明
└─────────────────────────────────────┘

关键经验: 规则约束要放在 prompt 的前面,因为大模型对开头的指令遵从度最高。

一些实用技巧

1. 用 XML 标签包裹重要内容

<rules>
以下是用户选择的生成规则,你必须严格遵守:
1. 每个测试用例必须关联需求 ID
2. 步骤数不少于 3 步
</rules>

模型对结构化标签的遵从度明显高于纯文本。

2. 给"反面示例"

⚠️ 禁止生成以下类型的场景:
- "全量测试""综合测试"等模糊场景
- 与需求无关的通用测试项

告诉模型不要做什么,比只告诉它要做什么更有效。

3. Few-shot:给几个例子

## 参考示例(学习风格和粒度,不要复制内容)
示例用例 1:
  标题:正常扫码跳转 - 有效二维码
  步骤:1. 打开扫码功能 → 2. 扫描有效商品码 → 3. 等待页面加载
  预期:成功跳转到商品详情页,显示正确商品信息

给 2-3 个真实例子,比写 10 行规则更管用。

入门要记住的三件事

  1. Prompt 是代码,不是随便写的文案 —— 要版本管理、要测试、要迭代
  2. 位置很重要 —— 最重要的指令放开头,模型对开头最敏感
  3. 不同模型对同一个 prompt 的表现不同 —— Claude 和 Doubao 对同一个 prompt 的输出质量可能差很大,需要分别调试

3.4 Tool Use —— 让模型能"动手"

一句话解释

Tool Use(也叫 Function Calling)是让大模型不只是"说",还能"做"的能力。你预先定义好一组工具(函数),模型在需要时会主动调用这些工具来完成任务。

为什么需要它?

大模型擅长理解和生成文本,但它做不了这些事:

  • 查数据库
  • 调用 API
  • 读写文件
  • 执行计算

Tool Use 让模型可以说:"我需要调用 查询订单 这个工具,参数是 order_id=12345",然后你的代码去执行,把结果返回给模型,模型再继续推理。

工作流程

用户:"帮我查一下订单 12345 的物流状态"
          ↓
模型推理:我需要查订单信息
          ↓
模型输出:调用工具 get_order_status(order_id="12345")
          ↓
你的代码:执行 get_order_status,返回 { status: "已发货", tracking: "SF1234" }
          ↓
模型继续:订单 12345 已发货,顺丰快递单号 SF1234

在代码里怎么实现?

以 Claude 的 Tool Use 为例,你需要定义工具的 JSON Schema:

const tools = [{
  name: 'generate_test_cases',
  description: '根据需求生成结构化的测试用例',
  input_schema: {
    type: 'object',
    properties: {
      // 要求模型先分析需求,再生成用例(强制思考)
      requirements_analysis: {
        type: 'object',
        properties: {
          total_requirements: { type: 'number' },
          requirement_summaries: {
            type: 'array',
            items: {
              type: 'object',
              properties: {
                requirement_id: { type: 'string' },
                summary: { type: 'string' },
                key_scenarios: { type: 'array', items: { type: 'string' } }
              }
            }
          }
        }
      },
      // 实际的测试用例输出
      test_cases: {
        type: 'array',
        items: {
          type: 'object',
          properties: {
            title: { type: 'string' },
            requirementId: { type: 'string' },
            steps: {
              type: 'array',
              items: {
                type: 'object',
                properties: {
                  order: { type: 'number' },
                  action: { type: 'string' },
                  expectedResult: { type: 'string' }
                }
              }
            },
            priority: { type: 'string', enum: ['P0', 'P1', 'P2'] }
          }
        }
      }
    },
    required: ['requirements_analysis', 'test_cases']
  }
}];

// 调用时指定强制使用工具
const response = await anthropic.messages.create({
  model: 'claude-sonnet-4-5-20250929',
  max_tokens: 64000,
  tools: tools,
  tool_choice: { type: 'tool', name: 'generate_test_cases' },  // 强制调用
  messages: [{ role: 'user', content: prompt }]
});

关键技巧: tool_choice: { type: 'tool', name: '...' } 可以强制模型使用指定工具,确保输出是结构化 JSON,而不是自由文本。

Tool Use vs 直接让模型输出 JSON

对比项直接输出 JSONTool Use
格式稳定性经常格式错误,需要大量容错Schema 约束,格式几乎不出错
流式支持需要自己做增量 JSON 解析原生支持 input_json_delta
复杂结构嵌套深了容易出错Schema 天然支持嵌套
适用场景简单输出(一段文本)结构化数据(表格、表单、API 响应)

入门要记住的三件事

  1. Tool Use 的本质是结构化输出 —— 不一定要"调用外部工具",纯粹当结构化输出约束用也很香
  2. Schema 设计很重要 —— required 字段一定要标,不然模型可能跳过
  3. 流式 Tool Use 要处理增量 JSON —— 模型会一块一块吐出 JSON 片段,你需要拼接后再解析

本章小结

概念一句话什么时候用
Embedding文字→向量,算距离需要语义搜索、相似度匹配时
RAG检索+生成,开卷考试模型需要用你的私有数据回答时
Prompt Engineering精心设计指令每次调用模型都要用
Tool Use让模型输出结构化数据需要可靠的 JSON/表格输出时

这四个概念互相配合:用 Embedding 做检索,用 RAG 把检索结果注入 Prompt,用 Prompt Engineering 让模型理解任务,用 Tool Use 保证输出格式。这就是一个 Agent 的核心工作流。

下一章,我们会用一个真实项目,把这些概念串起来实现。