ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

古籍知识库RAG实战:从热词到Skill封装,打造可调用的AI技能

古籍知识库RAG实战:从热词到Skill封装,打造可调用的AI技能 1. 从一堆散装热词里我看到了一个真实的需求缺口先把时间拨回到我决定动手做这个项目的那天。当时我在整理自己过去两年积累的一批古籍数字化笔记手里有《说文解字》的部首数据、《康熙字典》的扫描件索引、还有一些从公开渠道整理的古籍文本片段。东西不少但用起来极其别扭——想查一个字在某本书里的用法得在好几个文件夹和笔记软件之间来回跳想让AI帮我解释一段古文每次都要手动把上下文贴进去贴少了它答不准贴多了又超出上下文窗口。就在这个当口我注意到搜索热词里反复出现几个信号古籍、skill、RAG、chinese-classics-advisor还有一大堆围绕openclaw、ollama、rag知识库、book to skill的长尾词。这些词单独看很散但拼在一起指向一个非常具体的东西把古籍这类垂直领域的知识做成一个可以被AI调用的技能模块而不是每次从零开始喂上下文。这个判断不是拍脑袋来的。我翻了一圈热词发现几个高频组合特别有意思book to skill和skill脚本、skill插件同时出现说明已经有人在尝试把书籍内容转化成可复用的技能单元rag知识库能存储图片嘛、kg知识库、rag知识库和结构知识库区分以及应用场景这类问题说明大家在做知识库时卡在了选型和边界上rag瓶颈、rag检索增强、ontology rag则暴露了纯向量检索在古籍场景下的天然短板。我当时的直觉是古籍这个领域恰恰是RAG最容易翻车、也最需要技能化封装的场景之一。原因很简单——古籍的字词有大量异体字、通假字、避讳改字语义高度依赖注疏和上下文纯靠向量相似度召回十有八九会给你一堆看起来像但根本不是的结果。而如果把它做成一个skill把检索策略、注疏关联、版本比对这些逻辑固化进去效果会稳定得多。所以这个项目的缘起说白了就是一句话我不想再每次手动给AI喂古籍上下文了我要把这件事做成一个能反复调用的技能。这篇文章就是整个项目的起点记录我会把为什么做、怎么想清楚边界、技术选型时踩了哪些坑、以及第一版跑通时遇到的真实问题原原本本写出来。如果你也在做垂直领域的知识库或者AI技能封装尤其是中文古典文献方向这篇应该能帮你少走一段弯路。2. 古籍知识库为什么不能直接套通用RAG方案2.1 通用RAG在古籍场景下的三个硬伤我先说结论把古籍文本直接丢进一个通用RAG框架然后指望它回答某字在某书某篇的用法大概率会失望。这不是框架不行而是古籍这个领域的特性跟通用RAG的假设之间有结构性冲突。第一个硬伤是分词与异体字问题。现代中文RAG的检索链路通常是文本切块 → 向量化 → 相似度召回。但古籍里説和说、禮和礼、爲和为是混着出现的你如果不在预处理阶段做归一化向量模型会把它们当成不同的token召回率直接掉一大截。我实测过同一段《论语》文本不做异体字归一化时针对礼的查询召回率大概只有做归一化后的六成左右。第二个硬伤是语义依赖注疏。古籍的正文往往极简真正的信息量在注、疏、笺里。比如《诗经》一句关关雎鸠正文就四个字但毛传、郑笺、孔疏层层叠加含义差别很大。通用RAG按固定长度切块很容易把正文和对应的注疏切散导致召回时只拿到正文、丢了注疏或者反过来。这就引出了热词里那个很关键的问题——kg知识库、rag知识库和结构知识库区分以及应用场景。古籍场景其实更适合结构知识库 RAG的混合模式用结构化数据存版本、篇目、注疏的层级关系用RAG做语义召回两者互补。第三个硬伤是检索粒度和意图不匹配。用户问《说文》里天字怎么解他想要的可能是部首归属、本义、引申义、以及不同版本的差异。通用RAG只会返回几段包含天字的文本块至于这些块是不是同一个版本、是不是注疏、是不是正文它不区分。这就是为什么我在项目里坚持要引入**ontology本体**的思路——先把古籍的实体类型字、词、篇、书、注、疏、版本和它们之间的关系定义清楚再让检索在这张关系网上跑。2.2 为什么skill化比再建一个知识库更划算热词里skill出现的频率高得离谱从skill编码247到狗头军师skill到去ai味的skill说明大家已经意识到光有知识库不够还得有能调用知识库的动作单元。我自己的体会是知识库解决的是存什么skill解决的是怎么用。古籍这个场景怎么用的复杂度远高于存什么。举个例子同样是查一个字用户可能想要这个字在《说文》里的部首和本义这个字在《广韵》里的反切和音韵地位这个字在某部具体古籍里的实际用例这个字和它的异体字、通假字之间的关系。这四种意图对应的检索路径、数据源、输出格式都不一样。如果每次都靠用户自己写prompt去引导体验极差。而如果封装成一个skill把这些意图识别和路由逻辑固化进去用户只需要说帮我查一下仁字在《论语》里的用法skill内部自动完成意图判断、数据源选择、结果组织。这也是我为什么在项目里没有选择再建一个更大的知识库而是选择在现有知识库之上做skill封装。知识库的边际收益在递减但skill的边际收益在递增——每多封装一个意图用户体验就上一个台阶。2.3 从热词反推大家真正卡住的地方在哪我把热词里跟技术相关的部分做了个粗略归类发现卡点集中在三个地方卡点类型典型热词背后真实问题部署与环境openclaw部署、openclaw安装配置、ollama部署openclaw、node.js官网下载openclaw本地跑AI技能的环境门槛还是偏高尤其是Windows下知识库选型rag知识库、kg知识库、ontology rag、rag瓶颈不知道该用纯向量、图数据库还是混合方案技能封装skill脚本、skill插件、book to skill、agent skill知道要做skill但不知道从哪下手、怎么组织这三个卡点恰好对应了我这个项目要解决的三个层次环境层、数据层、技能层。后面的章节我会按这个顺序把每一层的真实操作和踩坑记录写清楚。3. 环境搭建Windows下跑本地古籍技能的真实路径3.1 为什么我最终选了本地部署而不是纯云端项目一开始我其实考虑过纯云端方案——把古籍数据传到云端用云上的向量数据库和模型服务。但试了两天就放弃了原因有三个而且都很现实。第一是数据体量和成本。我手里的古籍文本加上注疏、索引压缩后大概有十几个GB。如果全部做向量化按通用embedding模型的token消耗算一次全量索引的成本就不低更别说后续每次查询的向量化开销。本地部署虽然前期麻烦但边际成本几乎为零。第二是版本迭代频率。做古籍技能免不了要反复调整切块策略、归一化规则、检索权重。每次调整都要重新索引。如果走云端每次重新索引都是一笔钱本地的话晚上挂着跑就行。第三是隐私和可控性。虽然古籍文本本身大多是公版内容但我整理过程中加入了不少自己的标注、校勘记录和版本比对笔记这些东西我不太想传到第三方服务上。所以最终方案是本地部署模型服务 本地向量库 本地skill运行时。热词里ollama部署openclaw、openclaw windows 搭建这些词说明很多人也在走这条路但Windows下的坑确实不少。3.2 Windows环境下的三个必踩坑我用的是一台Windows 11的机器配置是32GB内存、RTX 4070。按说跑个7B到14B的模型没问题但实际搭建过程中还是踩了几个坑这里逐个说。第一个坑WSL2的状态检查。热词里有一条openclaw无法安全验证 sl2环境。请在powershell中运行wsl --status这个我太有共鸣了。很多本地AI工具在Windows上依赖WSL2但WSL2的默认配置不一定满足要求。我的做法是先在PowerShell里跑wsl --status wsl --list --verbose确认默认发行版是WSL2而不是WSL1。如果是WSL1用wsl --set-default-version 2切换。这一步看起来简单但我第一次搭的时候就是因为默认版本是1导致后续模型加载一直报内存相关的错排查了大半天才定位到。第二个坑Node.js版本与原生模块编译。热词里node.js官网下载openclaw说明很多人是从Node.js这条路进来的。我建议直接用nvm-windows管理Node版本不要用系统级安装。原因是很多AI工具链依赖特定版本的Node系统级安装切换起来很痛苦。我目前用的是Node 20 LTS配合npm config set msvs_version 2022来确保原生模块能用正确的编译工具链。如果遇到node-gyp报错先装Visual Studio Build Tools勾选C 生成工具和Windows 10/11 SDK。第三个坑端口冲突与防火墙。本地模型服务默认会占端口比如Ollama默认11434很多向量库默认8000或8080。我第一次跑的时候11434被另一个服务占了导致skill调用模型时一直超时但日志里只显示connection refused不告诉你是端口冲突。排查方法是netstat -ano | findstr :11434找到占用进程后要么改配置换端口要么停掉冲突服务。另外Windows防火墙对本地回环地址一般放行但如果你用了Docker或者WSL2网络命名空间不同可能需要额外配置。3.3 模型选型为什么我没有追最新的模型这块我纠结了很久。热词里ollama部署openclaw、ollama 简易本地 rag 知识库这些组合很火说明Ollama是很多人的入口。我最后选的是Ollama Qwen2.5 14B的量化版本理由如下中文古籍理解能力我对比过几个开源模型在古文断句、词义解释上的表现Qwen系列在中文古典文本上的表现明显更稳尤其是对异体字和通假字的处理。量化后的资源占用14B的Q4量化版本大概占9GB显存我的407012GB能跑留出余量给向量检索。Ollama的易用性一条ollama run qwen2.5:14b就能起服务API兼容OpenAI格式skill调用起来方便。这里有个经验不要一上来就追最大的模型。我试过32B的版本效果确实好一点但推理速度慢到影响交互体验而且显存不够会溢出到内存整体反而更卡。古籍技能这个场景模型的主要任务是意图识别、结果组织和解释生成不是做复杂的推理14B足够。4. 数据层古籍文本从一堆文件到可检索知识的转化4.1 异体字归一化一个被低估的关键步骤数据层我做的第一件事不是切块不是向量化而是异体字归一化。这一步在通用RAG教程里几乎没人提但在古籍场景下是决定性的。我的做法是建一张映射表把常见异体字、通假字、避讳字映射到标准字形。数据来源主要是公开的异体字字典和Unicode的CJK兼容区映射。映射表大概覆盖了八千多组处理逻辑是# 简化示意实际映射表更大 variant_map { 説: 说, 禮: 礼, 爲: 为, 閒: 闲, 閒: 间, # 注意一字多映射的情况 } def normalize(text): return .join(variant_map.get(ch, ch) for ch in text)这里有个坑一字多映射。比如閒在不同语境下可能对应闲或间简单映射会丢信息。我的处理是保留原始字形同时附加归一化后的字形作为检索字段。也就是说存储时两个都存检索时用归一化字段召回展示时用原始字形。这样既保证了召回率又不丢失原文信息。实测下来做了归一化之后针对常见字的查询召回率从六成出头提升到了九成左右。这个提升幅度比换一个更好的embedding模型带来的收益还大。4.2 切块策略按篇-段-注三层结构切而不是按字数通用RAG教程通常建议按固定字数切块比如512个token一块重叠128个token。我在古籍上试过这个方案效果很差。原因是古籍的语义单元不是固定长度的而是有明确的结构层级书 → 篇 → 章 → 句 → 注。我最终的切块策略是三层结构切块第一层篇级块。整篇作为一个大块用于回答某篇讲了什么这类宏观问题。第二层段级块。按自然段切每段附带篇名和上下文摘要用于回答某段的意思。第三层句注级块。把正文句子和对应的注疏绑定在一起作为一个检索单元用于回答某句怎么解。这样切的好处是检索时可以根据意图选择不同层级的块。用户问宏观问题就召回篇级块问细节就召回句注级块。而且句注级块把正文和注疏绑在一起避免了前面说的正文注疏被切散的问题。代价是索引体积变大了大概是固定切块的1.5倍。但考虑到本地存储成本低这个代价可以接受。4.3 向量库选型为什么最后用了混合方案向量库这块我试了三个方案纯FAISS、Chroma、以及FAISS SQLite的混合方案。最后选了混合方案。纯FAISS的问题是它只管向量不管元数据。我想按版本、篇名、注疏类型做过滤FAISS本身不支持得自己在外面套一层。Chroma支持元数据过滤但它的持久化和并发性能在数据量上去之后不太理想我索引到大概五十万块的时候查询延迟明显上升。混合方案是FAISS负责向量召回SQLite负责元数据和结构化关系。具体流程是查询先走SQLite根据意图和过滤条件缩小候选范围在候选范围内用FAISS做向量召回召回结果再回SQLite补充元数据版本、篇名、注疏来源。这个方案的好处是结构化过滤和语义召回各司其职而且SQLite的查询能力可以用来实现热词里提到的kg知识库那部分功能——比如查某字在某版本某篇的所有出现直接走SQL就行不用向量检索。5. Skill封装把检索逻辑固化成可调用的动作单元5.1 Skill的边界怎么划一个意图一个skill还是一个领域一个skill这是我在设计阶段纠结最久的问题。热词里skill编码247、skill编码193这种编号方式暗示了一种细粒度skill的思路——每个小功能一个skill。但我实际做下来发现古籍场景更适合中等粒度的skill划分。太细的话比如查部首一个skill、查反切一个skill、查用例一个skill用户得记住一堆skill名字体验反而差。太粗的话一个古籍助手skill包打天下内部逻辑会复杂到难以维护。我最终的划分是按意图域划分目前定义了四个skill字词查询skill处理单字、词语的本义、引申义、异体字关系篇章解读skill处理某篇、某段的整体含义和注疏版本比对skill处理同一文本在不同版本间的差异引用溯源skill处理某句话出自哪里这类问题。每个skill内部再做意图细分和路由。这样用户只需要记住四个入口内部逻辑又足够清晰。5.2 意图识别为什么我用了规则模型的双层路由意图识别这块我一开始想纯用模型做后来发现不行。原因是古籍领域的查询表达非常多样而且有很多领域特有的模式。比如《说文》里天字怎么解和天在说文中的解释语义一样但表达差很多。纯模型路由在边界case上不稳定。我最后用的是规则模型的双层路由第一层规则路由用正则和关键词匹配处理高置信度的模式。比如查询里出现出自、来源就路由到引用溯源skill出现版本、异文就路由到版本比对skill。这一层覆盖了大概七成的查询速度快且稳定。第二层模型路由规则没命中的交给模型做意图分类。模型只需要在四个skill之间做选择任务简单准确率高。这个双层设计的好处是高频查询走规则快且省算力长尾查询走模型灵活。实测下来整体意图识别准确率在九成以上而且响应速度比纯模型路由快不少。5.3 输出组织怎么让古籍查询结果像人话Skill的输出组织是我花时间最多的地方。因为古籍查询的结果如果直接堆原文用户读起来很累。我的做法是分层输出第一层直接答案。用一两句话给出核心结论比如仁在《论语》中的核心含义是爱人强调推己及人的实践。第二层依据。列出支撑这个结论的原文和注疏标注出处。第三层延伸。如果有相关的异体字、通假字、或不同版本的解释差异附在最后。这个分层结构让用户可以先看结论有兴趣再往下看依据。实测下来用户满意度比直接堆原文高很多。这里有个细节引用原文时一定要标注版本和出处。古籍的版本差异很大同一句话在不同版本里可能完全不同。我在输出里强制要求标注版本信息比如据中华书局点校本或据某宋刻本。这个习惯是从做校勘的朋友那里学来的一开始觉得麻烦后来发现这是专业性的底线。6. 第一版跑通时遇到的真实问题与排查过程6.1 检索结果看起来对但实际错的排查第一版跑通后我拿了一批测试查询去验证发现一个很隐蔽的问题检索结果看起来相关但实际答非所问。比如我查信在《孟子》中的用法skill返回了几段包含信字的文本但其中一段其实是《孟子》里信作为人名出现的例子不是作为德目出现的。向量检索把信这个字面匹配上了但没区分它在语境中的角色。排查过程是这样的我先看了召回结果发现向量相似度都很高说明embedding层面没问题。然后我检查了元数据过滤发现过滤条件里没有词性或语义角色这个维度。也就是说系统只知道这段文本包含信字不知道这个信是德目、人名还是其他。修复方案是在数据层增加语义角色标注。具体做法是对每个字词的出现标注它在句中的角色主语、宾语、定语等和语义类型德目、人名、地名、器物等。这个标注一部分靠规则一部分靠模型辅助。标注后检索时可以按语义类型过滤避免字面匹配但语义不符的问题。这个坑让我意识到古籍场景下字面匹配远远不够必须引入语义角色这个维度。这也是为什么我在项目里越来越重视ontology的建设。6.2 长查询的上下文溢出问题第二个问题是长查询。用户有时候会贴一大段古文进来问这段话什么意思。这段古文可能有三五百字加上skill内部的检索结果和注疏很容易超出模型的上下文窗口。我的解决方案是两阶段处理第一阶段压缩。先用一个轻量模型对用户输入做摘要和关键信息提取把长文本压缩成核心查询意图加关键实体。第二阶段检索与生成。用压缩后的查询去检索检索结果也做筛选只保留最相关的几块再交给主模型生成回答。这个方案的效果不错但引入了一个新问题压缩阶段可能丢信息。我的缓解办法是压缩时保留原文的引用位置生成回答时如果需要可以回查原文。这样既控制了上下文长度又不丢可追溯性。6.3 模型编造古籍原文的抑制第三个问题最棘手模型有时候会编造古籍原文。比如它可能生成一句《论语》云君子以文会友以友辅仁但这句其实出自《颜渊》篇它把出处标错了更严重的是它可能把不同篇的句子拼在一起生成一句看起来像古籍但实际不存在的话。这个问题在古籍场景下是致命的因为用户很难分辨。我的抑制方案有三层第一层检索约束。生成时强制模型只能引用检索结果中出现的原文不允许自由发挥。实现方式是在prompt里明确要求所有引用必须来自以下检索结果并标注编号。第二层后验校验。生成后用规则检查所有引用的原文是否真的在检索结果里出现过。如果出现检索结果里没有但回答里有的引用标记为可疑。第三层出处核对。对每个引用回查数据库确认出处是否正确。如果出处对不上降级处理或提示用户。这三层下来编造问题基本被控制住了。但代价是生成速度慢了一些因为多了校验步骤。我的取舍是古籍场景下准确性优先于速度。宁可慢一点也不能给用户错误的信息。7. 这个项目后续还能往哪走第一版跑通之后我列了一个后续的扩展清单这里分享几个我觉得最有价值的方向。第一个方向是版本比对的自动化。目前版本比对skill还是半自动的需要用户指定要比对的版本。后续我想做成自动的——用户给一段文本skill自动在多个版本里找到对应段落列出差异。这个功能对做校勘的人会很有用。第二个方向是注疏关系的图谱化。目前注疏关系还是存在SQLite里查询能力有限。后续想迁移到图数据库把字-词-句-篇-注-疏-版本之间的关系建成一张图支持更复杂的关联查询。这其实就是热词里kg知识库和ontology rag的思路。第三个方向是skill的开放化。目前四个skill是我自己定义的后续想做成可配置的——用户可以根据自己的需求组合出新的skill。比如做诗词研究的人可能想要一个格律分析skill做历史研究的人可能想要一个纪年换算skill。这些都可以在现有框架上扩展。第四个方向是移动端。热词里如何用termux安装openclaw手机版下载步骤说明有移动端需求。我试过在手机上跑轻量模型效果一般但如果把重计算放在本地服务器、手机只做交互体验会好很多。这个方向我还在探索。最后说一个我自己的体会做垂直领域的AI技能最难的不是技术而是领域知识的数字化。古籍这个领域大量的知识是隐性的、依赖专家经验的。把这些隐性知识转化成可计算的规则和结构是整个项目里最耗时、也最有价值的部分。技术框架可以换模型可以升级但这部分领域知识的积累是真正的护城河。如果你也在做类似的事情我的建议是先把领域知识的边界摸清楚再动手写代码。我一开始急着搭环境、调模型结果发现真正卡住我的是我不知道古籍查询到底有哪些意图类型。后来花了整整一周时间把能找到的古籍查询需求都梳理了一遍才把skill的边界定下来。这一周看起来是没写代码但实际上是整个项目里回报最高的一周。
返回列表