向量问题排查与删除
环境搭建与配置¶
- 环境搭建指南 - 完整的环境变量配置、API 密钥设置与部署说明
1. RAG 管道概览¶
1.1 系统架构¶
- 目标:网站集成 RAG 聊天机器人,基于 Cloudflare Workers
- 核心组件:数据摄取管道 → 向量存储 → 查询处理 → 答案生成
- 数据来源:Hugo 网站 Markdown 文件(
content/
目录) - 后端模型:Gemini 与通义千问(Qwen)可切换
- 基础设施:Cloudflare Workers + Vectorize + 边缘计算
1.2 高层工作流程¶
- 内容摄取:Markdown 文件 → 文本分块 → 嵌入向量 → 向量存储
- 查询处理:用户问题 → 查询嵌入 → 向量相似度检索
- 上下文检索:Top-K 相似分块 → 元数据过滤 → 来源汇总
- 答案生成:检索上下文 + 用户问题 → LLM → 结构化回复
1.3 关键特性¶
- 📁 目录递归处理:批量处理 Hugo 内容目录,智能草稿过滤
- 🌐 多语言支持:中英文内容,语言专属处理
- ⚡ 高性能:简化查询逻辑,无 fallback 延迟
- 🛡️ 草稿感知:兼容 Hugo 草稿过滤及级联继承
2. 实体关系图¶
2.1 数据流架构¶
flowchart TD
A(["Markdown 文件 (.md)\ncontent"]) -->|"matter.js + markdownToPlain()"| B(["纯文本"])
B -->|"chunkText 中文感知"| C(["文本分块,最多 800 字"])
C -->|"Embedding text-embedding-v4 或 text-embedding-004"| D(["向量嵌入,1024 或 768 维"])
D -->|"generateShortId 和元数据"| E(["向量记录"])
E -->|"Cloudflare Vectorize upsert"| F(["向量索引"])
subgraph 检索
Q(["用户查询"]) -->|"Embed"| QV(["查询向量"])
QV -->|"相似度与过滤"| F
F -->|"Top-K 匹配"| R(["检索上下文与来源"])
R -->|"Prompt 与上下文"| L(["LLM: qwen-turbo-latest 或 gemini-2.5-flash-lite"])
L -->|"答案与来源"| O(["生成答案与来源"])
end
style A fill:#fff5e6,stroke:#f5a623,rx:10,ry:10
style B fill:#fff,stroke:#ccc,rx:10,ry:10
style C fill:#e6f7ff,stroke:#1890ff,rx:10,ry:10
style D fill:#f0e6ff,stroke:#722ed1,rx:10,ry:10
style E fill:#fff,stroke:#ccc,rx:10,ry:10
style F fill:#e8ffe6,stroke:#52c41a,rx:10,ry:10
style Q fill:#fff,stroke:#ccc,rx:10,ry:10
style QV fill:#fff,stroke:#ccc,rx:10,ry:10
style R fill:#fff,stroke:#ccc,rx:10,ry:10
style L fill:#fff0f6,stroke:#eb2f96,rx:10,ry:10
style O fill:#f6ffed,stroke:#389e0d,rx:10,ry:10
2.2 核心实体¶
erDiagram
MARKDOWN_FILE ||--o{ TEXT_CHUNK : "包含"
TEXT_CHUNK ||--|| VECTOR_EMBEDDING : "嵌入为"
TEXT_CHUNK ||--|| METADATA : "由...描述"
QUERY ||--o{ QUERY_RESULT : "产生"
VECTOR_EMBEDDING ||--o{ QUERY_RESULT : "匹配"
MARKDOWN_FILE {
string path
string title
string language
string frontmatter
}
TEXT_CHUNK {
string id
string text
int index
string sourcePath
}
METADATA {
string title
string url
string language
string source
}
VECTOR_EMBEDDING {
string id
float[] values
int dimension
}
QUERY {
string id
string text
string language
}
QUERY_RESULT {
string vectorId
float score
}
2.3 实体说明¶
- Markdown 文件:原始 Hugo 内容(frontmatter + 正文)
- 文本分块:按标题与句子分割的片段
- 向量嵌入:数值表示(依赖模型维度)
- 元数据:附加于每个分块,用于过滤与归属
- 查询结果:相似度匹配列表
3. 详细数据格式¶
3.1 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 }[];
}>;
}
3.2 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"
}
// 响应
{
"answer": "Kubernetes 是一个开源的容器编排平台...",
"sources": [
{
"id": "a1b2c3d4e5f6-0",
"url": "https://jimmysong.io/blog/kubernetes-intro/",
"title": "Kubernetes 入门指南",
"source": "zh/blog/kubernetes-intro/index.md"
}
]
}
4. 代码流程:端到端管道¶
4.1 摄取管道(scripts/fast-ingest.ts
)¶
- 文件发现:
globby()
扫描 content 目录下.md
文件 - 并行处理:同时处理
MAX_CONCURRENT_FILES
个文件 - 内容提取:
matter.js
解析 frontmatter,markdownToPlain()
转为纯文本 - 中文分块:
chunkText()
按标题与句子边界分割 - 批量嵌入:
getBatchEmbeddings()
高效批量处理分块 - 元数据生成:
toUrlFromPath()
生成 URL,generateShortId()
生成唯一 ID - 向量上传:每批 300 条 upsert 到 Cloudflare Vectorize
4.2 嵌入生成(src/providers/embeddings.ts
)¶
// 通义千问嵌入(批量处理)
{
model: "text-embedding-v4",
input: ["text1", "text2", ...] // 每次最多 10 条
}
// Gemini 嵌入(单条处理)
{
model: "models/text-embedding-004",
content: { parts: [{ text: "单条文本" }] },
outputDimensionality: 1024
}
4.3 向量存储与上传(src/worker.ts
)¶
- 鉴权:管理操作需
Bearer ${ADMIN_TOKEN}
- 数据校验:向量维度校验(
EMBED_DIM
) - 批量操作:每次最多 300 条向量
- 元数据保存:完整元数据与向量一同存储
4.4 查询处理(src/rag/retriever.ts
)¶
- 查询嵌入:用户问题 → 嵌入向量
- 直接检索:全量向量查询,无语言过滤
- 元数据处理:提取文本上下文和来源信息
- URL 转换:根据语言偏好转换 URL(如需)
- 来源去重:按 URL 去除重复来源
4.5 答案生成(src/providers/llm.ts
)¶
// 通义千问聊天生成
{
model: "qwen-turbo-latest",
messages: [
{ role: "system", content: "请用中文回答,并在末尾列出来源路径。" },
{ role: "user", content: "构建的提示词包含上下文和问题" }
]
}
// Gemini 内容生成
{
contents: [
{ parts: [{ text: "完整的提示词" }] }
]
}
5. 模型角色与能力¶
5.1 text-embedding-v4(通义千问嵌入模型)¶
- 主要功能:文本转 1024 维向量
- 批量处理:每次最多 10 条(高效)
- 语言支持:优化中英文
- 应用场景:
- 摄取时文档分块
- 检索时查询向量化
- API 地址:
https://dashscope.aliyuncs.com/compatible-mode/v1/embeddings
5.2 qwen-turbo-latest(通义千问聊天模型)¶
- 主要功能:根据检索文档生成上下文答案
- 输入处理:系统提示 + 用户问题 + 检索上下文
- 输出生成:结构化中文回复,附来源
- 能力:
- 多轮对话
- 上下文感知
- 来源归属
- 语言专属回复
- API 地址:
https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
5.3 备选:Gemini 模型¶
- 嵌入:
text-embedding-004
(768/1024 维) - 生成:
gemini-2.5-flash-lite
- 限制:嵌入不支持批量
- 应用:Qwen 不可用地区的备选方案
6. 端到端示例会话¶
6.1 完整摄取示例¶
# 环境变量设置
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": {...}}
6.2 完整查询示例¶
// 用户查询
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 入门指南"
}
]
};
6.3 小部件集成示例¶
<!-- 网站集成 -->
<script
src="/path/to/widget.js"
data-endpoint="https://your-worker.workers.dev"
defer
></script>
<div id="ai-chatbot"></div>
用户交互流程:
- 用户打开聊天窗口
- 输入:"什么是微服务架构?"
- 小部件 POST 到
/chat
接口 - RAG 管道处理查询
- 返回结构化答案与来源链接
- 小部件展示答案并支持复制
6.4 性能特性¶
- 摄取速度:约 1000 文档/分钟(Qwen 批量处理)
- 查询延迟:端到端约 3-5 秒(简化后速度提升)
- 向量存储:每 1 万文档约 50MB
- 并发用户:Cloudflare Workers 边缘网络可扩展
- 语言支持:中英文自动检测与切换(无过滤延迟)
可用文档¶
核心功能¶
- 目录递归处理 - 批量目录处理功能详解
- 🎯 递归文件发现与处理
- 🛡️ 智能草稿过滤机制
- 📊 进度跟踪与性能监控
- 🔧 最佳实践与故障排查
技术深度¶
- Vectorize 结构文档 - 向量数据库结构与设计
- Markdown 向量映射 - 内容处理管道
- 检索流程 - 查询处理与上下文检索
- 答案生成流程 - LLM 集成与回复格式化
高级主题¶
详细文档下一步¶
- 搭建与部署指南 - 步骤化安装说明
- API 参考 - 完整接口文档
- 配置指南 - 环境变量与自定义
- 故障排查 - 常见问题与解决方案
- 性能优化 - 大规模部署调优
- 集成示例 - 更多前端集成模式
7. 向量 ID 查询与删除¶
在 RAG 管道维护和调试过程中,可能需要定位、查询或删除特定的向量记录。以下为常用操作方法:
7.1 查询最新向量 ID¶
可通过内置脚本快速获取最近上传的向量 ID 列表,便于后续定位和管理。
# 查询最新向量 ID
npm run fetch-vector-ids
- 默认输出最近批次的向量 ID 列表(通常为 100 条)。
- 可根据脚本参数调整数量或过滤条件,详见
scripts/fetch-latest-vector-ids.ts
。
7.2 根据 URL 查询向量 ID¶
如需根据内容 URL 查询对应的向量 ID,可使用:
npm run search-url -- --url="https://jimmysong.io/blog/kubernetes-intro/"
- 支持通过 URL 精确定位相关向量。
- 输出包含匹配的向量 ID 及其元数据信息。
7.3 删除指定向量 ID¶
如需删除某些已知 ID 的向量,可使用以下命令:
# 删除单个或多个向量 ID
npm run delete-vector-ids -- --ids="a1b2c3d4e5f6-0,a1b2c3d4e5f6-1"
- 支持逗号分隔批量删除。
- 删除操作不可逆,请谨慎操作。
- 具体逻辑见
scripts/delete-vectors-by-id.ts
。
7.4 常见场景举例¶
- 内容误上传:先通过查询脚本定位 ID,再用删除脚本移除。
- 数据清理:批量导出 ID 后,分批删除无效或过期向量。