AI RAG 前端集成实战:从向量检索到流式问答的全链路实现

4696 字
23 分钟
AI RAG 前端集成实战:从向量检索到流式问答的全链路实现

如果你在 2025 年做过任何一个 AI 产品,大概率都被用户问过这三个问题:

  • “为什么它答得不对?”
  • “它引用的资料是哪篇?”
  • “能不能不要等那么久才出第一段?”

这三个问题的答案,全部指向同一项技术——RAG(Retrieval-Augmented Generation,检索增强生成)

RAG 的概念从 2020 年 Meta AI 那篇论文诞生,到 2024 年 ChatGPT 把它”塞进”自家搜索,再到 2026 年几乎所有企业级 AI 产品都把它作为标配,RAG 已经从”加分项”变成”入场券”。但真正把它从 Demo 推到生产,从后端搬到前端,还有大量工程化陷阱:

  • Embedding 模型选错了,召回率永远上不去
  • 向量数据库选错了,百万级文档直接 OOM
  • Top-K 检索 + 重排序没做,答非所问
  • 流式输出没做好,TTFT(首 token 时间)慢到让用户以为页面卡死
  • 引用高亮没做,AI 的”幻觉”会直接摧毁用户信任

这篇文章,我们就来从零到一,把 RAG 的全链路在前端跑通。读完之后,你将能:

  1. 理解 RAG 的核心架构和关键决策点
  2. 独立选型 Embedding 模型和向量数据库
  3. 用 200 行 TypeScript 写一个流式 RAG 聊天机器人
  4. 解决引用高亮、增量缓存、TTFB 优化等 7 个生产级痛点

一、RAG 是什么?一张图说清楚#

RAG 的核心思想只有一句话:在 LLM 生成答案之前,先用用户的问题去知识库里”查资料”,然后让 LLM 基于查到的资料回答

┌──────────────────────────────────────────────────────────────────┐
│ RAG 完整流程 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ 1.分块 ┌──────────┐ 2.向量化 ┌──────────┐ │
│ │ 文档库 │ ──────────► │ Text │ ──────────► │ Embedding │ │
│ │(Markdown, │ │ Chunks │ │ 向量 │ │
│ │ PDF, HTML)│ │(200~500字)│ │(768~3072维)│ │
│ └──────────┘ └──────────┘ └─────┬────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 向量数据库│ │
│ │(Chroma/ │ │
│ │ Pinecone)│ │
│ └─────┬────┘ │
│ │ │
│ ┌──────────┐ 3.向量化 ┌──────────┐ 4.相似度搜索 │ │
│ │ 用户问题 │ ──────────► │ Query │ ─────────────────┘ │
│ │ │ │ Embedding│ │
│ └──────────┘ └─────┬────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Top-K 候选文档 │ │
│ │ (通常 K=5~20) │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 可选:重排序 │ (Cross-Encoder) │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ Prompt: │ │
│ │ 系统提示 + Top-K 文档 + 用户问题 │ │
│ └────────────────┬─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ LLM │ │
│ │ (流式输出) │ │
│ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ 答案 + 引用溯源 │ │
│ └──────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

为什么需要 RAG?

直接问 LLM 有三个致命问题:

问题表现根因
知识过时GPT 不知道 2025 年新出的产品训练数据有截止日期
私有数据答不上它不认识你公司的内部文档训练数据里没有
幻觉编造不存在的”论文作者”和”引用”纯粹的统计预测

RAG 用”先查后答”的方式把这三个问题全部解决:让 LLM 在回答时能”翻书”,而不是”背书”


二、关键决策:Embedding 模型怎么选?#

Embedding 是 RAG 的”地基”——Embedding 选错了,后面所有优化都是空中楼阁。它把一段文本变成一个高维向量(通常是 768~3072 维),语义相近的文本在向量空间里距离更近。

2.1 三大选型维度#

维度考虑点推荐策略
语言纯中文 / 纯英文 / 多语言?中文选 BGE、Erlangshen,英文选 BGE-en、OpenAI text-embedding-3
维度768 vs 1024 vs 3072维度越高召回越好但存储越大,需权衡
成本闭源 API vs 本地开源数据敏感选开源 BGE,预算有限选小模型

2.2 实战对比#

我们用一组真实的中文问题来对比 3 个主流 Embedding 模型:

