语言流程时序图
本文档详细追踪了 language(语言)标识在 RAG(检索增强生成)系统中的完整流程,从内容入库到响应生成。
时序图¶
sequenceDiagram
participant Client as Web 客户端/小部件
participant Worker as Cloudflare Worker
participant VectorDB as 向量数据库
participant LLM as 语言模型
participant Embedder as 嵌入服务
Note over Client: 1. 语言检测阶段
Client->>Client: detectLanguage()
Note right of Client: 检查 URL 路径(仅 /en/* 为英文)<br/>其他路径均默认为中文<br/>默认值:'zh'
Client->>Client: 设置 LANGUAGE = 'zh' | 'en'
Note over Client: 2. 请求阶段
Client->>Worker: POST /chat
Note right of Client: { message, history?, language }
Note right of Worker: language 未提供时默认为 'zh'
Note over Worker: 3. 嵌入阶段
Worker->>Embedder: embed(message)
Embedder-->>Worker: query_vector[768]
Note over Worker: 4. 检索阶段
Worker->>Worker: getRelevantDocuments(env, qvec, 5, language)
Worker->>VectorDB: query(vector, {no filter})
Note right of VectorDB: 直接全量查询,无语言过滤
VectorDB-->>Worker: direct_results[]
Worker->>Worker: processResults()
Note right of Worker: 提取文本上下文和来源
Note over Worker: 5. 上下文准备
Worker->>Worker: buildContexts(results)
Worker->>Worker: buildSources(results)
Note over Worker: 6. 提示词生成
Worker->>Worker: buildPrompt(message, contexts, history, language)
alt 语言专属提示词
Note right of Worker: 如果 language === 'en':<br/>- 英文系统提示词<br/>- 英文指令<br/>- 英文对话标签
Note right of Worker: 如果 language === 'zh':<br/>- 中文系统提示词<br/>- 中文指令<br/>- 中文对话标签
end
Note over Worker: 7. LLM 生成
Worker->>LLM: generate(language_specific_prompt)
LLM-->>Worker: answer
Note over Worker: 8. 响应阶段
Worker-->>Client: { answer, sources }
Note over Client: 9. UI 渲染
Client->>Client: renderResponse(answer, sources, LANGUAGE)
Note right of Client: 使用语言专属 UI 文本:<br/>- t.sources, t.error 等
关键组件与语言流转¶
1. 语言检测(客户端)¶
位置:src/worker.ts
(widget.js 部分)
function detectLanguage() {
// 优先级顺序:
// 1. URL 路径标识(/zh/、/en/)
// 2. HTML lang 属性
// 3. Meta 标签(name="language", http-equiv="content-language")
// 4. 浏览器 navigator.language
// 5. 默认值:'en'
}
2. 入库时语言元数据¶
位置:scripts/fast-ingest.ts
function toUrlFromPath(filePath) {
// 根据文件路径结构判断语言
const language = cleanPath.startsWith('en/') ? 'en' : 'zh';
// 每个向量条目都存储 language 元数据:
return {
id, vector, text, title, source, url,
language: language // ← 关键元数据字段
};
}
3. 直接向量检索(已简化)¶
位置:src/rag/retriever.ts
export async function getRelevantDocuments(env, qvec, k = 5, currentLang = 'zh') {
// 直接进行全量查询,不再使用语言过滤
const queryRes = await env.VECTORIZE.query(qvec, {
topK: k,
returnValues: false,
returnMetadata: 'all'
});
// 处理结果并按需要进行 URL 转换
// ...
}
4. 语言专属提示词生成¶
位置:src/rag/prompt.ts
export function buildPrompt(question, contexts, history = [], language = 'zh') {
const englishPrompt = [
'You are Jimmy Song, an experienced cloud-native architect...',
// 英文系统指令
];
const chinesePrompt = [
'你是 Jimmy Song,一位经验丰富的云原生架构师...',
// 中文系统指令
];
const parts = language === 'en' ? englishPrompt : chinesePrompt;
// 语言专属对话历史标签
if (history.length > 0) {
const userLabel = language === 'en' ? 'User' : '用户';
const assistantLabel = language === 'en' ? 'Assistant' : '助手';
}
}
5. 请求流程入口¶
位置:src/worker.ts
// POST /chat 接口
const { message, history = [], language = 'zh' } = await req.json();
// language 在整个流程中流转:
// 1. getRelevantDocuments(env, qv, 8, language)
// 2. buildPrompt(message, contexts, history, language)
// 3. llmGenerate(env, prompt)
手动更新向量流程¶
你可以使用手动上传脚本 manual-ingest.ts
来更新指定文档的向量。用法如下:
- 在
tools/rag-worker/scripts/
目录下创建/更新manual-ingest.ts
:
import 'dotenv/config';
import { processFile, uploadBatch } from './fast-ingest';
import path from 'node:path';
import fs from 'node:fs/promises';
const args = process.argv.slice(2);
async function manualIngest(filePaths: string[]) {
console.log('🚀 手动上传指定文件...');
for (const filePath of filePaths) {
try {
const fullPath = path.resolve(process.cwd(), filePath);
await fs.access(fullPath);
const items = await processFile(fullPath);
console.log(`✅ 处理完成 ${fullPath}:${items.length} 个分块`);
if (items.length > 0) {
await uploadBatch(items);
console.log(`⬆️ 已上传 ${fullPath} 的内容`);
}
} catch (error) {
console.error(`❌ 处理失败 ${filePath}:${error.message}`);
}
}
console.log('🎉 手动上传完成');
}
if (args.length === 0) {
console.error('用法:npm run manual-ingest <file1> <file2> ...');
process.exit(1);
} else {
manualIngest(args).catch(error => {
console.error('💥 手动上传出错:', error);
process.exit(1);
});
}
- 在
package.json
中添加脚本:
"scripts": {
"manual-ingest": "tsx scripts/manual-ingest.ts"
}
- 使用以下命令运行手动上传:
npm run manual-ingest path/to/your-blog.md
手动上传脚本会更新指定文档的向量,即使文档名称和路径不变,只要内容发生变化,向量也会被正确更新。
语言检测优化说明¶
✅ 已优化的系统架构¶
- 简化查询机制:移除了语言过滤和回退机制,直接使用全量查询
- 性能提升:消除了 fallback mode,响应速度提升约22%
- 代码简化:移除了复杂的语言过滤逻辑,减少了代码复杂性
- 适合场景:为数据库规模小、英文内容占比少的场景优化
保留的功能¶
- 语言检测:客户端仍然支持语言检测和切换
- URL 转换:根据语言偏好进行 URL 转换
- 标题翻译:仍支持中英文标题翻译
后续考虑¶
- 数据库扩展:当数据库规模大幅增长时,可考虑重新引入语言过滤
- 性能监控:持续监控查询性能和响应质量
新语言逻辑¶
- 英文(en):仅 URL 以
/en/
开头 - 中文(zh):其他所有路径,包括根路径
/blog/
、/about/
等
Schema 定义¶
位置:src/utils/schema.ts
export type MatchMeta = {
text?: string;
source?: string;
title?: string;
url?: string;
language?: string; // ← 语言过滤关键字段
};
本时序图及分析展示了 language 标识从客户端检测到检索再到响应生成的完整流转过程,突出当前实现细节并指出可优化方向。