`kb` 知识库命令:建、灌、查、删

2026-08-10

一个带 RAG 的项目,命令行里最先被用起来的通常就是知识库那一组命令。DeepTutor 把它放在 deeptutor_cli/kb.py:我们 2026-08-10 采集时这个文件是 286 行,七个子命令全在里面。行数不多,但里面有一处分工和大多数人的直觉是反的——如果你按惯性去用,第一条命令就会以退出码 1 收场。

全文的行号与数字都对应同一份仓库快照:HEAD 456f9c2,采集日 2026-08-10,deeptutor/__version__.py 里的版本号是 1.5.11。你自己 clone 之后行号可能已经漂了,文件名、命令名和选项名是稳的,照着找即可。

先说清本文的边界:这一篇只读命令层,也就是 deeptutor_cli/kb.py 这一个文件。知识库的类型模型、清单文件、切块与检索参数写在 deeptutor/knowledge/deeptutor/services/rag/ 那一侧,我们另有专门的篇目讲,这里不重复搬。

一、七个子命令与它们的位置

kb 是顶层 Typer 应用注册的命令组之一,组的 help 文案是 Manage knowledge bases.deeptutor_cli/main.py:36-46)。组下面挂了 7 个子命令,位置如下(deeptutor_cli/kb.py):

子命令行号做什么
list62列出全部知识库
info117看单个库的详情
set-default128设默认库
create139从文档初始化一个新库
add185往已有库里追加文档
delete228删库
search251在库里检索

入口是控制台脚本 deeptutorpyproject.toml:78 里的 deeptutor = "deeptutor_cli.main:main"),也可以用 python -m deeptutor_clideeptutor_cli/__main__.py:1-5)。所以下文写成 deeptutor kb ... 的地方,换成 python -m deeptutor_cli kb ... 是同一条路。

按会不会改动磁盘上的东西分一下:只读的是 list / info / search,会写盘的是 create / add / set-default / delete,其中 delete 不可逆。

二、反直觉的那一处:create 建不出空库

按大多数 CLI 的惯例,create 负责起一个空壳,add 负责往里灌东西,两步分得干干净净。这里不是。

kb create <name> 的签名在 kb.py:139-155:位置参数是新库名字,选项只有两个——--doc/-d(可以重复给多次)和 --docs-dir。它的执行顺序是:先把名字过一遍 validate_knowledge_base_name,名字校验不通过时命令不会继续(具体拒绝规则在 deeptutor/knowledge/naming.py 一侧);再检查同名库是否已存在,已存在则报错退出 1;然后收集文档路径。

关键在收集之后那一步(kb.py:163-165):如果一个文档都没收集到,它打印 Provide at least one supported document (--doc or --docs-dir). 然后退出 1。也就是说,这个命令没有”先建个空库回头再说”的路径,创建和首次灌入是绑死的一件事。

这个设计带来的实际差别是:你的操作顺序必须调过来。不是”先 create 占个名字,再慢慢 add”,而是先把第一批文档准备好,再 create;后续增量才轮到 add。如果你的脚本是按”建库—检查—灌入”三段写的,第一段就会挂。

顺带一个同层的细节:名字不是随便起的,要先过 validate_knowledge_base_namedeeptutor/knowledge/naming 里的函数,调用点在 kb.py:139-155 这段签名与前置校验里)。哪些字符会被拒、有没有长度限制,属于 deeptutor/knowledge/naming 那一侧的规则,本文没有核实,别猜——名字被打回来时直接去读那个模块。

三、灌:add 默认不收重复文档

kb add <name>kb.py:185,它与 create 共用同一个文档收集函数 _collect_documents()kb.py:34-58),所以两边给文档的方式是一套。它底层调 add_documents(...),这里有个写死的入参值得记住:allow_duplicates=Falsekb.py:210-226)。

配套的是同一段里的输出分支:当返回值为 0 时,它打印 No new unique documents were indexed.。这句话的含义很具体——命令走完了,但一份新文档都没进去。你重复 add 同一批文件,或者把一个已经灌过的目录再指一次,落到的就是这条分支。