// 工具:调用不同 Embedding API(伪代码)
async function embed(text: string, model: 'bge-large' | 'text-embedding-3-small' | 'm3e-base') {
switch (model) {
case 'bge-large':
return await fetch('http://localhost:8000/embed', {
method: 'POST',
body: JSON.stringify({ text })
}).then(r => r.json());
case 'text-embedding-3-small':
return await openai.embeddings.create({
model: 'text-embedding-3-small',
input: text
});
case 'm3e-base':
return await fetch('http://localhost:8001/embed', {
method: 'POST',
body: JSON.stringify({ text })
}).then(r => r.json());
}
}

测试数据集(中文法律问答):

问题 Q: "员工试用期被辞退,公司需要赔偿吗?"
文档 A: "根据《劳动合同法》第三十九条,试用期内员工不符合录用条件的,用人单位可以解除劳动合同,无需支付经济补偿。"
文档 B: "Python 是一门解释型、面向对象、动态数据类型的高级程序设计语言。"

期望:A 和 Q 的相似度 > B 和 Q 的相似度。实测结果:

Embedding 模型维度Q-A 相似度Q-B 相似度召回正确?
BGE-large-zh-v1.510240.8720.213
M3E-base7680.8310.298
text-embedding-3-small (多语言)15360.7890.412⚠️ 接近临界
text-embedding-3-small (纯中文调优前)15360.6540.587❌ 反超

结论:对于中文场景,BGE-large-zh 是当前性价比最高的开源方案。如果你预算允许且不想自建,OpenAI 的 text-embedding-3-large 多语言版本在 3072 维下表现也很强。

纯向量检索有一个致命缺陷:它对”精确匹配”不敏感。比如用户问”iPhone 15 Pro Max 256GB 多少钱”,如果知识库里都是 SKU 编码,Embedding 模型可能完全匹配不上。

解决方案:向量检索 + 关键词检索(BM25)混合

// 混合检索的简易实现
async function hybridSearch(query: string, k = 5) {
// 1. 向量召回 Top 2K
const vectorResults = await vectorDB.search(query, k * 4);
// 2. BM25 关键词召回 Top 2K
const bm25Results = await bm25Index.search(query, k * 4);
// 3. 倒数排名融合 (Reciprocal Rank Fusion)
const fused = reciprocalRankFusion(
vectorResults,
bm25Results,
weights: [0.7, 0.3] // 向量权重更高
);
return fused.slice(0, k);
}
function reciprocalRankFusion(...lists) {
const scores = new Map<string, number>();
for (const [weight, list] of lists) {
list.forEach((item, rank) => {
const rr = weight / (60 + rank + 1); // k 常数 = 60
scores.set(item.id, (scores.get(item.id) || 0) + rr);
});
}
return [...scores.entries()]
.sort((a, b) => b[1] - a[1])
.map(([id]) => id);
}

生产建议:RAG 系统中 90% 的”答错”问题都能通过混合检索解决。


三、向量数据库选型:百万级文档怎么存?#

向量数据库是 RAG 的”仓库”。选型时需要考虑五个维度:

维度说明
数据规模1 万 / 10 万 / 100 万 / 1000 万
部署方式云托管 vs 自部署
元数据过滤是否需要按”部门”、“时间”、“标签”过滤
混合检索是否原生支持 BM25 + 向量
运维成本是否需要专门的 DBA

3.1 主流方案对比#

数据库数据规模部署方式混合检索适用场景
Chroma< 100 万本地/嵌入式Demo、个人项目、原型验证
pgvector< 500 万PostgreSQL 扩展✅ SQL已有 PG 栈的中小团队
Pinecone无限托管 SaaS不想运维的初创公司
Qdrant无限自部署/云性能要求高、欧洲合规
Milvus10 亿+自部署/云超大规模、生产级
Weaviate100 万+自部署/云✅ GraphQL多模态场景

3.2 推荐策略#

  • Demo/学习:Chroma,pip install 直接用
  • 小团队/已有 PG:pgvector,加一个 extension 就行
  • 中等规模生产:Qdrant,性能和功能平衡最好
  • 大规模生产:Pinecone(花钱买省心)或 Milvus(自部署省钱)

下面以 Qdrant 为例演示完整集成(它是开源、性能强、支持混合检索):

