feat: 改进标签系统 — 新增品类/费用/强度/预约标签,timeSlot 参与选活动
- IdeaCategory 扩展 7→9,新增 experience(体验)和 nature(自然) - 替换 outdoor boolean 为 costLevel/intensity/needsBooking 三个高价值字段 - AI 标注 prompt 同步更新,行程规划新增强度交替、费用平衡、预约提醒原则 - selectIdeasForSlots 重写为四优先级:timeSlot+category > category > timeSlot > 任意 - 前端想法卡片展示费用/强度/预约标签
This commit is contained in:
+22
-6
@@ -11,11 +11,15 @@ const TAG_SYSTEM_PROMPT = `你是一个周末活动分析助手。用户会输
|
||||
|
||||
返回字段:
|
||||
- category: 活动品类,必须是以下之一:
|
||||
"dining"(餐饮美食)| "outdoor"(户外活动)| "entertainment"(娱乐休闲,如电影、KTV、密室)| "shopping"(购物逛街)| "sports"(运动健身)| "culture"(文化艺术,如博物馆、展览)| "relaxation"(放松休息,如SPA、咖啡、下午茶)
|
||||
"dining"(餐饮美食)| "outdoor"(户外活动)| "entertainment"(娱乐休闲,如电影、KTV、密室)| "shopping"(购物逛街)| "sports"(运动健身)| "culture"(文化艺术,如博物馆、展览)| "relaxation"(放松休息,如SPA、咖啡、下午茶)| "experience"(体验活动,如手作、烘焙、插花、DIY)| "nature"(自然户外,如露营、徒步、赶海)
|
||||
- timeSlot: 最适合的时间段,必须是以下之一:
|
||||
"morning"(上午)| "afternoon"(下午)| "evening"(晚上)| "flexible"(任意时间都可以)| "all_day"(需要一整天)
|
||||
- estimatedMinutes: 预估活动时长(整数,单位分钟)
|
||||
- outdoor: 是否户外活动(布尔值)
|
||||
- costLevel: 费用等级,必须是以下之一:
|
||||
"free"(免费)| "budget"(人均<50元)| "moderate"(人均50-200元)| "premium"(人均>200元)
|
||||
- intensity: 体力强度,必须是以下之一:
|
||||
"chill"(轻松休闲)| "moderate"(适度活动)| "active"(需要体力)
|
||||
- needsBooking: 是否需要提前预约/购票(布尔值)
|
||||
- searchQuery: 在地图服务上搜索的关键词(品牌名、地名或品类名)
|
||||
- searchType: 搜索策略,必须是以下之一:
|
||||
"brand"(连锁品牌,有多个分店)| "place"(唯一地点,如某个公园)| "category"(模糊品类,搜附近匹配的)
|
||||
@@ -29,6 +33,9 @@ const SCHEDULE_SYSTEM_PROMPT = `你是一个周末行程规划师。根据用户
|
||||
2. 尊重活动的时间偏好(公园上午、正餐在饭点、电影灵活)
|
||||
3. 活动之间留出合理的交通时间(15-30分钟)
|
||||
4. 如果有"category"类型的活动,选择离其他已确定地点最近的候选
|
||||
5. 高低强度活动交替安排,避免连续高体力活动
|
||||
6. 费用平衡,避免连续安排 premium 级别活动
|
||||
7. 需要预约(needsBooking)的活动,在 reason 中提醒用户提前预约
|
||||
|
||||
返回 JSON 格式:
|
||||
{
|
||||
@@ -57,6 +64,9 @@ export interface ScheduleContext {
|
||||
estimatedMinutes: number;
|
||||
searchQuery: string;
|
||||
searchType: string;
|
||||
costLevel?: string;
|
||||
intensity?: string;
|
||||
needsBooking?: boolean;
|
||||
}[];
|
||||
candidates: Record<
|
||||
string,
|
||||
@@ -85,16 +95,20 @@ export async function tagIdea(content: string): Promise<IdeaTags | null> {
|
||||
|
||||
const parsed = JSON.parse(text);
|
||||
|
||||
const validCategories = ["dining", "outdoor", "entertainment", "shopping", "sports", "culture", "relaxation"];
|
||||
const validCategories = ["dining", "outdoor", "entertainment", "shopping", "sports", "culture", "relaxation", "experience", "nature"];
|
||||
const validTimeSlots = ["morning", "afternoon", "evening", "flexible", "all_day"];
|
||||
const validSearchTypes = ["brand", "place", "category"];
|
||||
const validCostLevels = ["free", "budget", "moderate", "premium"];
|
||||
const validIntensities = ["chill", "moderate", "active"];
|
||||
|
||||
if (
|
||||
!validCategories.includes(parsed.category) ||
|
||||
!validTimeSlots.includes(parsed.timeSlot) ||
|
||||
!validSearchTypes.includes(parsed.searchType) ||
|
||||
typeof parsed.estimatedMinutes !== "number" ||
|
||||
typeof parsed.outdoor !== "boolean" ||
|
||||
!validCostLevels.includes(parsed.costLevel) ||
|
||||
!validIntensities.includes(parsed.intensity) ||
|
||||
typeof parsed.needsBooking !== "boolean" ||
|
||||
typeof parsed.searchQuery !== "string"
|
||||
) {
|
||||
return null;
|
||||
@@ -104,7 +118,9 @@ export async function tagIdea(content: string): Promise<IdeaTags | null> {
|
||||
category: parsed.category,
|
||||
timeSlot: parsed.timeSlot,
|
||||
estimatedMinutes: parsed.estimatedMinutes,
|
||||
outdoor: parsed.outdoor,
|
||||
costLevel: parsed.costLevel,
|
||||
intensity: parsed.intensity,
|
||||
needsBooking: parsed.needsBooking,
|
||||
searchQuery: parsed.searchQuery,
|
||||
searchType: parsed.searchType,
|
||||
};
|
||||
@@ -162,7 +178,7 @@ export async function generateSchedule(
|
||||
用户出发位置:纬度 ${ctx.userLocation.lat},经度 ${ctx.userLocation.lng}
|
||||
|
||||
活动列表:
|
||||
${ctx.ideas.map((idea, i) => `${i + 1}. "${idea.content}"(品类:${idea.category},偏好时间:${idea.timeSlot},预估${idea.estimatedMinutes}分钟)`).join("\n")}
|
||||
${ctx.ideas.map((idea, i) => `${i + 1}. "${idea.content}"(品类:${idea.category},偏好时间:${idea.timeSlot},预估${idea.estimatedMinutes}分钟${idea.costLevel ? `,费用:${idea.costLevel}` : ""}${idea.intensity ? `,强度:${idea.intensity}` : ""}${idea.needsBooking ? ",需预约" : ""})`).join("\n")}
|
||||
|
||||
各活动的候选地点:
|
||||
${Object.entries(ctx.candidates)
|
||||
|
||||
Reference in New Issue
Block a user