feat: 行程卡片间显示交通路线描述和时间
- get_travel_time 解析 Amap segments 提取线路名和站数 - PlanItem 新增 transitToNext / transitDescription 字段 - finalize_plan schema 加入 transit_to_next_description - 修复 Turbopack 中文引号解析报错 - UI 连接器改为两行布局,路线描述与时长分行显示
This commit is contained in:
@@ -110,6 +110,34 @@ function selectIdeasForSlots(ideas: TaggedIdea[], availableHours: number): Tagge
|
||||
return selected;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Transit segment parser
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function parseTransitSegments(
|
||||
segments: Record<string, unknown>[],
|
||||
): { description: string; mode: string } {
|
||||
const parts: string[] = [];
|
||||
let hasSubway = false;
|
||||
let hasBus = false;
|
||||
|
||||
for (const seg of segments) {
|
||||
const bus = seg.bus as { buslines?: Record<string, unknown>[] } | undefined;
|
||||
if (!bus?.buslines?.length) continue;
|
||||
for (const line of bus.buslines) {
|
||||
const name = String(line.name ?? "");
|
||||
const viaNum = Number(line.via_num ?? 0);
|
||||
const isSubway = String(line.type ?? "").includes("地铁") || String(line.type ?? "").includes("轨道");
|
||||
if (isSubway) hasSubway = true; else hasBus = true;
|
||||
parts.push(viaNum > 0 ? `${name}(${viaNum}站)` : name);
|
||||
}
|
||||
}
|
||||
|
||||
const mode = hasSubway ? "地铁" : hasBus ? "公交" : "步行";
|
||||
const description = parts.length > 0 ? parts.join(" → ") : mode;
|
||||
return { description, mode };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POI search (shared by both paths)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -205,7 +233,7 @@ const AGENT_SYSTEM_PROMPT = `你是一个周末行程规划 Agent。你有以下
|
||||
4. 如果搜索结果不理想(0结果或不相关),尝试换关键词重搜
|
||||
5. 用 get_travel_time 检查关键路段的公共交通时间,如果某段超过 45 分钟考虑换更近的地点
|
||||
6. 综合地理位置、时间安排、活动特点,规划最优路线
|
||||
7. 确认无误后 finalize_plan 提交
|
||||
7. 确认无误后 finalize_plan 提交,每个活动填写 transit_to_next_minutes 和 transit_to_next_description(直接复制 get_travel_time 返回的 durationMin 和 description,最后一项不填)
|
||||
|
||||
规划原则:
|
||||
- 地理位置相近,最小化移动距离
|
||||
@@ -229,6 +257,8 @@ interface FinalizePlanDay {
|
||||
lng: number;
|
||||
duration: number;
|
||||
reason: string;
|
||||
transit_to_next_minutes?: number;
|
||||
transit_to_next_description?: string;
|
||||
}[];
|
||||
summary: string;
|
||||
}
|
||||
@@ -333,10 +363,10 @@ function buildAgentTools(
|
||||
return JSON.stringify({ error: "未找到公交路线" });
|
||||
}
|
||||
const transit = data.route.transits[0];
|
||||
return JSON.stringify({
|
||||
distanceKm: Math.round(Number(data.route.distance) / 100) / 10,
|
||||
durationMin: Math.ceil(Number(transit.duration) / 60),
|
||||
});
|
||||
const durationMin = Math.ceil(Number(transit.duration) / 60);
|
||||
const distanceKm = Math.round(Number(data.route.distance) / 100) / 10;
|
||||
const { description, mode } = parseTransitSegments(transit.segments ?? []);
|
||||
return JSON.stringify({ durationMin, distanceKm, description, mode });
|
||||
} catch {
|
||||
return JSON.stringify({ error: "路线查询失败" });
|
||||
}
|
||||
@@ -345,7 +375,7 @@ function buildAgentTools(
|
||||
progressAfter: (_args, result) => {
|
||||
const r = JSON.parse(result);
|
||||
if (r.error) return `路线查询失败`;
|
||||
return `公共交通约 ${r.durationMin} 分钟(${r.distanceKm}km)`;
|
||||
return `${r.description} 约 ${r.durationMin} 分钟(${r.distanceKm}km)`;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -374,6 +404,14 @@ function buildAgentTools(
|
||||
lng: { type: "number" },
|
||||
duration: { type: "number", description: "时长(分钟)" },
|
||||
reason: { type: "string", description: "选择理由" },
|
||||
transit_to_next_minutes: {
|
||||
type: "number",
|
||||
description: "从本活动结束后到下一活动的预计交通时间(分钟)。最后一个活动不填",
|
||||
},
|
||||
transit_to_next_description: {
|
||||
type: "string",
|
||||
description: "到下一活动的交通方式描述,直接使用 get_travel_time 返回的 description 字段值,例如:地铁2号线(6站) → 地铁9号线(3站)。最后一个活动不填",
|
||||
},
|
||||
},
|
||||
required: ["time", "activity", "poi", "address", "lat", "lng", "duration", "reason"],
|
||||
},
|
||||
@@ -440,6 +478,14 @@ async function runAgentPlanGeneration(
|
||||
lng: Number(item.lng) || 0,
|
||||
duration: Number(item.duration) || 60,
|
||||
reason: String(item.reason ?? ""),
|
||||
...(item.transit_to_next_minutes != null && Number(item.transit_to_next_minutes) > 0
|
||||
? {
|
||||
transitToNext: Math.round(Number(item.transit_to_next_minutes)),
|
||||
...(item.transit_to_next_description
|
||||
? { transitDescription: String(item.transit_to_next_description) }
|
||||
: {}),
|
||||
}
|
||||
: {}),
|
||||
}))
|
||||
: [],
|
||||
summary: String(day.summary ?? ""),
|
||||
|
||||
Reference in New Issue
Block a user