从零到一打造RAG智能问答系统:当《蛊真人》遇上了AI

一场意外的邂逅

你有没有想过,让AI陪你重温一部百万字的小说?想象一下,你可以随时向AI提问:"方源为什么要炼制定仙游?"或者"白凝冰的最终结局是什么?"------AI不仅能够准确回答,还能引用小说原文作为佐证。

这不是科幻小说,而是我最近完成的一个小项目:基于RAG(Retrieval-Augmented Generation,检索增强生成)技术的智能问答系统。今天,我想和你分享这个从零到一的实战过程,让《蛊真人》这部经典网络小说遇上了现代AI技术。

什么是RAG?为什么要做RAG?

在深入了解代码之前,让我们先理解RAG的核心思想。

传统的大语言模型(LLM)虽然强大,但它们有天然的局限:知识截止日期固定,无法获取最新信息;训练数据有限,对于特定领域的专业知识了解不足;而且容易产生"幻觉",即编造不存在的事实。

RAG技术正是为解决这些问题而生。它的工作流程简单而优雅:

  1. 知识库构建:将你的文档(无论是小说、技术文档还是法律条文)切割成小块,并向量化存储
  2. 语义检索:当用户提问时,系统将问题向量化,在知识库中检索最相关的文档片段
  3. 增强生成:将检索到的相关片段作为上下文,连同用户问题一起交给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,这是一种高效的近似最近邻搜索算法,在海量数据中也能快速检索。

第四步:语义搜索------从模糊匹配到智能理解

传统搜索依赖关键词匹配,而语义搜索能理解问题的真实含义。当用户问"贾金生是谁杀的?"时,系统会:

  1. 将问题向量化
  2. 在Milvus中搜索最相似的向量
  3. 返回相关的原文片段
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技术的魅力:

  1. 知识可更新:只需更新向量库,无需重新训练模型
  2. 事实可追溯:每个回答都有原文支持,减少幻觉
  3. 扩展性强:可以轻松接入新文档,构建多领域知识库

当然,也有一些值得改进的地方:

  • 分块策略:对于诗歌、对话等特殊文体,需要更精细的分割策略
  • 重排序(Rerank):初步检索后,可以二次排序提高精度
  • 混合检索:结合关键词搜索和语义搜索,效果更佳

结语:RAG的无限可能

从一部网络小说出发,我构建了一个完整的RAG智能问答系统。这个过程让我深刻理解了RAG的每个环节,也看到了它的广阔应用前景。

无论是企业知识库、智能客服,还是教育辅导系统,RAG都能让AI真正"懂"你的数据,为用户提供准确、可信的智能服务。

正如《蛊真人》中方源的修炼之路一样,RAG技术的探索也是一条充满挑战和机遇的道路。希望这篇文章能为你开启这扇大门,让你也体验当AI遇上知识的美妙时刻。

相关推荐
武子康2 小时前
调查研究-215 Anthropic 双线扩张:从 Claude 模型公司到 AI 工业栈
人工智能·openai·claude
AIGCmagic社区2 小时前
Unlimited OCR 论文精读:R-SWA 如何实现一次性长文档解析
人工智能·算法·aigc
dayuOK63073 小时前
可灵AI获近30亿美元融资,视频大模型赛道开启IPO竞赛
人工智能·ai作画·新媒体运营·aigc·ai写作
怕浪猫3 小时前
第10章 实战项目二:自动化研发运维Agent
aigc·openai·ai编程
武雄(小星Ai)3 小时前
Claude Code vs Codex CLI:两周深度实测,差距比想象的大
ai·openai·开发工具·claude·对比评测
西安老张(AIGC&ComfyUI)15 小时前
第021章:ComfyUI文生音频Qwen3-TTS模型数字人音色设计(二)
aigc·音视频·comfyui
Code_Artist17 小时前
Trae AI 创造力大赛创意作品:AI 数字克隆人——让你有无数个分身!
人工智能·llm·aigc
北鹤M18 小时前
如何将模特导入AI实现电商智能换装,主流工具体验分享
人工智能·aigc
云卷云舒___________18 小时前
Google Gemini 3.5 Pro泄露、GPT-5.6套餐限额放宽、Meta Watermelon追平GPT-5.5 | 7月4日 AI日报
google·meta·openai·gemini·ai日报·gpt56·watermelon