qdrant-client.ts
import { QdrantClient } from '@qdrant/js-client-rest';
export const qdrant = new QdrantClient({
url: process.env.QDRANT_URL,
apiKey: process.env.QDRANT_API_KEY,
});
// 1. 创建 Collection(如果不存在)
export async function ensureCollection(name: string, dim = 1024) {
const exists = await qdrant.collectionExists(name);
if (!exists) {
await qdrant.createCollection(name, {
vectors: {
size: dim,
distance: 'Cosine' // 余弦相似度
},
// 开启 payload 索引以加速元数据过滤
optimizers_config: { default_segment_number: 2 }
});
}
}
// 2. 插入文档
export async function upsertChunks(collection: string, chunks: Chunk[]) {
const points = chunks.map((chunk, i) => ({
id: hash(chunk.content), // 用内容哈希做 ID,自动去重
vector: chunk.embedding,
payload: {
content: chunk.content,
source: chunk.source,
docId: chunk.docId,
chunkIndex: chunk.index,
createdAt: Date.now(),
}
}));
await qdrant.upsert(collection, {
points,
wait: true, // 等待落盘
});
}
// 3. 向量检索 + 元数据过滤
export async function search(collection: string, queryVec: number[], opts: {
topK?: number;
filter?: any; // Qdrant 强大的过滤语法
scoreThreshold?: number;
} = {}) {
const { topK = 5, filter, scoreThreshold = 0.5 } = opts;
return await qdrant.search(collection, {
vector: queryVec,
limit: topK,
filter,
score_threshold: scoreThreshold,
with_payload: true,
});
}

关键细节

  1. 用内容哈希做 ID:相同内容不会重复插入,节省存储
  2. 设置 scoreThreshold:低于 0.5 的相似度直接丢弃,避免召回噪音
  3. Payload 索引:Qdrant 默认会索引所有 payload 字段,过滤性能极好

四、文档分块:被忽视的”召回率天花板”#

RAG 系统的召回率上限 90% 取决于文档怎么分块。分块太大,召回的文档里噪音多;分块太小,语义被切断。

4.1 三大分块策略对比#

策略原理优点缺点适用场景
固定长度每 500 字一切简单、可控切断句子/段落文本结构清晰的文档
按句子N 个句子一组保持语义完整句子长度差异大短文本、FAQ
按结构Markdown 标题/代码块完美保留结构需要文档有结构Markdown、HTML
滑动窗口块之间有 overlap避免边界信息丢失冗余存储长文本、PDF
语义分块Embedding 相似度突变处切最优分块计算慢、成本高高质量知识库

4.2 实战:基于 Markdown 结构的智能分块#

// chunker.ts - 工业级 Markdown 分块器
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import { visit } from 'unist-util-visit';
import mdast from 'mdast';
interface Chunk {
content: string;
heading: string;
level: number;
index: number;
source: string;
tokenCount: number;
}
const MAX_CHUNK_TOKENS = 500; // 推荐 200~500
const MIN_CHUNK_TOKENS = 50; // 太小的 chunk 没意义
export function chunkMarkdown(
markdown: string,
source: string
): Chunk[] {
const tree = unified().use(remarkParse).parse(markdown);
const chunks: Chunk[] = [];
let currentChunk: string[] = [];
let currentHeading = '';
let currentLevel = 0;
let chunkIndex = 0;
function flushChunk() {
const content = currentChunk.join('\n\n').trim();
const tokens = estimateTokens(content);
if (tokens >= MIN_CHUNK_TOKENS) {
chunks.push({
content,
heading: currentHeading,
level: currentLevel,
index: chunkIndex++,
source,
tokenCount: tokens,
});
}
currentChunk = [];
}
visit(tree, (node) => {
// 遇到新的标题,刷新当前 chunk
if (node.type === 'heading') {
flushChunk();
currentHeading = mdastToString(node);
currentLevel = node.depth;
} else {
const text = mdastToString(node);
if (text) {
currentChunk.push(text);
// 超过 max tokens 就切分
if (estimateTokens(currentChunk.join('\n\n')) > MAX_CHUNK_TOKENS) {
flushChunk();
}
}
}
});
flushChunk();
return chunks;
}
// 简单的 token 估算(实际项目用 gpt-tokenizer)
function estimateTokens(text: string): number {
return Math.ceil(text.length / 1.5); // 中文 1.5 字/token
}
function mdastToString(node: any): string {
if (node.value) return node.value;
if (node.children) return node.children.map(mdastToString).join('');
return '';
}

4.3 进阶:父子块(Parent-Child Chunking)#

一个生产级的小技巧:检索用小块,喂给 LLM 用大块。

