* refractor(proactive): upgrade proactive feature with disentangled job and steps * refactor(proactive): apply audit fixes - rename read-side job 'proactive' -> 'proactive_read' (less confusing vs the refresh pipeline) - drop dedicated agent_wrapper.proactive; extraction reuses the default wrapper - simplify schema: remove unused ProactiveExtractOutput/TopicUpdate, drop resource_paths - extract no longer scans resource/ directly (daily notes already carry resource content) - update tests and docs accordingly * feat(proactive): strict extract-output gate and prompt total budget - parse_extract_reply now requires a contract section (follow_ups/extends/updates as a list); non-empty replies with misspelled section names trigger the existing one-shot retry instead of silently checkpointing changed files - pack_paths gains max_total_chars; extract packs newest daily material first, keeps the first file on overflow, and records omitted files in a trailer (default budget 300000 chars, configurable via max_total_chars) - tests: schema gate unit, schema-error retry e2e, budget unit + e2e * feat(proactive): add scenario-card plan step and generative agenda step * feat(proactive): digest-personal profile personalization and leaner LLM contract - extract/plan/agenda now draw a user profile block from <digest_dir>/personal/*.md (frontmatter description + body excerpt, per-file budget, profile.md fallback) - all daily access honours the configured daily_dir (prompt paths parameterized, config-driven fallbacks) so workspaces using e.g. memory/ work unchanged - schema trim: drop dead fields errors/material_paths, carry_forward_all -> count - shrink LLM output contract: new topics emit title/reason/confidence/paths only; keywords removed end-to-end, evidence derived from paths[0] (updates keep it) * fix(proactive): skip checkpoint when extract reply stays unusable after retry Two consecutive unparseable replies now short-circuit the round without checkpointing, so the same material is retried next round instead of being silently consumed (closes the residual audit #1 gap: the structural gate detected schema-wrong output but a double failure still checkpointed). * fix(proactive): replace running bool with reference-counted job activity tracker for the idle gate * refactor(proactive): remove job activity tracking and idle gate, restore job tree to upstream * fix(proactive): address second audit round (readonly reader, mtime checkpoint, wider fallbacks, profile containment, horizon content, expiry boundary) * refactor(dream): strip interests.yaml ownership from dream, proactive is now the sole writer * refactor(dream): separate proactive topic generation * ci: update renamed auto dream smoke test * fix(proactive): complete refresh migration and docs --------- Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
9.5 KiB
Proactive
proactive_read 是 ReMe 的主动记忆读取接口。它不重新分析 daily,也不调用 LLM,只读取独立 proactive refresh
流程写出的兴趣主题:
daily/<date>/interests.yaml
上层 Agent 可以用它获取“今天值得主动关注什么”,再决定是否提醒、追问、推荐下一步或生成主动洞察。
interests.yaml 由 proactive refresh writer 链路生成,默认通过 proactive_refresh_cron 定时执行;
proactive_read 只负责读取和暴露结果。Auto Dream 是独立的 daily-to-digest 流程,不读取或写入 proactive 状态。
配置入口
默认配置在 reme/config/default.yaml。同一组 refresh steps 有两个入口:用于维护和调试的本地 one-shot job,
以及按应用时区每天 18:00 执行的定时任务:
proactive_refresh:
backend: base
enable_serve: false
steps: &proactive_refresh_steps
- backend: proactive_extract_step
file_catalog: proactive
scan_days: 2
carry_forward_days: 14
max_carry_forward_topics: 20
llm_timeout_seconds: 300
max_chars_per_file: 60000
max_total_chars: 300000
- backend: proactive_topics_step
known_threshold: 0.85
known_threshold_calibrated_for: text-embedding-v4@1024
min_push_confidence: 0.5
max_topics: 10
- backend: proactive_plan_step
- backend: proactive_agenda_step
- backend: proactive_finish_step
file_catalog: proactive
proactive_refresh_cron:
backend: cron
cron: "0 18 * * *"
steps: *proactive_refresh_steps
上面的 anchor 只是为了紧凑展示;default.yaml 中显式写出了两组 steps。读取 job 为:
proactive_read:
backend: base
description: "Proactive: read daily/<date>/interests.yaml and expose the latest user-interest topics."
parameters:
type: object
properties:
date:
type: string
default: ""
include_content:
type: boolean
default: true
horizon_days:
type: integer
default: 1
min_confidence:
type: number
default: 0.4
steps:
- backend: proactive_step
min_confidence: 0.4
参数含义:
| 参数 | 作用 |
|---|---|
date |
要读取的日期,格式为 YYYY-MM-DD。为空时使用应用时区中的今天。 |
include_content |
是否在 answer 和 metadata 中返回 YAML 原文,默认 true。 |
horizon_days |
读取单日曝光文件,或在更宽时间窗下读取 truth source;默认 1。 |
min_confidence |
返回 topic 的最低置信度,默认 0.4;旧版 topic 使用 0.5。 |
Refresh 成本、文件与关闭方式
没有 daily Markdown 发生变化时,refresh 会在调用 LLM 前结束,也不会生成新的曝光文件。有变化素材时,extract 通常调用一次 LLM;回复不可用时最多重试一次。存在 push candidates 时,plan 会再调用一次;候选数大于一个时, agenda 会再调用一次。因此默认链路单轮最多调用四次 LLM。
Refresh 会维护可重建的 truth source daily/_proactive.yaml,写入 daily/<date>/interests.yaml,并推进独立的
proactive file catalog。Auto Dream 不读取或写入这些 proactive 产物。
如需关闭自动 refresh,请使用不包含 proactive_refresh_cron job 的显式应用配置。若仍需按需维护,可保留
本地 one-shot 的 proactive_refresh;它设置了 enable_serve: false,不会暴露到 HTTP 或 MCP。
输入契约
当前 proactive refresh 生成的文件如下:
version: 2
date: 2026-06-20
generated_at: 2026-06-20T18:00:00+08:00
push: true
topics:
- id: b2120f3573cb
title: 记忆检索链路的质量回归
reason: 用户近期持续修改 search、node_search 和 dream 集成链路。
kind: follow_up
confidence: 0.86
first_seen: 2026-06-20
last_evidence_at: 2026-06-20
evidence: daily/2026-06-20/session.md
paths:
- daily/2026-06-20/session.md
agenda:
- topic_id: b2120f3573cb
title: 记忆检索链路的质量回归
scenario_type: resume_task
opener: 下次发布前先回顾最近的检索回归。
next_action: 对比失败查询与上一个索引快照。
preconditions: []
delivery: in_conversation
linked_memory: []
order_reason: 证据较新且下一步明确。
suppressed: []
当前 v2 topic 包含稳定 ID、类型、置信度、证据日期和来源路径。读取器仍兼容包含 title、reason、evidence、
keywords、paths 的 v1 文件;缺少 v2 置信度时按 0.5 处理。
返回结果
成功读取时,proactive_step 会在主要 answer 中返回 summary 和 topics;当 include_content=true 时还会返回
content。相同的结果字段也会保留在标准 response metadata 中:
| 字段 | 说明 |
|---|---|
date |
实际读取的日期。 |
path |
daily/<date>/interests.yaml。 |
topics |
解析后的 topic 列表。 |
content |
YAML 原文;仅在 include_content=true 时返回。 |
skipped |
文件不存在时为 true。 |
error |
读取或解析异常。 |
summary |
简短摘要。 |
agenda |
当日主动议程(可选,仅 v2 文件携带时返回)。 |
当当天的 interests.yaml 由 proactive refresh 链路生成并携带议程时,answer 会附带
agenda 字段:按顺序排列的当日议程条目,每条包含 topic_id、title、scenario_type、
opener(自然口吻的开场白)、next_action(最小可执行动作)、preconditions、
delivery、linked_memory 与 order_reason。读侧会过滤已解决或低于 min_confidence
的主题对应的议程条目;文件不含议程时该字段不出现。
文件存在且解析成功时,answer 是结构化数据,例如:
{
"summary": "Read 1 proactive topic(s) from daily/2026-06-20/interests.yaml",
"topics": [
{
"id": "b2120f3573cb",
"title": "记忆检索链路的质量回归",
"reason": "用户最近反复修改了 search、node_search 和 dream integration。",
"kind": "follow_up",
"confidence": 0.86,
"first_seen": "2026-06-20",
"last_evidence_at": "2026-06-20",
"evidence": "daily/2026-06-20/session.md",
"paths": ["daily/2026-06-20/session.md"]
}
],
"agenda": [
{
"topic_id": "b2120f3573cb",
"title": "记忆检索链路的质量回归",
"scenario_type": "resume_task",
"opener": "下次发布前先回顾最近的检索回归。",
"next_action": "对比失败查询与上一个索引快照。",
"preconditions": [],
"delivery": "in_conversation",
"linked_memory": [],
"order_reason": "证据较新且下一步明确。"
}
],
"content": "date: 2026-06-20\n..."
}
当 include_content=false 时,answer 不包含 content 字段。文件缺失和读取失败仍分别返回明确的
Skipped: ... 和 Error: ... 消息。
文件不存在时不会报错,而是成功返回 skipped:
Skipped: interests file not found at daily/2026-06-20/interests.yaml
这让上层 Agent 可以把“今天还没有 proactive refresh 结果”当作正常空状态处理。
运行方式
通过正常应用生命周期立即执行一次 refresh:
reme start job=proactive_refresh date=2026-06-20
该命令可能调用配置的 LLM,并更新 _proactive.yaml、interests.yaml 与 proactive catalog;它不会运行 Auto
Dream。
读取生成的 topics:
reme proactive_read date=2026-06-20
不返回 YAML 原文:
reme proactive_read date=2026-06-20 include_content=false
与 auto_dream 的关系
Proactive refresh 和 Auto Dream 各自独立消费 daily notes:
daily notes -> auto_dream -> digest
daily notes -> proactive_refresh_cron -> daily/<date>/interests.yaml -> proactive_read -> upper-level agent
Proactive 职责边界如下:
| 模块 | 职责 |
|---|---|
proactive_refresh |
从本地 CLI 单次运行 refresh writer 链路。 |
proactive_refresh_cron |
每天 18:00 运行同一套 writer 链路。 |
proactive_step |
读取 interests.yaml,暴露给上层 Agent。 |
proactive_read 不修改任何文件,不更新 catalog,也不负责判断是否应该主动打扰用户。它只提供当天主题材料;是否推送、何时推送、用什么语气推送,应由调用方根据产品策略决定。
失败模式
| 场景 | 行为 |
|---|---|
interests.yaml 不存在 |
success=true,skipped=true,topics=[]。 |
| YAML 无法读取或解析异常 | success=false,answer 返回错误摘要。 |
| YAML 存在但没有合法 topics | success=true,topics=[]。 |
因此推荐调用方先检查 success,再检查 skipped,最后检查 topics 是否为空。