Solon v3.8.3

team - 示例1 - 旅行安排

</> markdown
2026年1月15日 下午6:14:59

示例代码

import demo.ai.agent.LlmUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.noear.solon.ai.agent.AgentSession;
import org.noear.solon.ai.agent.session.InMemoryAgentSession;
import org.noear.solon.ai.agent.team.TeamAgent;
import org.noear.solon.ai.agent.team.TeamTrace;
import org.noear.solon.ai.agent.react.ReActAgent;
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.chat.ChatModel;
import org.noear.solon.ai.chat.prompt.Prompt;
import org.noear.solon.ai.chat.tool.MethodToolProvider;
import org.noear.solon.annotation.Param;

/**
 * 自动化管家决策测试
 * <p>验证目标:Supervisor(管理节点)能否协调 Searcher 获取天气,并由 Planner 根据天气反馈动态调整策略。</p>
 */
public class TeamAgentTravelTest {

    @Test
    public void testTravelTeam() throws Throwable {
        ChatModel chatModel = LlmUtil.getChatModel();
        String teamId = "auto_travel_agent";

        // 1. 组建团队:定义具有条件反射能力的子智能体
        TeamAgent travelTeam = TeamAgent.of(chatModel)
                .name(teamId)
                .agentAdd(ReActAgent.of(chatModel)
                        .name("searcher")
                        .description("负责查询实时天气。必须基于 query 工具的 Observation 给出明确结论。")
                        .toolAdd(new MethodToolProvider(new WeatherService()))
                        .build())
                .agentAdd(ReActAgent.of(chatModel)
                        .name("planner")
                        .description("行程规划专家。要求:必须优先阅读协作历史中的天气信息!" +
                                "如果历史显示下雨,严禁安排户外步行,必须安排室内场馆(如博物馆、室内商场)。")
                        .build())
                .maxTotalIterations(8) // 增加迭代上限,确保团队有足够空间完成“搜索-规划-校验”循环
                .build();

        // 2. 使用 AgentSession 替代 FlowContext
        // Session 会自动管理底层的 FlowContext 快照和多 Agent 间的消息传递
        AgentSession session = InMemoryAgentSession.of("sn_travel_2026_001");

        // 3. 执行协作任务
        // 增加背景:引导 Agent 关注环境因素
        String userQuery = "我现在在东京,请帮我规划一天的行程。";
        String result = travelTeam.call(Prompt.of(userQuery), session).getContent();

        System.out.println("--- 团队协作方案 ---\n" + result);

        // 4. 协作轨迹与决策检测
        // 从 team 中提取当前 session 的轨迹信息
        TeamTrace trace = travelTeam.getTrace(session);
        Assertions.assertNotNull(trace, "未生成协作轨迹");

        System.out.println("--- 协作步骤摘要 ---");
        trace.getSteps().forEach(s -> System.out.println("[" + s.getSource() + "]: " + s.getContent()));

        // 核心逻辑断言:检测 Planner 是否针对 WeatherService 返回的“特大暴雨”做出了规避动作
        boolean isLogicCorrect = result.contains("室内") ||
                result.contains("博物馆") ||
                result.contains("商场") ||
                result.contains("美术馆");

        if(!isLogicCorrect) {
            System.err.println("决策失败:Planner 忽略了 Searcher 的暴雨警告,未能正确调整为室内行程!");
        }

        Assertions.assertTrue(isLogicCorrect, "Planner 未能基于历史天气数据动态调整行程方案");
    }

    /**
     * 模拟天气服务:返回极端天气以测试 Agent 的反应。
     */
    public static class WeatherService {
        @ToolMapping(description = "获取指定城市的实时天气预报")
        public String query(@Param(description = "城市名称,例如:东京") String city) {
            // 返回恶劣天气,强制 Planner 必须改变其预设的户外方案
            return "【气象警报】" + city + "目前遭遇特大暴雨,风力 8 级,所有户外景点(如公园、塔顶观景台)暂时关闭。";
        }
    }
}