import 'dart:convert'; enum Intent { createEvent, searchEvent, unknown } class AiDecisionEngine { /// 意图匹配规则(顺序敏感:searchEvent 优先级高于 createEvent) static final List<(RegExp, Intent)> _orderedPatterns = [ (RegExp(r'^查看|^有什么|^今天.*日程|^明天.*安排|^查询'), Intent.searchEvent), (RegExp(r'提醒|开会|预约|安排.*时间|创建.*日程'), Intent.createEvent), ( RegExp(r'明天.*\d|今天.*\d|后天.*\d|\d{1,2}点|\d{1,2}:\d{2}'), Intent.createEvent, ), ]; Intent matchIntent(String text) { for (final (pattern, intent) in _orderedPatterns) { if (pattern.hasMatch(text)) return intent; } return Intent.unknown; } Map? tryExtractEventArgs(String text) { if (matchIntent(text) != Intent.createEvent) return null; final args = {}; final titleMatch = RegExp(r'提醒(.+?)(?:明天|今天|几点|$)').firstMatch(text); if (titleMatch != null) { args['title'] = titleMatch.group(1)?.trim() ?? text; } else if (RegExp(r'\d{1,2}[:点]|\d{1,2}点').hasMatch(text)) { args['title'] = text .replaceAll(RegExp(r'\d{1,2}[:点]\d{0,2}|明天|今天|后天'), '') .trim(); } if (args['title'] == null || (args['title'] as String).isEmpty) return null; final timeMatch = RegExp( r'(明天|今天|后天)?\s*(\d{1,2})[:点](\d{2})?', ).firstMatch(text); if (timeMatch != null) { final dayStr = timeMatch.group(1) ?? '今天'; final hour = int.parse(timeMatch.group(2)!); final minute = int.parse(timeMatch.group(3) ?? '0'); final now = DateTime.now(); DateTime startAt; switch (dayStr) { case '明天': startAt = DateTime(now.year, now.month, now.day + 1, hour, minute); break; case '后天': startAt = DateTime(now.year, now.month, now.day + 2, hour, minute); break; default: startAt = DateTime(now.year, now.month, now.day, hour, minute); } args['startAt'] = startAt.toIso8601String(); args['timezone'] = 'Asia/Shanghai'; } if (!args.containsKey('startAt')) return null; return args; } bool shouldTriggerToolCall(String text) => matchIntent(text) == Intent.createEvent; Map? getToolCallArgs(String text) { if (!shouldTriggerToolCall(text)) return null; return tryExtractEventArgs(text); } ForceTriggerResult? tryForceTrigger(String text) { final match = RegExp(r'#tool:(\w+)\s*(\{.*\})?').firstMatch(text); if (match == null) return null; final toolName = match.group(1)!; final argsJson = match.group(2); Map? args; if (argsJson != null) { try { args = jsonDecode(argsJson) as Map; } catch (_) { args = {}; } } return ForceTriggerResult(toolName: toolName, args: args ?? {}); } } class ForceTriggerResult { final String toolName; final Map args; ForceTriggerResult({required this.toolName, required this.args}); }