Solon v3.8.0

multi - 示例5 - 团队嵌套

</> markdown
2026年1月4日 下午10:47:27

示例代码

import demo.ai.agent.LlmUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.noear.solon.ai.agent.Agent;
import org.noear.solon.ai.agent.team.TeamAgent;
import org.noear.solon.ai.agent.team.TeamInterceptor;
import org.noear.solon.ai.agent.team.TeamTrace;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.prompt.Prompt;
import org.noear.solon.flow.FlowContext;
import org.noear.solon.flow.Node;

public class TeamAgentNestedHitlTest {
    @Test
    public void testNestedHumanIntervention() throws Throwable {
        ChatModel chatModel = LlmUtil.getChatModel();

        // 1. 构建子团队
        TeamAgent devTeam = TeamAgent.of(chatModel).name("dev_team")
                .addAgent(new Agent() {
                    @Override public String name() { return "Coder"; }
                    @Override public String description() { return "写代码"; }
                    @Override public String call(FlowContext ctx, Prompt p) { return "代码写好了。"; }
                }).build();

        // 2. 构建父团队
        TeamAgent projectTeam = TeamAgent.of(chatModel)
                .name("manager")
                .addAgent(devTeam)
                .addAgent(new Agent() {
                    @Override public String name() { return "Reviewer"; }
                    @Override public String description() { return "审代码"; }
                    @Override public String call(FlowContext ctx, Prompt p) { return "Perfect. [FINISH]"; }
                })
                .interceptor(new TeamInterceptor() {
                    @Override
                    public void onNodeStart(FlowContext ctx, Node n) {
                        if (Agent.ID_SUPERVISOR.equals(n.getId())) {
                            TeamTrace trace = ctx.getAs("__manager");
                            // 逻辑:如果开发已完成,但未获得经理批准,则中断
                            if (trace != null && trace.getFormattedHistory().contains("dev_team")) {
                                if (!ctx.containsKey("approved")) {
                                    System.out.println("[HITL] 拦截:等待经理批准 dev_team 的成果...");
                                    ctx.stop();
                                }
                            }
                        }
                    }
                })
                .build();


        String yaml = projectTeam.getGraph().toYaml();

        System.out.println("------------------\n\n");
        System.out.println(yaml);
        System.out.println("\n\n------------------");


        FlowContext context = FlowContext.of("hitl_job");

        // 第一次调用:执行完 dev_team 后在 Supervisor 处被拦截
        projectTeam.call(context, "开发支付模块");

        // 使用 isStopped 判定流程停止
        Assertions.assertTrue(context.isStopped(), "流程应该被拦截,未到达结束节点");
        Assertions.assertEquals(Agent.ID_SUPERVISOR, context.lastNodeId());

        // 验证 Reviewer 尚未介入
        TeamTrace trace1 = context.getAs("__manager");
        Assertions.assertFalse(trace1.getFormattedHistory().contains("Reviewer"));

        // 第二次调用:注入批准信号,恢复执行
        context.put("approved", true);
        String result = projectTeam.call(context);

        Assertions.assertTrue(result.contains("Perfect"));
        System.out.println("最终协作历史:\n" + trace1.getFormattedHistory());
    }
}