把一个成熟的日历助手升级为 AI 原生产品,难点并不是接入大模型,而是让用户可以用自然语言表达意图,同时保住原有排期体验的确定性。Reclaim 的演进所呈现的核心方向正是如此:重新设计人机交互和请求处理方式,但不要求整个调度系统从头再写。
对于已经拥有用户、规则和历史行为的产品,更稳妥的路径不是让模型直接操作日历,而是在自然语言与既有调度引擎之间加入一层可验证的“意图协议”。
自然语言不应该绕过调度系统
传统日历产品通常接收结构化输入:开始时间、持续时长、参与者、日历和重复规则。自然语言请求则更加模糊:
- “明天下午找 90 分钟做方案。”
- “本周和设计团队约一次,不要占用午休。”
- “如果客户会议改期,把健身计划挪到晚上。”
这些请求包含目标、约束、偏好和条件,但不一定包含唯一答案。AI 层适合做的是把表达转换成结构化意图,而不是直接写入日历。
一个相对安全的调用链可以设计为:
用户请求
↓
语言模型:识别动作、时间窗口、约束和缺失信息
↓
意图校验器:检查字段、权限、冲突与置信度
↓
既有调度引擎:搜索可行时间并应用原有规则
↓
预览与确认
↓
日历写入层
这样划分有两个直接收益:
- 调度语义保持稳定。 工作时间、缓冲时间、优先级、冲突处理等规则仍由原有引擎负责。
- 模型可以替换。 只要输出协议不变,后续更换模型或提示词不会迫使调度服务一起重构。
用“意图协议”隔离模型的不确定性
模型输出不应是一句“已经帮你安排好了”,而应是机器可以校验的对象。例如:
{
"action": "create_focus_block",
"duration_minutes": 90,
"window": {
"date": "tomorrow",
"earliest": "13:00",
"latest": "17:00"
},
"calendar": "work",
"constraints": ["avoid_existing_events"],
"needs_confirmation": true
}
这层协议应明确区分三类信息:
- 事实参数:时长、参与者、日期、时区。
- 软偏好:尽量安排在下午、避免连续会议、优先某个日历。
- 执行权限:仅生成建议、确认后创建,或者允许自动调整。
尤其不要让“模型置信度高”自动等价于“可以执行”。置信度描述的是解析判断,并不代表用户授权。删除事件、移动外部参与者会议、修改重复日程等高影响操作,应始终提高确认等级。
一个可运行的最小边界层
下面是一个不依赖第三方包的 Python 示例。它不是 Reclaim 的实际实现,而是一种可以改造的最小架构:interpret() 模拟自然语言解析,validate() 则代表模型与调度引擎之间的确定性边界。
将代码保存为 calendar_intent.py,然后运行 python calendar_intent.py。
import json
import re
from dataclasses import dataclass, asdict
from typing import Optional
ALLOWED_ACTIONS = {'create_focus_block', 'find_meeting_time'}
ALLOWED_CALENDARS = {'work', 'personal'}
@dataclass
class Intent:
action: str
duration_minutes: Optional[int]
date: Optional[str]
earliest: Optional[str]
latest: Optional[str]
calendar: str
needs_confirmation: bool
def interpret(text: str) -> Intent:
"""演示解析器;生产环境可替换为返回同一结构的模型调用。"""
duration_match = re.search(r'(\d+)\s*(?:分钟|minutes?)', text, re.I)
duration = int(duration_match.group(1)) if duration_match else None
is_afternoon = '下午' in text or 'afternoon' in text.lower()
is_tomorrow = '明天' in text or 'tomorrow' in text.lower()
return Intent(
action='create_focus_block',
duration_minutes=duration,
date='tomorrow' if is_tomorrow else None,
earliest='13:00' if is_afternoon else None,
latest='17:00' if is_afternoon else None,
calendar='work',
needs_confirmation=True,
)
def validate(intent: Intent) -> list[str]:
errors = []
if intent.action not in ALLOWED_ACTIONS:
errors.append('unsupported action')
if intent.duration_minutes is None:
errors.append('duration is missing')
elif not 15 <= intent.duration_minutes <= 480:
errors.append('duration must be between 15 and 480 minutes')
if intent.calendar not in ALLOWED_CALENDARS:
errors.append('calendar is not allowed')
if intent.date is None:
errors.append('date is ambiguous')
if bool(intent.earliest) != bool(intent.latest):
errors.append('time window is incomplete')
return errors
def build_plan(text: str) -> dict:
intent = interpret(text)
errors = validate(intent)
return {
'status': 'needs_clarification' if errors else 'ready_for_preview',
'intent': asdict(intent),
'errors': errors,
}
if __name__ == '__main__':
request = '明天下午找 90 分钟做方案'
print(json.dumps(build_plan(request), ensure_ascii=False, indent=2))
接入真实模型时,只替换 interpret(),并要求模型输出符合固定 JSON Schema。validate() 和后续调度逻辑仍使用普通代码实现。生产环境还应补充:
- 用户时区和夏令时检查;
- 日历及参与者权限校验;
- 事件版本号,防止基于过期状态修改;
- 幂等键,避免重试时创建重复事件;
- 模型输入、结构化输出和最终操作的审计记录;
- 调度前再次读取日历,避免解析期间出现的新冲突。
演进式上线,而不是一次性交出控制权
AI 原生并不意味着所有操作都改成聊天,也不意味着隐藏已有界面。日历是高频且高风险的生产力工具,用户往往仍需要查看时间网格、拖动事件和精确修改规则。自然语言更适合作为新的入口,既有界面则继续承担预览、纠错和细粒度控制。
可以按风险逐步上线:
- 只读理解:解释某天空闲时间,不执行修改。
- 生成候选方案:调用原有调度引擎,但仅展示预览。
- 确认后执行:低风险的新建操作由用户确认。
- 有限自动化:只对用户明确授权的规则自动执行,并提供撤销能力。
评估效果时,不要只观察模型是否正确识别句子。更有意义的指标包括:澄清次数、预览后的取消率、错误修改率、撤销率,以及从提出请求到成功排期的总时间。
最终检查清单也很直接:模型是否只负责理解意图?调度规则是否仍可测试和复现?高影响操作是否需要确认?每次写入能否撤销和审计?只要这些边界仍然清晰,就可以在保留成熟体验的同时,让产品逐步获得自然语言能力,而无需推倒重来。