一件事由谁拍板
一条消息它最后回没回,要过好几关;每一关只管一个问题,最后的结果也只记在拍板的那一关上。所以「它为什么没回」总有一个确定的答案,在网页后台查得到。
八层,各管一件事
| 层 | 管的问题 | 停在这一层时,后台写的 |
|---|---|---|
| 硬规则 | 能不能回:权限、安全、隐私 | 已阻止、硬规则拦下 |
| 有没有空 | 睡着、出门、在做事、闹情绪躲着、说好先不聊 | 认知内核:先看着、认知内核:不理…… |
| 群聊过滤 | 群里没叫它的话,值不值得插一句 | 没到门槛、意愿低、旁观、退让、让给真人、心智判不回 |
| 模型 | 想不想回、回什么、回多长 | 模型决定不回、重新规划后不回、语义复核不回 |
| 节奏 | 什么时候发 | 发言间隔太近、等接话间隔、参与次数用完、排着的回复过时了、回之前被打断 |
| 把关 | 发出去之前,稿子能不能用 | 草稿为空 |
| 技术原因 | 和想不想回无关的事 | 关注位忙 |
| 说出去了 | — | 回了、点了个表情、回完被打断 |
每一轮只记在拍板的那一层上。认知内核(2.7)自己选了不说话的,结果写成「认知内核:先看着」「认知内核:不理」这样,按它给的理由归层:理由是权限、安全、隐私的,归硬规则;是睡着、在做事这一类的,归有没有空;其余的归群聊过滤。
冲着它来的话,不过群聊过滤
私聊、@ 它、回复它的话,只要不是硬规则拦下、它也不是没空,就会交给模型决定怎么回(2.6)。群聊过滤只管那些没叫它的群消息:大部分群聊在这一层就停下了,不花一次模型调用。
主动开口也一样
它主动找人、对方没回又补一句,最后的结果也归到这几层:
- 想了想决定不说了:模型;
- 写出来的那句没过质量检查、和刚说过的重复:把关;
- 发之前状态变了(睡着了、出门了):有没有空;
- 发之前那件事已经过时了、当天的次数用完了:节奏;
- 影子模式下「本来会发」:硬规则(影子模式本身就是一道不让发的规矩,2.19)。
在后台看
网页后台「决策记录」页,选一个服务器:
- 谁拍的板 · 近 24 小时:每一层停了多少轮;
- 24h 行为结果:每一种结果各几次,用的是上面表里那些说法;
- 决策路径:点开一轮,看认知内核当时有哪些候选做法、各打了多少分、被哪条规矩拦下(2.7)。
这里只有分数、规矩、理由和结果,不展示模型的思考过程。
对应的代码
- 层和结果码:
src/aeria/cognition/decision_layers.py(OUTCOME_LAYERS、OUTCOME_LABELS、decision_layer:认知内核记的nhcx_*结果按理由码的前缀归层,PERMISSION./SAFETY.这类是硬规则,LIFECYCLE./SLEEP.这类是有没有空,其余是群聊过滤)。新加结果码要在这里登记,测试会查。 - 记下来:
src/aeria/bot.py的_record_decision_trace;按层数:src/aeria/storage.py的decision_layer_counts;接口:src/aeria/webui.py的/api/decisions。 - 页面:
webui_frontend/src/routes/Decisions.tsx的DecisionLayers。 - 测试:
tests/test_decision_authority.py(「代码里用到的每个结果码都归了层」「认知内核选了不说话_看理由码是哪一层」「直接叫到的_意愿再低也交给模型判断」)。
没做到的
- 「决策记录」页一次看一个服务器:私聊里的那几轮,这一页上看不到。
- 停在「有没有空」的轮次,结果栏写的是认知内核选的做法(比如「先看着」);为什么没空(睡着了、在做事),要点开那一轮的决策路径看理由。