fix: 优化AI解析,智能提取标题和内容
- 改进标题提取:去除"提醒"、"帮我"等前缀,保留事件名称 - 改进内容提取:提取日期时间后的描述性文字 - 添加优先级识别:红色、绿色、黄色标记 - 添加提前提醒识别:提前X天/小时/分钟提醒 - 更新SYSTEM_PROMPT:指导AI正确提取各字段 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
beedafc7d1
commit
1ff0693901
129
src/routes/ai.ts
129
src/routes/ai.ts
@ -16,11 +16,14 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
{
|
||||
"parsed": {
|
||||
"type": "anniversary 或 reminder",
|
||||
"title": "事件标题",
|
||||
"title": "事件标题(简洁准确,去除"提醒"、"帮我"等前缀)",
|
||||
"content": "详细内容(仅提醒类型,如会议主题、待办事项等)",
|
||||
"date": "2026-02-13T15:00:00Z",
|
||||
"timezone": "Asia/Shanghai",
|
||||
"is_lunar": true 或 false,
|
||||
"repeat_type": "daily, weekly, monthly, yearly 或 none"
|
||||
"repeat_type": "daily, weekly, monthly, yearly 或 none",
|
||||
"priority": "none, red, green 或 yellow(仅提醒,red=重要紧急,green=绿色,yellow=黄色)",
|
||||
"reminder_times": ["提前提醒时间点数组,如 2026-02-12T09:00:00Z"](仅提醒)
|
||||
},
|
||||
"response": "友好的确认消息"
|
||||
}
|
||||
@ -32,6 +35,18 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
- 如果用户提到"提醒"、"任务"、"todo"、"待办"、"会议"、"约会"、"上课"等 → type = "reminder"
|
||||
- 如果不确定,优先识别为"reminder"(提醒更常用)
|
||||
|
||||
### 标题提取规则:
|
||||
- 标题应该简洁,只包含事件名称
|
||||
- 去除"提醒我"、"帮我"、"请帮我"等开头
|
||||
- 去除日期、时间等描述性文字
|
||||
- 例如:"提醒我明天上午9点开会" → title = "开会"
|
||||
- 例如:"提醒我春节回家" → title = "春节回家"
|
||||
|
||||
### 内容提取规则(仅提醒类型):
|
||||
- content 应该包含事件的详细内容
|
||||
- 提取日期时间之后剩余的描述性文字
|
||||
- 例如:"提醒我明天上午9点开会讨论项目进度" → content = "讨论项目进度"
|
||||
|
||||
### 日期识别优先级:
|
||||
1. 农历日期:识别"农历正月初一"、"农历三月三"、"阴历八月十五"等 → is_lunar = true
|
||||
2. 节假日:春节、元宵节、龙抬头、清明、端午、七夕、中元节、中秋、重阳、寒衣节、腊八节、小年、除夕
|
||||
@ -50,6 +65,17 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
- "每年" → repeat_type = "yearly"
|
||||
- "不重复"、"仅一次" → repeat_type = "none"
|
||||
|
||||
### 优先级识别(仅提醒):
|
||||
- "重要"、"紧急"、"红色" → priority = "red"
|
||||
- "绿色" → priority = "green"
|
||||
- "黄色" → priority = "yellow"
|
||||
- 无优先级描述 → priority = "none"
|
||||
|
||||
### 提前提醒识别:
|
||||
- "提前1天提醒" → reminder_times = [eventDate - 1天]
|
||||
- "提前2小时提醒" → reminder_times = [eventDate - 2小时]
|
||||
- "提前30分钟提醒" → reminder_times = [eventDate - 30分钟]
|
||||
|
||||
## 重要规则:
|
||||
1. date 必须是用户提到的具体日期时间,格式为 ISO 8601: YYYY-MM-DDTHH:mm:ssZ
|
||||
2. 如果用户说"明天",date = (当前时间 + 1天) 的对应时间点
|
||||
@ -255,15 +281,6 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
priority = 'yellow';
|
||||
}
|
||||
|
||||
// Detect content (after colon or special markers)
|
||||
let content = '';
|
||||
if (message.includes(':') || message.includes(':')) {
|
||||
const parts = message.split(/[::]/);
|
||||
if (parts.length > 1) {
|
||||
content = parts[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Detect repeat type
|
||||
let repeatType: 'daily' | 'weekly' | 'monthly' | 'yearly' | 'none' = 'none';
|
||||
if (lowerMessage.includes('每天') || lowerMessage.includes('每日')) {
|
||||
@ -276,23 +293,88 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
repeatType = 'yearly';
|
||||
}
|
||||
|
||||
// Extract title - 去除"提醒"、"帮我"等前缀
|
||||
// Detect reminder times (提前提醒)
|
||||
let reminderTimes: string[] = [];
|
||||
// 提前X天提醒
|
||||
const daysMatch = lowerMessage.match(/提前(\d+)天/);
|
||||
if (daysMatch) {
|
||||
const days = parseInt(daysMatch[1]);
|
||||
const reminderDate = new Date(targetDate);
|
||||
reminderDate.setDate(reminderDate.getDate() - days);
|
||||
reminderTimes.push(reminderDate.toISOString());
|
||||
}
|
||||
// 提前X小时提醒
|
||||
const hoursMatch = lowerMessage.match(/提前(\d+)小时?/);
|
||||
if (hoursMatch) {
|
||||
const hours = parseInt(hoursMatch[1]);
|
||||
const reminderDate = new Date(targetDate);
|
||||
reminderDate.setHours(reminderDate.getHours() - hours);
|
||||
reminderTimes.push(reminderDate.toISOString());
|
||||
}
|
||||
// 提前X分钟提醒
|
||||
const minutesMatch = lowerMessage.match(/提前(\d+)分钟/);
|
||||
if (minutesMatch) {
|
||||
const minutes = parseInt(minutesMatch[1]);
|
||||
const reminderDate = new Date(targetDate);
|
||||
reminderDate.setMinutes(reminderDate.getMinutes() - minutes);
|
||||
reminderTimes.push(reminderDate.toISOString());
|
||||
}
|
||||
// 默认提醒(不提前)
|
||||
if (reminderTimes.length === 0) {
|
||||
reminderTimes = [];
|
||||
}
|
||||
|
||||
// Extract title - 智能提取标题
|
||||
let title = message;
|
||||
let extractedContent = '';
|
||||
|
||||
// 去除常见的开头
|
||||
const prefixes = ['帮我', '提醒我', '提醒', '请帮我', '我要', '我想'];
|
||||
const prefixes = ['帮我', '提醒我', '提醒', '请帮我', '我要', '我想', '帮我提醒'];
|
||||
for (const prefix of prefixes) {
|
||||
if (title.startsWith(prefix)) {
|
||||
if (title.toLowerCase().startsWith(prefix)) {
|
||||
title = title.slice(prefix.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 去除冒号后面的内容作为标题(保留标题)
|
||||
if (title.includes(':') || title.includes(':')) {
|
||||
title = title.split(/[::]/)[0];
|
||||
|
||||
// 处理"提醒XXX提醒时间YYY"这种格式 - 提取标题和内容
|
||||
// 查找最后一个"提醒"或"在"后面的内容作为内容
|
||||
const reminderMatch = message.match(/(?:提醒|在|于)\s*(.+)$/);
|
||||
if (reminderMatch && reminderMatch[1]) {
|
||||
extractedContent = reminderMatch[1].trim();
|
||||
}
|
||||
// 去除日期相关文字
|
||||
title = title.replace(/[\d一二三四五六七八九十]+[月日号]/g, '').replace(/星期[一二三四五六日天]/g, '').trim();
|
||||
title = title.substring(0, 50) || title;
|
||||
|
||||
// 去除冒号后面的内容
|
||||
if (title.includes(':') || title.includes(':')) {
|
||||
const colonParts = title.split(/[::]/);
|
||||
title = colonParts[0].trim();
|
||||
if (!extractedContent && colonParts.length > 1) {
|
||||
extractedContent = colonParts.slice(1).join(':').trim();
|
||||
}
|
||||
}
|
||||
|
||||
// 去除日期和时间相关文字
|
||||
// 去除"明天"、"后天"、"下周"等相对日期
|
||||
title = title.replace(/(?:明天|后天|今天|下周|下个月|明年|今年|去年)/g, '');
|
||||
// 去除具体日期如"3月8日"、"2024年5月1日"
|
||||
title = title.replace(/\d{1,4}[年/-]\d{1,2}[月/-]\d{1,2}[日号]?/g, '');
|
||||
title = title.replace(/[\d一二三四五六七八九十]+[月日号]/g, '');
|
||||
// 去除星期几
|
||||
title = title.replace(/星期[一二三四五六日天]/g, '');
|
||||
title = title.replace(/周一|周二|周三|周四|周五|周六|周日/g, '');
|
||||
// 去除时间如"上午9点"、"下午3点"、"14:30"
|
||||
title = title.replace(/(?:上午|中午|下午|晚上)?\s*\d{1,2}(?::\d{2})?\s*(?:点|时)/g, '');
|
||||
// 去除"在"、"于"等介词
|
||||
title = title.replace(/^[在于是于]\s*/g, '');
|
||||
// 去除多余空格
|
||||
title = title.trim();
|
||||
|
||||
// 如果标题为空或太短,使用原始消息
|
||||
if (title.length < 2) {
|
||||
title = message.substring(0, 30);
|
||||
}
|
||||
// 限制标题长度
|
||||
title = title.substring(0, 50);
|
||||
|
||||
// Mock date (tomorrow at 9 AM local time by default for China UTC+8)
|
||||
const targetDate = new Date();
|
||||
@ -341,8 +423,11 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
if (eventType === 'reminder' && priority !== 'none') {
|
||||
parsed.priority = priority;
|
||||
}
|
||||
if (eventType === 'reminder' && content) {
|
||||
parsed.content = content;
|
||||
if (eventType === 'reminder' && extractedContent) {
|
||||
parsed.content = extractedContent;
|
||||
}
|
||||
if (eventType === 'reminder' && reminderTimes.length > 0) {
|
||||
parsed.reminder_times = reminderTimes;
|
||||
}
|
||||
|
||||
// Generate response
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user