`kb` 知识库命令:建、灌、查、删
一个带 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):
| 子命令 | 行号 | 做什么 |
|---|---|---|
list | 62 | 列出全部知识库 |
info | 117 | 看单个库的详情 |
set-default | 128 | 设默认库 |
create | 139 | 从文档初始化一个新库 |
add | 185 | 往已有库里追加文档 |
delete | 228 | 删库 |
search | 251 | 在库里检索 |
入口是控制台脚本 deeptutor(pyproject.toml:78 里的 deeptutor = "deeptutor_cli.main:main"),也可以用 python -m deeptutor_cli(deeptutor_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_name(deeptutor/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=False(kb.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
create 和 add 共用同一个收集函数 _collect_documents()(kb.py:34-58),它的行为只有两条需要记:
- 显式路径与目录会一起去重。你既
-d指了某个文件、又用--docs-dir指了它所在的目录,这个文件只会算一次。 - 目录是递归的。走的是
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,可选 json(kb.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 json;kb 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.json(deeptutor/services/path_service.py:206-217、deeptutor/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 语义不一样
最后一处对照,用的人多但容易踩:
- 顶层
run与chat的--kb是 list 类型,可以重复给(main.py:85-95、chat.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 直接相关的语义差。
九、可以照着核的五步
- 打开
deeptutor_cli/kb.py:163-165,确认无文档时create的处理是打印提示并退出 1,而不是建一个空库。 - 打开
kb.py:210-226,确认allow_duplicates=False与那句No new unique documents were indexed.的触发条件。 - 打开
kb.py:34-58,确认--docs-dir是recursive=True的递归收集,并确认显式路径与目录之间会去重。 - 打开
kb.py:28-31与deeptutor/services/path_service.py:206-217,把两条目录拼法并排看一遍。 - 打开
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_documents、naming 与 deeptutor/tools/rag_tool.py 的实现都没有读,凡是涉及”建库过程里到底发生了什么""检索质量如何”的部分,本文一律不下结论。文中的默认值都是源码里的默认配置,不是运行结果的保证;这组命令会读取你本机磁盘上的文档目录,并按你配置的 provider 发起调用,用之前请自行评估。
本文依据 DeepTutor 官方仓库(github.com/HKUDS/DeepTutor)的 README、AGENTS.md、
pyproject.toml 与 deeptutor/ 下的源码整理,核对日 2026-08-10,对应仓库快照 456f9c2(版本 1.5.11)。
本文内容为仓库源码与文档口径,我们没有安装、部署或运行过该项目,也没有调用过其中任何一个模型 API,
因此不涉及生成质量、响应速度与教学效果的任何描述。
参数与默认值随版本变动,请以仓库最新代码与 --help 的实际输出为准。
安全相关做法请结合自身环境评估,本文不构成安全方案建议。