
前言随着AI Agent在业务系统中大规模落地基于 Spring AI Alibaba LangGraph-Java构建的Java智能体应用越来越多。但绝大多数开发者仅开启了LLM基础可观测忽略了Agent工作流业务层的观测能力建设。生产环境中经常出现Agent莫名跳过节点、无限循环、State状态被篡改、上下文溢出等诡异问题依靠传统LLM Trace完全无法定位根因。核心痛点总结Python生态LangGraph自带LangSmith一站式观测完整覆盖工作流、状态、节点流转Java生态无官方对等组件Spring AI Alibaba仅提供LLM层观测Agent工作流层完全需要业务自研埋点本文分层拆解Agent可观测能力边界提供可直接上线的Java埋点代码、架构方案、落地优先级、踩坑避坑经验帮助Java项目实现完整的Agent全链路可观测。一、Agent可观测双层架构基础LLM层 业务工作流层完整的AI Agent可观测分为两层两层能力互补缺一不可绝大多数项目的观测盲区都出在第二层缺失。1.1 第一层LLM基础可观测框架开箱即用Spring AI Alibaba 底层基于 Micrometer OpenTelemetry 实现可观测通过 spring-ai-observability 模块自动埋点无需手动编码。✅ 自动采集能力1、Metrics指标输出PrometheusLLM调用耗时、成功率/失败率、重试次数输入/输出Token消耗统计FunctionCall工具调用次数、耗时、异常计数2、OTel Trace链路完整覆盖「应用 → LLM模型服务」调用链路支持按需记录Prompt、Response报文。3、结构化日志自动生成LLM请求、响应标准化日志方便问题溯源。依赖引入Maven!-- Spring AI OTel可观测核心依赖 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-observability/artifactId/dependency!-- Micrometer OTel指标导出器 --dependencygroupIdio.micrometer/groupIdartifactIdmicrometer-registry-otel/artifactId/dependency核心配置application.ymlmanagement:otlp:tracing:endpoint: http://127.0.0.1:4318/v1/tracestracing:sampling:probability: 1.0 # 开发全量采样生产按需调低spring:ai:observability:include-prompt: true # 记录请求报文到Spaninclude-completion: true # 记录响应报文到Span能力边界与局限性✅ 可解决大模型超时、限流、报错、Token消耗过高、工具调用异常等底层问题❌ 完全无法解决Agent工作流业务问题无法感知LangGraph内部逻辑无法识别当前执行节点generate/review/revise/end无法统计循环迭代轮次、终止原因无法监控State状态变更、State污染篡改问题无法查看条件边路由跳转逻辑、非法路由异常无法统计上下文窗口Token占用率无法预防溢出这也是生产中Agent出现诡异流转、状态异常却无法排查的核心原因。1.2 第二层Agent工作流业务可观测业务自研LangGraph-Java 无官方内置观测能力也没有Java版LangSmith等价组件所有工作流层面观测必须业务自行埋点。需要重点观测的四大业务维度1、流程流转指标各节点执行次数、耗时、异常率条件边路由源节点、原始路由key、实际跳转节点工作流总迭代轮次、终止类型正常结束/最大轮次终止/异常失败2、State安全观测对抗State污染监控路由核心字段 next_node校验非法路由值统计篡改异常解决模型输出跑偏、Prompt注入导致的状态污染问题。3、上下文健康指标实时统计每轮迭代上下文Token占窗口上限比例提前预警上下文溢出风险。4、Agent行为指标Linter校验次数、自动修复成功率、摘要截断触发次数等业务行为监控。核心埋点位置LangGraph-Java专属1. 节点统一包装器AOP式统一封装所有节点统一采集耗时、日志、异常指标避免重复编码2. 条件边路由函数拦截路由决策记录原始路由、真实路由、非法路由异常二、Java实战全套可落地埋点代码2.1 条件边路由埋点State污染检测核心实现路由白名单校验、非法路由指标统计、结构化日志记录精准拦截State篡改问题。import io.micrometer.core.instrument.Counter;import io.micrometer.core.instrument.MeterRegistry;import lombok.extern.slf4j.Slf4j;import org.lang4j.graph.GraphState;import org.springframework.stereotype.Component;import java.util.Set;Slf4jComponentpublic class AgentRouteObserver {private final Counter invalidRoutingCounter;// 合法路由节点白名单严格限制跳转范围private static final SetString ALLOW_NEXT_NODE Set.of(review, revise, end);public AgentRouteObserver(MeterRegistry meterRegistry) {this.invalidRoutingCounter meterRegistry.counter(agent.workflow.routing.invalid);}/*** 带可观测、防污染的条件路由函数*/public String routeWithObserve(GraphState state) {// 读取模型输出的原始路由值String rawNext state.Stringvalue(next_node).orElse(review);String actualNext;// 校验非法路由修复State污染if (!ALLOW_NEXT_NODE.contains(rawNext)) {actualNext review;invalidRoutingCounter.increment();log.warn([AgentWorkflow] 检测到非法路由存在State污染风险原始路由:{}, 强制重置为:{}, rawNext, actualNext);} else {actualNext rawNext;}// 结构化日志用于流转溯源log.info([AgentWorkflowRoute] 源节点:revise_node, 原始路由:{}, 实际路由:{}, rawNext, actualNext);return actualNext;}}挂载到Graph工作流Graph.builder().addNode(revise, reviseNode)// 挂载带观测的条件路由.addConditionalEdges(revise, agentRouteObserver::routeWithObserve).addNode(review, reviewNode).addNode(end, endNode).build();2.2 统一节点包装器全局节点埋点对所有LangGraph节点统一包装自动采集节点耗时、执行日志、异常指标零侵入适配所有业务节点。import io.micrometer.core.instrument.MeterRegistry;import io.micrometer.core.instrument.Timer;import lombok.extern.slf4j.Slf4j;import org.lang4j.graph.GraphState;import org.lang4j.graph.NodeFunction;import org.springframework.stereotype.Component;Slf4jComponentpublic class WorkflowNodeWrapper {private final MeterRegistry meterRegistry;public WorkflowNodeWrapper(MeterRegistry meterRegistry) {this.meterRegistry meterRegistry;}/*** 统一包装LangGraph节点注入可观测能力*/public NodeFunction wrapNode(String nodeName, NodeFunction originNode) {return state - {Timer.Sample sample Timer.start(meterRegistry);try {log.info([AgentWorkflowNode] 进入节点:{}, nodeName);// 执行原始节点业务逻辑GraphState outState originNode.apply(state);log.info([AgentWorkflowNode] 退出节点:{}, nodeName);return outState;} catch (Exception e) {log.error([AgentWorkflowNode] 节点执行异常节点:{}, nodeName, e);// 节点异常指标上报meterRegistry.counter(agent.workflow.node.error, node, nodeName).increment();throw e;} finally {// 节点耗时指标上报sample.stop(meterRegistry.timer(agent.workflow.node.duration, node, nodeName));}};}}业务节点接入方式// 包装原始节点统一注入埋点能力NodeFunction wrappedReviseNode workflowNodeWrapper.wrapNode(revise, originReviseNode);NodeFunction wrappedReviewNode workflowNodeWrapper.wrapNode(review, originReviewNode);// 注册到工作流Graph.builder().addNode(revise, wrappedReviseNode).addNode(review, wrappedReviewNode).build();2.3 上下文窗口利用率埋点防溢出实时监控上下文Token占用比例支持阈值告警解决上下文窗口溢出导致的Agent失效问题。本节补齐完整工具类、Token计算逻辑、生产可用调用位置彻底闭环代码。2.3.1 完整 AgentContextMonitor 工具类import io.micrometer.core.instrument.MeterRegistry;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;Slf4jComponentpublic class AgentContextMonitor {private final MeterRegistry meterRegistry;public AgentContextMonitor(MeterRegistry meterRegistry) {this.meterRegistry meterRegistry;}/*** 上报上下文窗口利用率指标* param currentToken 当前上下文Token数* param maxToken 模型最大窗口Token上限*/public void recordContextUtil(int currentToken, int maxToken){double utilization (double) currentToken / maxToken;// 注册监控指标agent.context.utilizationmeterRegistry.gauge(agent.context.utilization, utilization);// 分级日志告警可被ELK/Loki采集做告警规则if(utilization 0.4){log.warn([AgentContext] 上下文占用偏高利用率:{}, utilization);}if(utilization 0.7){log.error([AgentContext] 上下文临界溢出利用率:{}, utilization);}}}2.3.2 Spring AI 原生Token计算工具方法用于从GraphState中读取对话历史、自动计算当前上下文Token总量项目直接复用即可。import org.lang4j.graph.GraphState;import org.springframework.ai.chat.messages.ChatMessage;import org.springframework.ai.tokenizer.TokenCountCalculator;import org.springframework.ai.tokenizer.openai.OpenAiTokenCountCalculator;import org.springframework.stereotype.Service;import java.util.List;Servicepublic class AgentTokenUtil {// Spring AI 官方Token计算器适配主流大模型private final TokenCountCalculator tokenCalculator new OpenAiTokenCountCalculator();/*** 从GraphState读取messages并计算总Token数*/public int calcMessageTokens(GraphState state){ListChatMessage messageList state.value(messages).orElse(List.of());return tokenCalculator.calculateTokenCount(messageList);}}2.3.3 三种生产级调用位置优先级从高到低方案一条件边路由处调用生产最优推荐每一轮Agent迭代只统计一次指标无重复、不爆炸贴合工作流一轮执行周期。// 在 AgentRouteObserver#routeWithObserve 方法头部注入private final AgentContextMonitor agentContextMonitor;private final AgentTokenUtil agentTokenUtil;// 构造器注入public AgentRouteObserver(MeterRegistry meterRegistry,AgentContextMonitor agentContextMonitor,AgentTokenUtil agentTokenUtil) {this.invalidRoutingCounter meterRegistry.counter(agent.workflow.routing.invalid);this.agentContextMonitor agentContextMonitor;this.agentTokenUtil agentTokenUtil;}public String routeWithObserve(GraphState state) {// 每轮迭代结束路由决策前统计上下文占用int currentToken agentTokenUtil.calcMessageTokens(state);// 根据自身模型配置修改最大窗口如4096、8192、16384int maxWindowToken 8192;agentContextMonitor.recordContextUtil(currentToken, maxWindowToken);// 原有路由校验逻辑不变String rawNext state.Stringvalue(next_node).orElse(review);String actualNext;int round state.Integervalue(round).orElse(0);if (!ALLOW_NEXT_NODE.contains(rawNext)) {actualNext review;invalidRoutingCounter.increment();log.warn([AgentWorkflow] 检测到非法路由存在State污染风险原始路由:{}, 强制重置为:{}, rawNext, actualNext);} else {actualNext rawNext;}log.info([AgentWorkflowRoute] 源节点:revise_node, 原始路由:{}, 实际路由:{}, rawNext, actualNext);agentTraceHelper.addAgentTraceAttrs(revise, rawNext, actualNext, round);return actualNext;}方案二统一节点包装器调用调试全覆盖所有节点执行完成都会统计适合开发调试生产慎用指标高频上报。// WorkflowNodeWrapper 新增注入private final AgentContextMonitor agentContextMonitor;private final AgentTokenUtil agentTokenUtil;public WorkflowNodeWrapper(MeterRegistry meterRegistry,AgentContextMonitor agentContextMonitor,AgentTokenUtil agentTokenUtil) {this.meterRegistry meterRegistry;this.agentContextMonitor agentContextMonitor;this.agentTokenUtil agentTokenUtil;}public NodeFunction wrapNode(String nodeName, NodeFunction originNode) {return state - {Timer.Sample sample Timer.start(meterRegistry);try {log.info([AgentWorkflowNode] 进入节点:{}, nodeName);GraphState outState originNode.apply(state);// 节点执行完成后统计上下文占用int currentToken agentTokenUtil.calcMessageTokens(outState);agentContextMonitor.recordContextUtil(currentToken, 8192);log.info([AgentWorkflowNode] 退出节点:{}, nodeName);return outState;} catch (Exception e) {log.error([AgentWorkflowNode] 节点执行异常节点:{}, nodeName, e);meterRegistry.counter(agent.workflow.node.error, node, nodeName).increment();throw e;} finally {sample.stop(meterRegistry.timer(agent.workflow.node.duration, node, nodeName));}};}方案三LLM调用后精准统计Token精准匹配直接读取LLM返回的Usage数据统计最精准适合仅监控模型调用Token消耗场景。ChatResponse response chatModel.call(prompt);// 读取模型真实消耗Tokenint inputTokens response.getMetadata().getUsage().getInputTokens();int outputTokens response.getMetadata().getUsage().getOutputTokens();int totalTokens inputTokens outputTokens;agentContextMonitor.recordContextUtil(totalTokens, 8192);2.3.4 生产落地避坑要点优先使用方案一条件边埋点一轮迭代一次指标数据平稳无抖动LangGraph-Java为异步执行必须使用执行完成后的outState/当前轮次state计算禁止使用入参旧状态根据实际模型配置修改 maxWindowToken4096/8192/16384Grafana可配置规则利用率持续 40% 预警、70% 严重告警提前规避上下文溢出2.4 OTel Span扩展业务链路关联轻量生产方案默认LLM Span无法体现工作流信息这里提供生产最优轻量方案不新建子Span仅追加业务Tag低侵入、低成本规避异步线程Span丢失问题。核心思想不去创建新的 Span就在已经存在的 LLM / 现有 Span 上面直接追加自定义标签 (tag)。✅优点不用管异步线程、不会链路丢失、性能开销极小。❗限制不会多出一条独立链路节点只是给已有链路打标记。AgentTraceHelper 就是一个工具类做一件事拿到当前正在执行的 Span往上面贴业务标签。import io.micrometer.tracing.Span;import io.micrometer.tracing.Tracer;import org.springframework.stereotype.Component;Componentpublic class AgentTraceHelper {private final Tracer tracer;public AgentTraceHelper(Tracer tracer) {this.tracer tracer;}/*** 向当前OTel Span追加Agent业务属性*/public void addAgentTraceAttrs(String nodeName,String rawRoute,String actualRoute,Integer workflowRound) {// 获取当前线程正在运行的span就是SpringAI生成的那个spanSpan currentSpan tracer.currentSpan();if (currentSpan ! null) {// 贴标签key‑value格式会存到Jaeger/ZipkincurrentSpan.tag(agent.workflow.node, nodeName);currentSpan.tag(agent.workflow.raw_route, rawRoute null ? : rawRoute);currentSpan.tag(agent.workflow.actual_route, actualRoute null ? : actualRoute);currentSpan.tag(agent.workflow.round, String.valueOf(workflowRound));}}}在路由埋点中接入Trace扩展// 路由逻辑末尾追加链路信息int round state.Integervalue(round).orElse(0);agentTraceHelper.addAgentTraceAttrs(revise, rawNext, actualNext, round);最终效果你在 Jaeger/Zipkin 点开那条 LLM 调用链路详情页就能看到这几个自定义 tagagent.workflow.nodereviseagent.workflow.raw_routeendagent.workflow.actual_routeendagent.workflow.round2含义这条大模型调用来自 Agent 的 revise 节点第 2 轮循环原始路由是 end实际跳转 end。 做到一条 Trace同时看到大模型调用 Agent 工作流上下文。⚠️坑tracer.currentSpan() 在异步线程可能拿到 null。拿不到就什么都不做不会报错只是没有 tag。2.5 进阶方案独立子Span调试环境专用如需完整链路拓扑可新建独立子Span但生产不推荐。LangGraph-Java为异步执行线程切换会导致Span上下文丢失、链路串色存储成本更高。核心思想额外创建一条全新独立 Span在链路图上多出一块叫Agent‑Conditional‑Route和 LLM Span 形成父子层级看得更直观。// 创建子spanSpan span tracer.nextSpan().name(Agent-Conditional-Route).tag(rawRoute, rawRoute).tag(actualRoute, actualRoute).start();// 把span绑定到当前线程上下文try (Tracer.SpanInScope ws tracer.withSpan(span)) {// 这里面执行路由业务逻辑} finally {span.end(); // 必须关闭span}为什么生产不推荐LangGraph‑Java 是异步任务会丢到线程池执行。创建 Span 是在 A 线程业务逻辑跑到 B 线程B 线程感知不到这个 Span上下文丢失链路断了、串到别的请求上链路串色。同时每轮迭代新建 Span存储量上涨。适合本地调试看链路拓扑线上环境优先用【打 Tag 轻量方案】。三、完整Agent可观测整体架构层级核心组件能力说明底层框架自带Spring AI Observability OTel Micrometer采集LLM调用、FunctionCall、Token、耗时、异常输出Trace到Zipkin/JaegerMetrics到Prometheus中层业务自研LangGraph-Java自定义埋点节点包装器、条件边埋点覆盖节点流转、循环轮次、State污染、上下文利用率输出指标结构化日志上层运维Prometheus Grafana Loki/ELK可视化大盘、非法路由告警、上下文溢出告警、工作流流转日志复现重要提醒市面上多数LLM观测平台仅代理HTTP请求只能看Prompt/Response无法感知LangGraph工作流内部状态与流转无法解决Agent专属问题。四、Python与Java生态观测能力对比LangGraph Python官方内置LangSmith一站式全自动观测记录拓扑流转、State变更、节点执行全流程LangGraph Java无官方观测组件无等价LangSmith能力仅基础LLM观测可用工作流层全部需要自研埋点五、生产落地优先级P0/P1/P2P0 紧急落地零成本、高收益开启Spring AI OTel基础观测打通LLM、工具调用Trace链路接入节点包装器、条件边结构化日志埋点开启非法路由WARN日志快速排查State污染问题无需搭建大盘仅靠日志即可解决80%Agent异常问题P1 指标告警生产稳定必备上报核心业务指标非法路由计数、节点耗时、上下文利用率、最大迭代终止次数配置告警规则非法路由突增、上下文利用率超40%、工作流强制终止异常P2 可视化与链路关联优化体验搭建Grafana Agent专属工作流大盘将工作流节点、路由、轮次信息关联到OTel Span实现一条Trace贯通业务流转LLM调用六、核心踩坑总结与最佳实践6.1 常见误区只开启LLM层OTel观测仅能看到模型调用日志Agent出现状态篡改、异常跳转、无限循环时完全无法定位问题根因。6.2 落地最佳顺序结构化日志 → 指标告警 → 可视化大盘切勿一上来追求完整可视化优先保障问题可排查。6.3 Span方案选型建议生产环境优先轻量方案追加Span Tag低侵入、无异步链路丢失风险、存储成本低测试/调试环境可新建独立子Span获取完整拓扑链路七、总结Spring AI Alibaba 解决了LLM基础调用的可观测而 LangGraph-Java 构建的Agent工作流、State状态安全、节点流转、上下文健康等业务层可观测必须由业务自行落地。Java生态Agent可观测没有开箱即用的完整方案通过本文的分层埋点方案、全套代码、落地优先级规范可以快速补齐Java Agent观测短板彻底解决生产中Agent诡异异常无法排查的痛点支撑AI Agent从Demo稳定走向生产大规模落地。