这里要提醒一句判定方式:看到 No new unique documents were indexed. 这句提示,说明问题出在”这批文档被判为不是新的”,而不是路径写错了(路径压根收不到文件时,报的是上一节那句 Provide at least one supported document,两句话不一样,别混)。至于这条分支之后的退出码是多少、去重是按什么键判的,kb.py 这一层没有给出答案,去 deeptutor/knowledge/add_documents 里看。

四、两个来源怎么合并:_collect_documents

createadd 共用同一个收集函数 _collect_documents()kb.py:34-58),它的行为只有两条需要记:

  1. 显式路径与目录会一起去重。你既 -d 指了某个文件、又用 --docs-dir 指了它所在的目录,这个文件只会算一次。
  2. 目录是递归的。走的是 FileTypeRouter.collect_supported_files(base, recursive=True),也就是子目录里的文件同样会被收进来。

第二条是这一层最容易低估的地方:你给一个目录,它会沿着子目录一路往下走,把 FileTypeRouter 认识的文件全部收上来交给建库流程。这是在你本机磁盘上做的遍历,指目录之前先想清楚那底下都有什么——尤其别顺手把一个混着私人材料的根目录扔进去。哪些扩展名算”支持”,由 FileTypeRouter 决定,那份清单不在本文核实范围内。

另外,建库这一步要不要走 embedding、走哪一个,取决于你在 deeptutor init 里配了什么。向导第 3 步的 Embedding 是可以按 [s] 跳过的(init_cmd.py:207-290)——跳过之后 kb create 这条线会怎么表现,我们没有核实,不做推断。要看当前配的是什么,用 deeptutor config show,它会输出 ports / llm / embedding / search / language / tools,且所有 api_key 一律显示成 "***""(not set)"config_cmd.py:68-92)。

五、查:三条不同的”看”

kb list--format/-f,默认 rich,可选 jsonkb.py:63-115)。rich 模式下是一张五列表格:Name / Status / Documents / RAG Provider / Default。最后一列对应 set-default 设的默认库,一眼就能看出当前默认是哪一个。

kb info <name> 是单库详情(kb.py:117),kb search <name> <query> 才是真检索(kb.py:251-274)。search 有两个选项:--mode 默认 "hybrid"--format/-f 默认 rich;底层调的是 deeptutor.tools.rag_tool.rag_search

关于 --mode 要说清一件事:默认值确实是 hybrid,但还能填哪些值,命令层没有把可选项写出来,取值范围在 rag_search 那一侧。本文不猜,也不给”建议用哪个 mode”的说法——想知道就去读 deeptutor/tools/rag_tool.py

要把 kb search 的结果喂给别的脚本时,用 --format jsonkb list 同理。这两个 -f 是这一组里仅有的两个格式开关。

六、删:确认在哪一层

kb delete <name> 默认走交互确认,--force/-f 跳过确认(kb.py:228-237)。CI 或批处理里必须带 -f,否则会卡在等待输入上。

顺带说明本站读者常遇的一处平台差异:CLI 的 Ctrl-C 拦截器 _SigintInterceptor 仅在 POSIX 生效,Windows 上没有 add_signal_handler 时它退化为 no-op(docstring 在 common.py:264-274)。这不是 kb 组特有的行为,但你在 Windows 终端里按 Ctrl-C 中断一个正在跑的建库命令时,走的是与 Linux/macOS 不同的那条路径。

七、库落在哪个目录

这是另一处值得单独核的地方。CLI 里知识库的根目录写在 kb.py:28-31

get_path_service().project_root / "data" / "knowledge_bases"

deeptutor init 写配置的落点是另一条拼法:设置目录是 get_settings_dir() = <data>/user/settings,模型目录文件是 <data>/user/settings/model_catalog.jsondeeptutor/services/path_service.py:206-217deeptutor/services/config/model_catalog.py:20);data 根本身由 get_runtime_data_root() 给出,等于 <runtime-home>/data,而 runtime home 的优先级是「显式参数 → DEEPTUTOR_HOME 环境变量 → 当前工作目录」(deeptutor/runtime/home.py:8:12-27)。

