☰
拒绝AI“黑盒”:2025年,我用SkyWalking“监控”了自己的Agent大模型应用
2026/9/29 5:31:06 网站建设 项目流程

1. 为什么你的 LangChain Agent 需要一个“行车记录仪”

2025 年做 Agent 开发,最让人抓狂的不是 Prompt 写不好,而是你根本不知道它到底在干什么。一个基于 LangChain 的 RAG 助手,用户问一句“帮我查下上季度的报销政策”,表面上看是 3 秒返回,实际上内部可能跑了 Embedding、向量检索、Rerank、LLM 决策、工具调用、再 LLM 总结整整六个环节。哪个环节慢了、哪个环节 Token 爆了、哪个工具被反复调用了,全靠猜。

SkyWalking 在这里扮演的角色,就是给 Agent 装一个“行车记录仪”。它原本是 Apache 旗下的 APM 链路追踪工具,在微服务领域用来追踪 HTTP、RPC、数据库调用,现在通过 Python Agent 的@runnable装饰器和自定义 Span,可以把 LLM 调用、向量检索、工具执行这些 AI 特有的节点全部串成一条 Trace。你打开 SkyWalking UI,看到的不再是黑盒,而是一条带耗时、带 Tag、带层级关系的瀑布流。

这篇文章适合两类人:一是已经在用 LangChain 搭 Agent、但被延迟和 Token 成本折磨的开发者;二是想把传统微服务可观测性经验迁移到 LLMOps 的运维同学。我会给出可复制的 SkyWalking 接入配置骨架、Agent 侧埋点代码、上报参数,以及验证请求是否成功的具体动作。目标很明确:把“黑盒推理”变成“可追踪的调用链”。

2. 前置准备:TaoToken 与 SkyWalking 环境怎么搭

在讲埋点之前,先把两个基础环境说清楚。一个是模型调用入口,一个是链路追踪底座。

模型调用这块,我目前用的是 TaoToken 的 API 作为统一入口。它的好处是兼容 OpenAI 的接口格式,LangChain 里ChatOpenAI只要改base_url和api_key就能直接切过去,不用改业务代码。你可以在官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 注册后,到控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 创建一个 API Key,然后在 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 复制出来备用。接口地址是 https://taotoken.net/api ,注意这个不带 UTM 参数,直接填在base_url里即可。

SkyWalking 这边,你需要三样东西:OAP Server(负责接收和聚合数据)、UI(负责可视化)、Python Agent(负责在 Agent 应用里埋点上报)。最省事的做法是用 Docker Compose 起一个单机版,OAP 的 gRPC 端口默认 11800,HTTP 端口 12800,UI 默认 8080。Python Agent 通过pip install apache-skywalking安装,然后在应用启动最前面初始化。

这里有个容易踩的坑:SkyWalking Python Agent 的初始化必须放在所有业务 import 之前,否则部分自动埋点会失效。我试过把config.init写在 LangChain import 后面,结果 HTTP 调用能追踪到,但自定义的@runnableSpan 死活不上报。后来把初始化挪到文件第一行才正常。

3. 可复制配置:SkyWalking 接入骨架与 Agent 埋点

下面这套配置是我实际跑通的骨架,你可以直接抄。先看环境变量和初始化部分。

# skywalking_init.py # 必须在所有业务代码 import 之前执行 from skywalking import agent, config config.init( collector_address='127.0.0.1:11800', # OAP gRPC 地址 service_name='langchain-agent-demo', # 服务名,UI 上按这个筛选 service_instance_name='agent-node-01', # 实例名,多副本时区分 agent_name='skywalking-python-agent', protocol='grpc', logging_level='INFO', trace_ignore_path='/health,/metrics', # 忽略健康检查 sw_agent_collector_backend_services='127.0.0.1:11800', ) agent.start()

初始化完成后,在 LangChain 的关键节点上加@runnable装饰器。注意Layer的选择会影响 UI 上的图标和分类,LLM 调用用Layer.Http,向量库用Layer.Database,工具执行用Layer.Function。

# agent_trace.py from skywalking import Layer from skywalking.decorators import runnable from skywalking.trace.context import get_context from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate llm = ChatOpenAI( model='gpt-4o-mini', base_url='https://taotoken.net/api', api_key='你的_TaoToken_API_Key', temperature=0.2, ) @runnable(op='VectorDB/Search', layer=Layer.Database) def search_knowledge_base(query: str): span = get_context().active_span() span.tag('db.system', 'milvus') span.tag('db.operation', 'similarity_search') span.tag('query.length', str(len(query))) # 这里替换成你真实的向量检索逻辑 docs = ['报销政策文档片段A', '报销政策文档片段B'] span.tag('result.count', str(len(docs))) return docs @runnable(op='LLM/ChatCompletion', layer=Layer.Http) def call_llm(prompt: str): span = get_context().active_span() span.tag('llm.provider', 'taotoken') span.tag('llm.model', 'gpt-4o-mini') span.tag('llm.prompt_chars', str(len(prompt))) response = llm.invoke(prompt) span.tag('llm.completion_chars', str(len(response.content))) return response.content @runnable(op='Tool/Execute', layer=Layer.Function) def execute_tool(tool_name: str, tool_input: str): span = get_context().active_span() span.tag('tool.name', tool_name) span.tag('tool.input.length', str(len(tool_input))) # 模拟工具执行 return f'{tool_name} 执行结果' def agent_flow(user_input: str): docs = search_knowledge_base(user_input) context = '\n'.join(docs) prompt = f'基于以下资料回答问题:\n{context}\n\n问题:{user_input}' answer = call_llm(prompt) return answer

