一文读懂 AI 应用常见术语
前言
RAG / Context / Prompt / Function Calling / MCP / Skill / Rules 这些术语是啥呢?
文章旨在抛砖引玉,通过 NodeJS 代码来实践,了解核心 AI 应用的各个术语概念。
1️⃣ RAG(Retrieval-Augmented Generation)
👉 让AI先查资料再回答
-
📖 解释: 模型不清楚的东西,先去“翻资料库”,再生成答案。
-
RAG = AI + 外挂知识库
而这个知识库可以是:
- 📁 本地文件(PDF / Word / Excel)
- 🌐 网络搜索(理论上 API 直接获取的不算 RAG)
- 🧾 数据库
- 🎥 视频 / 音频(转文字或视觉帧提取)
-
比如 ChatGPT 上传文件之后,再去问答该文件的信息,AI 就会知道根据你上传文件的内容来回答
代码示例
架构流程
用户提问
↓
问题向量化(nomic-embed-text)
↓
余弦相似度检索 vectorStore
↓
拼接 Prompt(问题 + 相关资料)
↓
LLM 生成回答(qwen:7b)
↓
输出答案
项目结构
这里的 data.txt 就是 “资料”
rag-demo/
├── index.js
├── data.txt
├── package.json
向量原理
向量简单说就是:把一段文字转换成一串数字(向量),这样就能用数学方法计算两段文字的"相似度"。
核心代码
问答模型采用本地的 ollama qwen:7b
还需要下载一个向量模型(就是用它换成 AI 可看懂的向量数据)
- 1
ollama pull nomic-embed-text
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
import fs from 'fs';
// ⚠️ Node 18+ 自带 fetch
const OLLAMA_URL = 'http://localhost:11434';
// 1️⃣ 读取知识库
const rawText = fs.readFileSync('./data.txt', 'utf-8');
const docs = rawText.split('\n');
// 2️⃣ 获取 embedding(Ollama)
async function getEmbedding(text) {
const res = await fetch(`${OLLAMA_URL}/api/embeddings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'nomic-embed-text',
prompt: text,
}),
});
const data = await res.json();
if (!data.embedding) {
throw new Error(`getEmbedding failed: ${JSON.stringify(data)}`);
}
return data.embedding;
}
// 3️⃣ 向量库(内存)
const vectorStore = [];
async function init() {
for (const doc of docs) {
const embedding = await getEmbedding(doc);
vectorStore.push({ text: doc, embedding });
}
}
// 4️⃣ 余弦相似度
function cosineSimilarity(a, b) {
const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
const normA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const normB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dot / (normA * normB);
}
// 5️⃣ 检索
async function retrieve(query, topK = 2) {
const queryEmbedding = await getEmbedding(query);
const scored = vectorStore.map((item) => ({
...item,
score: cosineSimilarity(queryEmbedding, item.embedding),
}));
scored.sort((a, b) => b.score - a.score);
return scored.slice(0, topK).map((item) => item.text);
}
// 6️⃣ 调用 Ollama 生成回答
async function generate(prompt) {
const res = await fetch(`${OLLAMA_URL}/api/generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'qwen2.5:7b',
prompt,
stream: false,
}),
});
const data = await res.json();
return data.response;
}
// 7️⃣ RAG问答
async function ask(question) {
const context = await retrieve(question);
const prompt = `
你是一个公司HR,请严格根据以下资料回答问题:
资料:
${context.join('\n')}
问题:
${question}
`;
const answer = await generate(prompt);
console.log('📚 命中资料:', context);
console.log('🤖 AI回答:', answer);
}
// 🚀 运行
(async () => {
await init();
console.log('===== 不使用RAG =====');
const noRag = await generate('公司年假多少天?');
console.log(noRag);
console.log('\n===== 使用RAG =====');
await ask('公司年假多少天?');
})();
ata.response;
}
// 7️⃣ RAG问答
async function ask(question) {
const context = await retrieve(question);
const prompt = `
你是一个公司HR,请严格根据以下资料回答问题:
资料:
${context.join("\n")}
问题:
${question}
`;
const answer = await generate(prompt);
console.log("📚 命中资料:", context);
console.log("🤖 AI回答:", answer);
}
// 🚀 运行
(async () => {
await init();
console.log("===== 不使用RAG =====");
const noRag = await generate("公司年假多少天?");
console.log(noRag);
console.log("\n===== 使用RAG =====");
await ask("公司年假多少天?");
})();
通过余弦相似度算法来计算,分数越接近 1,代表两个向量(即两段内容)越相似。
通过调试可以看到 score 分数,和答案匹配度 0.8。
低分的代表无用吗?
这个就涉及到 阈值 这个概念,可以设置“严格模式”来让模型只相信高分的,这个要根据场景不同来调试效果。
加载中...
2️⃣ Context(上下文)
👉 AI当前“记住”的内容
-
📖 解释: AI 之所以有“记忆”是因为根据的是上下文来回答的,即 “当前发送的信息+之前的问答信息”
就像人一次只能记住几句话,越多越吃力
-
⚠️ 关键点:
- 超出长度(token) = 忘记
- 顺序很重要
我更改了对内存要求更低的模型 “gemma3:1b”
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
// context-demo.js
const OLLAMA_URL = 'http://localhost:11434';
// /api/chat 为多轮对话
async function chat(messages) {
const res = await fetch(`${OLLAMA_URL}/api/chat`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gemma3:1b',
messages: messages, // /api/chat 用 messages 字段
stream: false,
}),
});
const data = await res.json();
return data.message?.content; // /api/chat 返回 data.message.content
}
async function noContextDemo() {
console.log('=== ❌ 无 context ===');
let res1 = await chat([{ role: 'user', content: '我叫小明' }]);
console.log('AI:', res1);
let res2 = await chat([{ role: 'user', content: '我叫什么名字?' }]);
console.log('AI:', res2);
}
async function withContextDemo() {
console.log('\n=== ✅ 有 context ===');
const messages = [];
// 用户说话
messages.push({ role: 'user', content: '我叫小明' });
let res1 = await chat(messages);
console.log('AI:', res1);
// 把AI回复也加入上下文
messages.push({ role: 'assistant', content: res1 });
// 再问问题
messages.push({ role: 'user', content: '我叫什么名字?' });
let res2 = await chat(messages);
console.log('AI:', res2);
}
//noContextDemo();
withContextDemo();
执行 noContextDemo() 方法,大模型就会表现的“失忆”,因为模型需要你提供给它“上下文”,也就是之前说过的话。
执行 withContextDemo() 方法,其实就是把信息放在数组内,一起提供给大模型。
现在很多 AI 聊天的 APP 都加入了记忆功能,其实就是提取了你和它聊天的关键信息。
3️⃣ Prompt(提示词)
👉 你对AI说的话
-
📖 解释: 你怎么问,AI就怎么答
提问方式决定答案质量
-
开发中常用的提示词工程:
- 角色设定 (Role):你是谁?
- 任务描述 (Task):你要做什么?
- 上下文 (Context):背景信息是什么?
- 约束条件 (Constraints):有什么限制?
- 输出格式 (Output Format):你想要什么格式?
这种是早期在文本聊天提供给 AI 的代码的详细背景,这样让其能够更好地理解你要做的事。
像现在的 Cursor、Claude 等 AI 编程工具都会通过 tools 来读取本地的代码,以及提前设置好的 rules(规则),实际上给大模型发送的 prompt 是类似这样:
[SYSTEM INSTRUCTION]
You are an expert coding assistant.
Task: Refactor the selected code block.
[FILE CONTEXT]
Path: src/components/UserList.tsx
Language: TypeScript
[ANALYSIS RESULT] (这是关键!)
- The selected block is inside a React functional component.
- Variable 'users' is of type User[] defined in parent scope.
[CODE TO MODIFY]
(你选中的原始代码)
[USER REQUEST]
把这个循环改成使用 map
4️⃣ Function Calling (函数调用)
👉 AI调用工具返回结果
- 📖 解释: AI 调用工具的过程,“提供参数 -> 执行逻辑 -> 返回结果”。
- 比如 ChatGPT 的联网搜索,就是一个工具的调用,爬虫搜索得到最新的信息
在 AI 世界里,模型不能直接执行代码。它只能“说话”。
-
流程:
- 描述 (Definition):你先告诉模型:“我有一个叫
calculateTotal的工具,它需要price和tax两个数字参数。”(文本描述)。 - 思考 (Reasoning):模型分析用户的这句话:“帮我算一下总价,价格 100,税 0.1”。模型推理出:“用户想要计算,我应该调用
calculateTotal”。 - 提议 (Proposal):模型不执行计算,而是返回一段文本(通常是 JSON):
{ "name": "calculateTotal", "arguments": { "price": 100, "tax": 0.1 } }。” - 执行 (Execution):你的代码(前端或后端)截获这个返回,识别出这是调用请求,然后真正地去执行
calculateTotal函数。 - 反馈 (Feedback):把执行结果(比如
110)再发给模型,模型才继续回答用户:“总价是 110。”
- 描述 (Definition):你先告诉模型:“我有一个叫
-
特点:
- 有思考:模型根据上下文决定是否调用、调用哪个。
- 只动口不动手:模型本身只是个“指挥官”,它发出的调用指令只是一段文本,必须靠外部代码去落实。
- 黑盒:模型不知道函数内部是怎么实现的,它只知道函数的“说明书”(Schema)。
这让模型拥有了和外界沟通的能力,它可以执行函数、获取API、搜索网页等。
传统 Function Calling 的痛点
在没有 MCP 之前,每个 AI 接入工具都需要写定制代码:
- OpenAI + GitHub = 定制适配器 A
- Claude + GitHub = 定制适配器 B
- Gemini + GitHub = 定制适配器 C
开发者通常会在代码里写死一个巨大的 tools 数组:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
// 传统模式:每次请求都必须把所有工具塞进去
const allTools = [
{ name: "tool_1", description: "...", parameters: {...} },
{ name: "tool_2", description: "...", parameters: {...} },
// ... 直到 tool_50
{ name: "tool_50", description: "...", parameters: {...} },
];
await chatCompletion({ messages: [...], tools: allTools });
结果:哪怕用户只是说“你好”,你也浪费了发送 50 个工具定义的 Token。如果工具定义很多,且用户还多轮对话,这可能瞬间消耗几千甚至上万个 Token。
这就要说到下个概念 MCP
5️⃣ MCP(Model Context Protocol)
👉 让 AI 能“标准化地调用外部工具和数据”
- 📖 人话解释: MCP 是 “工业标准”,统一相同的标准来调用不同的工具。
MCP = 协议标准 + 官方多语言 SDK (实现基础) + 社区构建的具体 Server/Client (最终实现)。
Function Calling 并非被取代了,而是在其基础上标准化了,简单来说是 MCP 的子集。
架构
- 1
- 2
- 3
- 4
- 5
MCP Client(如 Cursor、Claude等)
↓
MCP Protocol(JSON-RPC over stdio/SSE)
↓
MCP Server(提供 Tools、Resources、Prompts)
协议
工具怎么描述(Tool Schema)
告诉 AI 这个工具做啥的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
{
name: 'getBeijingTime',
description: '获取当前北京时间',
parameters: {
type: 'object',
properties: {},
},
}
AI 和工具怎么对话(通信格式)
- 1
- 2
- 3
- 4
{
"tool": "getBeijingTime",
"arguments": {}
}
实现基础 SDK
MCP 不仅仅有协议标准,比如还提供了 TypeScript SDK 可以直接调用方法来开发,不用重头来写底层代码,就跟 axios 本质上是使用 http 协议发起请求一个道理。
最终实现
分为 Server/Client 端
简单理解就是:Client 端(Claude Desktop、Cusor、Kiro等)和 第三方工具厂商(GitHub、Figma、AWS 等)
Server 端
工具提供方,常见的 MCP Server有:
-
Context7 MCP
-
功能:获取最新技术文档(解决模型训练数据过时问题)
-
场景:"React 19的新特性是什么?"——自动获取最新文档
-
-
GitHub MCP Server(官方维护 ⭐)
-
功能:仓库管理、文件读写、PR操作、Issue管理、代码搜索
-
安装方式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
{ "mcpServers": { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "<YOUR_TOKEN>"} } } } -
工具提供方会提供 API 给 AI 来调用
Client 端
- 读取工具描述
- 决定是否调用
- 发请求
MCP 大致流程
通过Client AI 调用 Server 流程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
用户问:"帮我看看 GitHub 用户 octocat 最近在做什么"
↓
AI 模型决定调用 get_user_repos
↓
MCP Client 发送请求到 MCP Server:
{
"method": "tools/call",
"params": {
"name": "get_user_repos",
"arguments": {
"username": "octocat",
"sort": "updated"
}
}
}
↓
MCP Server 调用真实 GitHub API
↓
返回简化后的数据给 AI:
[
{
"name": "Hello-World",
"url": "https://github.com/octocat/Hello-World",
"stars": 2500,
"language": "JavaScript"
}
]
↓
AI 生成回答:"octocat 最近更新了 Hello-World 项目,这是一个有 2500 星的项目..."
MCP 的优化机制:分层加载 (Lazy Loading / Dynamic Discovery)
MCP 协议设计了一个三阶段交互流程,允许客户端(如 Cursor)智能地决定何时发送什么信息给模型。
第一阶段:只列出名字 (List Tools)
当客户端连接 MCP Server 时,它首先只请求工具的元数据列表(名称、简要描述),而不获取完整的参数 Schema。
- MCP 请求:
tools/list - MCP 返回:
- 1
- 2
- 3
- 4
- 5
[ { "name": "search_db", "description": "Search the database" }, { "name": "send_email", "description": "Send an email" }, ... // 只有名字和一句话描述,非常短! ] - 此时模型看到了什么?
- 智能客户端策略:客户端(如Cursor)不会立刻把这 50 个名字全部塞进模型的 System Prompt 里。
第二阶段:按需获取详情 (Get Tool Definition)
只有当模型确定可能需要某个特定工具时,客户端才会向 MCP Server 请求该工具的完整定义(包含详细的参数 JSON Schema)。
- 场景:用户问“帮我查一下数据库里的用户表”。
- 动作:客户端识别到意图,单独向 MCP Server 请求
tools/get(name: "search_db")。 - MCP 返回: 当前工具完整的参数 schema。
- 发送给模型: 此时,客户端才将
search_db的完整定义放入 Prompt 发送给模型。 - 节省效果:其他 49 个无关工具的定义根本没有被发送给模型。
第三阶段:动态上下文 (Resources)
除了工具,MCP 还有 Resources(资源)。
- 传统模式:你可能要把整个文档内容读出来塞进 Prompt。
- MCP 模式:模型只看到资源的 URI(如
file://logs/error.log)。只有当模型说“我想读取这个文件”时,客户端才去读取文件内容并传给模型。这避免了把大量无关数据预加载到上下文中。
6️⃣ Skill(文档)
👉 Skill 通过工作流、工具调用、知识库,告诉 AI 该怎么做
- 📖 人话解释: 给大模型的“说明书”
比如这个 vercel 官方提供的 skill,可以让 AI 变成真正的“资深Next.js工程师”
https://github.com/vercel-labs/next-skills
| 技能名称 | 功能描述 |
|---|---|
next-best-practices | 应用规则与最佳实践:基于代码检查(lint)结果,自动应用 Next.js 官方推荐的项目结构、命名规范、性能优化等规则。 |
next-cache-components | 缓存组件处理:专门处理 Next.js 中缓存相关组件的迁移或使用,例如将旧的 unstable_cache API 迁移到新的 cache API。 |
next-upgrade | 版本升级助手:帮助开发者将 Next.js 项目从一个版本升级到另一个版本,处理依赖更新、API 变更和配置迁移等复杂任务。 |
7️⃣ Rules(文档)
👉 Rules 是指约束、规范 AI 行为
- 📖 人话解释: 给大模型的“规章手册”
比如开发 Next.js 的时候,代码风格、命名规范、技术栈约束、语言、项目结构等
各个厂家的方式不一样,不过大同小异,个人觉得 kiro 这个做的就不错,可以自动帮你分类生成,只不过它这里叫 Steering。
可以去网上找这种模版,或者直接让 AI 生成合适不同项目使用的 rules