两处是两条不同的拼法:一条从 project_root 直接拼 data/knowledge_bases,一条走 PathService 的 settings 目录。project_root 在本文的核实范围之外,我们不替它推导等价关系,也不推断二者是否落在同一棵目录树下——说到差异为止。你要确认自己的库到底建在哪,最直接的办法是先 deeptutor kb list 拿到名字,再在磁盘上搜 knowledge_bases 这个目录名。

顺便记住 DEEPTUTOR_HOME 这个变量:run_init 会把它设成解析出的 runtime home,并重置 PathService 等三个单例,注释里明写不重置会「静默写错地方」(init_cmd.py:24-48:412-418)。多个工作区来回切的人,这是最容易出岔子的一个环境变量。

八、命令行的 --kb 与 REPL 的 /kb 语义不一样

最后一处对照,用的人多但容易踩:

  • 顶层 runchat--kblist 类型,可以重复给(main.py:85-95chat.py:46-56)。
  • 进了 REPL 之后的 /kb <name>替换语义:state.knowledge_bases = [] if value == "none" else [value]chat.py:219-221)。填 none 是清空,填名字是”只留这一个”。

也就是说,你在命令行上挂了多个 kb 进 REPL,之后在会话里敲一次 /kb x,剩下的就只有 x 了——它不是”追加一个”。REPL 的其余部分我们另有一篇专门讲,这里只借这一条与 kb 直接相关的语义差。

九、可以照着核的五步

  1. 打开 deeptutor_cli/kb.py:163-165,确认无文档时 create 的处理是打印提示并退出 1,而不是建一个空库。
  2. 打开 kb.py:210-226,确认 allow_duplicates=False 与那句 No new unique documents were indexed. 的触发条件。
  3. 打开 kb.py:34-58,确认 --docs-dirrecursive=True 的递归收集,并确认显式路径与目录之间会去重。
  4. 打开 kb.py:28-31deeptutor/services/path_service.py:206-217,把两条目录拼法并排看一遍。
  5. 打开 kb.py:251-274,确认 --mode 的默认值是 hybrid,并顺着 rag_search 去找可选值——命令层没写。

一段可以照着改的命令示例:

# 先备好第一批文档,再建库(建库与首次灌入是同一步)
deeptutor kb create <你的库> --docs-dir <你的文档目>

# 后续增量
deeptutor kb add <你的库> --doc <某个文档路>

# 看看有哪些库、哪个是默认
deeptutor kb list --format json

# 检索,结果给脚本用
deeptutor kb search <你的库> "<你的查询>" --mode hybrid --format json

# 非交互环境删库
deeptutor kb delete <你的库> --force

以上为按仓库中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。

需要说明的边界:本文只读了 deeptutor_cli/kb.py 这 286 行的命令层,deeptutor/knowledge/ 下的 initializer、add_documentsnamingdeeptutor/tools/rag_tool.py 的实现都没有读,凡是涉及”建库过程里到底发生了什么""检索质量如何”的部分,本文一律不下结论。文中的默认值都是源码里的默认配置,不是运行结果的保证;这组命令会读取你本机磁盘上的文档目录,并按你配置的 provider 发起调用,用之前请自行评估。


本文依据 DeepTutor 官方仓库(github.com/HKUDS/DeepTutor)的 README、AGENTS.mdpyproject.tomldeeptutor/ 下的源码整理,核对日 2026-08-10,对应仓库快照 456f9c2(版本 1.5.11)。 本文内容为仓库源码与文档口径,我们没有安装、部署或运行过该项目,也没有调用过其中任何一个模型 API, 因此不涉及生成质量、响应速度与教学效果的任何描述。 参数与默认值随版本变动,请以仓库最新代码与 --help 的实际输出为准。 安全相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。