几个关键参数说明。collector_address填 OAP 的 gRPC 地址,不是 UI 地址,很多人第一次会填错。service_name在 UI 上会作为一级筛选条件,建议按业务命名,比如rag-assistant-prod。trace_ignore_path用来过滤掉健康检查这类无意义请求,避免 Trace 列表被刷屏。

如果你用的是 LangChain 的 AgentExecutor,可以在AgentExecutor的callbacks里挂一个自定义 CallbackHandler,在on_llm_start、on_tool_start、on_tool_end这些钩子里手动创建 Span。这样即使不用装饰器,也能覆盖到 Agent 内部的决策循环。

4. 验证请求:怎么确认 Trace 真的上报成功了

配置写完,别急着优化,先确认数据有没有上来。验证分三步。

第一步,本地跑一次agent_flow('报销流程是什么'),观察控制台有没有 SkyWalking Agent 的启动日志和上报日志。正常情况你会看到类似SkyWalking Python Agent started和Reported trace segment的输出。如果只有启动日志没有上报日志,说明 Span 没被创建,检查@runnable是否加在了被调用的函数上。

第二步,打开 SkyWalking UI,默认地址http://localhost:8080。在顶部服务列表里找到langchain-agent-demo,进入 Trace 页面。你应该能看到刚才那次请求的 Trace,点进去是一条瀑布流:最外层是agent_flow,下面挂着VectorDB/Search、LLM/ChatCompletion两个子 Span,每个 Span 右侧显示耗时。

第三步,点开LLM/ChatCompletion这个 Span,在 Tags 区域确认llm.provider=taotoken、llm.model=gpt-4o-mini、llm.prompt_chars这些自定义 Tag 是否都在。如果 Tag 缺失,说明get_context().active_span()拿到的不是当前 Span,可能是装饰器嵌套层级不对。

验证通过后,你可以做一个简单的延迟对比实验。在search_knowledge_base里加一个time.sleep(2)模拟慢查询,重新跑一次,然后在 UI 上看VectorDB/Search的耗时是不是变成了 2 秒以上。这一步能帮你确认耗时归因是准确的,后面做优化时才有可信依据。

如果你在验证模型调用是否正常返回,可以先用模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 单独测一下 Key 和模型名是否匹配,排除掉鉴权问题再回到链路追踪。

5. 本篇常见错排查:Trace 不上报、Span 断链、Tag 丢失

接入过程中最容易遇到三类问题,我按排查顺序列一下。

问题一:UI 上完全看不到服务。先确认 OAP 的 11800 端口是否可达,用telnet 127.0.0.1 11800测一下。如果 OAP 在容器里,注意collector_address不能填localhost,要填宿主机的局域网 IP 或者容器网络里的服务名。另外检查service_name是否和 UI 筛选条件一致,有时候服务其实上报了,只是被过滤条件挡住了。

问题二:Trace 有,但 Span 是断开的,LLM 调用没挂在 Agent 主链路上。这通常是上下文传递问题。SkyWalking Python Agent 依赖线程上下文来串联 Span,如果你在 LangChain 里用了异步或者多线程,@runnable装饰的 Span 可能变成独立 Trace。解决办法是在异步函数里手动用with get_context().new_span(...)创建子 Span,或者改用 SkyWalking 的ContextCarrier做跨线程传递。

问题三:自定义 Tag 在 UI 上不显示。检查两点:一是span.tag()的 key 不要用中文或特殊字符,建议用llm.prompt_chars这种点分命名;二是 Tag 的 value 必须是字符串,传 int 或 float 会被静默忽略。我踩过一次坑,span.tag('token.count', 1500)死活不显示,改成str(1500)就出来了。

还有一个隐蔽问题:如果你同时装了多个 APM 探针(比如 SkyWalking 和 OpenTelemetry),可能会出现 Span 冲突导致数据错乱。建议一个应用只保留一个探针,或者在初始化时显式关闭自动埋点,只保留手动埋点。

6. 从 Trace 到优化:让 Agent 的每一步都有据可查

链路追踪的价值不在于“看到”,而在于“看到之后能改”。有了 SkyWalking 的 Trace 数据,你可以做三件很实际的事。

第一件是延迟归因。把VectorDB/Search、LLM/ChatCompletion、Tool/Execute的 P99 耗时拉出来对比,一眼就能看出瓶颈在检索还是在生成。我之前的项目里,一直以为 LLM 慢,结果 Trace 显示向量检索占了 60% 的时间,把 Embedding 服务从 CPU 迁到 GPU 后整体延迟降了四成。

第二件是 Token 成本追踪。通过llm.prompt_chars和llm.completion_chars这两个 Tag,你可以在 SkyWalking 的聚合查询里按服务、按时间段统计字符消耗量,再结合模型单价估算成本。如果发现某个工具的 System Prompt 特别长,就可以针对性做 Prompt 压缩。

第三件是异常告警。SkyWalking 支持基于 Trace 指标的告警规则,比如“单条 Trace 内LLM/ChatCompletion调用次数超过 5 次”就触发告警。这能帮你抓住 Agent 死循环——它在“思考-行动”里反复横跳却不产出结果时,Token 正在被无限消耗。

如果你打算把 Agent 长期跑在生产环境,建议把 Coding Plan 也配上,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它更适合需要持续调用、长期编码和 Agent 场景的用量模式。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 LangChain、LlamaIndex 这些框架的对接示例,配合 SkyWalking 的埋点一起用,基本能把 Agent 的可观测性闭环搭起来。

最后说一个我自己的习惯:每次上线新版本的 Agent,先跑一轮固定测试用例,然后在 SkyWalking 里对比新旧版本的 Trace 耗时分布和 Token 消耗。数据不会骗人,哪次改动引入了性能回退,链路图上一目了然。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询