主动联系的流水线
从「想起一个人」到一条主动消息发出去、再到「他没回」变成情绪,中间每一步在哪个文件、存在哪张表、为什么这么分。整条路上只有最后「把话说自然」那一步调用模型。
一条路,七步
- 感知(
cognition/impulse_sense.py):维护循环的「冲动」那一步(默认五分钟一次)给每个候选的人拍一张快照(关系、上次聊天、没聊完的话题、到期的承诺、最近的里程碑……),每个sense_*函数只看一种真实状态,判断它构不构成「想起这个人」,并把那个状态的编号原样写进source_event_ids。十种由头见ImpulseTrigger。 - 念头入库(
SocialImpulse,表social_impulses):状态LATENT→CONSIDERING→WAITING,最后落到SENT、KEPT_TO_SELF(想说但没说)、EXPIRED(过时了)、SUPPRESSED(被睡觉、勿扰、配额压下去)、CANCELLED(前提没了,比如对方先开口了)、FAILED(发失败了,不算对方无视)之一。 - 权衡(
cognition/impulse.py的weigh):想找的劲(approach)和顾虑(avoidance)各算一串,得出SEND、WAIT、KEEP_TO_SELF、WITHDRAW(被晾怕了)、EXPIRE。 - 配额:同一个人两次之间的最短间隔、一个人一天几次、所有人一天几次、一天最多生成几次(
ProactiveConfig)。没额度就WAITING,并在日志里说清是配额拦的。 - 时机(
cognition/opportunity.py的judge_opportunity):不好的时候分「等得回来」(留着,过一会儿再看)和「等不回来」(直接过期)。 - 话题(
cognition/topic_candidates.py):从八种真实来源拼候选(bot._pick_topic_for),本地打分,没有证据编号的丢掉,第一名过门槛才用;挑不出来就KEPT_TO_SELF。打开了语义复核的,这里再问一次模型,只能往保守改(semantic_deliberation.apply_verdict)。 - 交给跟进去发(
bot._schedule_impulse_contact):生成一个SocialFollowUpIntent(最早两分钟后),由维护循环的「跟进」那一步认领,照常过回复的几道关、照常生成、照常发。
发出去以后
- 记账(
outreach_service.py,表proactive_outreach;念头在social_impulses,交给跟进的计划在social_follow_up_intents):发给谁、什么时候、因为什么由头。 - 他回了:记下隔多久回的,学他的回复习惯(
opportunity.ReplyHabit,按人和语境分开)。 - 他没回:维护循环的「外联扫描」(默认十分钟一次,
outreach.py)把等够了(patience_minutes,默认三小时)还没回的结掉,再按学来的他的习惯判断这次沉默反不反常(attribute_silence):心情按增量往下压一点;连着被晾到withdraw_after次开始收手;更多次才走关系证据去动印象。这是整个系统里唯一一处「沉默本身产生后果」的地方。
影子模式
[proactive].shadow_mode 打开时,前六步照常走、理由照常记,第七步不发,念头记成 SUPPRESSED(理由「影子模式:决定发,但不真的发」);跟进那一条在影子模式下记 follow_up_shadow_would_send。放开频率之前先开它读日志。
改它的时候
- 加一种由头:
ImpulseTrigger加一项,impulse_sense.py加一个sense_*(必须带上真实状态的编号),topic_candidates._TRIGGER_AFFINITY写它偏好哪类话题,impulse.py里它算正向还是负向。 - 加一种话题来源:
TopicSource加一项和它的说法(_SOURCE_WORDS),build_candidates收原料(证据编号必填),bot._pick_topic_for传进去。 - 调频率:先看决策记录里
proactive_*、follow_up_*各种收尾的比例(3.14),再动ProactiveConfig。
对应的代码
- 感知和权衡:
src/aeria/cognition/impulse_sense.py、src/aeria/cognition/impulse.py(ImpulseState、ImpulseAction、weigh);入口src/aeria/bot.py的_impulse_tick。 - 时机、话题、复核:
cognition/opportunity.py、cognition/topic_candidates.py、cognition/semantic_deliberation.py。 - 发出去以后:
src/aeria/outreach_service.py、cognition/outreach.py;bot.py的_outreach_sweep_tick、_social_follow_up_tick。 - 配置:
[proactive](threshold、probability、min_gap_minutes、daily_cap_per_user、daily_cap_total、soft_cooldown_minutes、sweep_interval_minutes、shadow_mode)、[proactive.impulse]、[proactive.outreach]、[semantic_deliberation]。 - 测试:
tests/test_impulse.py、tests/test_impulse_sense.py、tests/test_impulse_invariants.py、tests/test_impulse_bot_wiring.py、tests/test_topic_and_opportunity.py、tests/test_outreach.py、tests/test_outreach_service.py。
没做到的
- 七步里每一步都可能把念头留下来,「它为什么一直没找他」要翻好几处:日志里有每一步的一行,决策记录里只有最后的收尾。
- 「他说过这会儿在忙」没有信号,时机只靠学来的钟点。
- 认知内核的「环境主动」(在频道里自己开个话题,
bot._proactive_cognition_tick)是另一条路,不走这七步;它要[nhcx].proactive_enabled和[proactive].enabled都开着。