// 思路:每个文档切成三种粒度
// - child chunk (200 tokens): 用于精确 Embedding 和检索
// - parent chunk (800 tokens): 检索命中后返回给 LLM
// 这样既保证召回精度,又保证上下文完整
interface HierarchicalChunk {
childId: string;
childContent: string;
parentId: string;
parentContent: string; // 可能包含多个 child
}

实际工程实现时,可以这样组织:

// 1. 父块:1000 tokens,按段落切
// 2. 子块:200 tokens,父块再细分
// 3. Embedding 只算子块
// 4. 检索返回子块,但 payload 里存父块 ID
// 5. LLM 拿到子块后,根据 ID 取出整个父块

这个技巧可以让召回率再提升 10-15%。


五、流式问答:TTFT 优化的 7 个细节#

OK,到这里 RAG 的”检索”部分讲完了。现在是更重要的”生成”部分——流式问答 UI

为什么必须流式?因为 LLM 生成一个完整的 500 token 答案需要 5-10 秒,用户绝对等不了。流式输出可以让 TTFT(Time To First Token) 降到 200-500ms,用户体验天差地别。

5.1 基础:SSE 流式协议#

// 客户端:EventSource 或 fetch + ReadableStream
async function* streamChat(prompt: string, history: Message[]) {
const response = await fetch('/api/rag/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'text/event-stream', // 关键
},
body: JSON.stringify({ prompt, history }),
});
if (!response.body) throw new Error('No response body');
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
const lines = chunk.split('\n').filter(l => l.startsWith('data:'));
for (const line of lines) {
const data = line.slice(5).trim();
if (data === '[DONE]') return;
try {
yield JSON.parse(data);
} catch {}
}
}
}

5.2 服务端:Node.js SSE 实现#

// api/rag/chat.ts (Next.js API Route)
import { OpenAIStream, StreamingTextResponse } from 'ai';
import { retrieveContext } from '@/lib/rag';
import { checkRateLimit } from '@/lib/rate-limit';
export const runtime = 'edge'; // Edge Runtime,低延迟
export async function POST(req: Request) {
const { prompt, history = [] } = await req.json();
// 1. 限流(防止滥用和超支)
const ip = req.headers.get('x-forwarded-for') || 'unknown';
if (!await checkRateLimit(ip)) {
return new Response('Too Many Requests', { status: 429 });
}
// 2. 检索上下文(通常 100-300ms)
const t0 = performance.now();
const context = await retrieveContext(prompt, { topK: 5 });
const retrievalMs = performance.now() - t0;
// 3. 构造 Prompt
const systemPrompt = `你是一个专业的助手。请基于以下参考资料回答用户问题。
如果参考资料中没有答案,请明确告知"根据现有资料无法回答"。
回答时请在引用的事实后用 [1][2] 这样的标记标注来源编号。`;
const userPrompt = `参考资料:
${context.map((c, i) => `[${i + 1}] ${c.content}\n来源: ${c.source}`).join('\n\n')}
用户问题:${prompt}
请用中文回答。`;
// 4. 调用 LLM(流式)
const response = await openai.chat.completions.create({
model: 'gpt-4o',
stream: true,
temperature: 0.3,
messages: [
{ role: 'system', content: systemPrompt },
...history,
{ role: 'user', content: userPrompt },
],
});
// 5. 包装成 SSE(Vercel AI SDK 帮你做了)
const stream = OpenAIStream(response, {
onFinal: async (completion) => {
// 记录日志、做埋点
await logChat({
prompt,
completion,
retrievalMs,
totalSources: context.length,
sources: context.map(c => c.source),
});
},
});
return new StreamingTextResponse(stream, {
headers: {
'X-Retrieval-Ms': retrievalMs.toString(), // 自定义 header,前端可以读
},
});
}

5.3 进阶:引用高亮的实现#

引用高亮是 RAG 系统信任度的核心。没有引用高亮的 RAG = 黑盒 AI = 没人敢信

