SpringBootAI 接入观测云 MCP 最佳实践

    在企业场景中,单独使用大模型通常会遇到两个问题:一是模型只能基于训练数据和上下文进行回答,无法直接获取实时的日志、监控、链路和仪表板信息;二是前端交互、模型调用、工具接入和链路观测如果混在一起,项目会很快变得难以扩展和排查。这个项目采用“前端轻交互、后端统一编排、按需接入 MCP、结合 DDTrace 做链路观测”的架构,就是为了解决这两个问题,让通用问答和基于真实观测数据的问答可以在同一个系统里协同工作,同时保持结构清晰、调用可追踪、问题可定位。

    springboot-ai-mcp 是一个基于 Spring Boot 3、Spring AI、Guance MCP 和 Vue 3 的 AI Agent 演示项目。它的目标不是单纯提供一个聊天接口,而是演示如何把大模型能力、观测云 MCP 工具能力和一个可直接访问的 Web UI 组合起来,形成一套可用于企业问答和可观测性排障的最小实现。

    1. 基础架构介绍

    项目采用轻量前后端一体化结构,整体链路如下:

    Vue 页面 -> /api/chat -> ChatController -> AgentService -> 纯模型回答 或 MCP 工具增强回答 -> ChatResponse
    

    职责如下:

    • ChatController 负责接收请求和参数校验
    • AgentService 负责会话 ID 兜底、问题路由、模型调用和 MCP 启用
    • SpringAiAgentConfig 负责模型与 MCP Client 装配
    • ChatRequest、ChatResponse 负责统一输入输出结构

    这套结构的好处是前端简单、后端职责集中,后续无论替换模型还是扩展工具,都不需要改动整体交互方式。

    2. 观测云 MCP 接入流程

    项目当前通过 McpSyncClient 接入观测云 MCP Server,推荐按以下流程理解和使用:

    • 在 application.yml 中配置观测云 MCP 地址、endpoint、API Key 和站点标识。
    • 在 SpringAiAgentConfig 中基于 WebClientStreamableHttpTransport 创建 McpSyncClient。
    • 在 AgentService 中根据用户问题内容判断是否命中观测云场景。
    • 如果命中日志、监控、仪表板、DQL、链路等关键词,则初始化 MCP Client。
    • 通过 SyncMcpToolCallbackProvider 将 MCP 工具注入 Spring AI 的对话调用。
    • 模型在回答过程中自动调用观测云工具获取真实数据,再组织最终回复。
    guance:
      base-url: https://obsy-ai.guance.com
      endpoint: /obsy_ai_mcp/mcp
      api-key: <guance-api-key>
      site-key: <site-key>
    

    当前项目的关键做法有两点:

    • 非观测云问题直接走模型,不引入额外工具调用
    • MCP Client 采用懒初始化,避免重复初始化和无效连接

    建议保留这种“按场景启用工具”的方式,这样响应链路更清晰,也更容易排查问题。

    AgentService 部分代码如下:

    public CompletableFuture<ChatResponse> dispatch(ChatRequest request) {
        String conversationId = request.getConversationId();
        if (conversationId == null || conversationId.isBlank()) {
            conversationId = UUID.randomUUID().toString();
        }
    
        String finalConversationId = conversationId;
        return CompletableFuture.supplyAsync(() -> {
            try {
                log.info("开始处理Agent请求, conversationId={}, provider={}, model={}",
                        finalConversationId, properties.getProvider(), properties.getModel());
                boolean useMcp = shouldUseMcp(request.getMessage());
                log.info("Agent路由决策, conversationId={}, useMcp={}", finalConversationId, useMcp);
    
                String reply;
                if (useMcp) {
                    initializeGuanceClientIfNecessary();
                    log.info("启用观测云MCP工具, conversationId={}", finalConversationId);
                    reply = this.chatClient.prompt()
                            .system(properties.getSystemPrompt())
                            .user(request.getMessage())
                            .toolCallbacks(new SyncMcpToolCallbackProvider(guanceMcpClient))
                            .call()
                            .content();
                }
                else {
                    log.info("直接调用模型,不启用MCP工具, conversationId={}", finalConversationId);
                    reply = this.chatClient.prompt()
                            .system(properties.getSystemPrompt())
                            .user(request.getMessage())
                            .call()
                            .content();
                }
    
                log.info("Agent处理完成, conversationId={}, replyLength={}",
                        finalConversationId, reply == null ? 0 : reply.length());
    
                return new ChatResponse(
                        finalConversationId,
                        properties.getProvider(),
                        properties.getModel(),
                        reply
                );
            }
            catch (Exception ex) {
                log.error("Agent处理失败, conversationId={}", finalConversationId, ex);
                return new ChatResponse(
                        finalConversationId,
                        properties.getProvider(),
                        properties.getModel(),
                        buildFailureMessage(ex)
                );
            }
        });
    }
    

    3. DDTrace 接入流程

    1)启动应用时通过 -javaagent 注入 dd-java-agent

    2)通过启动参数设置服务名、环境、版本和 trace agent 端口,例如:

    -javaagent:/path/to/dd-java-agent.jar
    -Ddd.service=springboot-ai-mcp
    -Ddd.env=dev
    -Ddd.version=0.0.1
    -Ddd.trace.agent.port=9529
    

    3)在 logback-spring.xml 中保留以下日志字段:

    • %X{dd.service}
    • %X{dd.trace_id}
    • %X{dd.span_id}
    <property name="CONSOLE_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %X{dd.service} %X{dd.trace_id} %X{dd.span_id} - %msg%n"/>
    <property name="FILE_PATTERN"
              value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} %X{dd.service} %X{dd.trace_id} %X{dd.span_id} - %msg%n"/>
    

    4)应用启动后,DDTrace 会自动为 HTTP 请求、Service 调用以及底层 WebClient/Netty 请求建立链路。

    这样做的价值是把接口请求、Agent 执行和 MCP 外部调用串成一条可追踪链路,便于后续在 APM 中定位慢请求、异常调用和工具访问问题。

    4. 配置采集器

    1)安装 DataKit

    登录观测云平台,点击「集成」下的「DataKit」,这里是 ubantu 操作系统,所以选择 Linux 命令。

    2)开启采集器

    主要是开启两个采集器,ddtracelogging

    • Ddtrace 采集器
    cd /usr/local/datakit/conf.d/
    cp samples/ddtrace.conf.sample ddtrace.conf
    
    • Logging 采集器
    cd /usr/local/datakit/conf.d/
    cp samples/logging.conf.sample logging.conf
    

    logging.conf 需要调整配置

    # {"version": "1.88.1", "desc": "do NOT edit this line"}
    
    # Log collection configuration example
    [[inputs.logging]]
      # ========== File Configuration ==========
      # List of log file paths, supports glob patterns for batch specification
      # Recommended to use absolute paths with file extensions to avoid collecting unexpected files
      logfiles = [
      "/home/liurui/code/springboot-ai-mcp/logs/*.log"
       ]
    ...
    

    3)重启 DataKit

    datakit service -R
    

    5. 验证

    浏览器访问 http://localhost:8088

    检查验证结果:

    • 页面能正常发送和接收消息
    • 日志中能看到请求进入、路由决策、MCP 初始化和处理完成记录
    • 若启用了 DDTrace,日志中应出现 dd.trace_iddd.span_id

    如果问题命中观测云关键词,日志里还应能看到“启用观测云 MCP 工具”的相关信息。

    6. 观测云 UI 查看效果

    完成调用后,可以在观测云 UI 中重点查看两类效果:

    1)APM 链路

    • 查看 POST /api/chat 请求链路
    • 确认链路中包含 ChatController -> AgentService -> WebClient(MCP) 调用过程
    • 观察每个节点的耗时和异常情况

    实际调用过程还原(结合 trace + 代码)

    • 前端/客户端发起 POST /api/chat
    • ChatController.chat 收到请求后转发给 AgentService.dispatch
    • dispatch 检测消息内容(包含“观测云/监控器”等关键词)后,进入 MCP 工具增强 分支。
    • 先执行 MCP 客户端初始化与能力交互(trace 中为 3 组左右 /obsy_ai_mcp/mcp 短调用,200/202)。
    • 进入第一轮 ChatModel.call,调用智谱大模型(约 2.3s)。
    • 模型在中间阶段触发一次 MCP 工具调用(约 64ms),用于查询观测云数据。
    • 回到第二轮 ChatModel.call,再次调用智谱大模型(约 20.98s)整合工具结果并生成最终答复。
    • 控制器返回 200,整条链路结束。

    同时查看 chat 相关的日志详情,可以看到模型的输入和输出信息

    2)日志关联

    • 根据 dd.trace_id 检索日志
    • 查看本次请求是否走了纯模型回答,还是走了 MCP 工具增强回答
    • 确认 MCP 初始化、工具调用和最终返回是否完整

    7. 总结

    这个项目的核心价值是用一套很轻的 Spring Boot 架构,把大模型问答、观测云 MCP 工具调用和前端聊天交互串起来。它适合作为企业 AI Agent 的入门模板,尤其适合日志、监控、链路、DQL 等可观测性场景。实践上建议继续保持当前的分层方式、按需启用 MCP 的策略,以及 DDTrace 与日志关联的观测能力。

    源码地址:https://github.com/GuanceDemo/springboot-ai-mcp

    联系我们

    加入社区

    微信扫码
    加入官方交流群

    立即体验

    在线开通,按量计费,真正的云服务!

    立即开始

    选择观测云版本

    代码托管平台