1. 这项目到底想解决什么问题
1.1 学习场景里的真实痛点
先说说我为什么注意到这个项目。有段时间我需要啃一本四百多页的技术文档,看到后面忘前面,划重点划得满屏荧光色,真到用的时候还是想不起某个参数在哪一章出现过。找同事问吧,人家也没义务给你当导师;去搜索引擎翻吧,查到的还未必是手上这份版本的内容。当时我就想,如果有个东西能“住”在我电脑里,把我喂给它的这本书变成可检索、可追问、可出题的材料,那学习效率至少翻一倍。
这个项目本质上就是把大语言模型变成一个私人的、基于你文档的导师。它的核心工作方式可以概括成两句话:把你的书和文档切碎、索引;再通过问答和练习,让你和这些内容交互。和那种“随便聊聊天”的通用对话不一样,它所有回答都限定在你提供的资料范围内,你可以拿一份产品手册、一套内部规范、一本教材直接丢进去,然后让它按章节带你学。
正是这些特征让它在技术社区被贴上了 Show HN 的标签——在技术社区,大家晒出来的东西往往不只是演示,而是能真正跑起来、能自己部署的项目。对普通学习者来说,这意味着你不用把书页截图发给任何第三方,也不用担心哪天某个在线服务停了导致笔记白整理。一切都在本地。
1.2 self-hosted 意味着什么
Self-hosted(自托管)这个词听起来有点“折腾”,但实际含义很直接:项目跑在你自己的服务器或开发机上,文档和计算都在自己可控的范围内。这带来三个直接的好处。
第一是隐私。书可能是你花钱买的电子版,文档可能是公司的内部资料,笔记里可能还有私人碎碎念。这些东西如果交给一个在线知识库服务,等于把学习行为暴露给第三方。自托管之后,除了你主动接入的模型接口,没有任何中间商能看到你的原文。第二是可控性。在线服务说改版就改版,说限流就限流,说下架就下架。自己部署的版本只依赖两个东西:本机的运行环境,以及一个可用的模型接口。模型接口你还可以换成开源模型跑在本地,把整套流程完全封闭在自己的网络里。第三是成本。很多人在书和课程上花的钱不少,但是知识库服务按月付费,算下来一年也不少。自托管虽然要花点工夫,但长期用几乎是纯硬件成本。
当然,自托管也有门槛:你需要一点 Docker 或者命令行的基础,需要一台至少能跑得起嵌入模型和推理服务的机器。如果你只是想随便试试,先拿个几百MB的书本篇跑一遍也是可以的。这个项目给我最大的启发是,它把“私教”这件事的成本压到了几乎为零,而效果取决于你怎么用它。
2. 为什么选RAG路线而不是微调模型
2.1 微调的成本和壁垒
一开始我也困惑:要让模型懂我的书,为什么不直接微调一个模型?后来想明白,微调在这里是“杀鸡用牛刀”,而且用得很难受。
微调意味着你要准备训练样本,把你的文档整理成指令对,可能要几千条,还要做数据清洗、标注、回合训练。你的书如果更新了一章,你就要重新标注一部分数据、重新跑一轮训练。对个人和中小团队来说,这是很高昂的时间成本和算力成本。而且微调之后,模型的知识是“焊死”在参数里的,你不知道它到底记住了什么,出错了也无从查起。更不用说,为了微调一个模型,至少需要一个可训练的推理环境,普通笔记本很难跑得舒服。
相比之下,检索增强生成(Retrieval-Augmented Generation,简称RAG)是完全不同的思路:我不去改模型的固有参数,而是每次提问时,先从一个文档库里捞出相关的片段,再把“问题+片段”一起交给模型,让模型基于这些片段作答。相当于考试时允许你带教材,开卷答题。模型不需要死记硬背,它只需要会阅读、会归纳。
2.2 检索增强生成的教学闭环
用RAG架构来做导师,还有一个额外的好处:可解释性强。传统黑盒模型答错了你很难搞清楚原因;而RAG可以告诉你,这个答案是来自哪个文档、哪个章节、哪个段落。你可以顺着线索回去核验原文,这个能力对学习者非常重要。
在实现上,整个闭环大致是这样:先把文档按逻辑切块,给每一块生成向量表示(嵌入),存进向量库;用户提问时,把问题也转成向量,在库里做相似度检索,选出一批最相关的文本片段;再把这些片段连同用户的历史对话一起塞进 Prompt,交给大语言模型生成回答。回答如果不够,可以继续追问,系统会检索更多片段。
这个方案对个人用户特别友好,因为基础模型不需要换,你甚至可以用同一个模型做所有知识域。只要你把不同主题的书分别建库,教学导师就能在所有主题间无缝切换。而且,文档更新只需要重新处理对应的章节,重新生成向量即可,完全不需要重训模型。可以说,RAG解决了个人知识库“高频更新”和“低成本维护”两个最核心的问题。
2.3 教学过程是怎么设计的
光能回答问题还称不上“导师”,这个项目更有意思的地方在于它设计了主动教学的动作。从标题里的 teaches 就能看出,它的目标不是聊天机器人,而是“教你”。
按我看到的项目设计思路,教学过程大概包含这样几个环节。第一个环节是阅读前评估,它会先跟你确认学习目标,或者根据目录帮你生成一份阅读路线图。第二个环节是逐章引导:你上传一本书后,它可以按章节摘要、按页提取要点,或者直接随机抽一个知识点提问,像老师上课点名一样。第三个环节是练习生成:每个章节结束,它会基于原文自动生成选择题、简答题和判断题,答完还能回看你答错的题对应原文哪里。第四个环节是间隔复习,系统可以记录你掌握薄弱的点,隔天再拎出来问你。
这四个环节放到传统学习里,需要老师花大量时间备课、出题、批改。而在这里,全部由Prompt工程和检索流程自动化完成。当然,坏处也有——它没法像真人老师那样判断你是不是在敷衍,也没法给你的回答做情感反馈。但作为自学的补充工具,这套闭环已经足够实用了。
3. 核心实现细节拆解
3.1 文档预处理:从PDF到可检索片段
如果要把一份文档喂给导师,第一关就是把它变成机器能理解的文本。这个环节的坑,比大多数人想象得多得多。
PDF 如果本身是印刷版扫描件,就需要 OCR(光学字符识别),否则直接提取PDF文本会得到一堆乱码。对于 EPUB 和 Markdown 这类结构化的文本来源,处理相对简单,但也要注意标题层级、列表、表格怎么保留。很多项目的做法是,先按文档自带的标题结构把内容切成段落块,比如一级标题是一个大节,二级标题下再往下切。这样切出来的块天然有语义边界,不会把两个无关主题硬切到一起。
切分粒度是另一个关键参数。切得太粗,比如一整章作为一个块,检索的时候可能命中一大段,导致生成时上下文被无关信息占满;切得太细,比如一句话一个块,检索时又容易丢失上下文,回答断章取义。我的经验是,一般技术文档用 300–500 个 token 左右作为目标块大小,块与块之间留 50 个 token 的重叠,效果比较稳。重叠的意义在于,如果关键句恰好落在上一块末尾,它能在下一块的开头再出现一次,避免检索时漏掉边界信息。
3.2 嵌入与索引:向量检索的关键参数
嵌入模型把文本变成一串几百到几千维的浮点数向量,语义越接近的文本,向量在空间里的距离越近。这一步的质量,直接决定了检索能不能找到“真正相关”的段落。
选嵌入模型的时候,要看它支持什么语言。中文文档最好选支持中英双语的模型,否则英文模型对中文的语义理解会很弱。另外,嵌入模型对输入长度有限制,切块大小不能超过它的上限。许多开源嵌入模型支持 512–2048 token 的输入,和前面说的切块策略基本能对上。
向量索引和检索一般会交给向量数据库或向量索引库来做。比较轻量的做法是本地文件索引,数据集规模在一万块以内都够用;如果文档特别多,可以用专门的服务。检索时常用余弦相似度或内积,返回 TopK 个结果,K 一般取 4–8。K 太小,容易漏掉相关内容;K 太大,生成阶段的上下文会很长,既费token又影响回答质量。很多项目在默认配置里就把 TopK 调好,但如果你发现提问总答不到点上,可以手动把它调大一点,观察是否有改善。
3.3 生成与教学策略:Prompt设计心得
检索回来的片段只是素材,真正决定“导师”水平的,是生成阶段如何组织 Prompt。我拆解过不少类似实现,好的教学 Prompt 通常包含几个固定部分:系统角色设定、检索到的参考上下文、用户的原始问题、交互规则。
系统角色设定会写明你是谁的导师、应该采用什么语气、如果不能从上下文中找到答案应该怎么回答(比如明确说“文档里没有相关内容”,而不是脑补)。参考上下文是检索结果,最好按相关度排序,并标注来源,方便模型回答时引用。交互规则也很讲究,比如“先回答问题,再用原文佐证”“如果这是多选题,请逐条解释错误选项”。这些规则能显著减少“看着像那么回事,其实没答到点子上”的情况。
我自己在实践里有个体会:不要把所有的资料都一股脑塞进 Prompt,因为模型的注意力是有限的。与其塞五个不相关段落,不如只保留最相关的两个,让回答更聚焦。另外,历史对话也要做裁剪,只保留最近几轮,否则上下文一长,前面的关键信息会被稀释,回答会开始“飘”。
4. 手把手部署一套本地导师
4.1 环境准备与部署
下面按我实际跑通的流程来说明,项目配置不同,细节可能略有出入,但思路通用。你需要准备一台机器,Windows、macOS 或 Linux 都行,尽量有 8GB 以上内存;如果用本地模型推理,建议显卡显存至少 6GB,或者干脆用纯 CPU 跑一个小量级模型,速度慢一点,但不影响体验教学流程。
首先安装 Docker 和 Docker Compose,这是最常见的方式。然后新建一个目录,写好 docker-compose 配置文件。配置里通常有三个核心组件:前端界面、API服务、向量数据库。模型接口可以指向已经跑在某个地址上的推理服务,也可以让容器自己去调用。
下面是我脱敏后的示例配置,镜像名和路径按你实际拉取的项目调整:
services: tutor-web: image: example/tutor-web:latest ports: - "3000:3000" environment: API_BASE_URL: "http://tutor-api:8000" tutor-api: image: example/tutor-api:latest ports: - "8000:8000" volumes: - ./data:/app/data environment: LLM_BASE_URL: "http://host.docker.internal:11434/v1" LLM_API_KEY: "local-key" LLM_MODEL: "local-model" EMBEDDING_MODEL: "bilingual-embedding" EMBEDDING_DIM: 1024 VECTOR_DB_PATH: "/app/data/vector_store" CHUNK_SIZE: 400 CHUNK_OVERLAP: 50 RETRIEVE_TOPK: 5启动命令很简单:
docker compose up -d启动后,浏览器打开 http://localhost:3000。第一次进去,它一般会让你先设置模型信息。如果你本地已经有一个推理服务在跑,填它的地址和模型名即可;如果没有,可以先接入一个兼容接口的远程模型,把密钥填进去。需要注意,项目本身只负责流程编排,模型和嵌入都由你配置的模型服务提供。
4.2 首次使用的流程
部署完以后,首次使用的体验大致是:上传文档、建库、选择教学模式、开始学习。
上传文档时,系统会先解析文件,然后进入切片和向量化环节。如果你传的是大文件,这一步可能要等几分钟。处理完成后,你会看到文档被切成多少个片段,索引状态从“pending”变成“ready”。此时你可以在界面里直接输入一个问题,比如“这本书第3章的核心结论是什么”,系统会返回答案,并附上它引用的原文片段。这个引用功能很重要,你可以点过去核对答案是不是真的来自你的书。
接着可以选择“章节精读”或“随堂测验”。章节精读模式下,它依次给出每一节的摘要和关键要点,你可以逐节展开,遇到不懂的句子直接选中让“导师”解释成大白话。随堂测验模式则是在某一章的范围里随机出题,做完之后会有正确率报告,并告诉你哪几个知识点掌握得不好。这些环节的数据都会存在本地,下次打开还能接着复习。
4.3 个性化配置建议
部署不是终点,配置才是。我在使用中总结了几条很实用的调整建议。
第一,块大小按文档类型调。小说、故事类可以切大一点,因为语句之间的关系很强;技术手册、规范类稍微切碎一些,便于精确定位到某个参数说明。第二,如果文档里有大量表格,最好在解析阶段就把表格转成文本描述,否则向量化的时候表格结构会丢失。第三,接入的模型直接决定输出质量。如果你追求回答仔细,可以选参数更大的模型;如果追求速度,就选小一点。建议把“生成答案用大模型、计算嵌入用小模型”这个组合思路部署,性能和效果兼顾。
还有一点容易被忽略:历史会话的管理。项目生成出的对话记录都在本地,你可以定期清空一些旧的会话,避免目录膨胀。生产环境使用的话,建议加上备份机制,把 data 目录定期备份到另外一块磁盘。别小看这一步,很多本地部署项目最终都是因为数据没备份,重建时才发现文档库要重新处理一遍。
5. 常见问题与排查实录
5.1 检索质量差,回答牛头不对马嘴
这是最常遇到的问题。现象是,你问一个明确的问题,它引用的原文跟问题关系不大。排查顺序一般是:先看切块是否合理,再查嵌入模型选得对不对,最后看检索TopK是否太小。
我的处理办法是:先把问题换成原文里的原话再去搜,如果搜到的片段正确,说明切块和嵌入没问题,只是问题表达太口语化;如果搜到的片段依然不对,那就是切块太粗或嵌入模型对专业术语理解不够,可以缩小切块、换更强的嵌入模型。还有一个隐藏因素:有些项目默认按文件名字符串去过滤检索范围,文件名一旦和你询问的内容不一致,也会导致漏检。遇到这种情况,直接切换成“全局检索”模式再试。
下面是我整理的问题速查表,供你对照使用:
| 现象 | 可能原因 | 优先尝试的排查手段 |
|---|---|---|
| 引用的原文明显不相关 | 切块过大 / 嵌入模型语言支持不足 | 缩小CHUNK_SIZE,更换双语嵌入模型 |
| 关键段落总是漏掉 | TopK太小 / 块与块之间没有重叠 | 调大TopK,设置CHUNK_OVERLAP |
| 问口语化问题搜不到 | 问法与原文措辞差异大 | 用原文关键词复述问题,或开启全局检索 |
| 返回了其他文档的内容 | 多个文档混在一个库里 | 按文档或主题拆分向量库 |
5.2 回答出现幻觉,编造文档里没有的内容
LLM 在生成时天生有“补全”倾向,即使给它限定上下文,它也可能把脑子里已有的通用知识当成文档内容说出来。这是RAG系统里最需要警惕的问题。
缓解手段有三个。第一,Prompt里明确加上“如果检索到的内容不足以回答问题,请直接说不知道”,这个规则能挡掉一部分幻觉。第二,要求回答附带引用片段编号,没有引用标注的回答不予采信,这在很多项目里可以配置。第三,降低生成温度,也就是让模型回答更保守。温度参数一般默认0.7,做教学场景建议调到0.3以下,事实性会明显更好。我自己实测,把温度从0.7调到0.2之后,引用错误明显变少,但回答会显得“干巴”一些,这是可以接受的取舍。
5.3 内存和显存占用过高
自托管最现实的问题就是资源。Web端加上API服务,大概要吃掉1-2GB内存;如果再把本地模型和嵌入模型都加载进来,内存和显存会迅速见顶。
如果你只有一台普通笔记本,我的建议是:跑一个纯CPU可运行的轻量模型用于问答,嵌入模型也选CPU友好的版本,然后把向量索引放在本地文件里。虽然单次回答可能要多等几秒,但整体能跑起来。如果你有带独显的机器,可以把模型加载到GPU,其他组件留在内存,通常6GB显存已经足够流畅使用。另外,把向量数据库的持久化路径设置好,不要每次重启都重新建索引,能省下不少时间。
5.4 文档格式兼容问题
扫描版PDF、加密PDF、带复杂排版的EPUB,这三个是最容易翻车的。扫描版PDF必须先用OCR才能提取文字,否则向量化出来的全是乱码。加密PDF需要提供密码或事先解密。复杂排版EPUB里可能嵌套好多层容器,解析后内容顺序错乱,这时候需要预处理工具先把纯文本抽出来,或者先用一个转换脚本统一格式,再交给项目。
推荐的做法是,把所有待学习的文档统一转成 Markdown 或纯文本格式,再进行后续处理。虽然少了一些字体样式信息,但对接下来的切块、嵌入、检索影响极小。我在实际跑项目时,凡是直接传PDF的,都会先快速扫一眼解析结果,确认没有乱码再进入索引流程。
6. 从读死书到主动出题:扩展玩法与使用体会
6.1 扩成个人知识问答库
把教学范围扩大,它其实可以变成一个“第二大脑”式的个人知识库。你平时的读书笔记、会议记录、技术方案、考证资料,全部丢进同一个向量库里,统一索引,然后随时向它提问“我之前在一个方案里提到过某个性能指标,具体是多少”。因为检索是按语义来的,你不需要记住精确关键词,只要描述个大概,它就可能帮你翻出原文。
我在实际使用中,就把手头几本工具书和之前写的十几篇笔记全部并到一个库里,构建了一个“私人答疑助手”。遇到模糊记忆时,先问它,再顺着引用的原文确认。这比我打开一堆文件夹去Ctrl+F高效得多。你甚至可以让它定期把某一主题下的内容归纳成一张速查表,极大节省复习成本。
6.2 一周使用下来的真实体会
如果要给这个项目打分,满分十分我给八分。扣分的两分,一分扣在初期配置确实有点门槛,另一分扣在它偶尔还是会一本正经地给出错误引用,你必须保有核对原文的习惯。
但它的价值是真实存在的。过去一周,我拿着一本四百多页的技术手册,每天花半小时和它对话、做题、复盘。我能明显感觉到,以前读完后“好像懂了,又说不出来”的状态,变成了“能准确指出这个问题在哪一章、哪一节有解释”。这种转变,靠我自己翻书是很难做到的。所以我的建议是,如果你手边正好有一本一直想精读但读不下去的书,不妨把它丢给这样一个自托管导师,让它先从提出第一个问题开始。你会发现,私人教练这件事,其实离普通人没有那么远。