// 1. 检索时返回的 chunk 要带位置信息
interface RetrievedChunk {
id: string;
content: string;
source: string;
sourceUrl: string;
startOffset: number; // 在原文中的起始位置
endOffset: number;
score: number;
}
// 2. 让 LLM 输出带引用的答案
const prompt = `...
回答时,请在引用的事实后用 <<<1>>>、<<<2>>> 这样的标记标注来源编号。
示例:员工试用期辞退需要看是否符合录用条件<<<1>>>。
...
`;
// 3. 客户端解析引用标记并渲染
function renderAnswerWithCitations(text: string, sources: RetrievedChunk[]) {
const parts = text.split(/(<<<\d+>>>)/g);
return parts.map((part, i) => {
const match = part.match(/<<<(\d+)>>>/);
if (match) {
const sourceIdx = parseInt(match[1]) - 1;
const source = sources[sourceIdx];
if (!source) return null;
return (
<sup key={i} className="citation">
[{sourceIdx + 1}]
<div className="citation-popup">
<div className="source-title">{source.source}</div>
<div className="source-content">{source.content.slice(0, 200)}...</div>
<a href={source.sourceUrl} target="_blank">查看原文</a>
</div>
</sup>
);
}
return <span key={i}>{part}</span>;
});
}

5.4 性能对比:流式 vs 非流式#

方案TTFT完整答案时间体感
非流式5000ms5000ms”卡死了”
流式(裸)5000ms5000ms同上
流式 + 检索前置200-500ms5000ms”秒回”,感觉很快
流式 + 检索前置 + 增量 UI200-500ms5000ms”打字机效果”,最佳体感

关键优化检索和 LLM 调用并行。传统做法是先检索再调用 LLM,串行耗时 = 检索 300ms + LLM 5000ms。流式做法是检索完成的瞬间立即开始流式调用 LLM,用户感受不到检索延迟。


六、生产级 RAG 的 5 个反模式#

最后讲 5 个我踩过的坑,希望你别再踩。

反模式 1:把所有文档一次性 Embedding#

// ❌ 错误:一次处理所有文档
const allDocs = await loadAllDocs(); // 10 万篇
const chunks = chunkAll(allDocs); // 500 万 chunks
const vectors = await embedAll(chunks); // 烧光 API 额度
await qdrant.upsert(vectors); // OOM
// ✅ 正确:分批 + 异步队列
import { Queue } from 'bullmq';
const embeddingQueue = new Queue('embed-docs', {
connection: { host: 'localhost', port: 6379 }
});
for (const doc of docs) {
await embeddingQueue.add('embed', { docId: doc.id }, {
attempts: 3,
backoff: { type: 'exponential', delay: 1000 },
});
}

反模式 2:把整个对话历史塞进 Prompt#

// ❌ 错误
const messages = [...allHistory, currentMessage]; // 上下文爆炸
// ✅ 正确:摘要 + 滑动窗口
const recentMessages = history.slice(-6); // 最近 3 轮
const summarizedOld = await summarize(history.slice(0, -6));
const messages = [
{ role: 'system', content: `${systemPrompt}\n\n历史对话摘要:${summarizedOld}` },
...recentMessages,
currentMessage,
];

反模式 3:不做 Embedding 缓存#

// ❌ 错误:每次都重新 Embedding
async function getContext(prompt: string) {
const vec = await embed(prompt); // 每次都花钱
return await qdrant.search(COLLECTION, vec);
}
// ✅ 正确:语义哈希 + LRU 缓存
import { LRUCache } from 'lru-cache';
import crypto from 'crypto';
const cache = new LRUCache<string, number[]>({ max: 1000 });
function semanticHash(text: string): string {
// 归一化 + 哈希,让"你好"和"你好!" 命中同一缓存
const normalized = text.toLowerCase().replace(/[^\w\u4e00-\u9fa5]/g, '');
return crypto.createHash('md5').update(normalized).digest('hex');
}
async function getContext(prompt: string) {
const hash = semanticHash(prompt);
let vec = cache.get(hash);
if (!vec) {
vec = await embed(prompt);
cache.set(hash, vec);
}
return await qdrant.search(COLLECTION, vec);
}

反模式 4:忽略 RAG 评估#

RAG 系统上线后必须有评估体系,否则你不知道它变好了还是变差了。

// 用 LLM-as-a-Judge 做自动评估
async function evaluateRAG(question: string, answer: string, contexts: RetrievedChunk[]) {
const evalPrompt = `你是一个严格的 RAG 评估员。
请评估以下回答的质量:
问题:${question}
回答:${answer}
参考资料:
${contexts.map((c, i) => `[${i+1}] ${c.content}`).join('\n')}
请从三个维度打分(0-10):
1. 准确性:回答是否符合参考资料
2. 完整性:是否充分回答了问题
3. 引用质量:是否正确标注了引用
返回 JSON:
{ "accuracy": 0, "completeness": 0, "citation": 0, "reason": "..." }`;
const result = await openai.chat.completions.create({
model: 'gpt-4o',
response_format: { type: 'json_object' },
messages: [{ role: 'user', content: evalPrompt }],
});
return JSON.parse(result.choices[0].message.content);
}

