feat: 自发目标 pending intent(issue #40) #49

Merged
Yushu merged 1 commit from feature/40-spontaneous-goal into main 2026-07-23 09:05:52 +00:00
Member

这个 PR 做了什么

实现 issue #40「自发目标」(ADR-0006):给主模型一个通用的「待跟进意图」
context-mode 机制——不是新的独立主动出口,而是并入既有反应式/Drive Tick/
tier-2 环境信号三条触发路径的软提醒。

新增

  • pending_intent.pyPendingIntentRecord/is_expired/
    render_pending_intents/strip_intent_done_markers + 存储 port
    PendingIntentStoragePort。core 自己的内部记账语法
    {{intent_done:id}}),不经 host 可扩展的 @ 前缀标签体系
    (issue #28),避免混淆两套机制。
  • spontaneous_goal.pyfamiliar_reconnect_hint()——唯一给了 core 端
    启发式的候选类型(熟人久未联系,复用 issue #36 熟悉度 + issue #38
    距上次交互数据)。「话题新进展」/「冒出不懂的梗」两类候选刻意不建
    专门检测逻辑,留给模型自己在任意一轮判断是否值得调用同一个工具。
  • 新工具 note_pending_intent(content):模型记一条待跟进,不产生任何
    平台副作用(不在 OUTPUT_TOOL_NAMES 里,占 run_limit 份额,同
    update_profile 先例)。

接入

  • RuntimeLoop:新增 spontaneous_goal_unlocked/pending_intent_ttl_seconds
    构造参数——未解锁时工具连同已有意图一并对模型隐身(同 #22「能力为假=
    模型看不到」哲学)。run()/run_environment_signal() 把未过期意图并入
    冻结快照;_handle_send_message 在发送前剥离 {{intent_done:id}} 标记
    并删除对应记录。
  • drive_tick.assemble_light_context():新增 pending_intents_text/
    reconnect_hint 两个可选参数(此前 light context 里这个位置恒空,本
    ticket 是第一个真正写入方)。
  • __init__.py:三条触发路径(_debounced_flush/_evaluate_drive_tick_chat/
    _process_environment_signal)各自现算渐进解锁档位
    is_spontaneous_goal_unlocked,issue #35 早已建好,本 ticket 是第一个
    真正消费它的入口),传给 _build_runtime_loop_evaluate_drive_tick_chat
    额外读取熟悉度 + 距上次交互算 familiar_reconnect_hint
  • storage.py/sent_log.pyPendingIntentModel 表 + 内存版/持久化版两套
    PendingIntentStoragePort 实现(同既有契约测试先例,两套实现共用一份
    测试)。

测试

  • test_pending_intent.py/test_pending_intent_storage.py/
    test_spontaneous_goal.py:纯函数 + 存储契约单测。
  • test_drive_tick.pyassemble_light_context 新增两个参数的注入/省略。
  • test_runtime_loop.py:新增 TestSpontaneousGoal——工具可见性门控、
    调用产生记录并计入 run_limit、未过期/过期意图注入冻结快照、
    {{intent_done:id}} 消费剥离 + 删除记录,以及一个跨两轮 run() 调用
    的端到端生命周期测试(记一条意图 → 下一次触发看到提醒 → 模型带出并
    消费)。

全部 644 个测试通过,ty check/ruff check 干净(ruff format 未对
已有文件运行——仓库历史上这些文件是 CRLF,ruff format 只会产生纯换行符
噪音,不是这次改动的一部分)。

