10 KiB
10 KiB
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 新增
提醒系统独立于现有任务系统,但可以复用调度基础设施。