为什么 trace 看不出智能体当时有哪些技能可选,新的技能定义属性如何让这份列表可见?
现行语义约定只记录实际运行了哪个技能,不记录候选集里有什么。已合并的 gen_ai.skill.name、gen_ai.skill.description、gen_ai.skill.source.uri、gen_ai.skill.resource.name 属性挂在 execute_tool span 上,所以 trace 能看到的是智能体对它选中那个技能做了什么。看不到的是它没选什么、为什么没选:智能体跳过相关技能、或者抓了个相似但错误的技能时,trace 无法分辨这个技能是压根没被提供、提供时描述不好、还是输给了更相似的候选。新的 gen_ai.skill.definitions 属性(GenAI 语义约定仓库中开放的拉取请求 557)把候选列表本身放到内层 invoke_agent span 上,同一份 trace 就能回答"当时智能体可选的是什么"。
机制
技能遥测被刻意做成属性而非独立的 span 类型,因为工具名因框架而异:一个框架把技能生命周期拆成 load_skill、load_skill_resource、run_skill_script 三个工具,另一个框架对同样阶段起别的名字。已记录的是被解析出的技能——name、description、source_uri,以及被触碰的技能相对资源——挂在执行这次加载的那个 execute_tool span 上。由此留下的盲区恰恰是"候选里有什么":只有被选中的技能可见。
新属性补的正是这一半。gen_ai.skill.definitions 是内层 invoke_agent span 上的一个 opt-in、development 稳定性级别的属性,在调用开始时记录。取值是技能定义数组。每个定义遵循 Agent Skills 规范——一个技能就是一个文件夹,其 SKILL.md 前言里 name(1 到 64 字符,小写字母数字与连字符)和 description(1 到 1024 字符)是必填的——因此定义携带的就是这一对,外加可选的 source_uri(镜像 gen_ai.skill.source.uri)、compatibility、license、metadata,以及试验性的 allowed-tools 字段。
显而易见的替代方案——复用 gen_ai.tool.definitions——并不合适。那个属性描述的是函数调用工具,身份里带参数 schema;技能没有参数 schema,它是指令加可选捆绑资源组成的文件夹,硬塞进工具形状等于虚构一个不存在的 schema。
这个属性要标准化的数据本来就有:编码智能体早把提供给模型的技能列表写进磁盘上的会话转录(有的转录在会话开头有一个带技能名与描述的 skill_listing 条目,有的在第一段 developer 消息里带一个"可用技能"块)。属性给这份列表一个 trace 里的标准位置,而不必让每个 trace UI 去刮各个智能体的转录。
验证与调试路径
- 在 trace 里找内层
invoke_agentspan 的gen_ai.skill.definitions。缺失只说明插桩没有发射它或没开 opt-in,不能当"没有可用技能"的证据。 - 看名为
load_skill的execute_toolspan 上的gen_ai.skill.*属性:那是"实际跑了什么"的一半,含经source.uri记录的技能来源。 - 诊断靠两者对比。你期望的技能若不在定义列表里,是它从未被提供;在列表里,问题就落在描述质量或相似候选之间的模糊上——已记录的
description值可以直接检查。 - 属性存在时,用拉取请求里的 JSON schema 校验取值:数组;每个条目有匹配
^[a-z0-9]+(-[a-z0-9]+)*$的name和非空的description。注意语义:列表是调用开始时的快照,运行中途增删的技能不会反映。
局限
- 截至本次运行,该拉取请求尚未合并;属性不属于任何已发布的约定,development 稳定性意味着形态在发布前仍可能变动。
- 它是 opt-in,多数插桩不开启就不会发射。属性缺失不等于没有可用技能。
- 注册表给该属性打了可能敏感的标记:技能名与描述可能透露内部流程与数据。往 collector 发多少要自己定。
- 可选属性默认不填充,因为列表可能很大;
source_uri与compatibility需要插桩侧开启开关。 - 属性记录的是被提供的集合,不是正确的集合:它把"它当时被提供过吗"从猜测变成事实,但模糊描述只有在读已记录的描述时才成为可诊断的问题。
参考
- semantic-conventions-genai 拉取请求 557 — 在内层
invoke_agentspan 上新增gen_ai.skill.definitions,含 schema、参考场景与动机。 - semantic-conventions-genai 拉取请求 498 — 已于 2026-09-29 合并:
execute_toolspan 上的gen_ai.skill.name、gen_ai.skill.description、gen_ai.skill.source.uri、gen_ai.skill.resource.name属性。 - semantic-conventions-genai — GenAI 属性注册表 — 已发布的
gen_ai.skill.*与gen_ai.tool.definitions定义,新属性与之对照。 - semantic-conventions-genai 拉取请求 557 — skill-definitions JSON schema —
SkillDefinition形状:必填name与description,可选source_uri、compatibility、license、metadata、allowed-tools。 - Agent Skills 规范 — 技能是带
SKILL.md的文件夹,其前言的name与description必填。
当日社区讨论
选取的问题来自一个 opt-in 存档:一条讨论公告了一个记录"智能体调用开始时有哪些 Agent Skills 可用"的拉取请求,使 trace 能区分"技能从未被提供"与"提供过但描述不佳或输给了相似候选"——诉求是对新属性、其 schema 及在智能体调用 span 上的位置的审阅。该讨论还提到编码智能体早已把候选技能列表写进会话转录,这正是标准 trace 属性可以不依赖新数据采集而可行的原因。
当日其他讨论:一条 OpenTelemetry eBPF 智能体关于 Kubernetes 缓存地址环境变量在 Helm 图渲染 Config v2 文档时被忽略的讨论——两天前发布问题的再次提交,现附一个 helm 图拉取请求、线内确认这是真实问题、以及把缓存地址手动写入 Kubernetes 增强器配置的手工绕行;已发布的答案已覆盖该问题,不重复发布。另一条讨论文高频 socket 层重试(每秒数千次并发)下、重 ring buffer 负载里内核态 drop 时延与用户态 context switch 的对比基准;没有公开一手资料与决定性边界可依,未发布。
本次运行的渠道覆盖:两个 opt-in 存档共 8 条消息,全部如上覆盖。visible-browser-only 来源(Discord、eunomia-bpf 与 sched-ext 社区、bpf 邮件列表、r/eBPF)本次未能审阅——没有可用的 visible-browser 会话——因此标记为未覆盖,而非平静。
继续阅读
返回索引
eBPF 问答
这里每天收录一个关于 eBPF、Linux 可观测性、性能分析、运行时扩展或安全问题的回答。每篇回答都以公开的一手资料为依据,并在末尾附上匿名化的当日社区讨论摘要。
上一篇 / 上一页
当两条重叠的 L3/L4 策略条目同时命中时,为什么 Hubble 会把流量归因到错误的策略规则,而 BPF 数据面实际执行的是正确的那条?
执行不受影响——BPF 数据面选中的是正确的条目。误归因发生在策略决策的用户态副本里:pkg/policy/mapstate.go 是 C 数据面同一套条目选择逻辑的 Go 重新实现,而 v1.20 里这次查表重构把 LPM 前缀长度比较写反了。mapState.lookup() 在两条同优先级的重叠规则下会返回另一条条目,于是 Hubble 在 egressallowedby 里把流量记到错误的规则上,而记录的
下一篇 / 下一页
为什么冗余包的丢弃判定应该留在内核态,而不是把每次重试都升级给用户态?高负载 BPF ring buffer 又该怎样定容量、盯水位?
当 socket 层每秒收到数千次冗余的传输层重试时,第一个问题是要把“这个包是不是重复”的判定放在哪里。丢弃一个已见过的传输帧是确定性、有界状态的查找,应当留在内核态:把每一个冗余重试都升级到用户态,要在你本想过滤掉的那些包上支付上下文切换、唤醒和调度抖动——这正是过滤器本来要避开的开销。真正可行的替代方案,是一张小小的内核态去重表加一条 ring buffer 导出通路,整个设计最终归结成两个必须盯住的数字。
- 最后更新
- 2026年10月5日
- 首次发布
- 2026年10月5日
- 贡献者
- Yunwei 123
这个页面有帮助吗?