Skip to content

第13章 旅游规划优化,监控,部署

13-1 敏感资源处理方案

从配置中读取敏感资源,不能使用istatic

在resources下创建.env私有信息文件

properties
ALIBABA_DASHCOPE_KEY=
MODEL_NAME = 
BAIDU_MAP_KEY=
BAIDU_MAP_ADDR=

application配置文件中配置私有信息动态配置

yaml
spring:
  config:
    import: optional:file:.env[.properties]


agent:
  modeL_name: ${MODEL_NAME}
  alibaba_dashscope_key: ${ALIBABA_DASHCOPE_KEY}
mcp:
  baidu_map_addr: ${BAIDU_MAP_ADDR}
  baidu_map_key: ${BAIDU_MAP_KEY}

在创建配置工具类,是注入Bean中的,哪里需要使用私有信息就注入该配置类即可。

java
@Configuration
@Getter
public class Properties {
    //大模型名称
    @Value("${agent.model_name}")
    private String modelName;
    //阿云DashScope Key
    @Value("${agent.alibaba_dashscope_key}")
    private String alibabaDashscopeKey;

    //百度地图MCPKey
    @Value("${mcp.baidu_map_addr}")
    private String baiduMapAddr;

    @Value("${mcp.baidu_map_key}")
    private String baiduMapKey;
}

13-2 主管Agent暴露和用户交互的接口

用户与主管Agent交互,就是创建一个controller,将主管Agent注入到Bean中,controller调用即可

java
@Component
public class ManagerAgent {}


@RestController
public class ManagerAgentController {
    @Resource
    private ManagerAgent managerAgent;
    //用户提交旅游规划的PromptI
    @RequestMapping(value = "/trip", method = RequestMethod.POST)
    public void tripPlan() {
        ReActAgent manager = managerAgent.getManagerAgent();
        // managerAgent.run();
    }
}

13-3 docker搭建Agent跟踪和Token消费分析

大模型数据观察和分析平台Langfuse

13-4 旅游规划Agent回队集成Agent追綜观测

xml
<observation-extension.version>1.1.2.2</observation-extension.version>
<!-- ObservationAPI:能调用LangFuse 进行Agent数据观测 -->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-observation-extension</artifactId>
    <version>${observation-extension.version}</version>
</dependency>

敏感信息

properties
LANGFUSE_ADDRE=http://127.0.0.1:3000
LANGFUSE_SECRET_KEY=Yours Langfuse secret key
LANGFUSE_PUBLIC_KEY=Yours Langfuse public key

配置文件中

yaml
spring:
  config:
    import: optional:file:.env[.properties]
  ai:
    # LangFuse Agent数据观测observation:
    langfuse:
      #LangFuse 服务地址
      endpoint: ${LANGFUSE_ADDR}
      #私钥
      secret-key: ${LANGFUSE_SECRET_KEY}
      #公钥
      public-key: ${LANGFUSE_PUBLIC_KEY}
      #采样率(生产环境建议0.1-0.5)
      sampling-rate: 1.0

同理配置类中补充这几个属性

再初始化TelemetryTracer

java
@Service
@Slf4j
public class LangFuseUtils{

    private TelemetryTracer langfuseTracer;
    @Resource
    private Properties properties;

    //初始化 LangFuse Agent观测
    public TelemetryTracer initLangfuseTracing() {
        String endpoint = properties.getEndpoint();
        String authHeader = getAuthHeader();

        // 创建TelemetryTracer并配置Langfuse
        TelemetryTracer langfuseTracer = TelemetryTracer.builder()
                .endpoint(endpoint)
                .addHeader("Authorization", authHeader)
                .build();
        return langfuseTracer;
    }

    // 构建LangFuse的认证头@param
    private String getAuthHeader() {
        String publicKey = properties.getPublicKey();
        String secretKey = properties.getSecretKey();
        String credentials = publicKey + ":" + secretKey;
        String authHeader = "Basic" + Base64.getEncoder().encodeToString(credentials.getBytes());
        return authHeader;
    }
}

在主管智能体中注入这个工具类,在进入首行注册到全局即可。调用Agent - 自动追踪到Langfuse。

java
@Component
public class ManagerAgent {

   @Resource
   private LangFuseUtils langFuseUtils;

   public ReActAgent getManagerAgent(){
        TelemetryTracer tracer = LangFuseUtils.initLangfuseTracing());
        //注册到全局TracerRegistry
        TracerRegistry.register(tracer);
          ...
 
    }
}

