| # Project Aura — 调度引擎 (Scheduler) |
|
|
| ## 架构概览 |
|
|
| 调度系统分为三层:**心跳层 → 决策调度层 → 动作执行层** |
|
|
| ``` |
| 用户状态变化 / 定时心跳 |
| ↓ |
| ┌──────────────┐ |
| │ 心跳调度器 │ ← 自适应泊松过程 |
| │ Heartbeat │ |
| └──────┬───────┘ |
| ↓ 触发唤醒 |
| ┌──────────────┐ |
| │ 双系统调度器 │ ← 快/慢双通道 |
| │ DualSystem │ |
| ├──────────────┤ |
| │ 快系统(ms级) │ 规则引擎、阈值判断、状态匹配 |
| │ 慢系统(s级) │ LLM评估、上下文检索、意图生成 |
| └──────┬───────┘ |
| ↓ 输出决策 |
| ┌──────────────┐ |
| │ 执行调度器 │ ← 频率控制 + 队列管理 |
| │ Executor │ |
| └──────┬───────┘ |
| ↓ 发起动作 |
| 用户界面 |
| ``` |
|
|
| --- |
|
|
| ## 1. 心跳调度器 (Heartbeat Scheduler) |
|
|
| ### 数学模型 |
|
|
| 心跳间隔由自适应泊松过程决定: |
|
|
| ``` |
| λ(t) = λ₀ · (1 + α · Δt_idle / T_window) · (1 - β · interaction_density) |
| ``` |
|
|
| - λ₀: 基础心跳频率(默认 180秒) |
| - Δt_idle: 自上次用户交互以来的空闲时间 |
| - T_window: 观察窗口(默认 30分钟) |
| - α: 空闲加速因子(0.1~0.5)— 用户越久没交互,心跳越快 |
| - β: 交互密度衰减因子(0.3~0.8)— 用户越活跃,心跳越慢 |
|
|
| ### 状态机 |
|
|
| ``` |
| WAITING → (心跳触发) → CHECKING → (价值>阈值) → DECIDING → (决策完成) → WAITING |
| ↓ 价值<阈值 ↓ "等待更佳时机" |
| WAITING DEFERRED → (N分钟后) → WAITING |
| ``` |
|
|
| ### 代码实现 |
|
|
| ```python |
| import time |
| import random |
| import threading |
| from enum import Enum |
| from dataclasses import dataclass |
| from typing import Optional, Callable |
| |
| class HeartbeatState(Enum): |
| WAITING = "waiting" # 等待下次心跳 |
| CHECKING = "checking" # 正在检查是否需要唤醒 |
| DECIDING = "deciding" # 正在做决策 |
| DEFERRED = "deferred" # 延迟到更佳时机 |
| ACTIVE = "active" # 正在与用户交互 |
| |
| @dataclass |
| class HeartbeatConfig: |
| base_interval: float = 180.0 # 基础间隔(秒) |
| min_interval: float = 30.0 # 最小间隔 |
| max_interval: float = 600.0 # 最大间隔(10分钟无交互就降到最低频) |
| idle_accel_factor: float = 0.3 # 空闲加速 α |
| interact_decay: float = 0.5 # 交互衰减 β |
| observation_window: float = 1800.0 # 观察窗口(30分钟) |
| defer_backoff: float = 120.0 # 延迟后重试间隔 |
| |
| @dataclass |
| class UserContext: |
| last_interaction_time: float = 0.0 # 上次用户交互时间戳 |
| interaction_count_30m: int = 0 # 30分钟内交互次数 |
| current_state: HeartbeatState = HeartbeatState.WAITING |
| is_active_session: bool = False # 是否在活跃对话中 |
| device_mode: str = "normal" # normal / meeting / sleep / driving |
| |
| class HeartbeatScheduler: |
| """ |
| 自适应心跳调度器:决定何时唤醒决策系统 |
| """ |
| def __init__(self, config: HeartbeatConfig = None): |
| self.config = config or HeartbeatConfig() |
| self.context = UserContext() |
| self._timer: Optional[threading.Timer] = None |
| self._on_tick: Optional[Callable] = None |
| self._running = False |
| |
| def set_callback(self, callback: Callable): |
| """设置心跳触发回调""" |
| self._on_tick = callback |
| |
| def compute_interval(self) -> float: |
| """计算下一次心跳间隔""" |
| now = time.time() |
| idle_seconds = now - self.context.last_interaction_time |
| |
| # 基础间隔 + 空闲加速 |
| raw_interval = self.config.base_interval * ( |
| 1 + self.config.idle_accel_factor * idle_seconds / self.config.observation_window |
| ) |
| |
| # 交互密度衰减 |
| density = self.context.interaction_count_30m / (self.config.observation_window / 60) |
| raw_interval *= (1 - self.config.interact_decay * min(density, 1.0)) |
| |
| # 场景修正 |
| if self.context.device_mode == "meeting": |
| raw_interval *= 3 # 会议中降频 |
| elif self.context.device_mode == "sleep": |
| raw_interval *= 10 # 睡眠中极低频 |
| elif self.context.device_mode == "driving": |
| raw_interval *= 5 |
| |
| # 如果在活跃会话中,暂时停用心跳 |
| if self.context.is_active_session: |
| return self.config.max_interval |
| |
| # 夹紧到[min, max] |
| return max(self.config.min_interval, min(raw_interval, self.config.max_interval)) |
| |
| def record_interaction(self): |
| """记录用户交互""" |
| now = time.time() |
| self.context.last_interaction_time = now |
| self.context.interaction_count_30m += 1 |
| self.context.current_state = HeartbeatState.ACTIVE |
| ``` |
|
|
| --- |
|
|
| ## 2. 双系统调度器 (DualSystem Scheduler) |
|
|
| ### 核心思路 |
|
|
| 借鉴丹尼尔·卡尼曼的「思考,快与慢」理论: |
|
|
| - **快系统(System 1)**:规则引擎 + 轻量分类器,毫秒级响应,处理80%的日常判断 |
| - **慢系统(System 2)**:LLM驱动的深度评估,秒级响应,处理需要语境理解的复杂场景 |
|
|
| ### 决策流程图 |
|
|
| ``` |
| 输入:用户上下文 + 时间 + 设备状态 |
| │ |
| ▼ |
| ┌──────────────────────┐ |
| │ 快系统(System 1) │ |
| │ │ |
| │ 1. 规则过滤 │ |
| │ - 时间窗检查 │ |
| │ - 频率控制检查 │ |
| │ - 场景模式检查 │ |
| │ │ |
| │ 2. 状态模式匹配 │ |
| │ - 深夜+长时间空闲 │ |
| │ - 会话结束后未回复 │ |
| │ - 日程事件触发 │ |
| │ │ |
| │ 3. 快速价值评分 │ |
| │ Value = w₁·urgency │ |
| │ + w₂·recency│ |
| │ + w₃·affinity│ |
| └──────────┬───────────┘ |
| │ |
| ┌──────┴──────┐ |
| │ │ |
| Value>0.7 Value<=0.7 |
| │ │ |
| ▼ ▼ |
| 直接执行 ┌──────────────┐ |
| │ 慢系统(System2)│ |
| │ │ |
| │ 1. LLM评估 │ |
| │ 2. 上下文检索 │ |
| │ 3. 意图生成 │ |
| │ 4. 价值重估 │ |
| └──────┬───────┘ |
| │ |
| ┌─────┴─────┐ |
| │ │ |
| 价值>0.85 价值<=0.85 |
| │ │ |
| ▼ ▼ |
| 执行决策 推迟或放弃 |
| ``` |
|
|
| ### 代码实现 |
|
|
| ```python |
| from dataclasses import dataclass, field |
| from typing import List, Optional, Tuple |
| from enum import Enum |
| import time |
| |
| class Decision(Enum): |
| IMMEDIATE_CARE = "immediate_care" # 立即关心 |
| GENTLE_REMIND = "gentle_remind" # 温和提醒 |
| DEFER = "defer" # 延后 |
| SILENCE = "silence" # 保持沉默 |
| CONTENT_SUGGEST = "content_suggest" # 内容推荐 |
| CHECK_IN = "check_in" # 问候关心 |
| |
| @dataclass |
| class DecisionContext: |
| user_id: str |
| idle_minutes: float |
| time_of_day: str # morning/afternoon/evening/night |
| device_mode: str |
| last_conversation_topic: Optional[str] = None |
| user_mood: str = "unknown" # happy/sad/anxious/neutral/unknown |
| upcoming_events: List[str] = field(default_factory=list) |
| unread_count: int = 0 |
| |
| @dataclass |
| class ValueScore: |
| score: float # 0.0 - 1.0 |
| urgency: float # 紧急程度 |
| relevance: float # 相关性 |
| interruption_cost: float # 打扰成本 |
| confidence: float # 置信度 |
| reasoning: str = "" # 决策理由(用于思维快照) |
| |
| class FastSystem: |
| """ |
| 快系统:规则引擎 + 轻量评估 |
| 响应时间 < 50ms |
| """ |
| |
| # 规则集:优先级从高到低 |
| RULES = [ |
| # (条件函数, 决策结果, 价值分) |
| ("深夜独处", lambda ctx: ctx.time_of_day == "night" and ctx.idle_minutes > 120, |
| Decision.IMMEDIATE_CARE, 0.85), |
| |
| ("长时间沉默", lambda ctx: ctx.idle_minutes > 360 and ctx.time_of_day != "night", |
| Decision.GENTLE_REMIND, 0.70), |
| |
| ("会话搁置", lambda ctx: ctx.last_conversation_topic and ctx.idle_minutes > 30, |
| Decision.CHECK_IN, 0.65), |
| |
| ("会议中", lambda ctx: ctx.device_mode == "meeting", |
| Decision.SILENCE, 0.0), |
| |
| ("睡眠中", lambda ctx: ctx.device_mode == "sleep", |
| Decision.SILENCE, 0.0), |
| ] |
| |
| @classmethod |
| def evaluate(cls, ctx: DecisionContext) -> Tuple[Optional[Decision], ValueScore]: |
| """ |
| 快系统评估 |
| 返回 (决策, 价值分数) |
| """ |
| for rule_name, condition, decision, base_score in cls.RULES: |
| if condition(ctx): |
| # 简单价值计算 |
| urgency = min(ctx.idle_minutes / 120, 1.0) # 2小时达到最大紧迫度 |
| interruption = 0.3 if ctx.time_of_day == "night" else 0.1 |
| |
| score = ValueScore( |
| score=base_score * (1 + urgency * 0.3 - interruption * 0.2), |
| urgency=urgency, |
| relevance=base_score, |
| interruption_cost=interruption, |
| confidence=0.8, # 规则引擎置信度高 |
| reasoning=f"[Fast] 匹配规则: {rule_name}" |
| ) |
| return decision, score |
| |
| return None, ValueScore(score=0.0, urgency=0, relevance=0, |
| interruption_cost=0, confidence=0) |
| |
| |
| class SlowSystem: |
| """ |
| 慢系统:LLM驱动的深度评估 |
| 响应时间 500ms - 5s |
| """ |
| |
| @staticmethod |
| def evaluate(ctx: DecisionContext) -> Tuple[Optional[Decision], ValueScore]: |
| """ |
| 慢系统评估(调用LLM进行语义理解) |
| |
| 评估维度: |
| 1. 用户情感状态推断(从历史消息中提取) |
| 2. 当前场景的语义价值(是否有关联话题) |
| 3. 打扰成本的精细化估计(用户当前可能在做什么) |
| 4. 个性化偏好匹配(用户之前对类似行为的反馈) |
| |
| 返回决策和价值评分 |
| """ |
| # 这里调用LLM进行深度评估 |
| # prompt示例: |
| """ |
| 你是一个AI主动交互决策引擎。请评估在当前场景下是否应该主动发起对话。 |
| |
| 用户上下文: |
| - 空闲时间:{idle_minutes}分钟 |
| - 时段:{time_of_day} |
| - 设备模式:{device_mode} |
| - 上次话题:{last_conversation_topic} |
| - 用户情绪:{user_mood} |
| - 近期事件:{upcoming_events} |
| |
| 请从以下维度评分(0-1): |
| 1. 消息紧迫性:用户需要关注的事情 |
| 2. 话题相关性:当前是否有关联话题可以延续 |
| 3. 打扰风险:打扰用户的代价 |
| 4. 情感价值:这个互动能提供的情感价值 |
| |
| 综合评分 > 0.85 时建议主动发起,0.7-0.85 时延后,< 0.7 时保持沉默。 |
| """ |
| # 模拟返回 |
| return Decision.GENTLE_REMIND, ValueScore( |
| score=0.78, urgency=0.6, relevance=0.7, |
| interruption_cost=0.3, confidence=0.65, |
| reasoning="[Slow] 用户已空闲90分钟,上次话题有延续价值" |
| ) |
| |
| |
| class DualSystemScheduler: |
| """ |
| 双系统调度器:协调快慢系统的协作 |
| """ |
| |
| def __init__(self, fast_system=FastSystem, slow_system=SlowSystem): |
| self.fast = fast_system |
| self.slow = slow_system |
| self.confidence_threshold = 0.7 # 快系统置信度阈值 |
| self.deflection_count = 0 # 连续被跳过次数 |
| |
| def decide(self, ctx: DecisionContext) -> Tuple[Optional[Decision], ValueScore, str]: |
| """ |
| 主决策入口 |
| |
| 返回:(决策, 价值分数, 使用系统标识) |
| """ |
| # 1. 先跑快系统 |
| fast_decision, fast_score = self.fast.evaluate(ctx) |
| |
| # 2. 如果快系统有高置信度结果,直接采纳 |
| if fast_decision and fast_score.confidence >= self.confidence_threshold: |
| # 但连续被打断时逐渐降低信任 |
| effective_confidence = fast_score.confidence * (0.9 ** self.deflection_count) |
| if effective_confidence >= self.confidence_threshold: |
| return fast_decision, fast_score, "fast" |
| |
| # 3. 否则调慢系统做深度评估 |
| slow_decision, slow_score = self.slow.evaluate(ctx) |
| |
| # 4. 快慢系统加权融合 |
| combined_score = fast_score.score * 0.3 + slow_score.score * 0.7 |
| |
| # 5. 最终决策 |
| if combined_score >= 0.85: |
| self.deflection_count = 0 |
| return slow_decision or Decision.IMMEDIATE_CARE, ValueScore( |
| score=combined_score, |
| urgency=(fast_score.urgency + slow_score.urgency) / 2, |
| relevance=(fast_score.relevance + slow_score.relevance) / 2, |
| interruption_cost=(fast_score.interruption_cost + slow_score.interruption_cost) / 2, |
| confidence=(fast_score.confidence + slow_score.confidence) / 2, |
| reasoning=f"[Fusion] Fast({fast_score.score:.2f}) + Slow({slow_score.score:.2f}) = {combined_score:.2f}" |
| ), "fusion" |
| elif combined_score >= 0.70: |
| self.deflection_count += 1 |
| return Decision.DEFER, ValueScore( |
| score=combined_score, urgency=0, relevance=0, |
| interruption_cost=0, confidence=0, |
| reasoning="Deferred: 价值不足,等待更佳时机" |
| ), "defer" |
| else: |
| self.deflection_count += 1 |
| return Decision.SILENCE, ValueScore( |
| score=combined_score, urgency=0, relevance=0, |
| interruption_cost=0, confidence=0, |
| reasoning="Silence: 当前不值得打扰用户" |
| ), "silence" |
| ``` |
|
|
| --- |
|
|
| ## 3. 频率控制 & 反骚扰 (Frequency Control & Anti-Spam) |
|
|
| ### 核心约束 |
|
|
| ``` |
| 全局约束: |
| · 每小时主动发言 ≤ 3 次 |
| · 每天主动发言 ≤ 15 次 |
| · 连续两次主动发言间隔 ≥ 20 分钟 |
| |
| 场景约束: |
| · 用户标记"不喜欢"同一类内容后,7天内不再触发同类消息 |
| · 用户明确说"别打扰"后,24小时内完全静默 |
| · 深夜(23:00-07:00)仅允许紧急事件触发 |
| |
| 记忆约束: |
| · 同一话题主动发起 ≤ 2 次(避免唠叨) |
| · 每次主动发言后进入"冷静期",时长 = 反馈评分 × 基础冷静期 |
| ``` |
|
|
| ### 代码实现 |
|
|
| ```python |
| @dataclass |
| class FrequencyConfig: |
| max_per_hour: int = 3 |
| max_per_day: int = 15 |
| min_interval_seconds: int = 1200 # 20分钟 |
| silence_after_reject_hours: int = 24 |
| same_topic_max: int = 2 |
| night_silence_start: int = 23 # 23:00 |
| night_silence_end: int = 7 # 07:00 |
| |
| class FrequencyController: |
| """ |
| 频率控制 + 反骚扰 |
| """ |
| def __init__(self, config: FrequencyConfig = None): |
| self.config = config or FrequencyConfig() |
| self.action_log: List[float] = [] # 时间戳列表 |
| self.reject_log: List[Tuple[str, float]] = [] # (topic, 时间戳) |
| self.silence_until: float = 0 # 静默截止时间 |
| self.topic_count: dict = {} |
| |
| def can_act(self, decision: Decision, topic: str = "") -> Tuple[bool, str]: |
| """检查是否允许执行主动发言""" |
| now = time.time() |
| |
| # 1. 静默期内 / 被用户拒绝 |
| if now < self.silence_until: |
| return False, "Silence period active" |
| |
| # 2. 每小时上限 |
| recent_hour = [t for t in self.action_log if now - t < 3600] |
| if len(recent_hour) >= self.config.max_per_hour: |
| return False, "Hourly limit reached" |
| |
| # 3. 每天上限 |
| recent_day = [t for t in self.action_log if now - t < 86400] |
| if len(recent_day) >= self.config.max_per_day: |
| return False, "Daily limit reached" |
| |
| # 4. 最小间隔 |
| if self.action_log and (now - self.action_log[-1]) < self.config.min_interval_seconds: |
| remaining = self.config.min_interval_seconds - (now - self.action_log[-1]) |
| return False, f"Cooldown: {remaining:.0f}s remaining" |
| |
| # 5. 同一话题检查 |
| if topic and self.topic_count.get(topic, 0) >= self.config.same_topic_max: |
| return False, f"Topic '{topic}' already triggered {self.same_topic_max} times" |
| |
| # 6. 深夜限制 |
| hour = time.localtime().tm_hour |
| if self.config.night_silence_start <= hour or hour < self.config.night_silence_end: |
| if decision not in (Decision.IMMEDIATE_CARE,): |
| return False, "Night mode: only urgent allowed" |
| |
| return True, "OK" |
| |
| def record_action(self, decision: Decision, topic: str = "", feedback: float = 0.5): |
| """记录一次主动发言""" |
| now = time.time() |
| self.action_log.append(now) |
| |
| if topic: |
| self.topic_count[topic] = self.topic_count.get(topic, 0) + 1 |
| |
| # 根据反馈动态调整冷静期 |
| cooldown = self.config.min_interval_seconds * (1 + feedback) |
| |
| def record_rejection(self, topic: str = ""): |
| """记录用户拒绝""" |
| now = time.time() |
| self.reject_log.append((topic, now)) |
| self.silence_until = now + self.config.silence_after_reject_hours * 3600 |
| ``` |
|
|
| --- |
|
|
| ## 4. 完整调度链路示例 |
|
|
| ```python |
| # 初始化各组件 |
| heartbeat = HeartbeatScheduler() |
| fast = FastSystem() |
| slow = SlowSystem() |
| scheduler = DualSystemScheduler(fast, slow) |
| freq = FrequencyController() |
| |
| # 心跳触发回调 |
| def on_heartbeat_tick(): |
| ctx = DecisionContext( |
| user_id="user_001", |
| idle_minutes=90, |
| time_of_day="night", |
| device_mode="normal", |
| last_conversation_topic="工作压力", |
| user_mood="anxious", |
| ) |
| |
| decision, score, system = scheduler.decide(ctx) |
| |
| if decision == Decision.SILENCE or decision == Decision.DEFER: |
| return # 不打扰 |
| |
| allowed, reason = freq.can_act(decision, topic=ctx.last_conversation_topic) |
| if not allowed: |
| return # 频率限制 |
| |
| freq.record_action(decision) |
| |
| # 执行主动发言(调用生成模块) |
| execute_action(decision, score, ctx) |
| |
| # 启动心跳 |
| heartbeat.set_callback(on_heartbeat_tick) |
| heartbeat.start() |
| ``` |
|
|
| --- |
|
|
| ## 5. 性能指标 |
|
|
| | 组件 | 响应时间 | 内存 | 适用场景 | |
| |------|---------|------|---------| |
| | 心跳调度器 | <1ms | 2KB | 决定何时检查 | |
| | 快系统规则引擎 | 5-50ms | 100KB | 80%的日常决策 | |
| | 慢系统LLM评估 | 500ms-5s | 动态加载 | 20%需要深度理解的场景 | |
| | 频率控制器 | <1ms | 10KB | 每次执行前检查 | |
|
|