gogo-agent

行程方案设计工具设计细节

plan itinerary,是行程规划的"计算大脑",承担了最核心的数学逻辑。 ✅为什么行程方案设计不用Skill要用工具? 整个行程规划流程分为两个阶段,恰好对应使用了2种能力: 数据收集天然适合 Skill:调用外部 API …

TL;DR

plan itinerary,是行程规划的"计算大脑",承担了最核心的数学逻辑。 ✅为什么行程方案设计不用Skill要用工具? 整个行程规划流程分为两个阶段,恰好对应使用了2种能力: 数据收集天然适合 Skill:调用外部 API …

plan_itinerary,是行程规划的"计算大脑",承担了最核心的数学逻辑。

✅为什么行程方案设计不用Skill要用工具?

整个行程规划流程分为两个阶段,恰好对应使用了2种能力: 数据收集天然适合 Skill:调用外部 API 本质就是按照文档拼接命令参数,LLM 擅长理解"搜索上海到杭州的航班"然后映射到正确的 CLI 命令。 但方案计算为什么不能也用 Sk LLMentor

设计哲学

LLM 负责"主观"(偏好判断、打分) 工具负责"客观"(组合、归一化、排序、存储) 这种 LLM/Tool 职责分离是关键设计决策:如果让 LLM 自己做组合排序,它无法保证数值正确性(容易算错价格、遗漏组合);如果让工具理解偏好文本,又需要额外 NLP 或嵌入模型。

参数设计

@Tool(name = "plan_itinerary",
      description = "往返行程规划客观计算引擎:输入候选交通/酒店 + 你(LLM)预先产出的偏好分,"
                  + "在内存里做去×住×返组合、过滤非法组合、算总价/总耗时并归一化、做 policy 软约束检查、"
                  + "按三维权重算综合分,选出 4 类代表方案,将结果存入 Redis 并返回摘要。"
                  + "本工具只做客观数学,绝不解读偏好文本;偏好判断必须由你在 scores 参数里给出。")
public String planItinerary(
    @ToolParam(required = true)  String origin,          // 出发城市
    @ToolParam(required = true)  String destination,     // 目的城市
    @ToolParam(required = true)  String departure_date,  // 去程日期 YYYY-MM-DD
    @ToolParam(required = true)  String return_date,     // 返程日期 YYYY-MM-DD
    @ToolParam(required = false) String preferences,     // 用户偏好自由文本(来自 LTM)
    @ToolParam(required = true)  String candidates,      // 候选 JSON
    @ToolParam(required = true)  String scores,          // LLM 打的偏好分 JSON
    @ToolParam(required = false) String policy,          // 差旅政策 JSON
    AgentSessionContext sessionCtx                       // 透明注入,LLM 不感知
) { ... }

注意 sessionCtx 通过 ToolExecutionContext 自动注入,无 @ToolParam 注解——LLM 不需要知道它的存在。

计算流水线

核心计算代码

// 组合过滤:返程不能早于去程到达
private JSONObject tryBuildCombo(JSONObject outbound, JSONObject hotel, JSONObject inbound) {
    LocalDateTime outArr = parseDt(outbound, "arrival_time");
    LocalDateTime inDep  = parseDt(inbound,  "departure_time");
    if (inDep.isBefore(outArr)) return null;  // 非法组合,直接丢弃

    int totalTransit = durationMinutes(outbound) + durationMinutes(inbound);
    int nights = (int) ChronoUnit.DAYS.between(
            LocalDate.parse(departure_date), LocalDate.parse(return_date));
    double totalPrice = getDbl(outbound,"price") + getDbl(inbound,"price")
                      + getDbl(hotel,"price_per_night") * nights;
    // checkPolicy: 软约束打分,不删除组合
    PolicyResult pr = checkPolicy(totalPrice, hotel, outbound, inbound, policy);
    // ... assemble combo JSON ...
}

// Min-Max 归一化(越低越好 → higherIsBetter=false)
private double minmaxScore(double value, double min, double max, boolean higherIsBetter) {
    if (max == min) return 50.0;  // 所有候选相同时给中性分
    double normalized = (value - min) / (max - min);
    return higherIsBetter ? normalized * 100 : (1 - normalized) * 100;
}

// 三维加权综合分
double overall = round2(time_score * 0.3 + price_score * 0.2 + preference_score * 0.5);
版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。