13-5 测试主管Agent接收Prompt及结构化输出

创建控制器的输入输出对象PromptSchema/ResponseSchema

java
@Getter
public class PromptSchema {
    private String prompt;
}

public class ResponseSchema {
    //LLM响应
    public String response;
    // 必须有无参构造函数
    public  ResponseSchema() {}
}

控制器中的接口

java
@Resource
private ManagerAgent managerAgent;

//用户提交旅游规划的Prompt
@RequestMapping(value = "/trip",
        produces = "application/json;charset=UTF-8", method = RequestMethod.POST)
public ResponseSchema tripPlan(@RequestBody PromptSchema input) {
    ReActAgent manager = managerAgent.getManagerAgent();
    ResponseSchema response = managerAgent.run(input.getPrompt());
    return response;
}

修改主管智能体的输入输出参数

java
/**
 * 主管Agent
 */
@Component
public class ManagerAgent {
    private final ReActAgent agent;
    public ManagerAgent() {
        //PlanNotebook
        TripPlan plan = new TripPlan();
        //计划对象
        PlanNotebook planNotebook = plan.getPlan();
        this.agent = AgentUtils.getReActAgentBuilder("ManagerAgent", "负责用户需求的解决方案制定,以及任务分发")
                // .enablePlan()
                .planNotebook(planNotebook)
                .hook(new PlanHook(planNotebook))        // 拦截器
                .toolkit(new ToolUtils().getToolkit(plan))   // 工具包
                .structuredOutputReminder(StructuredOutputReminder.PROMPT) // 结构化输出
                .build();
    }

    public ReActAgent getManagerAgent() {
        return this.agent;
    }
    
    /**
     * description: Agent 运行
     */
    public ResponseSchema run(String prompt) {
        Flux<Event> stream = AgentUtils.streamResponse(agent, prompt);
        ResponseSchema result = stream
                //阻塞直到结束
                .blockLast()
                .getMessage()
                .getStructuredData(ResponseSchema.class); // 结构化输出
        return result;
    }
}

13-6 旅游规划打印出Agent的深度思考

根据模型,有的模型支持思考,就需要开启思考模式

java
/**
 * description: 创建ReAct Agent Builder
 */
public static ReActAgent.Builder getReActAgentBuilder(String name, String description) {
    return ReActAgent.builder()
            .name(name)
            .description(description)
            .model(DashScopeChatModel.builder()
                    //请求语言大模型的apikey
                    .apiKey("")
                    //所使用的语言大模型
                    .modelName("qwen3-max")
                    .stream(true)
                    // 开启思考模式
                    .enableThinking(true)
                    .build());
}

13-7 测试路线制定专员规划最优驾车路线

用户Prompt构建工具类

java
/**
 * 用户Prompt构建工具类
 */
@Slf4j
public class PromptUtils {
    public Msg getPrompt(String prompt){
        log.info("===构建的Prompt===:{}",prompt);
        return Msg.builder()
                .role(MsgRole.USER)
                .content(TextBlock.builder().text(prompt).build())
                .build();
    }
}

擅长制定最优驾车路线的Agent修改,添加参数

java
@Tool(description = "擅长制定最优驾车路线的Agent")
public String callRouteMakingAgent(@ToolParam(name = "prompt", description = "驾车的起点和终点") String prompt) throws NacosException {
    log.info("工具方法:路线制定智能体...正在调用中");
    A2aAgent agent = A2aAgent.builder()
            .name("RouteMakingAgent")
            .agentCardResolver(new NacosAgentCardResolver(NacosUtil.getNacosClient())) //创建 Nacos 的 AgentCardResolver
            .build();
    log.info("获取到的远程Agent描述:{}", agent.getDescription());
    log.info("这个工具方法传入的参数:{}", prompt);
    prompt = "调用百度地图MCP,制定最优驾车路线:" + prompt;
    //组装Prompt
    PromptUtils promptUtils = new PromptUtils();
    Msg userMsg = promptUtils.getPrompt(prompt);
    //远程Agent运行
    Msg remoteAgentResponse = agent.call(userMsg).block();
    assert remoteAgentResponse != null;
    String content = remoteAgentResponse.getTextContent();
    log.info("远程Agent返回:{}", content);
    return content;
}

13-8 课程总结