用户与凭据
初始管理员的密码由 TROVE_ADMIN_PASSWORD 决定;不提供时随机生成一个
20 位密码,只在首次引导时打印一次。所以第一次启动的输出要留住。
main.py:157 TROVE_ADMIN_PASSWORD service.py:141 token_urlsafe
用户管理走 /v1/admin/users 一组端点(增删改查)。每个用户可以签发多个 token
(便于轮换与按用途区分),token 形如 trove_ + 32 字节随机串,
默认有效期 720 小时。密码用 PBKDF2 哈希存储。
admin.py:165 users service.py:270 token_urlsafe
三个层次,别混
| 层 | 作用 | 在哪管 |
|---|---|---|
| 账号角色 | admin / 非 admin,决定能否进管理端 | 用户页面 |
| 数据源授权(grants) | 这个用户能问哪些源 | 用户页面 |
| 工具 ACL | analyst 角色,决定 catalog 工具可见性 | 运行时角色解析 |
grants 有三种取值,是三个不同的语义,别混:
| 取值 | 含义 |
|---|---|
| None | 拿不到授权依据 → 拒绝一切,不查任何数据源 |
| 空集合 | 有依据、依据为空 → 只放行默认数据源 |
| 非空集合 | 严格白名单 |
三种取值都落在「安全的一侧」:忘了授权不会放出全部数据源,读不到授权依据也不会退化成放行。
flowchart TB
U["用户"]:::entry --> L1["① 账号角色
admin / 非 admin → 能否进管理端"]:::gate
U --> L2{"② 数据源授权 grants
这个用户能问哪些源"}:::gate
U --> L3["③ 工具 ACL
analyst 角色 → catalog 工具可见性"]:::gate
L2 -- "None" --> R1["拿不到授权依据
拒绝一切,不查任何数据源"]:::err
L2 -- "空集合" --> R2["有依据、依据为空
只放行默认数据源"]:::entry
L2 -- "非空集合" --> R3["严格白名单"]:::entry
classDef entry fill:#71717a14,stroke:#71717a59,stroke-width:1px;
classDef gate fill:#f59e0b14,stroke:#f59e0b99,stroke-width:1.5px;
classDef err fill:#dc262614,stroke:#dc262699,stroke-width:1.5px;
policy.py:146 Principal deps.py:187 require_datasource
数据源与 KB
注册流程在接入数据源里讲了。这里补充管理端的操作要点:
| 操作 | 注意 |
|---|---|
| 注册 | 注册即探测,失败 400 并给出原因;同名不同身份会 409 拒绝 |
| KB 初始化 | 异步任务(返回 202),可查状态;会调 LLM,有成本 |
| KB 重载 | 同样是 202 + task_id;无条件重读全部 YAML,重建镜像并把条目重推给检索后端 |
| 测试连接 | 非破坏性探测,排障时先用它 |
admin.py:824 datasources admin.py:1097 kb admin.py:1039 test-connection
重载不是「改完 YAML 的必经步骤」:下一次提问会按内容摘要判断文件动没动过, 动过就重读,镜像与检索库一起更新(pg_hybrid / rag 有索引钩子,builtin 直接读镜像,没有推送这一步)。 重载的价值在无条件——它先清空同步摘要表再全量重读, 所以能修「镜像认为文件没变、检索库却落后」这类不一致。 它与初始化共用同一个按数据源键控的任务槽:同源已有任务在跑时返回 409,而不是排队等。
admin.py:1189 kb/reload service.py:962 force_sync pg_hybrid.py:111 index_file
自动回写的示例、蒸馏出的教训、自动学习的偏好,都停在 pending 等确认。 这个队列是管理端最该定期看的东西——它直接影响准确率, 而且积压久了会让人忘记上下文,确认质量下降。
定时任务
定时任务就是「按计划重复问一个问题」。它复用同一条查询链路, 只是多了调度、告警与冷却。
service.py:40 JobsService store.py:3 jobs.sqlite
支持 interval 与 cron 两种调度。告警评估在任务跑完后进行,带冷却窗口 (默认 30 分钟)——否则一个持续异常每轮都报,很快就会没人看。
两个容易出事的地方
trove serve 在生命周期里自带一个调度 tick(默认每 30 秒扫一次),
不需要另起 trove-cli schedule --daemon。
两个一起跑会把到期的任务执行两遍。
想让 serve 不调度,把轮询间隔设为 ≤0,改用外部 daemon。
main.py:421 内置调度 app.py:177 _job_tick config.py:523 scheduler_poll_seconds
任务跑出来的 SQL 如果停在待确认状态,调度器会直接以「批准」继续—— 否则无人值守的任务会永远卡住。
这意味着开 HITL 的任务不会得到人工复核。如果你的场景需要「定时跑但结果要人看过才能用」, 应该用告警+通知,而不是 HITL。
结果集有行数上限(200 行),避免一个失控的任务把大结果灌进通知里。
记忆运维
记忆子系统有三块需要管理端介入的地方:
| 端点 | 作用 |
|---|---|
| GET /v1/admin/memory/profile | 按(用户 × 数据源)看正确率、失败模式、已确认事实 |
| GET /v1/admin/memory/preferences | 自动学到的偏好草稿 |
| POST .../confirm · .../reject | 逐条处置 |
admin.py:1585 profile admin.py:1608 preferences
偏好草稿的来源是会话压缩时的 LLM 抽取:高置信度的候选直接进 user_facts,
低置信度的落到待确认队列。明确的一律不自动注入——
详见记忆子系统。
保留策略与周期维护
默认不清理任何东西。清理需要显式配 retention_days,
否则那些字段是空的(配置里对应项是注释掉的)。
models.py:75 retention_days service.py:405 run_lifecycle
默认清理是危险的方向:一个配错的保留期会静默删掉数据,而「没清理」最坏只是占磁盘。 对一套以「积累知识、越用越准」为卖点的系统来说,默认删数据的代价更大。
周期维护是另一件事——会话数量配额、孤儿会话、检查点深度、过期清扫。 这些有独立的默认值(如每用户 100 个会话、24 小时一轮清扫), 由服务的周期任务驱动。
maintenance.py:326 run_all app.py:245 run_lifecycle
注意「会话维护」与「记忆保留」是两套独立的东西: 前者管会话与检查点的数量(默认开),后者管记忆内容的过期(默认关)。 混在一起理解会得出错误的预期。