## 这个 PR 做了什么 实现 issue #40「自发目标」(ADR-0006):给主模型一个通用的「待跟进意图」 context-mode 机制——不是新的独立主动出口,而是并入既有反应式/Drive Tick/ tier-2 环境信号三条触发路径的软提醒。 ### 新增 - `pending_intent.py`:`PendingIntentRecord`/`is_expired`/ `render_pending_intents`/`strip_intent_done_markers` + 存储 port `PendingIntentStoragePort`。core 自己的内部记账语法 (`{{intent_done:id}}`),不经 host 可扩展的 `@` 前缀标签体系 (issue #28),避免混淆两套机制。 - `spontaneous_goal.py`:`familiar_reconnect_hint()`——唯一给了 core 端 启发式的候选类型(熟人久未联系,复用 issue #36 熟悉度 + issue #38 距上次交互数据)。「话题新进展」/「冒出不懂的梗」两类候选刻意不建 专门检测逻辑,留给模型自己在任意一轮判断是否值得调用同一个工具。 - 新工具 `note_pending_intent(content)`:模型记一条待跟进,不产生任何 平台副作用(不在 `OUTPUT_TOOL_NAMES` 里,占 `run_limit` 份额,同 `update_profile` 先例)。 ### 接入 - `RuntimeLoop`:新增 `spontaneous_goal_unlocked`/`pending_intent_ttl_seconds` 构造参数——未解锁时工具连同已有意图一并对模型隐身(同 #22「能力为假= 模型看不到」哲学)。`run()`/`run_environment_signal()` 把未过期意图并入 冻结快照;`_handle_send_message` 在发送前剥离 `{{intent_done:id}}` 标记 并删除对应记录。 - `drive_tick.assemble_light_context()`:新增 `pending_intents_text`/ `reconnect_hint` 两个可选参数(此前 light context 里这个位置恒空,本 ticket 是第一个真正写入方)。 - `__init__.py`:三条触发路径(`_debounced_flush`/`_evaluate_drive_tick_chat`/ `_process_environment_signal`)各自现算渐进解锁档位 (`is_spontaneous_goal_unlocked`,issue #35 早已建好,本 ticket 是第一个 真正消费它的入口),传给 `_build_runtime_loop`;`_evaluate_drive_tick_chat` 额外读取熟悉度 + 距上次交互算 `familiar_reconnect_hint`。 - `storage.py`/`sent_log.py`:`PendingIntentModel` 表 + 内存版/持久化版两套 `PendingIntentStoragePort` 实现(同既有契约测试先例,两套实现共用一份 测试)。 ### 测试 - `test_pending_intent.py`/`test_pending_intent_storage.py`/ `test_spontaneous_goal.py`:纯函数 + 存储契约单测。 - `test_drive_tick.py`:`assemble_light_context` 新增两个参数的注入/省略。 - `test_runtime_loop.py`:新增 `TestSpontaneousGoal`——工具可见性门控、 调用产生记录并计入 `run_limit`、未过期/过期意图注入冻结快照、 `{{intent_done:id}}` 消费剥离 + 删除记录,以及一个跨两轮 `run()` 调用 的端到端生命周期测试(记一条意图 → 下一次触发看到提醒 → 模型带出并 消费)。 全部 644 个测试通过,`ty check`/`ruff check` 干净(`ruff format` 未对 已有文件运行——仓库历史上这些文件是 CRLF,`ruff format` 只会产生纯换行符 噪音,不是这次改动的一部分)。
给主模型一个通用的"待跟进意图"机制(context-mode,ADR-0006):新工具
note_pending_intent 让模型在任意一轮把值得回头看看的念头记下来,不产生
任何平台副作用;之后同一 chat 的每一轮经冻结快照/light context 软提醒,
模型在合适时机自然带出,用 {{intent_done:id}} 内联标记消费。

熟人久未联系是唯一给了 core 端启发式的候选类型(复用 issue #36 熟悉度
+ issue #38 距上次交互数据),并入 Drive Tick 自己的 light context;
"话题新进展"/"冒出不懂的梗"留给模型自己判断是否值得调用同一个工具,
不需要专门的检测逻辑。整套机制挂在渐进解锁最后一档
(is_spontaneous_goal_unlocked,issue #35 早已建好)后面,未解锁时工具
和已有意图一并对模型隐身。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Yushu merged commit 8982686553 into main 2026-07-23 09:05:52 +00:00
Yushu deleted branch feature/40-spontaneous-goal 2026-07-23 09:05:52 +00:00
Sign in to join this conversation.
No description provided.