如何在 ai-engineering-hub 构建带来源引用的 NotebookLM 风格文档问答系统(Milvus + Zep 记忆)?
【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub
ai-engineering-hub仓库中的notebook-lm-clone/子项目实现了一个开源版 NotebookLM:把 PDF、文本等文档切块、向量化后存入 Milvus,提问时通过 RAG 检索生成带[1]、[2]引用标注的回答,并引用来源文件名与页码;对话历史则写入 Zep 的时序知识图谱,跨轮次保留上下文。本文按「配置环境 → 文档入库 → 带引用问答 → Zep 记忆 → Web UI」的顺序,把这条链路在本地跑通并逐项验证。
运行前提以项目文档为准:Python 3.11(pyproject.toml 要求>=3.11,<3.13),使用uv管理依赖;OPENAI_API_KEY为必填,ZEP_API_KEY在需要对话记忆时必填,ASSEMBLYAI_API_KEY和FIRECRAWL_API_KEY仅音频转录与网页抓取场景需要。
安装与配置:uv 环境加四个 API Key
在notebook-lm-clone/目录下按 README.md 的步骤安装依赖(MacOS/Linux):
uv venv source .venv/bin/activate # 安装依赖 uv sync # Additional steps (recommended) uv add -U yt-dlp # for latest version uv pip install pip # pip for TTS model dependenciesWindows 下激活虚拟环境改用.venv\Scripts\activate。依赖中对本场景直接相关的是pymilvus[milvus-lite]>=2.6.2(本地 Milvus)、zep-cloud>=3.4.3与zep-crewai>=1.1.1(记忆层)、fastembed(嵌入生成)、crewai(LLM 封装)。
然后在项目根目录创建.env,格式参照 .env.example:
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY> ASSEMBLYAI_API_KEY=<YOUR_ASSEMBLYAI_API_KEY> FIRECRAWL_API_KEY=<YOUR_FIRECRAWL_API_KEY> ZEP_API_KEY=<YOUR_ZEP_API_KEY>其中<YOUR_...>替换为你在各服务申请的 Key。只跑文档问答可以只填OPENAI_API_KEY和ZEP_API_KEY,后两个留空或去掉。
核心链路:引用元数据从哪来
先明确各环节角色,后面验证输出时才好判断:
| 环节 | 文件 | 关键点 |
|---|---|---|
| 文档切块 | doc_processor.py | PyMuPDF 解析,默认chunk_size=1000、chunk_overlap=200;PDF 逐页处理并记录page_number;支持.pdf、.txt、.md,其他格式抛Unsupported file format |
| 向量化 | embedding_generator.py | fastembed 的TextEmbedding,默认模型BAAI/bge-small-en-v1.5 |
| 向量存储 | milvus_vector_db.py | 默认MilvusClient(uri="./milvus_lite.db")(本地文件型 Milvus),集合名notebook_lm,embedding_dim=384;schema 含source_file、source_type、page_number、chunk_index、start_char、end_char等引用字段 |
| 引用问答 | rag.py | RAGGenerator默认模型gpt-4o-mini、temperature=0.1、max_tokens=2000;generate_response默认top_k=10、max_chunks=8、max_context_chars=4000 |
| 对话记忆 | memory_layer.py | NotebookMemoryLayer基于 Zep Cloud 客户端 +ZepUserStorage,默认mode="summary"、indexing_wait_time=10 |
引用的生成逻辑在RAGGenerator._format_context_with_citations:把检索到的每个 chunk 前缀标注成[1]、[2]……,同时收集source_file、page_number、chunk_id、relevance_score到sources_used;提示词要求模型「每条事实性陈述都带上引用编号,且只用上下文中的信息」。get_citation_summary()会把来源汇总成• 文件名 (类型) - Page N的形式。
注意embedding_dim默认 384 对应默认嵌入模型的输出维度;如果要换嵌入模型,需要给MilvusVectorDB传入与该模型一致的embedding_dim。
步骤一:把样例文档索引进 Milvus
仓库自带样例文档data/raft.pdf,milvus_vector_db.py的__main__块就是一条现成的入库脚本:解析data/raft.pdf→ 生成嵌入 →create_index()→insert_embeddings()→ 用 “What is the main topic?” 做一次检索。在 milvus_vector_db.py 文件所在的项目根目录执行:
uv run src/vector_database/milvus_vector_db.py预期输出(以运行日志为准,文档中未给出固定数值):
- 日志出现
Collection 'notebook_lm' created successfully(首次运行;再次运行会提示集合已存在); - 一行
Inserted N embeddings,N 为该 PDF 切出的 chunk 数; - 随后打印 top-5 检索结果,每条含
Score、Content前 200 字符和Citation——Citation里应能看到source_file为raft.pdf及页码信息。
如果引用字段正确出现在Citation里,说明入库链路没问题,可以进入问答。
步骤二:生成带引用的回答
rag.py 的__main__块是第二个验证点,它会先检查OPENAI_API_KEY,缺了会打印Please set OPENAI_API_KEY environment variable并退出。设置好后执行:
uv run src/generation/rag.py该脚本用测试问题 “What are the main findings discussed in the documents?” 调用generate_response,并额外调用generate_summary(summary_length="medium")。验证要点:
- 打印出
Query:、Response:,回答中的事实性陈述应带[1]、[2]形式引用; Sources Used (N)下面是引用摘要,格式为• raft.pdf (pdf) - Page N;- 如果库中没有检索到相关 chunk,
response会是固定文案I couldn't find any relevant information in the available documents to answer your question.,且sources_used为空——这时先回到步骤一确认数据已入库。
步骤三:接入 Zep 记忆层
memory_layer.py 的__main__块演示记忆层完整生命周期,使用固定身份user_id="test_user"、session_id="test_session_123"且create_new_session=True。执行:
uv run src/memory/memory_layer.py流程是:确保 user 存在 → 删除旧 thread 并创建新 thread(create_new_session=True时会调用zep_client.thread.delete再thread.create,即重置该会话的记忆)→ 保存一轮模拟对话(注意:这里写入的是硬编码的 mockRAGResult,用来打通存储链路,不是真实检索结果)→wait_for_indexing()等待默认 10 秒的 Zep 索引 → 读取上下文、图检索、会话摘要。
成功判定:依次打印Conversation Context:、Relevant Memories: N found、Session Summary: {...},最后一行是Memory integration test completed successfully。其中Session Summary会给出total_messages、user_messages、assistant_messages、context_available等字段,context_available为True说明 Zep 已完成索引并能返回会话上下文。
另外两个值得知道的接口:get_relevant_memory(query)走zep_client.graph.search(scope="episodes")做语义图检索;clear_session()会删除并重建当前 thread,运行前确认session_id就是要清空的那个会话。
步骤四:跑通 Web UI 与整条管线
Web 界面入口是 app.py,启动后在浏览器打开:
uv run app.py # 或 streamlit run app.py应用打开在http://localhost:8501,三栏布局:来源面板、聊天区和 Studio。上传文档后提问,回答中的[n]引用被渲染成可交互标注——悬停时通过vector_db.get_chunk_by_id(chunk_id)回查该 chunk 原文,弹出来源文件名、页码和最多 300 字符的 chunk 内容摘要,这就是「来源可核对」在界面上的落地形式。
最后可以用 tests/notebook_pipeline.py 做一次端到端串联,它按固定顺序执行:文档处理(process_documents→create_index(use_binary_quantization=False)→ 插入嵌入)→ 三个预设问题的问答(每轮结果若配置了 Zep 会通过save_conversation_turn写入记忆)→ 读取记忆上下文。执行:
uv run tests/notebook_pipeline.py两个前提:
- 开头的环境检查会列出
OPENAI_API_KEY: ✅/❌ REQUIRED和三个可选 Key 的状态;缺OPENAI_API_KEY时直接以Missing required OPENAI_API_KEY - cannot proceed退出。 test_pipeline里的test_documents列表默认是空的(只有注释占位),不填会打印No test documents provided - skipping document test而跳过入库。在你自己的副本中把文档路径填入该列表,例如data/raft.pdf,入库环节才会实际执行。
成功标志是结尾日志PIPELINE TEST COMPLETED SUCCESSFULLY!;问答部分会对每个问题打印A:回答和Sources: N documents used以及前 3 条引用的文件名与页码。
排查与限制
- 引用里没有页码:
page_number为空的 chunk 在入库时会被写成-1,检索结果格式化时转为None,引用行只显示文件名。文本类来源(source_type='txt')本身没有页码,属预期行为;PDF 来源若仍无页码,先确认走的是_process_pdf路径。 - 问答返回错误文案:
generate_response捕获异常后会返回I encountered an error while processing your question: ...并附异常信息,此时看前面日志的Error generating response定位是检索还是 LLM 调用失败。 - Zep 上下文读不到:刚写入的消息要等索引完成,代码里用
wait_for_indexing()(默认 sleep 10 秒)处理;get_conversation_context()失败时返回No conversation context available而不是抛错。 - 记忆层未生效:
tests/notebook_pipeline.py中只有检测到ZEP_API_KEY才会初始化NotebookMemoryLayer,否则memory为None,问答照常但没有记忆写入。 - 文档格式:
DocumentProcessor只接受.pdf、.txt、.md;音频、YouTube、网页分别依赖 AssemblyAI / Firecrawl 链路,不在本文的文档问答主路径内。 - Milvus 数据位置:向量数据默认落在当前目录的
./milvus_lite.db文件中,集合名固定为notebook_lm;想清空重建可调用delete_collection(),该操作会删除集合内全部数据,执行前确认是目标集合。
参考路径
- 安装、
.env配置与启动命令:notebook-lm-clone/README.md - 引用与检索参数:notebook-lm-clone/src/generation/rag.py
- 集合 schema 与引用字段:notebook-lm-clone/src/vector_database/milvus_vector_db.py
- Zep 记忆层:notebook-lm-clone/src/memory/memory_layer.py
- 端到端管线:notebook-lm-clone/tests/notebook_pipeline.py
- 仓库内还有逐步走查的 notebook-lm-walkthrough.ipynb,可作为下一步深入各模块的入口。
【免费下载链接】ai-engineering-hubIn-depth tutorials on LLMs, RAGs and real-world AI agent applications.项目地址: https://gitcode.com/GitHub_Trending/ai/ai-engineering-hub
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考