八组能力对照
| # | 能力组 | 一句话机制 | 详解 |
|---|---|---|---|
| 01 | 语义层 | 把计划确定性地编译成 SQL,编不动的地方明确拒绝或留骨架 | 语义层 |
| 02 | 本体 | 数据集声明成对象、关系声明业务名、维度链声明成层级——声明一次,全链用业务的词说话 | 本体 |
| 03 | 判定规则 | 同一份语义模型换个问法:「算不算有问题」,定时求值且零 LLM | 判定规则 |
| 04 | 知识库 | 五个 YAML 按「谁能写」分工,自动写入一律 pending | 知识库 |
| 05 | 混合检索 | 关键词 + 向量两路,加权 RRF 融合,分数归一化 | 混合检索 |
| 06 | 多数据源 | 一个注册表,驱动懒加载,注册即连接探测 | 本页 |
| 07 | 统一存储 | 内部状态一个抽象两档后端,迁移版本化且各自幂等 | 本页 |
| 08 | 评测基建 | 离线回放 + 带方向容差的回归门,判定不花钱 | 评测与回归门 |
flowchart LR
subgraph can["决定能答什么"]
SEM["01 语义层
计划 → 权威 SQL / 骨架"]:::gen
ONT["02 本体
对象 / 关系 / 层级声明"]:::gen
KB["04 知识库
五个 YAML 按谁能写分工"]:::entry
RET["05 混合检索
两路召回 → 加权 RRF"]:::exec
end
subgraph trust["决定答得对不对"]
DEC["03 判定规则
同一份语义模型,换个问法"]:::gate
EV["08 评测基建
离线回放 + 回归门:判定不花钱"]:::gate
end
subgraph where["决定在哪跑"]
DS["06 多数据源
注册表 + 驱动懒加载"]:::ext
ST["07 统一存储
一个抽象,两档后端"]:::entry
end
%% 无连线也要按左→右排:三组之间没有依赖关系,只是并列
can ~~~ trust ~~~ where
classDef entry fill:#71717a14,stroke:#71717a59,stroke-width:1px;
classDef gen fill:#6366f114,stroke:#6366f199,stroke-width:1.5px;
classDef exec fill:#14b8a614,stroke:#14b8a699,stroke-width:1.5px;
classDef gate fill:#f59e0b14,stroke:#f59e0b99,stroke-width:1.5px;
classDef ext fill:transparent,stroke:#71717a80,stroke-width:1.5px,stroke-dasharray:4 4;
一条贯穿的取舍:确定性优先
八组能力背后是同一个判断准则——能用代码算准的,不要交给模型。 这不是对模型能力的怀疑,而是三个具体的好处:
| 好处 | 在企业问数场景里意味着什么 |
|---|---|
| 可解释 | 「这个数怎么来的」有确定答案:哪个指标、哪条 join 路径、哪条规则放行 |
| 可回归 | 改动能被离线门拦住,而不是「看起来好像没变差」 |
| 换模型不退化 | 确定性部分的行为不随模型版本漂移 |
所以在这套系统里:join 路径由语义层算 (compiler.py:149 JoinResolver)、 编不动的地方有结构化的分因 (compiler.py:1374 PartialCompile)、 检索先做表锚定过滤再排序 (service.py:1618 keep)、 回归对比是纯函数 (gate.py:414 compare_metrics)。
06 多数据源:注册表与懒加载
数据源类型由一张注册表管理。关键设计是驱动懒导入: 导入注册表本身不要求装任何数据库驱动,只有真正去连某类库时才要求对应的包。 所以「支持六种数据源」不会让基础安装背上六个驱动的依赖。
registry.py:42 _ADAPTER_REGISTRY
管理端注册一个数据源时是事务性的:先连接探测成功,才落库, 才进注册表;任何一步失败都回滚,并以 400 报出原因。 这样不会出现「配置里有一个连不上的源」这种半成品状态。 完整流程见接入数据源。
07 统一存储:抽象管什么、不管什么
会话、任务、记忆、认证、设置、调度、检查点等内部状态,统一在一个
StorageBackend 抽象后面,由环境变量在后端之间切换。
sqlite.py:138 build_backend sqlite.py:166 resolve_backend
知识库镜像与 业务数据源留在抽象之外。前者因为 PostgreSQL 生产环境走的是另一条检索后端,后者因为用户的数据本来就不归 Trove 的状态管理管。
换句话说,它管的是「Trove 自己的状态」。把边界划在这里,是为了避免它膨胀成一个「所有数据库访问都要经过」的中间层——那种中间层最后会什么都抽象不好。
08 评测基建:不花钱的判定
这一组能力的目标很具体:让「这次改动有没有让系统变差」这个问题可以在不调用模型的前提下回答。
| 件 | 作用 |
|---|---|
| eval/replay.py | 录制轨迹离线回放打分(零 LLM、零网络) |
| eval/gate.py | 纯函数:指标口径化 → 对比 → Markdown 记分卡 |
| scripts/eval_gate.py | 把 baseline 与 current 结果文件对比,有回归即退出码 1 |
| 检索评测脚本 | Recall@k / MRR / nDCG,输出可被门消费的 scorecard |
gate.py:169 metrics_from_entries replay.py:321 score_replay gate.py:539 render_report