InfoForge:用作本地AI代理的RAG数据源的离线版维基百科
- 发布日期
- 2026年8月28日
- 更新日期
- 2026年9月16日
- 作者
- Jacob Lloyd —— 项目完成后,在 AI 协助下撰写
- 阅读时长
- 约 16 分钟阅读
简单来说: InfoForge实际上是整个英文版维基百科的备份副本,这些数据存储在我家里的电脑中。此外,还有一个小程序,它能让我使用的AI助手从这些数据中查找所需信息。当我提出问题时,助手不会仅凭记忆来回答,而是会在那些文章中搜索相关内容,然后给出答案并注明答案的来源文章。如果找不到任何相关信息,它就会说“我找不到相关的信息”,而不会胡编乱造。没有任何数据会被发送到任何公司,也没有人会记录下这些问题。即便在没有网络的情况下,它也能正常工作。
我询问了本地的8B模型:小说家夏目漱石的出生日期是什么时候。它回答说是“1867年2月9日”,接着还说出了它所参考的参考资料。其实这个模型本身并不知道这一事实。它是通过我家里的一台机器上存储的、容量达52.69 GB的维基百科数据库查到该信息的;整个查询过程完全无需互联网参与。那台机器就是 我的家用服务器。
简而言之:
- 它是什么:整个英文版维基百科被打包成一个 ZIM 文件(包含 1919 万条条目,为 2026 年 6 月的备份数据,不含图片),可在本地使用;同时配有一个小型的 Python 命令行工具,能将其作为本地模型的检索数据源。
- 需要花费多少:无需任何费用。只需下载一次即可,占用约 53 GB 的磁盘空间;不需要账号、API 密钥或订阅服务。
- 速度如何:生成一个完整的回答需要 4 到 10 秒,其中绝大部分时间都是语言模型在思考。搜索与文章获取操作本身则非常快速(具体数值见下文)。
- 使其实用的规则:当搜索未找到任何结果时,该工具会输出失败信息并以代码 3 退出;若数据源中确实没有所需答案,模型也会如实告知。使用该工具的智能体也会报告查找失败,而不会凭记忆胡乱回答。
- 它绝不会做的事:它不会向外发送任何请求。不存在第三方服务提供商、没有 API 调用,也不会记录任何查询日志。
更新日期:2026年9月16日。 在我最初撰写此文的两天后,那个简单的脚本便被打包成了命令行工具(版本2.0.0):包含 infoforge ask、search、serve start|stop|status|recreate、status 等命令,还有一个可检查整个链接链的 doctor 命令。下方的下载链接提供的是这个打包后的版本,而非最初的脚本。本页上的命令示例均来自v2版本,且是在2026年9月16日执行的;至于最初版本中那个关于小说《心》的经典回答,则被移到了“注意事项”部分,因为v2版本已无法找到该答案了。我还添加了真正的容器相关参数、服务单元配置、提示信息以及拒绝响应机制,并修正了关于获取时间的描述。
既然模型已经“知道一切”了,何必多此一举呢
因为它做不到,而且其失败方式相当糟糕。向一个小模型询问日期、某种物质的性质或某个地名,它会给出一个流畅、自信且看似合理的回答,但完全看不出该答案是否真实。它并非在撒谎;它根本无法区分什么是记忆中的事实、什么是自己生成的内容。对于文学创作来说这没问题,但对于事实信息而言则是灾难。
检索机制能解决问题,但常规的检索流程是以云搜索 API 为前提的:你的提问会被发送到某家公司,该公司会记录下这些信息,而你的助手随后便要依赖他人的服务可用性和服务条款了。在我家,正是这套 AI 系统来回答孩子们的问题,因此上述两个问题都让我颇为担忧。
维基百科同时解决了这两个问题。它是目前最全面的通用知识库——你可以合法地下载其全部内容,而这些数据仅需一张价格比一次夜生活消费还便宜的磁盘就能存储下来。Kiwix项目多年前便完成了艰巨的任务:它将整个维基百科打包成经过压缩且带有索引的ZIM格式,以便那些没有网络连接的学校、船只或其他场所使用。我现在只是让人工智能去读取它,而非通过浏览器访问罢了。
最终你会得到什么
提出一个问题,得到一个答案;随后是一个编号列表,列出了构成该答案所依据的维基百科条目。这是2026年9月16日的一次真实运行记录(服务器地址为回环地址):
$ infoforge ask "夏目漱石出生于哪一年?"
infoforge: 警告:跳过歧义页面:夏目
infoforge: 警告:跳过歧义页面:漱石
infoforge: 已根据5篇文章进行定位
根据提供的资料,夏目漱石出生于1867年2月9日。...
参考文献:
[1] 夏目漱石 — http://127.0.0.1:8080/content/wikipedia_en_all_nopic/Natsume_Soseki
[2] 夏目漱石 — http://127.0.0.1:8080/content/wikipedia_en_all_nopic/Natsume_S%C5%8Dseki
[3] 柳秉澈 — ...
这花了4.4秒。当没有任何可接地的东西时,就会出现这种情况:
$ infoforge search "qzxvbnm plorkwidget fnargle"
infoforge: 错误: 找不到与‘qzxvbnm plorkwidget fnargle’相关的数据源
$ echo $?
3
没有段落输出,无需猜测,还会返回一个调用脚本可以检查的退出码。ask命令同样返回退出码3,并提示用户尝试使用更简单的关键词,或者运行 infoforge doctor。完整的退出码说明如下:0表示正常;2表示配置或用法有误;3表示没有任何可依据的信息;4表示未引用任何来源(仅在启用 --strict 选项时出现);5表示所有模型端点均无法访问。即便出现退出码5的情况,该工具仍会输出它找到的文章,因此这次搜索并未白费。
工作原理
五个阶段,没有一个是巧妙的,我建议就保持这样吧。
关键词提取。 问题本身并非查询内容。Kiwix会对所有搜索词进行逻辑“与”运算,因此像“谁写了《Kokoro》?它是什么时候出版的?”这样的提问几乎匹配不到任何结果;而直接输入“Kokoro”则是一个不错的查询方式。该提取器的核心功能就是一个能够保留原单词顺序的停用词过滤器:
def keyword_query(question: str) -> str:
tokens = _TOKEN_RE.findall(question)
keywords = [
t for t in tokens
if t.lower() not in _STOPWORDS and t.lower() not in _WEAK_VERBS
]
return " ".join(keywords) if keywords else question.strip()
v2额外增加了两项功能:首先,会将问题中出现的首字母大写的短语作为文章标题来尝试匹配;其次,会对所有匹配结果重新排序,使得消歧义页面、单纯的年份以及“……列表”类页面排在后面。
搜索功能。 Kiwix的全文搜索实际上是基于真正的搜索索引实现的,而非简单的grep操作;正是这一点使得搜索功能得以实现。我在开发时,该搜索功能能在3到30毫秒内从1919万条条目中返回相关标题。2026年9月16日,在同一台机器上,于流量较高的时段执行同类查询时,耗时则变为90至730毫秒。我推测此时索引可能已从页面缓存中清除;不过无论如何,与模型处理所需时间相比这点延迟几乎可以忽略不计。整个系统中不存在任何嵌入向量、向量数据库,也没有需要在数据更新时重新构建的块存储机制。维基百科的编辑者们早已完成了内容分块工作——他们把这些块称为“文章”。
获取并裁剪。 kiwix-serve在本地回环接口上返回文章内容大约需要 1毫秒(处于“热状态”时)。我使用 curl 对刚阅读过的三篇文章进行测试,测得的时间在0.7至1.1毫秒之间;而首次访问时的耗时则为6至9毫秒。最初的草稿中我写的是“0.5毫秒”,却未说明这是热状态下的数值。裁剪环节是唯一需要人工判断的步骤。直接从文章开头截取内容常常会导致丢失正确答案——因为所需信息往往深藏在冗长的文章之中。因此,每篇文章都会保留一小段开头部分(约占总长度的四分之一),再加上一个与问题中出现频率最低的词汇最为匹配的文本窗口。
基础提示语。 这就是整个诀窍所在,它由四句话组成。首先是规则,然后是带编号的信息来源,最后是问题:
您是一个仅依据下方提供的源文本来回答问题的助手。
请仅使用这些源文本中的信息来回答用户的问题。如果源文本中并未包含答案,请明确说明这一点。
当引用源文本中的信息时,请注明文章标题(例如:“根据《文章标题》所述”)。
切勿编造任何源文本中不存在的事实、标题或网址。
来源1:
<经过删减的文章文本>
...
问题:<具体问题>
温度值设为0.3而非0。代码注释中提到,使用0.0时会导致某些本地构建在处理重复出现的源代码时出现死循环。这只是一个提示而已,并非绝对保证;正因如此才有了下一部分内容。
模型方面: 答案由本地推理模型生成(即 DeepSeek-R1 8B,运行在屋内的 GPU服务器上)。Q4_K_M格式的模型文件大小为5.0 GB;以f16精度计算,每1000个上下文token所需的KV缓存空间约为0.15 GB。因此,加上32,000个token的上下文窗口后,所需内存总量大约为10 GB。最终回复在输出前会移除其中的代码块。我最初测试时整个往返过程耗时约8秒;2026年9月16日进行的三次测试分别耗时为4.4秒、5.2秒和9.7秒。信息检索过程无需额外成本,但模型推理则不然。如今已不再有本地备用模型:我在2026年9月16日移除了迷你PC上的模型服务器。若GPU服务器未能响应请求,该工具会返回退出码5,同时仍会输出检索到的相关文章。
以“约束机制”实现信息可靠性
这是我最愿意捍卫的设计决策,不过它并非技术层面的考量。
那些要求“仅使用指定来源作答”的提示语本质上是一种指令。大多数情况下模型都会遵从;但在某些情况下,尤其是当可用来源有限且模型恰好记得相关信息时,它又会选择忽略该指令。倘若这种提示语就是系统唯一的防御手段,那么所谓的“信息可靠性”也不过是种主观感受罢了。
因此,信息可靠性的保障机制实际上是在模型外部实现的,具体分为三层:
- 无可用来源则不调用模型。 若检索未返回任何结果,工具绝不会将原始问题作为备用请求发送给大语言模型。此时它会返回退出码3——这也是所有调用程序都会检查的特定信号。
- 来源虽存在却无法提供答案。 提示语会明确要求模型如实说明这一情况;在此情形下整个运行流程仍被视为成功。这种诚实的“未找到相关答案”反馈正是系统正常运作的体现。
- 回答内容未引用任何来源。 工具会输出警告信息,提醒用户该回答缺乏可靠依据。若启用
--strict参数,则会返回退出码4。
除此之外,调用该工具的智能体也被要求必须上报“检索失败”状态,而非自行编造答案敷衍了事。这种拒绝行为会被逐级上报;上层程序也无法掩盖这一事实。
最终效果就是:该助手会明确告知用户“未能找到所需信息”。在很多人看来这似乎是个缺陷,实则不然。一个能坦诚回应“找不到答案”的模型远比那些胡编乱造的大型模型更有价值——因为前者提供的信息值得信赖,而后者则需要大量人工核实工作。若每次回复都需反复核实,那么使用大型模型也毫无意义。
可自主验证的隐私保障
市面上所有大型助手服务均设有隐私政策页面;而本工具则采用网络路径实现隐私保护:用户提问从终端发送至同一设备上的检索服务器,随后传递给家庭网络中的模型并返回结果。整个传输过程中无人能记录内容,也没有任何密钥可识别用户身份;更不存在随时可能变更的条款细则。在此处提出的疑问就如同翻阅书架上的书籍一般私密——因为本质上过程确实如此。
用户亦无需盲信我的说法:只需断开网络连接后观察工具能否正常运行即可验证其真实性。
占90%工作量的“枯燥环节”
整个流程搭建仅花费了一个周末时间,真正棘手之处在于三个容器配置参数。
维基百科服务器以无root权限的 Podman容器形式运行于启用了SELinux强制模式的Linux系统上;上述三项条件各耗费了我一个晚上的时间才得以解决。最终可行的命令如下:
podman run -d --name infoforge-kiwix \
--user root \
-p 127.0.0.1:8080:8080 \
-v "$HOME/zims:/data:z" \
ghcr.io/kiwix/kiwix-serve \
-k /data/wikipedia_en_all_nopic.zim
--user root参数:在无root权限的Podman环境下,容器内的root用户对应的是宿主机的普通用户。原镜像默认使用的用户属于子UID范围,无法访问用户主目录,因此尝试读取/data时会出现“权限被拒绝”错误。- 卷挂载时的
:z参数:在启用了SELinux的系统上必须重新标记绑定挂载的标签,否则即便是容器内的root用户也无法读取文件。此类错误往往表现为“文件缺失”,极易误导用户去错误的位置寻找文件。 - 显式指定ZIM文件路径(配合
-k参数):若未明确指定文件路径,镜像自带的启动脚本会试图以无文件状态运行kiwix-serve,最终因“参数不符合预期格式”而报错。-k(--skipInvalid)参数则允许即便有文件尚在下载的情况下服务器也能正常启动。
若宿主环境未启用SELinux或无需root权限映射,则可能无需设置上述参数。将监听地址绑定至127.0.0.1属于第二版更新内容:第一版原本监听整个局域网,但对于缺乏身份验证机制的知识库服务而言此设置毫无必要。
这三个参数的配置耗费了约90%的工时。这正是绝大多数自建服务的真实写照——正是那些文档刻意回避的部分。正因如此,很多人误以为自己不擅长此类工作;其实他们只是初次尝试,且面对着完全相同的配置难题罢了。
系统悄然失效却无人察觉
起初一切运行正常且速度飞快。我测试后记录了相关数据便不再关注。
一周后我试图再次使用它时却发现服务已消失。原来机器曾自动重启过,但容器并未随之恢复;由于未设置服务单元、监控机制或健康检查程序,全程无人知晓这一状况。更糟糕的是,本应告知智能体该工具存在的技能配置文件仅处于草稿状态且从未安装。于是整整一周内,家中所有智能体均只能凭记忆作答——它们根本不知还有其他查询途径。
经验总结极为简单:
任何既无自动启动机制又不为人所知的功能,本质上等同于从未存在过。
解决方案仅为一个12行代码的用户级服务单元(此处已删减注释)以及一份单页技能配置文件:
[Unit]
Description=InfoForge kiwix-serve(用于提供可靠答案的离线知识库)
After=default.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=%h/.local/bin/infoforge serve start
ExecStop=%h/.local/bin/infoforge serve stop
ExecReload=%h/.local/bin/infoforge serve recreate
TimeoutStartSec=180
[Install]
WantedBy=default.target
此处特意选用oneshot类型并配合RemainAfterExit参数:虽然serve start命令在容器启动后即结束运行,但服务状态仍需保持“活跃”直至容器停止运行,如此才能通过systemctl --user stop实现正确关闭。启用指令为 systemctl --user enable --now infoforge.service。由于serve start具备幂等性,当ZIM文件集合发生变动时仅需执行重载操作即可自动重建容器。
该技能配置文件则是供智能体查阅的共享说明文档:它阐明了工具的存在性、调用方式以及核心要求——即所有可靠答案必须附带引用来源。
我期盼已久的健康检查功能现由infoforge doctor实现。它会按顺序检测各环节状态(配置有效性、ZIM文件完整性、容器运行状态、HTTP服务连通性、测试搜索结果及模型端点响应情况),并逐一输出通过/失败状态(此处仅展示部分输出内容):
$ infoforge doctor
[PASS] config 配置文件解析成功 <config path>
[PASS] zims 检测到1个可用文件:wikipedia_en_all_nopic.zim
[PASS] container infoforge-kiwix容器正在运行,正常提供wikipedia_en_all_nopic.zim服务
[PASS] kiwix http http://127.0.0.1:8080响应正常
[PASS] search 在wikipedia_en_all_nopic文件中找到3条与“water”相关的记录
[PASS] llm primary <gpu server>/v1可正常访问,当前运行模型为...DeepSeek-R1-0528-Qwen3-8B...(已确认模型版本)
doctor: 所有检测项目均通过
最初版本仅由约700行有效代码的Python脚本构成——那段时期确实进展顺利。而真正让系统具备实用价值的则是上述服务单元与技能配置文件的引入;这一点我至今仍不断重新体悟。
深度融入智能体生态体系
如今InfoForge已成为 我所运行的本地智能体集群的共享工具,不再是需要单独记忆的零散脚本。需要获取特定信息的智能体会调用infoforge ask指令(若需机器可读格式则使用--json参数),随后获得附带引用来源的完整答案并传递至后续流程。这些引用信息的重要性远超表面所见:当智能体提及具体文章标题时,我只需点击一次即可完成验证;而这种可验证性正是确保看似合理的回答真正具备实用价值的前提条件。
注意事项
- 预留充足磁盘空间。 无图片版文件包大小约为53 GB,含图片版本则几乎翻倍。务必事先确认存储空间充足——尝试读取下载未完成的ZIM文件只会徒增麻烦。
- 数据时效性考量。 我使用的版本日期为2026年6月;而维基百科内容持续更新。在此系统逻辑下,任何晚于该日期的内容均被视为不存在。若牢记此规则则无碍,反之则可能产生误解。
- 搜索质量取决于关键词提取与标题匹配能力。 初版曾对“谁撰写了《Kokoro》及出版年份?”这类问题给出“夏目漱石,1914年”的准确答复并附上相关链接。但在2026年9月16日的第二版测试中,系统却将标题误判为拼写相近的专辑名《KoKoro》,且原小说条目甚至未能进入前五推荐列表。此时模型表现出应有的审慎态度:它列举了所有识别出的“Kokoro”相关内容并询问用户具体所指。当被问及“谁撰写了小说《Kokoro》?”时,系统明确回应指定来源中并无相关记录。这并非模型胡编乱造,而是检索环节失效所致。经观察绝大多数错误答复皆源于查询语句设计不当而非模型缺陷,故在升级更大规模模型前务必先优化检索机制。
- 上下文长度需精细调节。 内容过少则无法获取所需信息;内容过多又易导致模型偏离初始指令。建议针对实际关心的问题类型进行参数调优,而非单纯依赖理论测试。
- 刻意测试故障场景。 可尝试查询维基百科中不存在的内容并确认系统返回的是退出码3而非流畅的回答段落。若仅测试能正常获取答案的问题,则仅完成了整个流程中较简单的一半。
- 尽早配置服务单元。 具体方法参见前文说明。
现状与未来规划
目前家中已建立起一个配备高效检索索引的离线百科全书库,同时搭配了严格限制其信息输出范围的人工智能系统。这一切仅需一次下载操作及一个周末即可实现。该系统在网络中断时仍能正常运行且无法被人为停用;更重要的是它完全独立于任何商业盈利模式之外。
后续计划包括:在同一框架下扩充更多ZIM资源包(如Kiwix提供的医疗技术文献集以及我自己维护的若干维基站点),因为检索模块本身对知识库类型并无特殊要求。第二版已新增可将文档文件夹转化为ZIM格式的zimify指令。另一待解决问题即前文提到的《Kokoro》标题匹配错误——此现象实为标题匹配算法缺陷而非模型问题。
相关链接:我的本地AI智能体集群、StudioForge(模型运行所需的GPU服务器)、承载InfoForge的迷你PC,以及智能体的实际运行成本分析(InfoForge本身完全免费)。
下载
仅限个人使用免费。如果它替你省下了一下午的时间,欢迎点旁边的咖啡按钮支持一下。