Skip to content

语言流程时序图

本文档详细追踪了 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 来更新指定文档的向量。用法如下:

  1. 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);
});
}
  1. package.json 中添加脚本:
"scripts": {
"manual-ingest": "tsx scripts/manual-ingest.ts"
}
  1. 使用以下命令运行手动上传:
npm run manual-ingest path/to/your-blog.md

手动上传脚本会更新指定文档的向量,即使文档名称和路径不变,只要内容发生变化,向量也会被正确更新。

语言检测优化说明

✅ 已优化的系统架构

  1. 简化查询机制:移除了语言过滤和回退机制,直接使用全量查询
  2. 性能提升:消除了 fallback mode,响应速度提升约22%
  3. 代码简化:移除了复杂的语言过滤逻辑,减少了代码复杂性
  4. 适合场景:为数据库规模小、英文内容占比少的场景优化

保留的功能

  1. 语言检测:客户端仍然支持语言检测和切换
  2. URL 转换:根据语言偏好进行 URL 转换
  3. 标题翻译:仍支持中英文标题翻译

后续考虑

  1. 数据库扩展:当数据库规模大幅增长时,可考虑重新引入语言过滤
  2. 性能监控:持续监控查询性能和响应质量

新语言逻辑

  • 英文(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 标识从客户端检测到检索再到响应生成的完整流转过程,突出当前实现细节并指出可优化方向。