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

361 lines
10 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.
# 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 输出格式
```json
{
"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 生成时机
```python
# 每天晚上 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 调度逻辑
```python
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 提醒存储
```python
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 触发条件
```python
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 告知时机
```python
# 不是每次对话都告知,有概率控制
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 告知风格
```python
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
```python
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
```python
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
```python
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 每日摘要展示
```vue
<!-- 每日摘要卡片 -->
<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 主动提醒推送
```vue
<!-- 主动提醒弹窗 -->
<div v-if="activeReminder" class="reminder-toast">
<p>{{ activeReminder.content }}</p>
<button @click="snooze">稍后</button>
<button @click="dismiss">知道了</button>
</div>
```
---
## 9. 测试设计
### 9.1 每日摘要测试
```python
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 提醒调度测试
```python
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 主动告知测试
```python
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 新增
```
提醒系统独立于现有任务系统,但可以复用调度基础设施。