ReMe/docs/zh/proactive.md
imrewce 354837f9af
feat(proactive): separate proactive refresh from auto dream (#488)
* 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>
2026-09-07 17:23:37 +08:00

250 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Proactive
`proactive_read` 是 ReMe 的主动记忆读取接口。它不重新分析 daily也不调用 LLM只读取独立 proactive refresh
流程写出的兴趣主题:
```text
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 执行的定时任务:
```yaml
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 为:
```yaml
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 生成的文件如下:
```yaml
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 是结构化数据,例如:
```json
{
"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
```text
Skipped: interests file not found at daily/2026-06-20/interests.yaml
```
这让上层 Agent 可以把“今天还没有 proactive refresh 结果”当作正常空状态处理。
## 运行方式
通过正常应用生命周期立即执行一次 refresh
```bash
reme start job=proactive_refresh date=2026-06-20
```
该命令可能调用配置的 LLM并更新 `_proactive.yaml``interests.yaml` 与 proactive catalog它不会运行 Auto
Dream。
读取生成的 topics
```bash
reme proactive_read date=2026-06-20
```
不返回 YAML 原文:
```bash
reme proactive_read date=2026-06-20 include_content=false
```
## 与 auto_dream 的关系
Proactive refresh 和 Auto Dream 各自独立消费 daily notes
```text
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` 是否为空。