第二季第 8 篇。拆解对象:DeepFlux 的命令行工具
df_cli------编译产物叫deepflux,一个二进制、14 个顶级子命令。它是这个平台在"没有浏览器"的场合的完整替身:服务器上、CI 流水线里、客户机房断网时。本篇也是第二季前七篇的一次"工具串场"------101 篇的迁移红线、105 篇的 SSE、107 篇的沙箱,都能在它身上找到命令行的影子。
先交代真实状态。今天我在本机实跑了三条命令:
bash
$ deepflux --help
Available Commands:
bootstrap 初始化首个租户(全新安装后执行一次 · 幂等)
brand 白标交付配置
chat 交互式聊天 REPL · SSE 流式 · HITL 命令行审批
config Agent 配置 push/pull
data 数据导入导出
eval 评估 KB / Agent 质量
kb 知识库管理
license 私有化 License 校验
migrate 数据库 schema 迁移
pack Solution Pack 校验
rotate-secret-key 用新 DF_SECRET_KEY 重新加密 llm_providers.enc_api_key(BYOK 轮换)
session 会话操作
skills Skill 管理
user 本地账号管理(email+password 登录)
$ deepflux migrate status # 连本机测试库
schema_version=187 dirty=false
$ deepflux brand validate # 校验内置白标品牌配置
✓ brand deepflux valid
一个二进制,既能管理 agent 配置和知识库,也能直连数据库查版本、校验私有化授权。它是怎么组织成这样的?为什么有的命令要 API 地址、有的却要数据库连接串?
一、先弄懂三个词
CLI(命令行界面) :靠敲命令操作软件的方式,对应"图形界面"(点按钮)。服务器世界里 CLI 是一等公民:没有显示器、没法开浏览器的时候,SSH 终端里能敲的命令就是一切。cobra 是 Go 语言最流行的 CLI 框架(Kubernetes 的 kubectl 也用它),负责解析命令、参数、生成帮助文本------df_cli 的 --help 输出就是它生成的。
REST API :平台对外提供的服务接口,走 HTTP。正常情况下,配置管理、知识库上传这些操作由网页前端调用 API 完成。CLI 的多数子命令就是同一个 API 的命令行客户端------网页能做的它都能做,区别只是敲命令还是点鼠标。
DSN(数据源名称) :数据库的连接字符串,形如 postgres://用户:密码@主机:端口/库名。带"直连数据库"能力的命令需要它------因为它们绕过 API,直接读写数据库。这是下一节的主角。
二、一个二进制,两种人格
df_cli 的 14 个子命令按"通过谁干活"分成两类,这个分界是理解它的钥匙:
| 人格 | 子命令 | 需要什么 | 适用场合 |
|---|---|---|---|
| API 客户端 | config / kb / session / chat / data / eval / skills | --api-key + --base-url |
服务在跑,走正门 |
| 数据库直连 | migrate / bootstrap / user / rotate-secret-key | DEEPFLUX_PG_DSN(连接串) |
服务没起、安装期、密钥轮换 |
| 交付保障 | pack / brand / license | 本地文件 + 公钥 | 交付前离线校验 |
为什么必须有第二类"绕过 API 直连数据库"的命令?因为有些事发生在服务能干活之前 。数据库迁移要在服务启动前完成(第 101 篇的红线 #5"先迁移后启动"------交付文档里那行"migrate 一次性容器执行 df_cli migrate up,坏迁移不影响旧服务"就是它);首个租户要在任何登录发生前存在(下一节);加密密钥轮换要直接改写数据库里的密文。这些操作如果只能通过 API 做,就陷入了"要用服务来修服务"的死锁。CLI 是把平台自己的领域逻辑(迁移引擎、租户创建、密钥加密)装进一个不依赖网络入口的二进制------同一个代码库、同一套校验规则,只是不走 HTTP。
这个"同一套规则"不是口号:user create 的代码注释明确写着"装配与 ops 端点同一个 application command(单事务写齐 users + tenant_members 两表)"------命令行建的账号和管理界面建的账号走的是同一段业务代码,不会出现"CLI 建的账号少一张关联表"这种分叉。
三、鸡生蛋问题:bootstrap 的由来
bootstrap(初始化首个租户)是所有命令里出身最特别的一个------它不是设计出来的,是一次安装演练摔出来的。注释记录了事故现场:
2026-06-10 T6 安装演练发现:auth Provisioner 只 JIT 创建用户,租户必须预先存在(FindBySlug 失败 = 登录报 "tenant not found")。全新库无任何租户 → dev / OIDC 任何 IdP 都无法完成首次登录。
翻译成人话:账号隶属于租户(可以理解为"工作空间"),登录流程会自动创建 账号,但不会 自动创建租户------它假设租户已经在了。于一台全新装好的服务器上,第一个用户面对的是死循环:要登录得先有租户,要建租户得先登录。系统逻辑上没有 bug,但第一次启动永远走不通 。bootstrap 就是解开死结的那根针:往全新数据库里放入第一个租户,幂等(slug 冲突静默跳过,重复执行安全),如今是交付手册的固定步骤。
它的实现也有一处值得学的细节:直写 SQL 时必须把租户的配额、设置两个 JSON 列同时 写齐------注释警告"漏掉这些列会让新租户以零配额复活,第一次知识库上传就会被拦"。首租户出生的那一刻没有 aggregate(聚合对象)可用,只能手工把聚合落库的字段照抄一遍------并注释指明这是"镜像同一份快照",将来字段变了这里必须同步。
四、评估裁判:eval 与"不许假绿"
第三类值得细读的是 eval(评估)------它和第 102-107 篇讲的所有功能都不同:别的命令操作平台,eval 给平台打分。三个子命令各管一层:
eval run:跑检索评估集,输出 MRR / nDCG(信息检索领域的标准指标,通俗说就是"正确答案排得靠不靠前"的分数);eval trace <session-id>:导出一个会话的完整执行轨迹(第 105 篇的 SSE 事件序列),可挂确定性断言------"done 帧必须收尾、interrupt 不能悬挂、不允许裸 error";eval judge <session-id>:双层裁判------第一层就是上述断言,第二层用 LLM 对回答质量按维度打分。
这个命令族的演化史里藏着一个与本系列精神完全一致的时刻:2026-07-01 凌晨,连续两个 fix commit------03:13 "实事求是 修复三处代码逻辑缺陷",03:17 "eval judge 两层都没跑时标记 no_eval · 避免误导性'通过'"。后者正是第 104 篇讲的"假绿"哲学在评估域的化身:裁判自己没跑成,就要明确报告"没评",而不是让用户把"无结果"误读成"及格"。一个评估工具最大的失败不是评得差,是谎报通过------作者显然深谙此道。
五、交付三件套:pack / brand / license
剩下的命令都围绕同一件事:把软件交到客户手里(第 101 篇的主题)。
pack validate / plan / acceptance:Solution Pack(方案包,预置的 agent+知识库组合)的离线校验三连------能不能生成安装计划、能不能过 PoC 验收,全程"不写入数据、不调用外部站点";brand validate:白标交付的品牌配置校验(客户要求界面挂自己 logo/名称时)。我实跑的第三条命令就是它------内置默认 profile 一次通过;license verify <license.jwt>:私有化授权校验,验 Ed25519 签名、客户/实例绑定、有效期,输出脱敏摘要 (敏感字段不完整显示)。故意喂给它一个不存在的文件,报错形态干净:Error: read license: open /tmp/nonexistent.jwt: no such file or directory+ 完整用法提示------错误信息可执行(告诉你哪个参数没给对),这是 CLI 的基本修养。
rotate-secret-key 也属于这一组(BYOK 密钥轮换------客户自带加密密钥时定期更换),第 101 篇备份篇里它已出场过:轮换后必须递增密钥版本、保留旧版本可恢复能力。
六、藏在命令行里的全系列
最后把 df_cli 当作第二季的"索引页"串一遍:migrate 是 101 篇"先迁移后启动"红线的执行者;bootstrap 暴露了多租户模型(104 篇的 BC 划分)在冷启动时的一处断层;chat 是 105 篇 SSE 的命令行消费端------交互式 REPL、流式输出、HITL 审批在终端里用文字完成;它的 headless 模式还会把事件轨迹导出成 JSON 给 assert_run 做序列断言(105 篇"终止帧三兄弟"的自动化验证);eval 连着 XQD 内容质量战役的评估基建;user create 的"与 ops 端点同一个 application command"是 103 篇"充血模型、规则住一处"的直接受益者------规则在领域层只有一份,CLI 和 HTTP 各自调它,天然一致。
如实分层:本篇实跑的 4 条命令(--help / migrate status / brand validate / license verify 报错路径)全部是只读或纯校验 路径;bootstrap、user create、rotate-secret-key、migrate up 这些写路径本次未执行(避免动真实库),其行为描述均来自代码与交付文档,commit 与注释可复核。
小结
- CLI 的价值不是"另一种点按钮的方式",而是覆盖服务生命周期之外的时段。 安装前(migrate/bootstrap)、停机时(密钥轮换)、无浏览器的机房(第 101 篇的场景)------这些时刻 API 还没醒,直连数据库的命令是唯一入口。
- 同一套业务逻辑服务两个入口,是防分叉的根本手段。 CLI 建账号与界面建账号调同一个 command,将来规则改一处两边自动一致;反之"CLI 自己写一份 SQL"就是第 103 篇批评的规则复制。
- 评估工具的第一美德是不说谎。 "两层都没跑时标记 no_eval"这个凌晨 03:17 的 commit,和 104 篇的假绿事故、106 篇的 ADR-003 同出一源:让机器输出"不知道/没评",比让它输出一个体面的猜测有价值得多。
- 机器守的部分今天全绿 :14 个子命令 --help 完整、migrate status 直连真实库返回
schema_version=187 dirty=false、brand validate 通过、license verify 报错形态可执行。写路径未实测,如实分层。