gogo-agent

主智能体功能介绍与开发

MasterAgent 是整个智能差旅系统的总协调者(Orchestrator)。它本身是一个 ReActAgent,但不直接干"查政策、订机票、提审批"这类脏活,而是把各个专业子智能体(ItineraryManageAgent /…

TL;DR

MasterAgent 是整个智能差旅系统的总协调者(Orchestrator)。它本身是一个 ReActAgent,但不直接干"查政策、订机票、提审批"这类脏活,而是把各个专业子智能体(ItineraryManageAgent /…

MasterAgent 是整个智能差旅系统的总协调者(Orchestrator)。它本身是一个 ReActAgent,但不直接干"查政策、订机票、提审批"这类脏活,而是把各个专业子智能体(ItineraryManageAgent / ItineraryPlanAgent / InfoAgent / BookingAgent)注册成自己的工具,通过 LLM 的推理决定"当前这句话该交给谁处理",再把子智能体的产出整合成统一、连贯的回复返回给用户。 一句话概括它的定位:对上是用户的入口,对下是子智能体的调度中枢。 MasterAgent 处在请求链路的中后段。用户消息先经过"问题改写"和"意图识别"两级预处理,再进入 MasterAgent: 并非所有请求都会走 MasterAgent。当意图识别结果是"单意图 + 高置信 + 命中白名单"时,Pipeline 会走 tryPlanDirectDispatch 快路径直接命中某个子智能体,省掉 MasterAgent 一层 LLM 推理开销;只有多意图、低置信、或需要跨子智能体协调时才交给 MasterAgent。MasterAgent 是"兜底 + 复杂编排"的那条路径。

实现剖析

MasterAgent 用 Spring @Configuration + @Bean(name = "masterAgent") + @Scope("prototype") 定义。prototype 是刻意为之——每次请求都要根据当前会话上下文(用户是谁、哪个 session)重新构建一个 agent 实例,不能单例复用。

会话上下文

AgentSessionContext sessionCtx = AgentSessionContextHolder.get();
String userId = sessionCtx != null ? sessionCtx.getUserId() : null;

ToolExecutionContext masterToolCtx = sessionCtx != null
        ? ToolExecutionContext.builder().register(sessionCtx).build()
        : ToolExecutionContext.empty();

AgentSessionContextHolder 用的是 TransmittableThreadLocal,能自动把上下文传播到 Reactor 的 boundedElastic 线程池,所以子智能体经由 SubAgentProvider 懒加载时无需手动重新 set。这个 sessionCtx 随后被注册进 ToolExecutionContext,所有工具方法都能透明读到。

✅使用TTL实现多智能体之间的上下文传递

在我们的项目中,有一些参数,需要全局传递,比如userId,从用户一进来就确定了,我们需要在多个智能体之间传递、并且在tools、hook中也可能需要用到,那么如何实现这个参数的传递呢。 gogo-agent 用 Transmittable LLMentor

工具装配:子智能体即工具(Agent-as-Tool)

这是 MasterAgent 最核心的部分。除了两个内置工具(InfoQueryTools 通用信息查询、UserInteractionTools 主动提问),它把 4 个子智能体用 SubAgentProvider + SubAgentConfig 注册成可被 LLM 调用的工具:

toolkit.registration()
    .subAgent((SubAgentProvider<ReActAgent>) () ->
                    context.getBean("itineraryManageAgent", ReActAgent.class),
        SubAgentConfig.builder()
            .toolName("itinerary_manage_agent")
            .description("行程单全生命周期管理:收集信息、提交审批、查询差旅单/审批状态、取消/修改申请。参数:message(任务描述),可选 session_id(继续会话)。")
            .forwardEvents(false)
            .build())
    .apply();
// info_agent / itinerary_plan_agent / booking_agent 同理

几个要点: - 懒加载:子智能体通过 SubAgentProvider 的 lambda 在真正被调用时才从 Spring 容器 getBean 取出,配合 prototype 作用域保证每次拿到的都是带当前会话上下文的新实例。 - 跨 Agent 传参靠自然语言:子智能体作为工具,入参只有 message(任务描述)和可选 session_id。父 Agent 的 LLM 把意图序列化成自然语言 message 传下去,

模型选择:强模型推理 + 稳定模型压缩

.model(strongModel)                              // 主推理用强模型
.memory(AgentMemoryFactory.create(stableModel))  // 历史压缩/摘要用稳定模型

调度决策是"脑力活",用 strongModel;而记忆压缩/摘要是后台辅助,用更便宜稳定的 stableModel,成本与效果分离。

Hook 链:横切能力的挂载点

.hooks(List.of(new AutoContextHook(), executionLoggerHook, progressNotifierHook,
        sessionPersistenceHook, activeAgentPersistenceHook, new PendingToolRecoveryHook()))
Hook 作用
AutoContextHook 配合 AutoContextMemory,实际执行压缩/卸载
AgentExecutionLoggerHook 记录 Agent 执行轨迹,便于排障
ProgressNotifierHook 向前端推送执行进度
SessionPersistenceHook
ActiveAgentPersistenceHook 每轮推理前记录当前 session 的活跃 Agent
PendingToolRecoveryHook 为中断/孤立的 ToolUse 注入错误恢复,保证 HITL 稳定

执行配置与缓存

.toolExecutionConfig(ExecutionConfig.builder()
        .timeout(Duration.ofMinutes(15)).maxAttempts(3).build())  // 工具超时15分钟、重试3次
.maxIters(15)                                                     // 最多15轮推理
.generateOptions(GenerateOptions.builder().cacheControl(true).build())  // 开启system+末条消息缓存

子智能体的一次调用可能很耗时(比如规划要搜航班、比价),所以工具超时给到 15 分钟、允许重试 3 次;推理上限 15 轮防止死循环。 cacheControl(true) 让百炼对 system messages 和最后一条消息自动加 cache_control,显著降低多轮长 system prompt 的重复计费。

行为约束:全部写在系统提示词里

MasterAgent 的"性格"由 master-agent-system.md 定义,几条硬规则值得强调: - 禁止重复改写/识别意图:上游已经做好,直接用,避免重复劳动和不一致。 - 禁止在路由前向用户提问:先分派再说,ask_user 只在自己直接处理 greeting/unknown、或子智能体返回后需确认时才用。 - 结果优先透传:子智能体返回的完整方案(含确认问句)原样透传,禁止用自己生成的内容覆盖,尤其是审批摘要、行程方案这类含关键细节的内容。 - 内部名称脱敏:面向用户的文案里绝不出现 snake_case 的工具名/子智能体名/字段名,遗漏的要改写成中文自然语。

版本提示

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

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

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