定时跑的那些事
消息进来时跑的是对话那一条路;没人说话的时候,作息、主动找人、补看、学习、清理这些事由每个人格一条「维护循环」按顺序跑。这一篇讲它跑哪几步、各自多久一次、卡住了怎么知道、加一步要注意什么。
一轮
每个人格一条维护循环(bot.py 里 ticks 那一段)。一轮按顺序走完下面十三步,到了时间再做一次重清理,然后睡一会儿再来:间隔是 min(60, max(5, [conversation].state_retention_seconds / 4)) 秒,默认 60 秒。
每一步自己决定这一轮要不要真的干活:大部分步骤在实例上记着上次跑的时刻,不到点就直接返回。
| 步骤 | 做什么 | 自己的节奏 |
|---|---|---|
| 生命周期 | 认知内核的作息和活动往前走一步:困了没有、该不该睡、活动开始、结束(3.17) | 每轮 |
| 时间线 | 生活引擎手上的活动、睡眠、梦、发呆同步进人生时间线,只写变了的(3.16) | 每轮 |
| 承诺和里程碑 | 过期三天的承诺记成没做到;最近两天聊过的人,算有没有新的关系里程碑 | 10 分钟 |
| 记忆隐私检查 | 群的空间里混进了私聊来源的记忆,收紧成只在私聊里想得起来 | 6 小时(启动后第一轮就查) |
| 群聊门槛 | 按每个服务器最近 14 天插话的下场,重算群聊过滤的偏置 | 10 分钟 |
| 补回 | 醒着、网好了:不在时没看到的话挨个回头看一眼 | 每轮(有要补的才干活) |
| 学习补课 | 私聊里攒下没学过的消息,每轮最多开一个学习任务,走后台额度 | 每轮 |
| 外联扫描 | 主动找了人、等够了还没回的,结掉,变成心情和印象 | 10 分钟 |
| 冲动 | 有没有突然想起谁、想起了要不要说;整步不调用模型(3.18) | 5 分钟 |
| 社交自检 | 便宜的一道检查;过了才为一个人调一次模型 | 默认 20 分钟 |
| 跟进 | 认领一个到时间的联系计划,照常过回复的几道关发出去 | 每轮,一次最多一个 |
| 环境主动 | 认知内核在状态允许时开一次口 | 每轮,一次最多一个 |
| 记忆核心 | 重试没写成的、补向量、第一次启动时迁旧记忆、睡着时巩固(3.15);重的活放到后台任务里 | 补写 5 分钟;巩固睡着时 6 小时、醒着最多 24 小时一次 |
重清理,六小时一次(每次启动的第一轮也跑):按保留期清聊天记录([conversation].message_retention_days = 30、每个频道最多 messages_per_channel = 5000 条,人生时间线里和人有关的跟着走)、记忆的生命周期、中期记忆、运营事件、外联、念头、语义审议的记录。每一项各自兜底,一项失败不拖累后面几项。
卡住、死掉
维护循环有两种坏法,日志里都是一片安静:某一步抛了异常没人接,循环就此结束;某一步 await 了一个永远不回来的东西,循环停在那一行。src/aeria/maintenance_heartbeat.py 管这两件事:
- 每一步都包在
heartbeat.step(...)里,异常记下来,接着跑下一步; - 一步最多跑 120 秒(重清理 1800 秒),到了就掐掉,记一笔「超过多少秒没出来」;
- 一个独立的看门狗任务:一步跑过 90 秒(重清理 900 秒),就把维护任务当时的调用栈打进日志,直接指到卡住的那一行;
- 每 5 分钟打一行心跳,列出上一整轮每一步花了多久。心跳停了,就是循环死了或卡了。
加一步
- 写一个
async def _<名字>_tick(self, monotonic_now: float):先看离上次跑够不够久,不够就返回;把上次跑的时刻记在实例上(参照_commitment_milestone_tick)。 - 在
ticks里加一行:中文名(心跳和看门狗打的就是它)、调用、失败时的日志。顺序有讲究:要读生活引擎最新状态的,排在「生命周期」后面。 - 重的活(迁移、建索引、跑模型)放进
asyncio.create_task,在实例上记着任务,下一轮看它跑完没有(参照_hmc_tick)。不要让一步一跑就是几十秒:后面的步骤都在等它。 - 一步里最多调一次模型,而且要有自己的额度和间隔:维护循环每分钟一轮,不设间隔的模型调用一天就是一千多次。
对应的代码
- 循环本身:
src/aeria/bot.py里_maintenance_loop的ticks元组、_heavy_maintenance、_maintenance_watchdog。 - 心跳和看门狗:
src/aeria/maintenance_heartbeat.py的MaintenanceHeartbeat(heartbeat_interval= 300、stuck_after= 90、timeout_after= 120,重清理单独 900 / 1800)。 - 各步自己的节奏:
COMMITMENT_MILESTONE_EVERY= 600、MEMORY_PRIVACY_EVERY= 21600、JOIN_BIAS_EVERY= 600(看JOIN_BIAS_DAYS= 14 天)、HMC_OUTBOX_EVERY= 300、HMC_CONSOLIDATE_ASLEEP_EVERY= 6 小时;外联扫描[proactive].sweep_interval_minutes= 10,冲动是它的一半(最少 60 秒);社交自检social_self_check_interval_minutes= 20。 - 测试:
tests/test_maintenance_heartbeat.py(「跑得太久的那一步会被看门狗认出来_而且同一次只报一遍」「看门狗能指出卡住的协程停在哪一行」「重清理有自己的卡住门槛」)。
没做到的
- 一个人格一条循环,十三步串着跑:一步慢,后面的都晚;掐掉以前最多能晚两分钟。
- 每一步的节奏写在各自的常数里,没有一张总表;要知道「多久一次」得一个个找。