为什么你的 RAG 应用可能并不需要向量数据库
- 作者

- 姓名
- Nino
- 职业
- Senior Tech Editor
在当前的生成式 AI 开发中,检索增强生成(Retrieval-Augmented Generation, RAG)的标准蓝图似乎已经变得固定化:将文档块转化为向量,存入如 Pinecone 或 pgvector 这样的向量数据库,然后在每次用户查询时进行相似度搜索。虽然这种架构功能强大,但它引入了显著的操作复杂性、延迟和成本。对于许多通过 n1n.ai 使用高性能 LLM API 的开发者来说,这种复杂性往往是过剩的。
从本质上讲,RAG 只有三个核心步骤:找到相关的文本、将其粘贴到提示词中、让模型据此回答。这个定义中没有任何环节规定“寻找”步骤必须是向量搜索。如果你的知识库专注于特定领域且词汇一致,传统的关键字匹配往往能找回与向量搜索相同的文本块,而且无需嵌入(Embedding)、无需向量存储、无需额外的网络跳数,甚至完全不需要数据库。
关键字检索的优势分析
语义搜索(即向量嵌入)在处理模糊语言时表现出色。它能理解“心情低落”和“抑郁”是相关的。然而,许多专业知识库并不存在这个问题。在法律、医疗或特定产品文档等领域,用户倾向于使用该领域的专业术语。当查询和正确章节共享实际词汇时,关键字重叠的检索效果极佳。
关键字检索带来了向量无法提供的特性:
- 确定性:同样的查询总是返回相同的文本块,便于调试和测试。
- 零基础设施:索引可以作为纯文本或 JSON 随代码一同部署,存储在内存中。
- 极低延迟:省去了每次查询时调用 Embedding API 的开销。在配合 n1n.ai 提供的 DeepSeek-V3 等高速模型时,这种延迟的降低能显著提升用户体验。
实现零基础设施的检索引擎
我们可以通过以下三个步骤在边缘计算平台(如 Cloudflare Workers)上构建一个轻量级检索器:
1. 基于结构的文档分块
不要盲目地按 Token 长度切分。建议根据 Markdown 的 ## 标题进行切分。这样每个块都是一个逻辑完整的章节,标题本身就是极强的关键字信号。
2. 预计算关键字集
在构建索引时(Build Time),将每个块转换为一个去重后的关键字集合。我们需要进行转小写、去除停用词(Stopwords)以及过滤短字符操作。
// 示例:简单的分词与过滤逻辑
function tokenize(text: string): string[] {
const words = text.toLowerCase().match(/[a-z0-9\u4e00-\u9fa5]+/gi) ?? []
return [...new Set(words.filter((w) => w.length > 1 && !STOPWORDS.has(w)))]
}
3. 使用 Jaccard 相似度进行评分
对于用户的查询,将其分词后与每个块的预计算关键字集进行对比。计算交集大小除以并集大小(Jaccard Similarity)。
function retrieve(query: string, chunks: Chunk[], topK = 3): Chunk[] {
const qSet = new Set(tokenize(query))
if (qSet.size === 0) return []
return chunks
.map((chunk) => {
const overlap = [...qSet].filter((t) => chunk.keywords.includes(t)).length
const union = new Set([...qSet, ...chunk.keywords]).size
return { chunk, score: union ? overlap / union : 0 }
})
.filter((c) => c.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, topK)
.map((c) => c.chunk)
}
进阶:从 Jaccard 到 BM25
如果你的查询较长,或者希望更精确地衡量词项的重要性,可以将 Jaccard 换成 BM25。BM25 是搜索引擎过去几十年一直使用的经典排名函数,它会给稀有词项(Rare Terms)赋予更高的权重,并能更好地处理文档长度差异,而这一切依然是纯粹的数学计算,无需任何向量模型。
什么时候必须使用向量数据库?
虽然关键字检索非常高效,但当出现以下信号时,说明你已经超出了该方案的适用范围:
- 语义模糊性:用户经常使用描述性语言而非专业术语(例如问“我感觉很迷茫”而不是询问具体的心理咨询流程)。
- 语料库规模:当你的文档总量超过几兆字节,导致内存扫描变慢或超出边缘函数的内存限制时。
- 跨语言需求:需要用中文查询检索英文文档。
在这些情况下,你可以通过 n1n.ai 接入高性能的 Embedding 模型,或者构建混合搜索(Hybrid Search)系统,将关键字的精准度与向量的泛化能力结合起来。
生产环境的最佳实践
在构建基于 n1n.ai 的 RAG 应用时,有几个核心建议:
- 保持状态外部化:在 Serverless 环境中,使用 Redis 或 Cloudflare KV 存储会话历史,而不是存储在函数内存中。
- 引用溯源:在拼接 Prompt 时,务必包含源文档的标题和链接,并指示 Claude 3.5 Sonnet 或 OpenAI o3 等模型必须根据提供的上下文进行引用,严禁幻觉。
- 轻量化调用:避免使用笨重的厂商 SDK,直接使用
fetch调用 n1n.ai 的统一 API 端点,这能显著减小构建包的体积。
决策矩阵:关键字 vs 向量
| 维度 | 关键字匹配 (BM25) | 向量搜索 (Embeddings) |
|---|---|---|
| 基础设施 | 零(代码即索引) | 需要数据库与 Embedding 服务 |
| 查询成本 | 零 | 每次查询需支付 Embedding 费用 |
| 响应速度 | 极快 (< 10ms) | 较慢 (100-500ms) |
| 适用场景 | 术语明确的垂直领域 | 泛化意图、长尾语义查询 |
| 更新频率 | 随代码部署更新 | 需要重新跑 Embedding 任务 |
总结
不要因为某个架构图告诉你 RAG 必须长什么样就盲目跟风。RAG 的本质是“检索”加“生成”。对于许多中小型项目,在内存中进行关键字匹配不仅成本更低、速度更快,而且维护起来也更简单。当你通过 n1n.ai 获得顶尖模型的能力后,优化检索环节将成为提升 AI 应用质量的关键。
Get a free API key at n1n.ai