文章目录
-
- [1. 先唠两句:这玩意儿到底在干嘛](#1. 先唠两句:这玩意儿到底在干嘛)
- [2. 整条流水线长什么样](#2. 整条流水线长什么样)
-
- [2.1 七个阶段,一个都不能少](#2.1 七个阶段,一个都不能少)
- [2.2 三种输入走同一条路](#2.2 三种输入走同一条路)
- [3. 凭什么它就能改能删](#3. 凭什么它就能改能删)
-
- [3.1 反面教材:GraphRAG 的删除等于没删](#3.1 反面教材:GraphRAG 的删除等于没删)
- [3.2 WeKnora 的三根支柱](#3.2 WeKnora 的三根支柱)
- [4. 全量编译:起点长啥样,钱花在哪了](#4. 全量编译:起点长啥样,钱花在哪了)
-
- [4.1 数字不会说谎](#4.1 数字不会说谎)
- [4.2 钱到底花在哪了](#4.2 钱到底花在哪了)
- [5. 增量:新文档进来会发生什么](#5. 增量:新文档进来会发生什么)
-
- [5.1 两种增量,别搞混了](#5.1 两种增量,别搞混了)
- [5.2 新文档入库:理想情况下长啥样](#5.2 新文档入库:理想情况下长啥样)
- [5.3 如果新文档跟库里的主题重叠呢](#5.3 如果新文档跟库里的主题重叠呢)
- [5.4 同一份文档重传:每个旧 slug 都给你重写一遍](#5.4 同一份文档重传:每个旧 slug 都给你重写一遍)
- [5.5 "以最新一次为准",这话水分有多大](#5.5 "以最新一次为准",这话水分有多大)
- [5.6 投喂姿势就是省钱技巧](#5.6 投喂姿势就是省钱技巧)
- [6. 删除:删掉一篇文档之后会发生什么](#6. 删除:删掉一篇文档之后会发生什么)
-
- [6.1 删除的代码流程](#6.1 删除的代码流程)
- [6.2 实测:删掉那篇 78000 字符的长文](#6.2 实测:删掉那篇 78000 字符的长文)
- [6.3 为什么删得这么保守](#6.3 为什么删得这么保守)
- [6.4 悬空链接:删完没人收拾](#6.4 悬空链接:删完没人收拾)
- [6.5 复原测试:删了再投,能变回去吗](#6.5 复原测试:删了再投,能变回去吗)
- [7. 七个不靠谱的地方](#7. 七个不靠谱的地方)
-
- [7.1 页内剥离极其保守](#7.1 页内剥离极其保守)
- [7.2 去重只看标题,不认别名](#7.2 去重只看标题,不认别名)
- [7.3 删除之后没有"重放"这条路](#7.3 删除之后没有"重放"这条路)
- [7.4 块级引用只增不减](#7.4 块级引用只增不减)
- [7.5 目录没有统一的抽象坐标](#7.5 目录没有统一的抽象坐标)
- [7.6 目录会漂移](#7.6 目录会漂移)
- [7.7 删除留悬空链接](#7.7 删除留悬空链接)
- [7.8 规模画像:设计者假设的"健康库"长啥样](#7.8 规模画像:设计者假设的"健康库"长啥样)
- [8. 什么时候可以放心用,什么时候别指望它](#8. 什么时候可以放心用,什么时候别指望它)
-
- [8.1 可以放心用的场景](#8.1 可以放心用的场景)
- [8.2 别依赖它的场景](#8.2 别依赖它的场景)
- [8.3 跟 GraphRAG 怎么选](#8.3 跟 GraphRAG 怎么选)
- [9. 最后说几句](#9. 最后说几句)
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看, 传送门https://blog.csdn.net/qq_34419312
1. 先唠两句:这玩意儿到底在干嘛
你有没有过这种体验------往知识库扔了一堆文档,然后跟 AI 问问题,它给你编得头头是道,但你一查原文,好家伙,全是它自己脑补的。
于是有人就想了:要不咱先把文档"编译"成一套 wiki 页面,让 AI 老老实实翻页面去,别自己瞎想?
WeKnora 就是干这个的。你扔文档进去,它给你吐出来一堆结构化的 wiki 页面,每页讲一个概念或者一个实体,页与页之间还互相链接。听着是不是挺美?
但我跟你讲,这玩意儿有个特别拧巴的地方------它能加、能删,但你删完再重新加一遍,出来的东西跟原来不一样。就像你让一个人整理书架,他整理完你说"算了,恢复原样吧",他说"好嘞",然后给你摆了个差不多但绝对不一样的书架。你要跟他较真,他还振振有词:"我不是按你原来那样摆的吗?"
行,那咱今天就拆开看看,它到底是怎么个"能增能删但不靠谱"法。
2. 整条流水线长什么样
2.1 七个阶段,一个都不能少
它的编译流程分七步,我给你翻译成大白话:
第一步,抽候选------每篇文档过一遍,把里面"值得单独开一页讲讲"的概念和实体挑出来。就像你看书拿荧光笔划重点,只不过它划的不是句子,是"这玩意儿值得写个词条"。
第二步,去重------看看新挑出来的这些词条,跟库里已有的重不重。重了就别建新页了,往老页里凑。这步挺重要,不然同一个"RAG"能给你建出八个页面来。
第三步,引用匹配------把文档切成一小块一小块的,然后告诉它:"这个概念页,是由原文的哪几块撑起来的?"
第四步,summary 页------每篇文档固定生成一个总结页,就像书前面的内容提要。
第五步,目录规划------这么多页面,得分门别类放吧?它就给每个页面分配一个目录路径。
第六步,写页------这是最贵的一步。每个词条调一次大模型,把素材拼起来,生成一页正文。
第七步,链接注入------写完正文之后,看看正文里提到了哪些其他页面,自动加上 [[页面名]] 的内链。再把死链清一清,给索引页写个开场白。
2.2 三种输入走同一条路
有意思的是,全量编译、增量加文档、删除文档,走的是同一条流水线。不是三套代码,是一套代码根据输入情况走不同分支。
全量的时候,库是空的,第二步去重和第五步目录规划基本在空转------你库里啥也没有,跟谁重?往哪归?
增量的时候,第二步开始干活了------看看新文档跟老库撞不撞车。
删除更省事,第一到第五步它都不跑了,直接从第六步开始,把被删文档撑着的内容从页面里抠掉。
📌 记住这个分工:前三步和第四步,是"每篇文档各跑一遍"的,三篇文档进来就三份并行干活,互相不知道对方在看什么。后三步,是"一批文档合起来跑一遍"的,Reduce 阶段拿到整批的词条列表,统一写页、统一规划目录。
3. 凭什么它就能改能删
3.1 反面教材:GraphRAG 的删除等于没删
先说说隔壁的 GraphRAG。那玩意儿删文档是什么体验?你删了一个源文档,然后去看图谱、看社区报告------跟删之前一模一样。逐字节相同。退出码还是 0,好像它什么都做了又什么都没做。
你问它为啥?它说:"我这是增量索引啊,追加式的,你删文档我不管,下次全量重编译的时候再说。"
好家伙,下次全量重编译?那得等到猴年马月。而且它的增量也是追加的------社区不重划、度数不重算,同一份语料追加两轮,根节点数从 9 个变成 15 个。你品品,这结构都长变形了。
根本原因是什么?因为 GraphRAG 的产物是大模型转述过的社区报告。报告里那段话是哪个源文档贡献的?没人知道。所以你要删,都不知道从哪儿下手。
3.2 WeKnora 的三根支柱
WeKnora 能改能删,靠的是三根柱子。我给你挨个掰扯。
第一根柱子:正文素材用的是逐字原文块。
这是什么意思呢?写页的时候,它不是把原文改写成大模型自己的话,而是把原文块原封不动地塞进提示词里,让大模型贴着原文措辞写。提示词里说得特别明确:
text
You are a COMPILER, not a creative writer. Stay close to the verbatim source wording.
You may lightly reorder, deduplicate, and join related sentences,
but must not rephrase for style, expand short statements, or invent transitions.
翻译过来就是:"你是个编译者,不是作家。别给我整那些花里胡哨的改写。允许你稍微排排序、去个重、把相关的句子拼一拼,但不许为了文风改写、不许把短句拉长、不许自己加过渡句。"
这设计太妙了。为什么?因为一旦正文是逐字原文,"删掉某篇文档贡献的内容"就变成了一个文本判断问题------"这句话是不是只有被删那篇文档撑着的?是的话就删掉。"清清楚楚,明明白白。
你要是正文是大模型自由发挥写的,那"这句话是哪篇文档来的"就变成一笔糊涂账了。你说它是 A 文档的意思,我说它是 B 文档的意思,谁也说不清。
第二根柱子:页和文档、页和块的对应关系,是真存在数据库里的。
每个 wiki 页面在数据库里有两列关键信息:一列记着这页被哪些文档支撑着(source_refs),另一列记着这页引用了哪些原文块(chunk_refs)。
所以你要删一篇文档,它一查 source_refs,就知道哪些页受影响了。然后把这篇文档从这些页的引用列表里剥掉,完事。
当然了,这根柱子只建了一半。重解析的时候,旧的块引用不会清掉,只会往里加新的。这个后面细说。
第三根柱子:slug 是身份账本。
每个页面有个唯一标识,叫 slug,长这样:concept/rag。写页的时候,是按 slug 这个键来覆盖的------slug 对得上,就改这页;对不上,就新建一页。
那问题来了:新文档里又出现了一次"RAG",怎么知道它应该跟老的 concept/rag 是同一页,而不是新建一个 concept/rag-2?
它有三招。第一招,先把"这篇文档上次产出过的 slug 列表"喂给模型,相当于提醒它:"上次你建了这些页,这次看看能不能对上。"第二招,去重的时候按标题召回几个相似的已有页面,让模型判断是不是同一个东西。第三招,标题归一化之后一模一样的,直接复用,连模型都不用问。
✨ 这三根柱子分工还不一样:前两根主要服务"能删",第三根主要服务"能改"。别搞混了。
4. 全量编译:起点长啥样,钱花在哪了
4.1 数字不会说谎
我跑了两组全量编译,给你看看数据。
第一组,一篇 Anthropic 的文章,28956 字符,大概 5701 个 token。编译完出来多少页?45 页。花了多少 token?239912。
等会儿,239912 除以 5701 是多少?我算一下------42 倍。
你没听错。5701 token 的原文,编译出来烧掉了 24 万 token。翻了 42 倍。
第二组,两篇 Hamel 的 evals 文章,加起来 10 万多字符,2 万 token。编译出来 86 页,花了 50 万 token。25 倍。
哎?怎么倍数差这么多?42 倍和 25 倍?
因为每页的成本其实差不多------都是 4.6k 到 4.9k token 一页。差的是"每一千个源文 token 能建出多少页"。长文里几乎每段都能服务好几个主题,所以页建得多,摊下来倍数就低。FAQ 那种短段落,撑不起新页,倍数就高。
4.2 钱到底花在哪了
写页这一步,吃掉了 79% 到 82% 的 token。没跑------每个词条都要调一次大模型写正文,一个条目一次调用,这成本能不高吗?
剩下的按比例排:引用匹配占 7% 到 11%,抽候选大概 4%,summary 大概 4%,目录规划 1.4%,索引引言 0.1%。
还有个有意思的细节:空库第一次跑的时候,去重这一步一次调用都不花。库里啥也没有,跟谁重?直接返回。
😅 说白了,全量编译的钱几乎全砸在写页上了。每个条目一次调用,没什么省法。真正有意思的问题是第二篇文档进来会怎样------这才是日常用的场景。
5. 增量:新文档进来会发生什么
5.1 两种增量,别搞混了
"增量"其实是两码事。
第一种,你往已经有内容的库里再扔一篇新文档。这是日常用得最多的。新文档里抽到的词条,跟库里已有的重了就并进去,不重就建新页。
第二种,同一份文档改了之后重新上传。这时候就不一样了------这篇文档上次产出过的每个 slug,全部整页重写一遍。不管你改没改内容,只要你重传,它就重写。
你说这不是浪费吗?它说:"万一你改了呢?万一你没改但模型上次抽得不准呢?重写一遍最保险。"
行吧,你有钱你说了算。
5.2 新文档入库:理想情况下长啥样
我往已经装了两篇 evals 文章的库里,投了一篇主题相邻但不同的 Anthropic 文章。看看结果:
原来 95 页,现在 117 页。多了 22 页,其中 21 个内容页加 1 个 summary 页。
重点来了:已有内容页一个都没被并入。唯一被改的是索引页。
成本呢?2714 token 的源文,烧了 98773 token。36 倍。写页 21 次,占了 75%。
目录呢?多了一个一级目录,叫"项目与产品"。这正常------新主题嘛,总得有个新位置。你要不让它加新目录,它硬把新内容塞进旧目录,那才叫灾难。
这就是增量最理想的形态:新文档等于一堆新页,加上可能的一个新目录,老内容基本不动。
5.3 如果新文档跟库里的主题重叠呢
那就走"并入"这条线。后投的文档命中已有页,去重那步把 slug 归一过去,Reduce 就把两边的素材写进同一页。
我做了个探针实验,往 P6+P7 的库里补了两篇小文。你猜怎么着?
concept/rag 这个页面,来源引用从 1 个变成 2 个再变成 3 个,正文从 1849 字符涨到 2564 再涨到 2607。完美------多源页就是这么"长"出来的。
而且新增的 7 个概念页全部被塞进已有目录了,一级目录一个都没加。说明去重和目录规划在起作用。
但也不是没毛病。entity/langsmith 那页,同一件事被讲了两遍------一遍是摘要句,一遍是原文块。为什么?因为每篇素材都以一句描述打头,而编辑器只被允许"轻度重排、去重",没有被要求把这两者合并。它就老老实实地两边都留着了。
就像你让一个人整理两份材料,他说"你不让我大改啊,那我就都放着,你自己看吧"。
5.4 同一份文档重传:每个旧 slug 都给你重写一遍
这个才是"不幂等"的重灾区。
你把同一份文档原封不动再传一遍,你以为它会说"哦,一样的,跳过"?不可能。它会把这篇文档上次产出过的每个 slug,全部整页重写一遍。
我实测了一下:同一个库重解析一次,页数从 45 变成 54。
多了 9 页?为什么多了?因为模型这次抽候选的时候,多抽出了几个上次没抽到的概念。还有几个上次抽到的这次没抽到,按"这篇文档不再提了"处理,把来源剥掉,单源页直接删。
一增一减,页数就变了。
而且重写比新建还贵------因为提示词里要多塞一页旧正文进去。你说它是不是故意的?它说:"我不看旧正文怎么知道要改啥?"
行,好像有道理。但你这成本蹭蹭往上涨啊。
5.5 "以最新一次为准",这话水分有多大
很多人以为重解析之后,页面内容就是"最新版的"。太天真了。这句话在三个层面上的硬度完全不一样:
页面在不在------这个是硬的。上次抽到这次没抽到的 slug,直接走删除流程,程序说了算,不靠模型猜。
页面写成什么样------这个是软的。模型拿到旧正文加上新素材,被要求"删旧的、加新的、保留还有效的"。但问题是,它怎么知道哪句话是旧版本写的?它手里只有这篇文档上一版的 summary,没有句级归属。所以它只能猜。猜得准不准?看心情。
证据引用------这个是只增不减的。旧块和新块都挂在页面上,两代证据并存。你说这页的证据是哪些?它说"都是"。
📌 总结一下:页面的"在不在"以最后一次抽取结果为准(硬);页面的"写成啥样"以最后一次为目标但不保证(软);页面上挂的证据是累加的(旧的不去新的照来)。
5.6 投喂姿势就是省钱技巧
既然写页占了八成成本,那省成本的关键就是------别让同一个页被写两次。
它有个 30 秒的去抖窗口。你在 30 秒内连续上传的文档,会被攒成一批,一起处理。同一批里多篇文档命中同一个 slug,Reduce 只写一次页。
但你要是隔了一个小时才传第二篇,那第二篇就是单独一批。它命中的那个已有页,就得整页重写一遍------而且是更贵的那种重写。
所以怎么省钱?很简单:有关系的文档,一口气全传了,让它们落在同一个 30 秒窗口里。
如果你一次要传超过 5 篇同域文档,把 IngestBatchSize 调大点,别让它被切成两批。Map 并发度也调到跟批大小一样,不然排队。
当然了,省多少取决于批内 slug 的重叠率。两篇文章就共享 4 个词条的话,分成两批也就多写 4 次页,占那批 83 次写页的 5%,省不了几个钱。真正值得调参的,是同一条产品线的多篇公告------那种高度重叠的语料。
6. 删除:删掉一篇文档之后会发生什么
6.1 删除的代码流程
删一个知识条目,流程是这样的:先写个墓碑标记,清掉待处理的 ingest 队列,然后按 source_refs 反查哪些页受这篇文档影响,把这些页放进一个"撤回"队列,然后 Reduce 阶段逐 slug 处理。
多源页------走撤回分支。被删文档的摘要当"旧版本长什么样"的信号交给编辑器,让它在正文里判断:哪些内容只来自被删文档,删掉。
单源页------直接删。没有别的来源撑着了,这页留着干嘛?
然后落库的时候,把这篇文档的块引用从页面上剥掉。最后清死链、修剪空目录、重写索引页引言。
6.2 实测:删掉那篇 78000 字符的长文
我删了 P7 那篇 78647 字符的文档。来看看数据:
原来 117 页,删完之后 70 页。消失了 47 页------46 个内容页加 1 个 summary。干净利落。
花了多少 token?24388。源文是 15257 token,1.6 倍。你没听错------删一篇 1.5 万 token 的文档,只花了 2.4 万 token。比全量编译便宜多了。
为什么这么便宜?因为 47 个单源页是直接删数据库那一行,根本不调大模型。只有 5 个多源页各改了一次,加上索引引言重写,一共 6 次调用。
但重点来了------多源页的正文,几乎没动。
synthetic-data-generation 那页,3395 字符变成 3350 字符,少了 45 个字符。
rag 那页,少了 92 字符。
shreya-shankar,少了 276 字符------这是动得最多的了。
你再算算那 5 个多源页各自有多少块来自 P7?synthetic-data-generation 是 28 块里有 15 块来自 P7,快一半了。结果正文才少了 45 个字符?
45 个字符是什么概念?大概一句话。半句话。
它删了个寂寞。
6.3 为什么删得这么保守
不是模型不够聪明,是它手里没证据。
你要让它删一句话,它得回答两个问题:第一,这句话是不是来自被删的那篇?第二,它还有没有别的来源撑着?
它手里有什么?页面正文------但正文里没有句级归属,你不知道哪句来自哪篇。被删文档的 summary------不是原文,是摘要。其他来源的 summary------也是摘要。
你拿摘要去匹配正文里的句子,能匹配出个啥?只能模糊猜。两个问题都答不准,那最安全的选择是什么?不删。
对,不删。删错了要出大问题,不删最多就是留了点多余的内容。模型也懂这个道理------多一事不如少一事。
😤 所以删除这件事,真正干净的是"整页消失"(单源页),不是"页内剥离"。你指望它把多源页里属于被删文档的句子精准抠掉?它做不到。不是不想,是不能。
6.4 悬空链接:删完没人收拾
删完之后 lint 一查,坏链 0 变成 19。19 条 broken link,全部指向被删掉的那些页。
为什么没人清?因为清理逻辑只扫"本批受影响的页"------也就是引用了被删文档的那些页。而这些悬空链接挂在别的页上------那些页链接到了被删的页,但它们本身不引用 P7,所以不在扫描范围内。
就像你搬家了,你给你的朋友发了消息说"我搬了啊,别找我了"。但你朋友的朋友还在给他发你老地址的快递,你朋友没通知到所有人。快递就全退回去了。
而且它的自动修复 AutoFix 是要手动调 API 才跑的,删完不会自动执行。你得自己记得去点一下。
6.5 复原测试:删了再投,能变回去吗
这是我最好奇的实验。我把刚删掉的 P7,原封不动再投回去。看看能不能恢复成原来的样子。
结果:70 页变成 114 页。删除前是 117 页------少了 3 页。
被删掉的 47 个 slug,只回来了 27 个。剩下 20 个没回来。什么 entity/arize、entity/braintrust、entity/hex......上次抽到的,这次没抽到。
而且还多了 16 个原来没有的 slug。entity/bertscore、entity/rouge、concept/likert-scale......这次抽到的,上次没抽到。
一减一加,最后总数比原来少 3 页。
目录呢?29 个目录里有 11 个是空的。还有个 concept/error-analysis,原来在 评估方法/错误分析 下面,这次跑到 评估方法/自动化评估 下面了。留下一个空目录在那。
你说这是 bug 吗?不是。这是 LLM 编译的固有属性。你让一个人把同一堆资料重新整理一遍,他也不会给你逐字相同的第二份索引。
它的温度是 0.3,也没设 seed。从一开始就没打算做"可复现"。
✨ 所以别指望"删了再投能恢复原样"。你能指望的是------大概差不多,但细节肯定不一样。收口的成本在人身上:合并重复页、清理空目录,这些都得你自己来。
7. 七个不靠谱的地方
我按"会不会静默地坑你"排个序。越往前的,你没主动犯错也会中,而且还不容易发现。
7.1 页内剥离极其保守
这个前面说过了。多源页在源被删后正文几乎不动。结构决定的,不是模型的问题。你要是问"被删文档的独有断言有没有清干净"------答案是:没有。我们没验到"清干净"。
7.2 去重只看标题,不认别名
这个坑特别隐蔽。候选召回用的是 pg_trgm 的相似度匹配,只比标题。页面的别名从来不参与召回。
什么意思呢?你有一页叫"检索增强生成",别名是 RAG 和 Retrieval-Augmented Generation。新文档里出现了"Retrieval-Augmented Generation"这个词,你以为它会匹配到老页?不会。因为它只看标题,标题是中文的,对不上。它就给你新建一个页。
孪生页就这么来的。同一个东西,两个页面,内容还有重叠。你后期自己合并去吧。
7.3 删除之后没有"重放"这条路
删了再投不是复原。47 个 slug 回来 27 个,另新增 16 个。这是 LLM 编译的固有属性,人的重做也不幂等。但你得把这个当选型前提------要"可复原",你就得自己留页面快照。
它的 wiki_page_revisions 只保存每次覆盖前的版本,不负责重建被删页。所以删了就是删了,别想着找回来。
7.4 块级引用只增不减
页面落库的时候,块引用是取并集------旧的不去,新的照来。只有删除文档那条路才会剥块引用。
所以重解析同一篇文档之后,页面会同时挂着两代块引用。我实测 A5 那篇重解析之后,库里 142 个块,两代各 71 个,52 个页面全部双代。
这意味着什么?chunk_refs 不是"当前正文的证据清单",而是"历次写入的并集"。里面混着已经被替换掉的旧块。你要拿它做证据溯源,会翻车。
而且就算它完全准确,它也只是一份页级的块清单------正文里没有任何锚点把"这句话"指回"那个块"。所以"哪句话来自哪块"这个问题,在这套数据模型里根本不成立。
7.5 目录没有统一的抽象坐标
目录规划每次只看到"本批的新条目 + 已有目录池",提示词要求的是"这一批的条目落在一棵自洽的树上"。批内有规则,跨批没有任何约束。
什么后果?第一批的视野决定了顶层形状。后面批次装不下新内容的时候,就按那一刻这一批的抽象水准另造顶层。
我自己的库就是活教材:一层目录里混着三个轴------"组织与人物"是按实体类型分的,"评估工具/评估方法/评估概念"是按领域子分分的,"项目与产品"是按项目分的。三个轴混在同一层。
人导航的时候觉得有点乱,但还能忍。一旦你想把目录当枚举轴交给 agent------让它按目录列、按目录跳------这就从"不好看"升级成"检索能力缺口"了。
7.6 目录会漂移
目录分配是 LLM 的判断,温度 0.3,无 seed。同一份文档重投,同一个概念换个目录很正常。我那个 错误分析 就从 评估方法/错误分析 漂到了 评估方法/自动化评估。
这事儿有一部分是固有的------人重新整理一遍也会这样。但有三处本来可以修:
第一,没有 slug 到目录的历史账本。已经存在的页不会被改,但页面被删后重建的时候,新页要重新规划,规划不知道它以前住哪。存一份归属记录,重建时优先复用不就完了?
第二,空目录残留。修剪空目录的逻辑只在撤回涉及的目录链上跑,重投漂移留下的空目录不会被顺手清掉。我那 29 个目录里 11 个是空的。
第三,嵌入粗筛本身偏脆。目录数超过 60 个才启用,对每个条目硬取余弦 top 3,没有分数下限。真正的"家目录"排第 4 就静默消失了。
7.7 删除留悬空链接
这个前面说过了。清理只覆盖"引用被删文档的页",不覆盖"链接到被删页的页"。自动修复要手动调。
修法其实不难:删除的时候用被删页的入链反查,把这些页一并纳入清理范围。一行 SQL 的事。
7.8 规模画像:设计者假设的"健康库"长啥样
上面那些"能不能用"的判断,最后都落在几个写死的常数上。我给你列出来,你就知道设计者心里的库有多大了:
| 常数 | 值 | 干嘛用的 |
|---|---|---|
| 目录池上限 | 400 | 目录规划时拉已有目录当候选池,一次最多 400 条 |
| 全量喂给规划器 | ≤ 60 个目录 | 超过就切成近似召回,不再整池喂 |
| 提示词里的可复用目录 | 150 | 渲染进提示词的已有目录上限,超过就截断 |
| 一次规划装多少条目 | 60 | 本批条目分批规划,前一批的目录会喂给后一批 |
| 单篇文档输入 | 32768 字符 | 超出的部分不会产生新条目 |
| 交叉链接回填 | 不回填旧批次 | 新页只链本批新页,长尾链接交给 lint |
这些常数全是"几十、几百"量级。连起来就是设计者假设的健康库:几十到几百个目录、几千页以内。
超过这个区间它不会报错,而是逐级退化------精确复用变成近似召回,近似召回变成提示词截断,最后长尾不建链,全交给 lint 和人工。
8. 什么时候可以放心用,什么时候别指望它
8.1 可以放心用的场景
语料扁平的------一批同域文档,不是层层嵌套的组织资料。
你要的是一份人可读、可增量维护的中间产物------人能按目录和链接翻,agent 能读页面。
库的规模落在"几十到几百个目录、几千页以内",并且你能接受目录超过 60 个之后复用走近似召回。
你能接受结构会漂,并且愿意周期性跑 lint 加人工整理目录。
删除的语义是"来源撤回"------这篇不算数了------而且你不要求"页面上的相关句子一定被抹掉"。
批量导入同域文档的时候,一口气在 30 秒窗口内连续投喂。同一页只写一次而不是反复重写。写页占成本八成,这一条比任何参数都直接。
8.2 别依赖它的场景
超大语料------几千页以上。目录、身份、链接三件事的一致性全靠 LLM 加提示词,越过规模区间不会报错,只会逐级退化。
要求删除可逆、可审计的。删加重投不能复原,可审计也打折------块引用只增不减,没有句级锚点,"哪句话来自哪块"不可归因。
要求结构稳定的导航。同一份文档两次编译,目录和页面集合都会变。
要求"同一事物在库里唯一"。去重只比标题不认别名,中英对译、缩写这类同义可能裂成两页。
要求 agent 按目录、主题枚举与遍历。现在没有这条通道------目录不进检索,页面也没进向量索引,agent 只能正则找页或者按 slug 直读。要这条路得自己补。
8.3 跟 GraphRAG 怎么选
GraphRAG 把全局结构交给算法------Leiden 社区划分。代价是重:全量重编译,删除基本被忽略。好处是语料越大越压得动。
WeKnora 把结构交给 LLM,把变更做在局部。好处是能增能删。代价是结构会漂、规模上不去、目录只服务人。
选哪边?取决于你更怕"重"还是更怕"漂"。
9. 最后说几句
WeKnora 给你的不是"可逆、精确、结构稳定"的知识底座。它给你的是一份"大致正确、能跟着语料改的中间产物"。
你要拿它当知识库的最终答案来源,那大概率要失望。你要拿它当一个"帮你把一堆文档整理成可读页面"的工具,然后人来做最终的判断和整理------那它挺好用的。
工具没有好坏,就看你怎么用。你非要让它干它不擅长的事,那它也只能给你惊喜------或者惊吓。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/qq_34419312