1.子智能体SubAgent

子智能体是指被父级 Agent 当作"工具"来调用的下层 Agent。它不是普通的函数调用,而是一个具备独立推理能力的Agent,有自己的 prompt、tools、skills,能自主完成父 Agent 分配的专项任务。这本质上就是 AI Agent 领域的服务化拆分 —— 和微服务把单体拆成多个独立服务是同一个思想。

子智能体创建,现在我们的行程规划智能体下有一个子智能体景点推荐作为工具:

@Component
public class SuggestSightAgent {
    @Resource
    private AgentUtils agentUtils;

    public ReActAgent getSuggestSightAgent() {
        Toolkit toolkit = new Toolkit();
        //构建Skill,并将工具包和Skill结合
        SkillBox skillBox = new SkillBox(toolkit);

        //以文件形式读取Skill.md
        JarSkillRepositoryAdapter repo = null;
        try {
            repo = new JarSkillRepositoryAdapter(
                    "skills"
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //景点推荐技能
        AgentSkill SuggestSightsSkill = repo.getSkill("Suggest-Sights");
        
        //AgentSkill MakeATableSkill = repo.getSkill("Make-A-Table");
        skillBox.registerSkill(SuggestSightsSkill);
        //skillBox.registerSkill(MakeATableSkill);
        //注册工具
        skillBox.registration().tool(new Calculate());
        ReActAgent.Builder builder = agentUtils.getReActAgentBuilder(
                        "SuggestSightAgent",
                        "擅长景点推荐"
                )
                //挂载工具包
                .toolkit(toolkit)
                //挂载Skills
                .skillBox(skillBox);

        return builder.build();
    }
}

接着在主智能体当中把子智能体作为工具导入:

@Component
public class TripPlannerAgent {

    @Resource
    private AgentUtils agentUtils;


    @Bean
    public ReActAgent getTripPlannerAgent() throws Exception {
        Toolkit toolkit = new Toolkit();
        SuggestSightAgent suggestSightAgent = new SuggestSightAgent();

        //将子智能体作为工具
        toolkit.registration().subAgent(
                suggestSightAgent::getSuggestSightAgent
        ).apply();
        //构建skill ,将工具包和skill结合

        return agentUtils.getReActAgentBuilder(
                        "TripPlannerAgent",
                        "擅长处理景点行程规划"
                )
                //挂载工具包
                .toolkit(toolkit)
                .build();
    }
}

在工具包注册方法源码中就有这个段代码解释:

 public ToolRegistration subAgent(SubAgentProvider<?> provider) {
            return this.subAgent(provider, (SubAgentConfig)null);
        }

2.Skill配置

在resources文件下,创建这个目录:

.py是python的脚本

SKILL.md文件的头部写入:

---
name: Make-A-Table
description: 擅长在有限预算内规划出精彩的旅行体验
---

name属性必须和目录名字一样。

以另一个skill为主,skill的主要内容:

---
name: Suggest-Sights
description: 擅长在有限预算内规划出精彩的旅行体验
---

## 核心能力

### 1. 免费景点推荐

- 推荐自然风光、历史古迹、特色街区
- 提供最佳游览时间和小众路线
- 分享避开人群的技巧

### 2. 美食探索

- 推荐当地特色小吃和地道餐厅
- 侧重性价比高的本地美食
- 分享当地人常去的隐瞒美食店

### 3. 打卡点推荐

- 推荐网红打卡点和拍照圣地
- 分享小众但出片的秘密景点

### 4. 住宿建议

提供住宿方案:
- **经济型**:青旅、民宿
- **舒适型**:连锁酒店

## 工作流程

1. **分析规划**:综合考虑距离、时间、费用、景点分布因素
2. **方案输出**:提供景点推荐、美食指南、住宿方案

## 回答原则

1. **务实优先**:所有建议要考虑实际可行性和经济性
2. **信息准确**:提供具体的地址、价格区间、开放时间
3. **因地制宜**:根据季节、天气给出适当的建议

skillbox即可以注册工具也可以加载技能,而且主智能体不需要挂载skill。

行程规划主智能体下面有一个景点推荐子智能体挂载了景点推荐的skill。

3.结构化输出

让语言大模型返回类型,创建一个相应格式(必须要有一个无参构造函数):

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseSchema {
    private String response;

}
return agentUtils.getReActAgentBuilder("ManagerAgent", "主管Agent").planNotebook(planNotebook)
                //拦截器
                .hook(new PlanHook(planNotebook))
                .toolkit(toolkit)
                //结构化输出
                .structuredOutputReminder(StructuredOutputReminder.PROMPT)
                .build();

让它自动匹配我们规定要返回的格式。老模型是不支持的

4.Agent生命周期拦截器:

@Slf4j
public class PlanHook implements Hook {
    
    //监听用户输入
    private final UserAgent user;
    //计划步骤
    private final PlanNotebook plan;

    public PlanHook(PlanNotebook planNotebook) {
        this.user = UserAgent.builder()
                .name("User")
                .build();

        this.plan = planNotebook;

    }
    @Override
    public <T extends HookEvent> Mono<T> onEvent(T event) {
        /* **********************
         *
         * Hook 是对 HookEvent事件 的拦截
         * HookEvent事件:
         *
         * PreReasoningEvent:用户的输入事件
         * PostReasoningEvent: Agent推理思考过程事件
         * PreActingEvent: Agent执行过程准备调用工具的事件
         * PostActingEvent:Agent执行过程调用工具完成的事件
         *
         * *********************/
        //匹配不同的事件

        switch (event) {
            //用户输入事件
            case PreReasoningEvent e -> {

                String reason = e.getInputMessages().get(0).getTextContent();
                log.info("#### 用户的Prompt:#######");
                log.info(reason);

            }
            //推理思考事件
            case PostReasoningEvent e -> {

                String reason = e.getReasoningMessage().getTextContent();
                log.info("#### 思考过程:#######");
                log.info(reason);

                //当计划列表已生成
                Plan currentPlan = plan.getCurrentPlan();
                if (currentPlan != null) {
                    System.out.println("请输入修改意见: ");
                    user.call().block();
                }

            }
            //调用工具事件
            case PostActingEvent e -> {
                String toolName = e.getToolUse().getName();
                log.info("##### 调用工具:" + toolName);
            }
            default -> {
                // 其他事件忽略
            }
        }
        // 返回原事件
        return Mono.just(event);

    }
}

四个事件的的作用:

PreReasoningEvent e  用户输入
PostReasoningEvent e  推理完成 ← 计划监听在这里
PostActingEvent e  工具调用完成
default 忽略其他

其中与计划相关的是 PostReasoningEvent:

 case PostReasoningEvent e -> {
      Plan currentPlan = plan.getCurrentPlan();
      if (currentPlan != null) {           // 计划步骤已生成
          user.call().block();             // 暂停,等用户确认/修改
      }
  }

 当 Agent 推理完成并生成了计划步骤(PlanNotebook 中的子任务列表)后,这里检测到 currentPlan != null,就调用 UserAgent阻塞等待,让用户有机会审核计划、提出修改意见,然后 Agent 再继续执行。

 所以准确地说:PlanHook 是 Agent 生命周期拦截器,它的核心职责是"当计划生成后暂停,实现人在回路",而不仅仅是监听计划。

5.PlanNotBook

TripPlan类作为PlanNotebook 的配置工厂:

public class TripPlan {

    public PlanNotebook getPlan() {
        return PlanNotebook.builder()
                //计划步骤是否需要用户确认
                .needUserConfirm(true)
                //分解出来的子任务数量限制
                .maxSubtasks(5)
                //计划的存储方式
                //.storeage()
                .build();
    }
}

真正负责任务分解、分配、跟踪的是 PlanNotebook 这个对象。ManagerAgent 把它注入进来后,ReActAgent 在运行时会自动:

  1. 将用户复杂需求拆解成子任务列表
  2. 按步骤调用远程 Agent 执行
  3. 跟踪每个子任务状态(pending → in_progress → completed)
  4. 根据中间结果动态调整计划

  简单类比:TripPlan = 配置参数,PlanNotebook = 规划引擎,ManagerAgent = 执行者。

在ManagerAgen中声明:

@Component
public class ManagerAgent {

    @Resource
    private AgentUtils agentUtils;

    public ReActAgent getManagerAgent() {
        TripPlan plan = new TripPlan();
        //计划对象
        PlanNotebook planNotebook = plan.getPlan();
        ToolUtils toolUtils = new ToolUtils();
        //将远程Agent封装为工具的封装
        Toolkit toolkit = toolUtils.getToolkit(new RemoteAgentTool());

        return agentUtils.getReActAgentBuilder("ManagerAgent", "主管Agent").planNotebook(planNotebook)
                //拦截器
                .hook(new PlanHook(planNotebook))
                .toolkit(toolkit)
                //结构化输出
                .structuredOutputReminder(StructuredOutputReminder.PROMPT)
                .build();
    }

    public ResponseSchema run(String prompt) {

        ReActAgent agent = getManagerAgent();
        Flux<Event> stream = AgentUtils.streamResponse(agent, prompt);
        return stream.blockLast().getMessage().getStructuredData(ResponseSchema.class);
    }

}

            

ReActAgent 具备自主分解复杂任务的能力,并自动生成计划步骤。 启用规划有两种方式:

  - enablePlan():内部调用 PlanNotebook 的 Builder 构造方法,使用默认配置,适合快速启动。
  - planNotebook():传入自定义的 PlanNotebook 实例,可对规划行为进行精细配置(如子任务数量上限、是否需要用户确认等)。

  PlanNotebook 赋予主管 Agent 完整的规划闭环:

  1. 复杂任务分解
  2. 生成执行步骤
  3. 状态跟踪
  4. 动态调整
  5. 任务完成

  核心定位:PlanNotebook = 自主规划(Plan) + 自主决策(Act),是 ReActAgent 实现 "先规划、后执行、按需修正" 的引擎。


    

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