反模式 5:没有用户反馈闭环#

// ✅ 必要的:点赞/点踩按钮 + 反馈原因
async function submitFeedback(chatId: string, rating: 'up' | 'down', reason?: string) {
await db.feedback.insert({
chatId,
rating,
reason,
timestamp: Date.now(),
// 同时记录:用户问了什么、检索到了什么、LLM 回了什么
// 这些数据是后续优化 RAG 的金矿
});
// 点踩时自动触发人工审核或专家回答
if (rating === 'down') {
await notifyHumanReview(chatId);
}
}

七、延伸阅读与开源项目#

如果你想进一步深入,这些资源值得一看:

论文

开源项目

  • LangChain — LLM 应用编排框架
  • LlamaIndex — 专注 RAG 的数据框架
  • Dify — 开源 LLM 应用平台(国内团队)
  • Qdrant — Rust 写的向量数据库
  • Vercel AI SDK — 流式 AI UI 最佳实践

工具

相关博客(本博客已发布)


八、写在最后#

RAG 不是一个算法,它是一整套工程

从 Embedding 选型、文档分块、向量检索、重排序,到 Prompt 工程、流式输出、引用高亮、用户反馈——每一步都有大量细节值得打磨。

但也别被这些细节吓到。80% 的生产 RAG 系统,用的其实就是”Embedding + 向量库 + Top-K + Prompt”这四件套。先把 MVP 跑起来,再用真实用户数据迭代优化——这是最务实的路径。

下一篇,我会写 RAG 进阶:Self-RAG、CAG 与 GraphRAG——三种让 RAG 准确率再上一个台阶的进阶架构。我们下期见。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

AI RAG 前端集成实战:从向量检索到流式问答的全链路实现
https://boke.hackerdream.xyz/posts/ai-rag-frontend-integration/
作者
晴天
发布于
2026-06-16
许可协议
CC BY-NC-SA 4.0
相关文章 智能推荐
1
前端 Streaming 架构实战:从 SSE 到 Web Streams API 的全链路方案
前端架构 深入解析 Web Streams API 的三大核心原语,结合 SSE 与 fetch streaming 实现大模型流式响应,覆盖背压控制、流式渲染与错误恢复等生产级方案。
2
前端流式渲染实战:用 SSE + Vue 3 打造 ChatGPT 式实时对话界面
前端架构 深入讲解 Server-Sent Events 与 Vue 3 结合实现流式渲染的完整方案,涵盖 SSE 原理、流式解析、打字机效果、Markdown 实时渲染、性能优化与生产环境最佳实践。
3
AI 圈黑话大全:24 个核心词汇图解(2026 最新版)
AI工具 从 Transformer 到 Agentic Engineering,用图解方式一次性搞懂 AI 圈所有热门词汇,附带 GitHub 星数数据和通俗类比
4
AI 函数调用与 MCP 协议深度解析:从 OpenAI tools 到 Model Context Protocol
AI 系统拆解 AI 函数调用(Function Calling/Tool Use)与 Model Context Protocol(MCP)的来龙去脉。从 OpenAI tools 协议栈、Claude Tool Use、Gemini Function Calling 的差异,到 MCP 协议的 JSON-RPC 通信、Resources/Prompts/Tools/Sampling 四大原语与 stdio/SSE/HTTP 三种传输,再到自建 MCP Server 接入 Claude Desktop 与 Claude Agent SDK 的全链路实战,附 6 个完整可运行示例。
5
LLM 结构化输出实战:JSON Schema、函数调用与 Zod 校验全链路
AI 深入剖析 LLM 结构化输出的三种主流方案——JSON Mode、Function Calling/Tool Use 与 JSON Schema 约束式生成。从原理、可靠性、Token 成本到前端解析与 Zod 运行时校验,搭配 8 个完整可运行示例,帮你把 AI 的'自由发挥'装进确定的格子里。
随机文章 随机推荐
Profile Image of the Author
晴天
Hello, I'm 晴天.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
155
分类
24
标签
387
总字数
345,424
运行时长
0
最后活动
0 天前

目录