feat: 优化AI文本识别的prompt
- 增强意图识别:明确区分纪念日和提醒的规则 - 扩展农历节日识别:添加更多传统节日 - 扩展公历节日识别:添加情人节、妇女节、儿童节等 - 优化重复规则识别逻辑 - 优化mock函数:更智能的日期解析和标题提取 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c36a56e0a4
commit
beedafc7d1
129
src/routes/ai.ts
129
src/routes/ai.ts
@ -25,11 +25,36 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
"response": "友好的确认消息"
|
||||
}
|
||||
|
||||
重要规则:
|
||||
## 意图识别规则(非常重要):
|
||||
|
||||
### 纪念日 vs 提醒 区分:
|
||||
- 如果用户提到"生日"、"纪念日"、"结婚纪念日"、"节日"、"春节"、"中秋"、"端午"、"元宵"等 → type = "anniversary"
|
||||
- 如果用户提到"提醒"、"任务"、"todo"、"待办"、"会议"、"约会"、"上课"等 → type = "reminder"
|
||||
- 如果不确定,优先识别为"reminder"(提醒更常用)
|
||||
|
||||
### 日期识别优先级:
|
||||
1. 农历日期:识别"农历正月初一"、"农历三月三"、"阴历八月十五"等 → is_lunar = true
|
||||
2. 节假日:春节、元宵节、龙抬头、清明、端午、七夕、中元节、中秋、重阳、寒衣节、腊八节、小年、除夕
|
||||
3. 相对日期:"明天"、"后天"、"下周"、"下个月"、"明年"
|
||||
4. 星期几:"周一"、"周二"... "周日"、"星期天"
|
||||
5. 具体日期:"3月8日"、"5月1日"、"10月1日"
|
||||
|
||||
### 时间识别:
|
||||
- "上午9点"、"下午3点"、"晚上8点"、"14:30" → 转换为对应小时
|
||||
- 如果没有指定具体时间,默认使用 09:00:00Z
|
||||
|
||||
### 重复规则识别:
|
||||
- "每天" → repeat_type = "daily"
|
||||
- "每周" → repeat_type = "weekly"
|
||||
- "每月" → repeat_type = "monthly"
|
||||
- "每年" → repeat_type = "yearly"
|
||||
- "不重复"、"仅一次" → repeat_type = "none"
|
||||
|
||||
## 重要规则:
|
||||
1. date 必须是用户提到的具体日期时间,格式为 ISO 8601: YYYY-MM-DDTHH:mm:ssZ
|
||||
2. 如果用户说"明天",date = (当前时间 + 1天) 的对应时间点
|
||||
3. 如果用户说"后天",date = (当前时间 + 2天) 的对应时间点
|
||||
4. 如果用户说"下周三",date = 下周三的 09:00:00Z
|
||||
4. 如果用户说"下周三",date = 下周三的 09:00:00Z(必须是真正的下周,不是本周)
|
||||
5. 如果用户说"下午3点",date = 今天下午15:00:00Z 或用户指定的日期的15:00:00Z
|
||||
6. 如果用户没有指定具体时间,默认使用当天的 09:00:00Z
|
||||
7. NEVER 使用示例日期如 2024-01-02T15:00:00Z,必须使用用户提到的实际日期或合理的未来日期
|
||||
@ -39,7 +64,8 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
农历日期识别:农历正月初一 -> 需要转换为公历日期
|
||||
|
||||
节假日识别(设置为 is_holiday=true):
|
||||
春节、清明、劳动节、国庆、元旦、中秋、端午
|
||||
- 公历节日:元旦、情人节、妇女节、儿童节、劳动节、建军节、教师节、国庆节、圣诞节
|
||||
- 农历节日:春节、元宵节、龙抬头、清明、端午、七夕、中元节、中秋、重阳、寒衣节、下元节、腊八节、小年、除夕
|
||||
|
||||
时区处理:
|
||||
- 如果用户在中国,使用 "Asia/Shanghai" 时区
|
||||
@ -49,7 +75,8 @@ const SYSTEM_PROMPT = `你是一个帮助用户创建事件(纪念日或提醒
|
||||
请务必:
|
||||
- 解析用户真实意图,不要编造日期
|
||||
- 如果用户说"下周三",必须是真正下周星期三,不是今天也不是上周
|
||||
- 如果用户没有明确日期,使用合理的默认日期(通常是明天或用户提到的第一个日期)`;
|
||||
- 如果用户没有明确日期,使用合理的默认日期(通常是明天或用户提到的第一个日期)
|
||||
- 标题要简洁准确,去除"提醒"、"帮我"等前缀`;
|
||||
|
||||
const parseMessageSchema = z.object({
|
||||
message: z.string().min(1, 'Message is required').max(1000),
|
||||
@ -187,17 +214,36 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
// Simple mock parser for testing
|
||||
const lowerMessage = message.toLowerCase();
|
||||
|
||||
// Detect if it's a reminder (contains "提醒", "提醒我", etc.)
|
||||
const isReminder = lowerMessage.includes('提醒') || lowerMessage.includes('提醒我');
|
||||
// 意图识别:纪念日 vs 提醒
|
||||
const isReminder = lowerMessage.includes('提醒') || lowerMessage.includes('提醒我') ||
|
||||
lowerMessage.includes('任务') || lowerMessage.includes('todo') ||
|
||||
lowerMessage.includes('待办') || lowerMessage.includes('会议') ||
|
||||
lowerMessage.includes('约会') || lowerMessage.includes('上课');
|
||||
|
||||
// 意图识别:纪念日
|
||||
const isAnniversary = lowerMessage.includes('生日') || lowerMessage.includes('纪念日') ||
|
||||
lowerMessage.includes('节日') || lowerMessage.includes('春节') ||
|
||||
lowerMessage.includes('中秋') || lowerMessage.includes('端午') ||
|
||||
lowerMessage.includes('元宵');
|
||||
|
||||
// 强制使用正确类型
|
||||
const eventType = isAnniversary ? 'anniversary' : (isReminder ? 'reminder' : 'reminder');
|
||||
|
||||
// Detect lunar date
|
||||
const isLunar = lowerMessage.includes('农历') || lowerMessage.includes('阴历');
|
||||
const isLunar = lowerMessage.includes('农历') || lowerMessage.includes('阴历') ||
|
||||
lowerMessage.includes('正月') || lowerMessage.includes('腊月');
|
||||
|
||||
// Detect holiday
|
||||
const isHoliday = lowerMessage.includes('春节') || lowerMessage.includes('清明') ||
|
||||
lowerMessage.includes('劳动节') || lowerMessage.includes('国庆') ||
|
||||
lowerMessage.includes('元旦') || lowerMessage.includes('中秋') ||
|
||||
lowerMessage.includes('端午');
|
||||
const isHoliday = lowerMessage.includes('春节') || lowerMessage.includes('元宵') ||
|
||||
lowerMessage.includes('清明') || lowerMessage.includes('端午') ||
|
||||
lowerMessage.includes('七夕') || lowerMessage.includes('中元') ||
|
||||
lowerMessage.includes('中秋') || lowerMessage.includes('重阳') ||
|
||||
lowerMessage.includes('腊八') || lowerMessage.includes('小年') ||
|
||||
lowerMessage.includes('除夕') || lowerMessage.includes('劳动节') ||
|
||||
lowerMessage.includes('国庆') || lowerMessage.includes('元旦') ||
|
||||
lowerMessage.includes('妇女节') || lowerMessage.includes('儿童节') ||
|
||||
lowerMessage.includes('建军节') || lowerMessage.includes('教师节') ||
|
||||
lowerMessage.includes('圣诞节') || lowerMessage.includes('情人节');
|
||||
|
||||
// Detect priority (color)
|
||||
let priority: 'none' | 'red' | 'green' | 'yellow' = 'none';
|
||||
@ -230,17 +276,40 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
repeatType = 'yearly';
|
||||
}
|
||||
|
||||
// Extract title (simple heuristic - first phrase before date or colon)
|
||||
let title = message.split(/[::]/)[0] || message;
|
||||
title = title.split(/[\s\d\u4e00-\u9fa5]+/)[0] || title.substring(0, 50);
|
||||
// Extract title - 去除"提醒"、"帮我"等前缀
|
||||
let title = message;
|
||||
// 去除常见的开头
|
||||
const prefixes = ['帮我', '提醒我', '提醒', '请帮我', '我要', '我想'];
|
||||
for (const prefix of prefixes) {
|
||||
if (title.startsWith(prefix)) {
|
||||
title = title.slice(prefix.length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 去除冒号后面的内容作为标题(保留标题)
|
||||
if (title.includes(':') || title.includes(':')) {
|
||||
title = title.split(/[::]/)[0];
|
||||
}
|
||||
// 去除日期相关文字
|
||||
title = title.replace(/[\d一二三四五六七八九十]+[月日号]/g, '').replace(/星期[一二三四五六日天]/g, '').trim();
|
||||
title = title.substring(0, 50) || title;
|
||||
|
||||
// Mock date (tomorrow at 3 PM local time by default for China UTC+8)
|
||||
const tomorrow = new Date();
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
tomorrow.setHours(15, 0, 0, 0); // 默认下午3点(中国时区)
|
||||
// Mock date (tomorrow at 9 AM local time by default for China UTC+8)
|
||||
const targetDate = new Date();
|
||||
targetDate.setDate(targetDate.getDate() + 1);
|
||||
targetDate.setHours(9, 0, 0, 0); // 默认上午9点(中国时区)
|
||||
|
||||
// 检测相对日期
|
||||
if (lowerMessage.includes('今天')) {
|
||||
targetDate.setDate(new Date().getDate());
|
||||
} else if (lowerMessage.includes('后天')) {
|
||||
targetDate.setDate(new Date().getDate() + 2);
|
||||
} else if (lowerMessage.includes('下周')) {
|
||||
targetDate.setDate(new Date().getDate() + 7);
|
||||
}
|
||||
|
||||
// 检测消息中的时间(如"下午3点"、"下午四点"、"14:30"等)
|
||||
const timePattern = message.match(/(?:下午|晚上)?(\d{1,2})(?::(\d{2}))?\s*(?:点|时)/);
|
||||
const timePattern = message.match(/(?:上午|中午|下午|晚上)?(\d{1,2})(?::(\d{2}))?\s*(?:点|时)/);
|
||||
if (timePattern) {
|
||||
let hours = parseInt(timePattern[1]);
|
||||
const minutes = timePattern[2] ? parseInt(timePattern[2]) : 0;
|
||||
@ -248,14 +317,18 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
if (message.includes('下午') || message.includes('晚上')) {
|
||||
if (hours < 12) hours += 12;
|
||||
}
|
||||
tomorrow.setHours(hours, minutes, 0, 0);
|
||||
// 中午时间
|
||||
if (message.includes('中午') && hours < 12) {
|
||||
hours = 12;
|
||||
}
|
||||
targetDate.setHours(hours, minutes, 0, 0);
|
||||
}
|
||||
|
||||
// Build parsed result
|
||||
const parsed: any = {
|
||||
type: isReminder ? 'reminder' : 'anniversary',
|
||||
type: eventType,
|
||||
title: title.trim(),
|
||||
date: tomorrow.toISOString(),
|
||||
date: targetDate.toISOString(),
|
||||
timezone: 'Asia/Shanghai', // 默认使用中国时区
|
||||
is_lunar: isLunar,
|
||||
repeat_type: repeatType,
|
||||
@ -265,10 +338,10 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
if (isHoliday) {
|
||||
parsed.is_holiday = true;
|
||||
}
|
||||
if (isReminder && priority !== 'none') {
|
||||
if (eventType === 'reminder' && priority !== 'none') {
|
||||
parsed.priority = priority;
|
||||
}
|
||||
if (isReminder && content) {
|
||||
if (eventType === 'reminder' && content) {
|
||||
parsed.content = content;
|
||||
}
|
||||
|
||||
@ -281,9 +354,11 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
none: '不重复'
|
||||
}[repeatType];
|
||||
|
||||
let responseMsg = `我帮你记的是:
|
||||
const typeText = eventType === 'anniversary' ? '纪念日' : '提醒';
|
||||
|
||||
let responseMsg = `我帮你创建的是:
|
||||
**${title.trim()}**
|
||||
📅 ${tomorrow.toLocaleDateString('zh-CN')}`;
|
||||
📅 ${targetDate.toLocaleDateString('zh-CN')} ${targetDate.getHours()}:${String(targetDate.getMinutes()).padStart(2, '0')}`;
|
||||
if (repeatType !== 'none') {
|
||||
responseMsg += `\n🔄 ${repeatText}`;
|
||||
}
|
||||
@ -293,7 +368,7 @@ function mockParseResponse(message: string): { parsed: any; response: string } {
|
||||
if (isHoliday) {
|
||||
responseMsg += `\n🎉 节假日`;
|
||||
}
|
||||
if (isReminder && priority !== 'none') {
|
||||
if (eventType === 'reminder' && priority !== 'none') {
|
||||
const colorText = { red: '红色', green: '绿色', yellow: '黄色', none: '无' };
|
||||
responseMsg += `\n🏷️ ${colorText[priority]}`;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user