Files
JARVIS/development-doc/plan/memory-update/phase-m-3-proactive-reminder.md

10 KiB
Raw Blame History

Phase M.3:主动提醒系统

日期2026-04-04 状态:规划中 依赖M.1 (重要性评分), M.2 (遗忘曲线) 工作量5 天


1. 本阶段目的

让 Jarvis 从「等用户问」变成「主动关心」。

核心问题:

  • Jarvis 知道用户关心什么(高重要性记忆)
  • Jarvis 知道用户最近做了什么(每日摘要)
  • Jarvis 主动提醒,而不是等用户问

2. 核心架构

┌─────────────────────────────────────────────────────────────┐
│                 ProactiveReminderSystem                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐  │
│  │ DailyDigest  │ +  │ Reminder     │ +  │ MemoryInformer│  │
│  │ Generator    │    │ Scheduler    │    │              │  │
│  │              │    │              │    │              │  │
│  │ 每日摘要生成  │    │ 提醒调度     │    │ 主动提醒推送  │  │
│  └──────────────┘    └──────────────┘    └──────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3. 每日摘要生成器 (DailyDigestGenerator)

3.1 输入来源

今日输入:
- 用户今日对话摘要 (memory_summaries)
- 用户今日提到的重点 (high importance memories)
- 用户今日创建的任务 (tasks)
- 用户今日查阅的知识 (knowledge retrieval logs)

3.2 输出格式

{
  "date": "2026-04-04",
  "summary": "今天你主要在处理工作问题,提到了换工作的想法",
  "key_points": [
    {"content": "想换工作", "importance": 0.9, "source": "conversation"},
    {"content": "项目 deadline 是周五", "importance": 0.8, "source": "task"}
  ],
  "pending_questions": [
    {"content": "量子计算的问题还没完全理解", "importance": 0.6}
  ],
  "suggestions": [
    {"text": "明天可以继续聊换工作的话题", "reason": "重要性高且今天没深入"},
    {"text": "量子计算的资料可以找找更通俗的解释", "reason": "还没理解"}
  ]
}

3.3 生成时机

# 每天晚上 10 点生成
@scheduler.scheduled_task("cron", hour=22)
async def generate_daily_digest():
    """生成每日摘要"""
    digest = await generator.generate(user_id)
    await storage.save(digest)

4. 提醒调度器 (ReminderScheduler)

4.1 提醒类型

类型 触发条件 推送时机
后续提醒 用户说「回头提醒我」 指定时间
关联提醒 提到的话题有关联记忆 下次对话时
周期提醒 每周/每月固定事项 周期首日
遗忘提醒 归档记忆被重新提到 恢复后提醒

4.2 调度逻辑

class ReminderScheduler:
    def schedule_reminder(self, user_id: int, reminder: Reminder):
        """安排提醒"""
        
    def get_due_reminders(self, user_id: int) -> list[Reminder]:
        """获取到期的提醒"""
        
    def snooze_reminder(self, reminder_id: int, minutes: int):
        """推迟提醒"""

4.3 提醒存储

class Reminder:
    id: int
    user_id: int
    content: str
    trigger_type: str  # "time" / "context" / "periodic"
    trigger_at: DateTime
    context: dict  # 关联的 memory_id 等
    status: str  # "pending" / "sent" / "snoozed"
    created_at: DateTime

5. 主动记忆告知器 (ProactiveMemoryInformer)

5.1 触发条件

TRIGGERS = {
    "high_importance_topic": {
        "condition": "用户提到高重要性话题",
        "action": "提及关联记忆",
        "example": "你说想换工作,要不要看看之前收藏的 JD"
    },
    "repeat_question": {
        "condition": "用户重复问某个问题",
        "action": "主动说之前回答过",
        "example": "你之前问过量子计算,我再给你解释一下?"
    },
    "forgotten_context": {
        "condition": "用户提到已归档的记忆",
        "action": "提示可以恢复",
        "example": "这个话题你一个月前聊过,要我恢复一下吗?"
    },
    "pending_goal": {
        "condition": "用户设了目标但没进展",
        "action": "温和提醒",
        "example": "你之前说想学 Python有进展吗"
    }
}

5.2 告知时机

# 不是每次对话都告知,有概率控制
INFORM_PROBABILITY = {
    "high_importance_topic": 0.8,  # 高重要性话题 80% 主动提
    "repeat_question": 1.0,        # 重复问题 100% 主动提
    "forgotten_context": 0.5,      # 已归档话题 50% 提示
    "pending_goal": 0.3,           # 待办目标 30% 温和提醒
}

