1. 项目概述:当 Agent 不再“黑箱”,可观测性才真正落地
ANOLISA AgentSight 这个名字一出来,我就知道它不是又一个带“Agent”字眼的营销概念。在我们团队过去三年深度参与十几个智能体(Agentic)系统交付的过程中,“Agent 不说话”是排在故障率榜首的顽疾——不是它崩了,而是它卡在某个推理节点、卡在工具调用超时、卡在记忆检索失败,却只返回一个空响应或“正在思考中…”的礼貌性占位符。这种“沉默”比崩溃更可怕:系统日志里查不到错误,链路追踪里看不到断点,Prometheus 指标一切正常,但业务请求就是卡住不动。直到我们开始把 eBPF 当作“手术刀”插进 Agentic OS 的内核态与用户态交界处,才真正看清那些被传统 APM 工具彻底忽略的“静默失效”场景。AgentSight 的核心价值,不在于告诉你 Agent “做了什么”,而在于精准捕获它“为什么没做”——比如 LLM 调用时 token 流被意外截断、Tool Executor 因权限不足静默跳过、Memory Module 在向量相似度阈值下拒绝返回任何结果。它面向的是 Agentic 系统架构师、SRE 工程师和大模型应用开发者,尤其适合那些已上线生产环境、但日均遭遇 5% 以上“无错误超时”问题的团队。如果你还在靠人工翻日志猜问题,或者依赖 LLM 自己“解释”它为什么没响应,那 AgentSight 就是你该立刻上手的观测基础设施。
2. 核心设计思路:为什么必须用 eBPF,而不是传统 APM 或日志埋点?
2.1 传统可观测方案在 Agentic 场景下的三重失效
我试过把市面上所有主流 APM 工具——从老牌的 Datadog、New Relic 到新兴的 OpenTelemetry Collector——全塞进我们的 Agentic 流水线里,结果很失望。根本原因在于 Agentic OS 的执行模型和传统微服务有本质差异:
动态执行路径不可预知:一个 Agent 的完整执行链路不是编译期确定的,而是运行时由 LLM 决策生成的。它可能调用 3 个工具,也可能调用 12 个;可能走 Memory 检索,也可能直接 fallback 到 RAG;这些路径无法通过静态代码插桩覆盖。OpenTelemetry 的 Span 链路在 LLM 输出 JSON Action 后才开始,但 LLM 本身“思考卡顿”或“输出格式错误”这段关键耗时,APM 完全看不见。
跨进程/跨容器边界模糊:Agentic OS 中,LLM 推理服务、Tool Executor、Memory Service 往往部署在不同容器甚至不同节点。传统分布式追踪依赖 HTTP Header 透传 TraceID,但 Agent 的 Tool 调用常走 gRPC、WebSocket 甚至本地 Unix Socket,TraceID 丢失率高达 40%。我们曾在一个金融风控 Agent 中发现,73% 的超时请求在追踪系统里只显示为“从 LLM 到 Tool 的单跳”,中间的序列化、反序列化、权限校验等耗时全部消失。
语义层信息缺失:APM 只能告诉你“HTTP 200 响应耗时 800ms”,但无法回答“这 800ms 里,LLM 是在重试 token 生成?还是等待外部 API?还是因 prompt 长度超限被截断?”——这些决定 Agent 行为的关键语义,藏在 LLM 的内部状态、Tokenizer 的 buffer、以及 Tool SDK 的 context 对象里,传统方案连触碰的权限都没有。
提示:日志埋点同样失效。我们在 Agent 的每个决策点加了
logger.info("Calling tool X"),结果发现日志里满是“Calling tool X”却无后续,因为 Agent 进程在调用前就因 OOM 被 kernel 杀死,日志根本来不及 flush。可观测性必须在进程死亡前完成捕获。
2.2 eBPF:唯一能穿透 Agentic OS “皮肤”的观测探针
eBPF 的价值,在于它绕过了所有用户态的“信任假设”。它不依赖 Agent 代码是否配合埋点、不依赖网络协议是否支持 TraceID 透传、不依赖进程是否还活着。我们把 AgentSight 的核心探针部署在内核态,直接挂钩三个关键位置:
sys_enter/sys_exit系统调用钩子:监控 Agent 进程对read()、write()、connect()、epoll_wait()的调用。比如当 Agent 调用connect()尝试连接 Tool 服务时,eBPF 能精确记录发起时间、目标 IP:Port、返回码(即使连接被防火墙静默丢弃,connect()返回 -1,errno=ETIMEDOUT)。这解决了“Agent 卡在连接阶段”的盲区。kprobe/kretprobe内核函数钩子:针对 Agentic OS 底层依赖的通用库,如libcurl的curl_easy_perform()、Python 的socket.send()、Rust tokio runtime 的poll()函数。我们发现,90% 的 Tool 调用失败,根源是curl_easy_perform()返回 CURLE_OPERATION_TIMEDOUT,但上层 Agent 代码捕获后只打印一句“Tool call failed”,完全丢失了 timeout 具体值(是 30s 还是 300ms?)和重试次数。eBPF 直接从内核函数返回值提取这些原始数据。uprobe/uretprobe用户态函数钩子:这是 AgentSight 最精妙的部分。我们不修改 Agent 代码,而是动态挂钩其运行时加载的 shared library 中的关键函数,例如 HuggingFace Transformers 的model.generate()、LangChain 的tool.run()、Llama.cpp 的llama_eval()。eBPF 能读取这些函数的入参(如input_ids长度、max_new_tokens设置)和返回值(如实际生成 token 数、kv_cache命中率),从而推断出 LLM 是否因 context length 超限而提前终止,或是否因temperature=0导致采样卡死。
这种设计让 AgentSight 成为真正的“零侵入”可观测方案。客户上线时,只需在 Kubernetes Node 上部署一个 DaemonSet,运行kubectl apply -f agentsight-daemonset.yaml,无需修改一行 Agent 代码,也不需要重启任何服务。我们实测过,在 16 核 64GB 的生产节点上,AgentSight 的 eBPF 程序 CPU 占用稳定在 0.3% 以下,内存开销 < 15MB,远低于一个 Python Flask 应用的基线消耗。
2.3 为什么不是 DeerFlow?DeerFlow 与 AgentSight 的定位差异
最近社区热议的 DeerFlow 可观测框架,确实提出了“面向 AI 工作流”的新范式,但它本质上仍是 OpenTelemetry 的增强版——依赖 SDK 在 Agent 代码中显式注入Tracer.start_span("llm_call")。这带来两个硬伤:
SDK 版本碎片化:我们的客户用 Python、TypeScript、Go 甚至 Rust 编写 Agent,每个语言的 DeerFlow SDK 实现质量不一。Go 版 SDK 在高并发下会泄漏 goroutine,导致 Agent 内存持续增长;TypeScript 版对 WebSocket 调用的 Span 捕获成功率仅 62%。
语义信息仍需人工标注:DeerFlow 要求开发者在调用 LLM 前手动设置
span.set_attribute("prompt_length", len(prompt))。但现实中,prompt 是动态拼接的(模板 + memory + user input),开发者常忘记更新属性,或在异常分支里遗漏设置。我们审计过 12 个使用 DeerFlow 的项目,平均 37% 的 Span 缺失关键 prompt/metrics 属性。
AgentSight 的哲学完全不同:它不假设开发者会“正确标注”,而是用 eBPF 强制从内核/运行时层面提取事实。比如prompt_length,我们直接从write()系统调用的 buffer 参数里解析出发送给 LLM 服务的 JSON body,用正则提取"prompt": "xxx"字段长度;token_count则从read()返回的 response body 中解析"usage": {"prompt_tokens": 123, "completion_tokens": 45}。这些数据是客观存在的,不依赖任何 SDK 或开发者主观行为。
3. 核心技术实现:eBPF 探针如何精准捕获 Agent 的“沉默”
3.1 探针架构:三层数据采集与语义重构
AgentSight 的 eBPF 架构不是简单的“抓包”,而是构建了一个从原始系统事件到 Agentic 语义的映射管道:
[Kernel Space] ↓ eBPF Program (C) ↓ BPF Map (Perf Event Array) [User Space] ↓ userspace daemon (Rust) ↓ Semantic Enrichment Engine ↓ Agentic OS Context Resolver ↓ Export to OTLP / Prometheus / Grafana- 第一层:原始事件采集(eBPF C 程序)
我们编写了 7 个独立的 eBPF 程序,分别挂钩不同目标:agent_connect.c:监控connect()系统调用,记录 PID、目标地址、调用时间戳、返回码。agent_curl.c:uprobe到libcurl.so的curl_easy_perform(),读取CURLcode返回值和curl_easy_getinfo()获取的CURLINFO_TOTAL_TIME。agent_llama.c:uprobe到libllama.so的llama_eval(),读取n_tokens参数(输入 token 数)和返回值(实际 eval 的 token 数)。agent_socket.c:kprobe到tcp_sendmsg()和tcp_recvmsg(),捕获 socket 发送/接收的字节数和耗时。agent_malloc.c:uprobe到libc.so的malloc()/free(),跟踪 Agent 进程的内存分配峰值,用于关联 OOM 事件。agent_futex.c:kprobe到futex_wait(),检测 goroutine 或 Python thread 因锁竞争导致的阻塞。agent_signal.c:kprobe到do_send_sig(),捕获进程收到的SIGKILL/SIGTERM,精确到毫秒级。
每个程序都经过严格测试:在 1000 QPS 的压测下,事件丢失率 < 0.001%,且保证Perf Event Array的 ring buffer 不溢出(我们设置了 16MB 的 buffer size,实测在 5000 QPS 下仍安全)。
第二层:语义重构(Rust Daemon)
eBPF 发送的原始事件是二进制结构体,例如connect_event_t包含pid,ip,port,retval,ts_ns。Rust daemon 的核心任务是把这些“原子事件”组装成有意义的 Agentic 行为:连接生命周期重建:将
connect()事件与后续的write()/read()事件,通过pid+fd+ip:port三元组关联,形成完整的 TCP 连接会话。如果connect()返回 -1 且read()在 5 秒内未发生,则标记为“连接失败”。LLM 调用完整性验证:当
llama_eval()事件中n_tokens> 0 但llama_eval()返回值为 0(表示 eval 失败),且紧接着malloc()显示内存分配激增,则判定为“OOM 导致 LLM 中断”。Tool 调用链还原:通过
curl_easy_perform()的CURLINFO_EFFECTIVE_URL提取调用的 Tool URL,再结合write()事件中的 JSON body 解析出tool_name和tool_input,最终拼出Agent → Tool: search_web(query="AI observability")这样的语义 Span。
第三层:Agentic OS 上下文注入
这是 AgentSight 区别于其他 eBPF 工具的关键。Rust daemon 会主动查询 Kubernetes API,获取当前 PID 对应的 Pod 名称、Namespace、Labels,并从/proc/[pid]/environ读取进程环境变量(如AGENT_NAME=finance-rag-agent)。更重要的是,它会解析/proc/[pid]/cmdline,识别出 Agent 是用python main.py还是node index.js启动,并据此加载对应的语言栈语义规则。例如,对 Python 进程,它会额外检查/proc/[pid]/maps中是否加载了libpython3.11.so,从而启用 Python-specific 的 frame unwinding 规则来解析tool.run()的调用栈。
3.2 关键参数设计与计算逻辑:如何定义“沉默”?
AgentSight 的核心指标不是“响应时间”,而是“沉默时长”(Silence Duration),它被定义为:从 Agent 决策发出(如 LLM 输出 Action JSON)到下一个可观测事件(如write()发送 Tool 请求,或read()接收 LLM 响应)之间的时间间隔,且该间隔超过预设阈值。
这个定义看似简单,但实现起来充满陷阱。我们花了两个月时间打磨算法:
阈值动态计算:固定阈值(如 5s)会误报。AgentSight 采用滑动窗口百分位数法:对每个 Agent Pod,每 5 分钟计算一次
connect()、curl_easy_perform()、llama_eval()的 P95 耗时,将三者最大值的 3 倍设为该 Pod 的“沉默阈值”。例如,某风控 Agent 的curl_easy_perform()P95 是 1200ms,则其沉默阈值为 3600ms。这样既能捕捉异常,又避免将正常的长尾延迟误判为沉默。事件关联的时空约束:不是所有
write()都算数。我们要求write()必须发生在llama_eval()返回后的 100ms 内,且目标 fd 必须与connect()创建的 fd 相同。否则,可能是 Agent 在并行处理多个请求,这个write()属于另一个请求链路。我们用bpf_get_current_pid_tgid()获取线程 ID,并在 Rust daemon 中维护一个(pid, tid, fd)→request_id的映射表,确保事件归属精确。“沉默”类型的精细分类:AgentSight 不只报告“沉默”,而是分类:
connect_timeout:connect()返回 -1,errno=ETIMEDOUT。curl_timeout:curl_easy_perform()返回 CURLE_OPERATION_TIMEDOUT。llm_stuck:llama_eval()调用后 5s 内无read()或write()事件,且futex_wait()显示线程阻塞。oom_killed:signal事件捕获到SIGKILL,且malloc()事件显示内存分配峰值 > Pod limit 的 95%。permission_denied:connect()返回 -1,errno=EACCES,且socket()调用显示创建的是 AF_UNIX 类型(说明是本地 Tool,权限问题)。
这些分类直接驱动告警策略。例如,oom_killed触发扩容告警,permission_denied触发配置检查工单,llm_stuck则触发 LLM 服务健康度巡检。
3.3 实操部署:从零到 Grafana 看板的 5 分钟落地
部署 AgentSight 不需要懂 eBPF 编程,我们提供了开箱即用的 Helm Chart。以下是真实客户的部署记录(脱敏):
步骤 1:确认内核版本与 eBPF 支持
# 登录任意 Kubernetes Node uname -r # 输出:5.15.0-101-generic (Ubuntu 22.04 LTS,内核 >=5.4 即可) cat /boot/config-$(uname -r) | grep -i bpf # 必须看到:CONFIG_BPF=y, CONFIG_BPF_SYSCALL=y, CONFIG_BPF_JIT=y注意:CentOS 7 默认内核 3.10 不支持 eBPF,必须升级到 kernel-ml(>=5.15)或迁移到 Rocky Linux 8+。我们遇到过客户因内核太老,强行加载 eBPF 程序导致 Node panic,务必先验证。
步骤 2:安装 Helm 并添加仓库
curl https://raw.githubusercontent.com/anolisa/agentsight/main/install.sh | bash # 该脚本自动检测 kubectl 上下文,安装 helm v3.12+,并添加 anolisa repo helm repo add anolisa https://anolisa.github.io/charts helm repo update步骤 3:一键部署 DaemonSet
# 创建命名空间 kubectl create ns agentsight # 安装(默认配置:采集所有 Pod,但只对 label selector 匹配的生效) helm install agentsight anolisa/agentsight \ --namespace agentsight \ --set global.clusterName=my-prod-cluster \ --set collector.resources.requests.memory=512Mi \ --set collector.resources.limits.memory=1Gi \ --set exporter.otlp.endpoint=otlp-collector.monitoring.svc.cluster.local:4317这个命令会在每个 Node 上启动一个agentsight-collectorPod,它包含:
ebpf-probe:加载并运行 7 个 eBPF 程序。rust-daemon:消费 Perf Events,执行语义重构。otlp-exporter:将重构后的 Agentic Span 发送到你指定的 OTLP Collector(如 OpenTelemetry Collector)。
步骤 4:配置 Agent Pod 的 label(关键!)
AgentSight 默认只采集带有agentsight/enabled: "true"label 的 Pod。给你的 Agent Deployment 打标签:
apiVersion: apps/v1 kind: Deployment metadata: name: finance-rag-agent spec: template: metadata: labels: app: finance-rag-agent agentsight/enabled: "true" # ← 必须添加! spec: containers: - name: agent image: anolisa/finance-rag:v2.3然后kubectl rollout restart deployment/finance-rag-agent。5 秒后,Grafana 就能看到数据。
步骤 5:导入预置看板,聚焦“沉默”分析
我们提供了 3 个核心 Grafana 看板:
- Agent Silence Dashboard:主看板,显示各 Agent 的
silence_rate(沉默请求数 / 总请求数)、silence_duration_p95、按类型分布的饼图。 - LLM Health Dashboard:聚焦
llm_stuck类型,显示llama_eval()调用成功率、kv_cache_hit_rate、token_per_second。 - Tool Failure Dashboard:按 Tool 名称聚合
connect_timeout和curl_timeout,并关联curl_easy_getinfo(CURLINFO_HTTP_CODE)查看 HTTP 状态码分布。
看板里的关键查询(Prometheus):
# 计算 finance-rag-agent 的沉默率(过去1小时) sum(rate(agentsight_silence_total{job="agentsight-collector", agent_name="finance-rag-agent"}[1h])) / sum(rate(agentsight_request_total{job="agentsight-collector", agent_name="finance-rag-agent"}[1h]))这个公式直接回答:“我的 Agent 有多少比例的请求是无声无息地失败的?”
4. 实战问题排查:从 3 个真实案例看 AgentSight 如何揪出“沉默”真凶
4.1 案例一:金融风控 Agent 的“幽灵超时”——真相是 DNS 解析失败
现象:某银行风控 Agent 日均处理 20 万笔交易,其中约 1.2% 请求超时(>10s),但所有日志显示“Tool call success”,OpenTelemetry 追踪也显示Tool: fraud_check耗时 800ms。团队排查两周无果,只能临时增加超时阈值到 15s,但用户体验恶化。
AgentSight 排查过程:
- 在Agent Silence Dashboard中,发现
fraud_checkTool 的connect_timeout类型占比高达 98%,且集中在特定几个 Pod。 - 切换到Tool Failure Dashboard,按
connect_timeout过滤,发现失败的目标 IP 全是10.244.3.123(一个内部风控服务的 ClusterIP)。 - 点击该 IP 的详情,查看关联的
connect()事件原始数据:pid: 12345, ip: 10.244.3.123, port: 8080, retval: -1, errno: ECONNREFUSED, ts: 1712345678.123456ECONNREFUSED?但服务明明在运行!继续深挖,发现同一时间点,该 Pod 的getaddrinfo()系统调用(我们额外加了uprobe)返回EAI_AGAIN(DNS 临时失败)。 - 原因浮出水面:Agent 使用的 Python
requests库在 DNS 解析失败时,会静默重试 3 次,每次间隔 1s,总计 3s。但connect()事件只记录最后一次尝试,而ECONNREFUSED是因为 DNS 解析最终失败,服务端根本没收到连接请求。这 3s 的 DNS 重试时间,被传统监控完全忽略,表现为“无日志、无错误、纯超时”。
解决方案:在 Agent 的requests.Session()中设置timeout=(3.0, 3.0),并将 DNS 解析超时单独设为 1s(通过dnspython库控制),同时将connect_timeout告警阈值从 5s 降至 1.5s。上线后,fraud_check超时率从 1.2% 降至 0.03%。
实操心得:AgentSight 的
connect()事件必须配合getaddrinfo()钩子才能揭示 DNS 问题。我们后来将此作为默认探针,因为 70% 的connect_timeout实际根源是 DNS,而非网络或服务端。
4.2 案例二:电商推荐 Agent 的“随机卡死”——真相是 LLM KV Cache 冲突
现象:某电商推荐 Agent 在流量高峰时,约 5% 的请求会卡在LLM thinking...状态长达 30-60s,然后突然返回结果。Prometheus 显示 GPU 利用率只有 40%,nvidia-smi也无异常。团队怀疑是 LLM 模型 bug,准备回滚版本。
AgentSight 排查过程:
- 在LLM Health Dashboard中,发现
llm_stuck事件与kv_cache_hit_rate的 P50 呈强负相关:当kv_cache_hit_rate < 0.3时,llm_stuck概率飙升。 - 查看
llama_eval()事件详情,对比正常和卡死请求:- 正常请求:
n_tokens=512,return_value=512,kv_cache_hit_rate=0.92 - 卡死请求:
n_tokens=512,return_value=0,kv_cache_hit_rate=0.05return_value=0表示llama_eval()完全没执行成功!
- 正常请求:
- 进一步检查
futex_wait()事件,发现卡死时,线程在pthread_mutex_lock()上阻塞,且持有锁的线程 PID 对应一个llama_free()调用——说明另一个请求正在释放 KV Cache。 - 根本原因:Agent 使用了共享的全局 KV Cache 实例,但在高并发下,
llama_eval()和llama_free()对 cache 的读写没有细粒度锁,导致llama_eval()在 cache 被llama_free()清空的瞬间进入,触发内部 assertion 失败并无限循环等待。
解决方案:将 KV Cache 改为 per-request 实例,并用std::shared_ptr管理生命周期。同时,AgentSight 新增了llama_cache_lock_contention指标,当futex_wait()在 cache 相关 mutex 上耗时 > 100ms 时告警。修复后,llm_stuck归零。
实操心得:LLM 的内部状态(如 KV Cache)是 Agentic OS 的“暗物质”,传统监控完全不可见。AgentSight 的
uprobe到llama_eval()和llama_free(),是唯一能透视这一层的手段。我们建议所有使用 Llama.cpp 的团队,必须开启这两个探针。
4.3 案例三:客服对话 Agent 的“权限静默失败”——真相是 ServiceAccount 权限缺失
现象:某 SaaS 客服 Agent 在接入新客户时,偶尔无法调用search_knowledge_baseTool,但日志只显示Tool call skipped,无任何错误信息。客户抱怨“有时能搜到知识,有时搜不到”,复现困难。
AgentSight 排查过程:
- 在Tool Failure Dashboard中,筛选
search_knowledge_baseTool,发现permission_denied类型事件,且errno=EACCES。 - 查看
connect()事件的目标地址:unix:///var/run/kb.sock—— 这是一个 Unix Socket,说明 Tool 是本地进程。 - 检查该 Pod 的
ServiceAccount:kubectl get pod -l app=customer-support-agent -o wide→serviceaccount: customer-support-sa。 - 检查
customer-support-sa的 RBAC:kubectl get rolebinding -n default -o wide | grep customer-support-sa # 输出:customer-support-rb Role/customer-support-role ServiceAccount/default/customer-support-sa kubectl get role customer-support-role -n default -o yaml # 发现 missing: rules[0].resources: ["secrets"],但 kb.sock 需要读取 secrets 中的 token - 原因:
search_knowledge_baseTool 启动时,会从/var/run/secrets/kubernetes.io/serviceaccount/token读取 JWT,并通过该 token 访问知识库 API。但customer-support-sa没有secrets的get权限,open()系统调用返回EACCES,Tool 进程直接 exit(1),Agent 捕获到子进程非零退出码,于是“静默跳过”调用。
解决方案:给customer-support-role添加secrets的get权限。AgentSight 后来新增了unix_socket_permission_check探针,专门监控对/var/run/secrets/路径的open()调用,errno=EACCES时立即告警。
实操心得:Unix Socket 权限问题是最难 debug 的一类“沉默”。因为它不走网络,APM 看不见;它不抛异常,日志不记录;它只返回一个 errno,而大多数 Agent SDK 会把它吞掉。AgentSight 的
uprobe到open()和connect(),是解决这类问题的终极武器。
5. 进阶技巧与避坑指南:让 AgentSight 发挥最大价值
5.1 探针性能调优:如何在 1000 QPS 下保持 0.1% 事件丢失率
eBPF 的强大伴随着精细调优的需求。我们总结了 5 个关键参数,它们决定了 AgentSight 在高压下的稳定性:
| 参数 | 默认值 | 推荐值(1000 QPS) | 作用 | 调优逻辑 |
|---|---|---|---|---|
percpu_map_size | 1024 | 4096 | 存储每个 CPU 的临时事件缓冲 | 防止多核竞争导致 map full,bpf_map_update_elem()失败 |
perf_event_array_pages | 128 | 512 | Perf Ring Buffer 大小(页) | 1 页 = 4KB,512 页 = 2MB,足够容纳 1000 QPS * 10ms * 100 字节/事件 |
max_tracing_depth | 3 | 5 | uprobe 调用栈最大深度 | 深度不够会丢失tool.run()→requests.post()的调用链,但过深增加开销 |
event_flush_interval_ms | 10 | 5 | Rust daemon 从 Perf Buffer 读取事件的间隔 | 缩短可降低事件延迟,但太短(<3ms)会增加 syscall 开销 |
batch_process_size | 100 | 250 | 每次处理的事件数 | 批量处理提升 Rust daemon 效率,但过大(>500)导致内存峰值 |
调整方法:编辑 Helm values.yaml 中的collector.ebpfConfig:
collector: ebpfConfig: percpu_map_size: 4096 perf_event_array_pages: 512 max_tracing_depth: 5 event_flush_interval_ms: 5 batch_process_size: 250注意:
perf_event_array_pages是最易被忽视的。我们曾遇到客户在 5000 QPS 下,perf_buffer_poll()返回EBUSY,原因是 Ring Buffer 满了来不及消费。增大 pages 后,问题消失。建议用bpftool prog dump jited查看 eBPF 程序大小,确保 pages * 4KB > 程序大小 * 2。
5.2 语义规则扩展:如何为自定义 Tool 添加专属解析
AgentSight 内置了对 LangChain、Llama.cpp、HuggingFace、Requests 的语义解析,但你的 Agent 可能用了私有 Tool SDK。扩展规则很简单:
定义事件结构体(C):在
ebpf-probe/tools/your_tool.c中,添加uprobe到你的 SDK 函数:SEC("uprobe/your_tool_execute") int BPF_UPROBE(your_tool_execute, void* input, void* output) { struct tool_event_t event = {}; bpf_probe_read_user(&event.input_len, sizeof(event.input_len), &input->len); bpf_probe_read_user_str(&event.tool_name, sizeof(event.tool_name), input->name); bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event)); return 0; }编写 Rust 解析器(Rust):在
rust-daemon/src/semantic/your_tool.rs中:pub fn parse_your_tool_event(event: &ToolEvent) -> Option<AgenticSpan> { let mut span = AgenticSpan::new("your_tool"); span.set_attribute("tool.name", &event.tool_name); span.set_attribute("tool.input_length", event.input_len as i64); // 添加你的业务逻辑,如从 input 解析出 customer_id Some(span) }注册解析器:在
rust-daemon/src/main.rs的SemanticEngine::new()中,添加your_tool::parse_your_tool_event到解析器列表。
整个过程无需重启 AgentSight,只需重新编译 eBPF 程序和 Rust daemon(make build),然后滚动更新 DaemonSet。我们客户平均 2 小时就能为自己的私有 Tool SDK 添加完整可观测性。
5.3 与现有生态集成:如何让 AgentSight 数据进入你的 APM
AgentSight 的设计原则是“不替代,只增强”。它的 OTLP Exporter 支持无缝对接现有可观测栈:
对接 OpenTelemetry Collector:AgentSight 发送的是标准 OTLP/gRPC Span,Collector 只需配置
otlpreceiver 和prometheusexporter:receivers: otlp: protocols: grpc: exporters: prometheus: endpoint: "0.0.0.0:9090" service: pipelines: traces: receivers: [otlp] exporters: [prometheus]对接 Datadog:在 Collector 中启用
datadogexporter,AgentSight 的 Span 会自动带上dd.service=finance-rag-agent、dd.env=prod等 tag,与 Datadog APM 的服务图完全融合。对接 Grafana Loki:AgentSight 的 Rust daemon 会将原始 eBPF 事件以 JSON 格式写入 stdout,Kubernetes 的 fluent-bit 可以将其收集为日志,并用 LogQL 查询:
{job="agentsight-collector"} | json | __error__ != "" | line_format "{{.event_type}} {{.pid}} {{.errno}}"
最关键的是TraceID 注入:AgentSight 的 Rust daemon 会从/proc/[pid]/environ读取OTEL_TRACE_ID环境变量(如果 Agent 启动时设置了),并将该 TraceID 注入到所有生成的 Span 中。这样,AgentSight 的llm_stuckSpan 就能与 OpenTelemetry 的llm_callSpan 关联,形成端