向量问题排查与删除
概述¶
RAG Worker 的运行时和检索相关配置已集中到 tools/rag-worker/src/config/settings.ts(项目路径)中。
这个文件是“单一配置源”,包含:
- 全量默认配置:
DEFAULT_RAG_CONFIG - 检索与 rerank 配置:
DEFAULT_RETRIEVAL_CONFIG、DEFAULT_RERANK_CONFIG - 上下文配置:
DEFAULT_CONTEXT_CONFIG。 - 预设(Presets):
PRESET_FAST、PRESET_ACCURATE、PRESET_BALANCED、PRESET_CONVERSATIONAL(集合为PRESETS) - 合并/生成工具:
mergeConfig()、createConfig()、getPreset()。 - 查询扩展模板、个人简介回退与个人查询关键词常量:
QUERY_EXPANSION_TEMPLATES、ABOUT_FALLBACKS、PERSONAL_QUERY_KEYWORDS。 - 向量 manifest 存储路径:
STORAGE_PATHS.vectorManifest(默认config/vector-manifest.json)。
为什么统一配置¶
集中式配置带来的优点:
- 单一维护点,修改配置只需编辑
settings.ts。 - 通过
PRESETS快速切换运行场景(性能、精度、对话优化等)。 - 工具链(Makefile、脚本、Worker)会读取这些默认值并基于环境变量运行。
基本使用示例(在 Worker / 脚本中)¶
import { createRAGPipeline } from './rag/pipeline';
import { PRESETS, createConfig, DEFAULT_RAG_CONFIG } from '../config/settings';
// 使用默认配置
const pipeline = createRAGPipeline(env);
// 使用预设 (fast / balanced / accurate / conversational)
const fastPipeline = createRAGPipeline(env, PRESETS.fast as any);
// 基于预设覆盖部分配置
const customConfig = createConfig({ retrieval: { topK: 12 } }, 'balanced');
const pipelineCustom = createRAGPipeline(env, { retrieval: customConfig.retrieval, context: customConfig.context });
Rerank 配置说明¶
- 默认
DEFAULT_RERANK_CONFIG使用provider: 'local'(内置算法),无需 Cohere 或 Jina API Key。 - 如需更高精度,可将
rerank.provider替换为cohere或jina并在环境中提供对应 API Key(COHERE_API_KEY/JINA_API_KEY)。
例如切换到 Cohere:
import { createConfig } from '../config/settings';
const cfg = createConfig({ retrieval: { rerank: { enabled: true, provider: 'cohere', model: 'rerank-multilingual-v3.0' } } });
并在运行环境中设置:
export COHERE_API_KEY=[REDACTED:api-key]
向量 manifest 与外部配置¶
- 向量 manifest 路径在
STORAGE_PATHS.vectorManifest中定义,默认是config/vector-manifest.json。 - 外部配置存储(如
title-dictionary)同样在settings.ts的常量或ExternalConfigOptions中定义。
与站点工具集成¶
- Makefile(
make rag-upload、make rag-deploy、make rag-reindex等)以及脚本(scripts/fast-ingest.ts)会读取或遵循settings.ts中的默认值与预设。 - 在修改了
settings.ts后,建议执行make rag-upload-config或make rag-deploy(若配置影响部署)以确保一致性。
变更与扩展¶
- 若需新增默认配置、预设或扩展 rerank provider,请在
settings.ts中添加相应的类型,并在 RAG 的实现(如MultiRouteRetriever、ContextManager)中读取。 - 所有新增配置项应配合
getPreset()/createConfig()做默认值合并,以避免运行时断言错误。
实体说明¶
- Markdown 文件:原始 Hugo 内容(frontmatter + 正文)
- 文本分块:按标题与句子分割的片段
- 向量嵌入:数值表示(依赖模型维度)
- 元数据:附加于每个分块,用于过滤与归属
- 查询结果:相似度匹配列表
详细数据格式¶
TypeScript 接口¶
向量元数据结构¶
export type MatchMeta = {
text?: string; // 分块实际文本内容
source?: string; // 相对 content 目录的文件路径
title?: string; // frontmatter 中的文档标题
url?: string; // 生成的网站 URL
language?: string; // 文档语言('zh' | 'en')
};
环境配置¶
export interface Env {
VECTORIZE: VectorizeIndex;
PROVIDER: 'gemini' | 'qwen';
GOOGLE_API_KEY?: string;
QWEN_API_KEY?: string;
QWEN_BASE?: string;
QWEN_EMBED_MODEL?: string;
LLM_MODEL?: string;
ADMIN_TOKEN: string;
EMBED_DIM: string;
}
向量索引操作¶
export interface VectorizeIndex {
upsert(items: {
id: string;
values: number[];
metadata?: Record<string, any>
}[]): Promise<void>;
query(vector: number[], opts: {
topK: number;
returnValues?: boolean;
includeMetadata?: boolean;
returnMetadata?: string;
filter?: { metadata: Record<string, any> };
}): Promise<{
matches: { id: string; score: number; metadata?: MatchMeta }[];
}>;
}
JSON 数据示例¶
Upsert 请求体¶
{
"items": [
{
"id": "a1b2c3d4e5f6-0",
"vector": [0.123, -0.456, 0.789, ...],
"text": "Kubernetes 是一个开源的容器编排平台...",
"source": "zh/blog/kubernetes-intro/index.md",
"title": "Kubernetes 入门指南",
"url": "https://jimmysong.io/blog/kubernetes-intro/",
"language": "zh"
}
]
}
聊天请求/响应¶
// 请求
{
"message": "什么是 Kubernetes?",
"history": [
{"type": "user", "content": "之前的问题"},
{"type": "bot", "content": "之前的回答"}
],
"language": "zh",
"pageContext": {
"title": "Kubernetes 入门指南",
"url": "https://jimmysong.io/blog/kubernetes-intro/",
"content": "Kubernetes 是一个开源容器编排平台..."
}
}
// 响应
{
"answer": "Kubernetes 是一个开源的容器编排平台...",
"sources": [
{
"id": "a1b2c3d4e5f6-0",
"url": "https://jimmysong.io/blog/kubernetes-intro/",
"title": "Kubernetes 入门指南",
"source": "zh/blog/kubernetes-intro/index.md"
}
]
}
代码流程:端到端管道¶
摄取管道(scripts/fast-ingest.ts)¶
- 文件发现:
globby()扫描 content 目录下.md文件 - 并行处理:同时处理
MAX_CONCURRENT_FILES个文件 - 内容提取:
matter.js解析 frontmatter,markdownToPlain()转为纯文本 - 中文分块:
chunkText()按标题与句子边界分割 - 批量嵌入:
getBatchEmbeddings()高效批量处理分块 - 元数据生成:
toUrlFromPath()生成 URL,generateShortId()生成唯一 ID - 向量上传:每批 300 条 upsert 到 Cloudflare Vectorize
嵌入生成(src/providers/embeddings.ts)¶
// 通义千问嵌入(批量处理)
{
model: "text-embedding-v4",
input: ["text1", "text2", ...] // 每次最多 10 条
}
// Gemini 嵌入(单条处理)
{
model: "models/text-embedding-004",
content: { parts: [{ text: "单条文本" }] },
outputDimensionality: 1024
}
向量存储与上传(src/worker.ts)¶
- 鉴权:管理操作需
Bearer ${ADMIN_TOKEN} - 数据校验:向量维度校验(
EMBED_DIM) - 批量操作:每次最多 300 条向量
- 元数据保存:完整元数据与向量一同存储
查询处理(src/rag/retriever.ts)¶
- 查询嵌入:用户问题 → 嵌入向量
- 直接检索:全量向量查询,无语言过滤
- 元数据处理:提取文本上下文和来源信息
- URL 转换:根据语言偏好转换 URL(如需)
- 来源去重:按 URL 去除重复来源
答案生成(src/providers/llm.ts)¶
// 通义千问聊天生成
{
model: "qwen-turbo-latest",
messages: [
{ role: "system", content: "请用中文回答,并在末尾列出来源路径。" },
{ role: "user", content: "构建的提示词包含上下文和问题" }
]
}
// Gemini 内容生成
{
contents: [
{ parts: [{ text: "完整的提示词" }] }
]
}
5. 模型角色与能力¶
text-embedding-v4(通义千问嵌入模型)¶
- 主要功能:文本转 1024 维向量
- 批量处理:每次最多 10 条(高效)
- 语言支持:优化中英文
- 应用场景:
- 摄取时文档分块
- 检索时查询向量化
- API 地址:
https://dashscope.aliyuncs.com/compatible-mode/v1/embeddings
qwen-turbo-latest(通义千问聊天模型)¶
- 主要功能:根据检索文档生成上下文答案
- 输入处理:系统提示 + 用户问题 + 检索上下文
- 输出生成:结构化中文回复,附来源
- 能力:
- 多轮对话
- 上下文感知
- 来源归属
- 语言专属回复
- API 地址:
https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
备选:Gemini 模型¶
- 嵌入:
text-embedding-004(768/1024 维) - 生成:
gemini-2.5-flash-lite - 限制:嵌入不支持批量
- 应用:Qwen 不可用地区的备选方案
端到端示例会话¶
完整摄取示例¶
# 环境变量设置
PROVIDER=qwen
QWEN_API_KEY=sk-xxx
ADMIN_TOKEN=your-admin-token
WORKER_URL=https://your-worker.workers.dev
CONTENT_DIR=../website/content
EMBED_DIM=1024
# 摄取流程
npm run ingest
处理流程:
- 发现:
../website/content/zh/blog/kubernetes/index.md - 提取:
title: "Kubernetes 容器编排",内容:Kubernetes是... - 分块:约 800 字分 3 块
- 嵌入:批量请求 → 3 个 1024 维向量
- 上传:
{"id": "a1b2c3d4e5f6-0", "vector": [...], "metadata": {...}}
完整查询示例¶
// 用户查询
const query = "Kubernetes 是什么?";
// 1. 查询嵌入
const queryVector = await embedder.embed([query]); // [0.123, -0.456, ...]
// 2. 向量检索
const results = await VECTORIZE.query(queryVector, {
topK: 8,
filter: { metadata: { language: 'zh' } }
});
// 3. 上下文组装
const contexts = results.matches
.map(m => m.metadata.text)
.join('\n---\n');
// 4. Prompt 构建
const prompt = `
基于以下上下文回答用户问题:
${contexts}
用户问题:${query}
请用中文回答,并在末尾列出参考来源。
`;
// 5. LLM 生成
const answer = await llmGenerate(env, prompt);
// 6. 响应组装
return {
answer: "Kubernetes 是一个开源的容器编排平台...",
sources: [
{
url: "https://jimmysong.io/blog/kubernetes-intro/",
title: "Kubernetes 入门指南"
}
]
};
小部件集成示例¶
<!-- 网站集成 -->
<script
src="/path/to/widget.js"
data-endpoint="https://your-worker.workers.dev"
defer
></script>
<div id="ai-chatbot"></div>
用户交互流程:
- 用户打开聊天窗口
- 输入:"什么是微服务架构?"
- 小部件 POST 到
/chat接口 - RAG 管道处理查询
- 返回结构化答案与来源链接
- 小部件展示答案并支持复制
性能特性¶
- 摄取速度:约 1000 文档/分钟(Qwen 批量处理)
- 查询延迟:端到端约 3-5 秒(简化后速度提升)
- 向量存储:每 1 万文档约 50MB
- 并发用户:Cloudflare Workers 边缘网络可扩展
- 语言支持:中英文自动检测与切换(无过滤延迟)
可用文档¶
核心功能¶
- 目录递归处理 - 批量目录处理功能详解
- 🎯 递归文件发现与处理
- 🛡️ 智能草稿过滤机制
- 📊 进度跟踪与性能监控
- 🔧 最佳实践与故障排查
技术深度¶
- Vectorize 结构文档 - 向量数据库结构与设计
- Markdown 向量映射 - 内容处理管道
- 检索流程 - 查询处理与上下文检索
- 答案生成流程 - LLM 集成与回复格式化
高级主题¶
详细文档下一步¶
- 搭建与部署指南 - 步骤化安装说明
- API 参考 - 完整接口文档
- 配置指南 - 环境变量与自定义
- 故障排查 - 常见问题与解决方案
- 性能优化 - 大规模部署调优
- 集成示例 - 更多前端集成模式
向量 ID 查询与删除¶
在 RAG 管道维护和调试过程中,可能需要定位、查询或删除特定的向量记录。以下为常用操作方法:
查询最新向量 ID¶
可通过内置脚本快速获取最近上传的向量 ID 列表,便于后续定位和管理。
# 查询最新向量 ID
npm run fetch-vector-ids
- 默认输出最近批次的向量 ID 列表(通常为 100 条)。
- 可根据脚本参数调整数量或过滤条件,详见
scripts/fetch-latest-vector-ids.ts。
根据 URL 查询向量 ID¶
如需根据内容 URL 查询对应的向量 ID,可使用:
npm run search-url -- --url="https://jimmysong.io/blog/kubernetes-intro/"
- 支持通过 URL 精确定位相关向量。
- 输出包含匹配的向量 ID 及其元数据信息。
删除指定向量 ID¶
如需删除某些已知 ID 的向量,可使用以下命令:
# 删除单个或多个向量 ID
npm run delete-vector-ids -- --ids="a1b2c3d4e5f6-0,a1b2c3d4e5f6-1"
- 支持逗号分隔批量删除。
- 删除操作不可逆,请谨慎操作。
- 具体逻辑见
scripts/delete-vectors-by-id.ts。
常见场景举例¶
- 内容误上传:先通过查询脚本定位 ID,再用删除脚本移除。
- 数据清理:批量导出 ID 后,分批删除无效或过期向量。