5.3 告知风格

INFORM_STYLE = {
    "casual": "对了,你之前提到...",
    "gentle": "不知道你有没有注意到...",
    "helpful": "我记起你关心这个,要不看看..."
}

6. 核心文件

6.1 新增文件

文件 职责
services/memory/daily_digest.py 每日摘要生成
services/memory/reminder_scheduler.py 提醒调度
services/memory/proactive_informer.py 主动告知
services/memory/reminder_model.py 提醒数据模型

6.2 修改文件

文件 修改内容
services/scheduler_service.py 集成主动提醒调度
services/memory_service.py 集成 ProactiveInformer
routers/conversation.py 主动告知触发点

7. API 设计

7.1 DailyDigestGenerator

class DailyDigestGenerator:
    async def generate(self, user_id: int, date: date = None) -> DailyDigest:
        """生成每日摘要"""
        
    async def get_recent_digests(self, user_id: int, limit: int = 7) -> list[Digest]:
        """获取最近 N 天的摘要"""

7.2 ReminderScheduler

class ReminderScheduler:
    async def create_reminder(self, user_id: int, content: str, trigger_at: datetime):
        """创建提醒"""
        
    async def get_due_reminders(self, user_id: int) -> list[Reminder]:
        """获取到期提醒"""
        
    async def snooze(self, reminder_id: int, minutes: int):
        """推迟提醒"""

7.3 ProactiveInformer

class ProactiveInformer:
    def should_inform(self, user_id: int, trigger_type: str) -> bool:
        """是否应该告知"""
        
    def get_inform_message(self, user_id: int, trigger_type: str, context: dict) -> str:
        """生成告知消息"""
        
    async def check_and_inform(self, conversation_context: dict) -> str | None:
        """检查并返回告知消息,无则返回 None"""

8. 前端集成

8.1 每日摘要展示

<!-- 每日摘要卡片 -->
<div v-if="dailyDigest">
  <h3>今日摘要</h3>
  <p>{{ dailyDigest.summary }}</p>
  <div v-for="point in dailyDigest.keyPoints">
    - {{ point.content }}
  </div>
  <div v-if="dailyDigest.suggestions.length">
    <h4>建议</h4>
    <p>{{ dailyDigest.suggestions[0].text }}</p>
  </div>
</div>

8.2 主动提醒推送

<!-- 主动提醒弹窗 -->
<div v-if="activeReminder" class="reminder-toast">
  <p>{{ activeReminder.content }}</p>
  <button @click="snooze">稍后</button>
  <button @click="dismiss">知道了</button>
</div>

9. 测试设计

9.1 每日摘要测试

async def test_digest_generation():
    digest = await generator.generate(user_id=1)
    assert digest.summary is not None
    assert len(digest.key_points) > 0

async def test_digest_includes_high_importance():
    # 高重要性记忆应该出现在摘要中
    pass

9.2 提醒调度测试

async def test_schedule_reminder():
    reminder = await scheduler.create_reminder(
        user_id=1,
        content="检查邮件",
        trigger_at=datetime.now() + timedelta(hours=1)
    )
    assert reminder.status == "pending"

async def test_get_due_reminders():
    due = await scheduler.get_due_reminders(user_id=1)
    assert len(due) >= 0

9.3 主动告知测试

def test_should_inform_probability():
    # 高重要性话题 80% 触发
    count = sum(informer.should_inform(1, "high_importance_topic") 
                for _ in range(100))
    assert 70 < count < 90  # 允许一点随机波动

def test_repeat_question_always_informs():
    # 重复问题 100% 触发
    assert informer.should_inform(1, "repeat_question") == True

10. 验收标准

标准 说明
每日摘要生成正常 22:00 自动生成
提醒创建正常 用户可创建提醒
提醒到期触发 定时推送
主动告知概率正确 按配置的概率触发
告知消息自然 像人说话,不生硬
用户可控制 可以关闭主动提醒
单元测试覆盖率 > 80%

11. 工作量估算

任务 工作量
DailyDigestGenerator 1.5 天
ReminderScheduler 1.5 天
ProactiveInformer 1 天
前端摘要展示 0.5 天
前端提醒推送 0.5 天
测试 1 天
合计 6 天

12. 与现有系统的关系

现有 SchedulerService
        │
        ├── 凌晨任务重建 → 现有功能
        ├── 每日摘要生成 → M.3 新增
        └── 提醒检查     → M.3 新增

提醒系统独立于现有任务系统,但可以复用调度基础设施。