一场意外的邂逅
你有没有想过,让AI陪你重温一部百万字的小说?想象一下,你可以随时向AI提问:"方源为什么要炼制定仙游?"或者"白凝冰的最终结局是什么?"------AI不仅能够准确回答,还能引用小说原文作为佐证。
这不是科幻小说,而是我最近完成的一个小项目:基于RAG(Retrieval-Augmented Generation,检索增强生成)技术的智能问答系统。今天,我想和你分享这个从零到一的实战过程,让《蛊真人》这部经典网络小说遇上了现代AI技术。
什么是RAG?为什么要做RAG?
在深入了解代码之前,让我们先理解RAG的核心思想。
传统的大语言模型(LLM)虽然强大,但它们有天然的局限:知识截止日期固定,无法获取最新信息;训练数据有限,对于特定领域的专业知识了解不足;而且容易产生"幻觉",即编造不存在的事实。
RAG技术正是为解决这些问题而生。它的工作流程简单而优雅:
- 知识库构建:将你的文档(无论是小说、技术文档还是法律条文)切割成小块,并向量化存储
- 语义检索:当用户提问时,系统将问题向量化,在知识库中检索最相关的文档片段
- 增强生成:将检索到的相关片段作为上下文,连同用户问题一起交给LLM,生成最终答案
这种"检索+生成"的模式,让AI不再凭空想象,而是基于真实文档回答问题。就像给AI配备了一个可以随时查阅的图书馆。
技术选型:为什么选择这些工具?
在开始编码之前,我需要选择合适的工具链。经过调研,我确定了以下技术栈:
- 向量数据库:Milvus(开源向量数据库,性能优异,支持百亿级向量检索)
- Embedding模型:OpenAI的text-embedding-3-small(1024维,平衡了效果和成本)
- LLM:通过OneAPI配置的DeepSeek模型(性价比高,中文能力强)
- 文档处理:LangChain框架(提供了丰富的文档加载器和分割工具)
这些工具的组合,构成了一个完整的RAG流水线。
第一步:环境搭建与数据准备
项目的第一步是配置环境。我使用.env文件管理敏感信息:
env
MILVUS_ADDRESS=your_milvus_address
MILVUS_TOKEN=your_milvus_token
OPENAI_API_KEY=your_api_key
OPENAI_BASE_URL=your_base_url
EMBEDDING_MODEL_NAME=text-embedding-3-small
MODEL_NAME=deepseek-chat
数据准备阶段,我下载了《蛊真人》的纯文本版本。这是一部约800万字的长篇小说,恰好适合测试RAG系统的性能。
第二步:知识库构建------让机器理解小说
知识库构建是整个系统的基石。我设计了以下流程:
2.1 章节识别与分割
小说有其天然的结构------章节。我实现了一个splitByChapters函数,使用正则表达式匹配"第一节"、"第二节"等标记:
javascript
function splitByChapters(text) {
const chapterRegex = /第[一二三四五六七八九十百千万]+节/g;
// 查找所有章节标题,按位置分割文本
// 返回包含章节内容和元数据的数组
}
这样做的好处是保持了小说的叙事完整性,每个片段都包含完整的故事情节。
2.2 文本分块(Chunking)
对于长文档,需要进一步分割成适合向量化的小块。我使用RecursiveCharacterTextSplitter,设置块大小为500字符,重叠50字符:
javascript
const textSplitter = new RecursiveCharacterTextSplitter({
chunkSize: 500,
chunkOverlap: 50,
});
重叠部分确保上下文不丢失,就像两个相邻的拼图块有重叠的边缘。
2.3 向量化与存储
对每个文本块,我调用OpenAI的Embedding API将其转换为1024维的向量:
javascript
async function getEmbedding(text) {
const result = await embeddings.embedQuery(text);
return result;
}
然后,将这些向量连同元数据(章节号、片段索引、书籍名称等)一起存入Milvus:
javascript
const insertData = await Promise.all(
chunks.map(async (chunk, chunkIndex) => {
const vector = await getEmbedding(chunk);
return {
id: `${bookId}_${chapterNum}_${chunkIndex}`,
book_id: bookId,
book_name: BOOK_NAME,
chapter_num: chapterNum,
index: chunkIndex,
content: chunk,
vector
}
})
)
为了提升性能,我使用Promise.all并发处理向量生成,大大加快了插入速度。
第三步:集合管理------Milvus的优雅设计
在Milvus中,数据以"集合"(Collection)的形式组织。我设计了一个ensureBookCollection函数,负责集合的创建、索引构建和加载:
javascript
async function ensureBookCollection(bookId) {
// 检查集合是否存在
const hasCollection = await client.hasCollection({
collection_name: COLLECTION_NAME,
});
// 如果不存在,创建集合并定义schema
// 字段包括:id, book_id, book_name, chapter_num, index, content, vector
// 创建向量索引,使用IVF_FLAT算法和COSINE相似度
await client.createIndex({
collection_name: COLLECTION_NAME,
field_name: 'vector',
index_type: IndexType.IVF_FLAT,
metric_type: MetricType.COSINE,
});
// 加载集合到内存
await client.loadCollection({
collection_name: COLLECTION_NAME
});
}
索引类型选择IVF_FLAT,这是一种高效的近似最近邻搜索算法,在海量数据中也能快速检索。
第四步:语义搜索------从模糊匹配到智能理解
传统搜索依赖关键词匹配,而语义搜索能理解问题的真实含义。当用户问"贾金生是谁杀的?"时,系统会:
- 将问题向量化
- 在Milvus中搜索最相似的向量
- 返回相关的原文片段
javascript
const searchResult = await client.search({
collection_name: COLLECTION_NAME,
vector: queryVector,
limit: 3,
metric_type: MetricType.COSINE,
output_fields: ['id', 'content', 'book_id', 'chapter_num', 'index', 'book_name'],
})
搜索结果按相似度得分排序,得分越高表示相关性越强。
第五步:智能问答------让知识活起来
最后,我将检索到的相关内容作为上下文,构建Prompt送给LLM:
javascript
const prompt = `
你是一个专业的《蛊真人》小说助手。基于小说内容回答问题...
请根据以下小说片段内容回答问题:
${context}
用户问题:${question}
回答要求:
1. 如果有相关信息,给出详细准确的回答
2. 可以综合多个片段的内容
3. 如果没有相关信息,如实告知
4. 可以引用原文内容
AI助手的回答:
`
这个Prompt设计有几个关键点:
- 角色扮演:明确AI的身份是"小说助手"
- 上下文注入:将检索到的片段作为事实依据
- 约束条件:要求基于片段回答,避免编造
- 引用支持:鼓励引用原文增强可信度
性能优化:让系统飞起来
在实际开发中,我遇到了一些性能瓶颈,并做了针对性优化:
批量向量化
最初,每个文本块单独调用Embedding API,处理800万字耗时数小时。改为Promise.all并发后,速度提升了近10倍。
索引优化
选择合适的索引类型和参数至关重要。对于1024维的向量,我设置nlist=1024,在搜索精度和速度之间取得平衡。
分章处理
按章节处理而不是一次性加载全部文本,既节约内存,又便于错误恢复。
效果展示
经过完整的流程,我的《蛊真人》问答系统可以回答各种问题:
- 事实性问题:"方源的第一个分身是什么?" → 系统检索到相关章节,准确回答"何春秋"
- 推理问题:"为什么方源要选择血道?" → 综合多个片段,分析人物动机
- 人物关系:"白凝冰和方源的关系演变" → 从不同章节提取信息,梳理完整脉络
经验总结与反思
通过这个项目,我深刻体会到RAG技术的魅力:
- 知识可更新:只需更新向量库,无需重新训练模型
- 事实可追溯:每个回答都有原文支持,减少幻觉
- 扩展性强:可以轻松接入新文档,构建多领域知识库
当然,也有一些值得改进的地方:
- 分块策略:对于诗歌、对话等特殊文体,需要更精细的分割策略
- 重排序(Rerank):初步检索后,可以二次排序提高精度
- 混合检索:结合关键词搜索和语义搜索,效果更佳
结语:RAG的无限可能
从一部网络小说出发,我构建了一个完整的RAG智能问答系统。这个过程让我深刻理解了RAG的每个环节,也看到了它的广阔应用前景。
无论是企业知识库、智能客服,还是教育辅导系统,RAG都能让AI真正"懂"你的数据,为用户提供准确、可信的智能服务。
正如《蛊真人》中方源的修炼之路一样,RAG技术的探索也是一条充满挑战和机遇的道路。希望这篇文章能为你开启这扇大门,让你也体验当AI遇上知识的美妙时刻。