1. 项目背景为什么可观测性需要“Agent Skills”先说结论Elastic Observability 的 Agent Skills本质上是给运维和开发者的 AI 助手装上一套可复用的“职业技能包”。它让原本只能聊天、提供建议的大模型真正具备调用 Elastic 观测数据和执行分析操作的能力比如自动查日志、算 SLO、关联 Trace 链路、定位磁盘水位异常甚至直接生成根因分析报告。我接触 Elastic Observability 已经有几年了从早期 ELK 时代的“日志搜索平台”到后来整合 Logs、Metrics、APM、Uptime、Profiling 的统一可观测性平台再到 8.x 开始把 AI Assistant 融入 Kibana最大的感受是工具链越来越完善但“使用者”的操作门槛一直还在那里。你仍然需要懂 ES|QL、懂 Query DSL、懂数据的字段语义才能在海量观测数据里捞出有效信息。Agent Skills 的出现很大程度上就是在补这块短板。做个不严谨但很贴切的类比大模型本身像一个刚毕业、知识面很广但没有任何实操经验的实习生。它能背出《可观测性最佳实践》的全部章节但你要让它“看看昨晚订单服务为什么响应变慢了”它不知道去哪查、查什么、查到之后怎么一步步验证。而 Agent Skills 就是给这个实习生配备的《老师傅操作手册》《工具箱》——手册告诉他遇到这类问题的标准处置路径工具箱给他真正可用的查询接口、API 和数据分析工具。这项目的适用人群非常明确运维工程师、SRE、平台开发、以及那些需要对大量观测数据做快速诊断的技术负责人。不需要你是精通 Elasticsearch 底层原理的专家但如果你完全不懂基本的数据结构概念上手会稍微吃力一点。下面我按自己的实践路径把思路、配置、踩坑完整拆开讲。2. 整体设计思路拆解Agent、工具与技能的三角关系要理解 Agent Skills先得理解 Elastic 在可观测性 AI 上的整体分层设计。我个人把它拆成三层这样看代码和配置就不会乱。2.1 第一层Agent执行者这层是真正跟大模型交互的“大脑”。在 Elastic 的语境里它对应的是 AI Assistant、或者你通过 Elastic API / Connector 接入的自定义 Agent。它可以是一个完整的对话系统也可以只是一个接收指令、调用工具、返回结果的轻量执行体。关键点是Agent 本身不拥有业务技能它只负责理解用户意图、拆解任务、调度下面两层的资源并把最终结果组织成人类能读懂的结论。所以它的核心指标是“对意图的解析准确率”而不是“知识覆盖面”。2.2 第二层Tools执行的手脚Tools 是 Agent 能调用的具体原子能力。在 Elastic Observability 环境里常见的 Tools 包括执行 ES|QL 查询从 Elasticsearch 索引里拉取日志或指标调用 APM 接口查询服务链路、吞吐量、延迟和错误率查询告警历史、获取 SLO 状态和燃烧率读取仪表盘Dashboard的 PANEL 数据或预设的聚合结果触发诊断脚本或调用内部 CMDB 接口补充资产信息这一层的设计核心是接口要小、语义要清晰。一个 Tool 只做一件事入参出参都可预期。比如apm_get_service_transactions这个 Tool 的入参是 service.name、time range出参是 transaction 列表、耗时百分位数、错误率。Agent 不需要理解 APM 底层怎么聚合数据它只需要知道“这个工具能给我什么”。2.3 第三层Skills方法论与决策路径Skills 是介于 Agent 和 Tools 之间的“套路封装”。它把一份操作流程标准化成可复用的技能比如“磁盘空间告警排查技能”“SLO 掉线根因诊断技能”“新版本发布后异常检测技能”。每个 Skill 内部通常会包含三样东西触发条件什么场景下该用这个技能比如收到“disk usage”相关告警执行路径按什么顺序调用哪些 Tools、每步期望得到什么结果决策规则如何根据中间结果判断下一步往哪走什么情况下停止排查这种设计的好处是显而易见的它把“老师傅脑子里的排查经验”从隐性知识变成了显式的、可版本化、可评审、可持续改进的配置资产。新人不需要靠三年踩坑来积累经验只要 Agent 加载了对应 Skill就能按标准路径走一遍流程。有朋友可能会问为什么要单独搞一层 Skills而不是让 Agent 直接通过 Prompt 调用 Tools答案是纯 Prompt 驱动的方式在复杂场景下高度不稳定。LLM 每轮生成的工具调用序列都可能漂移今天给它同样的输入明天可能就换了一条路径而且一旦某个 Tool 返回格式稍微变化Agent 就不知道该不该继续往下走。Skills 把这些不确定性收敛住路径是预设的LLM 只在预设的路口做选择题而不是自由发挥画地图。3. 核心机制与配置拆解如何让技能正确落地理论清楚了下面进入实操层。我会把 Agent Skills 的完整配置拆成“技能定义—数据与工具准备—权限与安全”三个小节每个部分都会给出能直接落地的参考示例和参数选型逻辑。3.1 技能定义名称、描述与触发条件的学问一个 Skill 的配置块长什么样以我维护过的一个真实场景为例——“磁盘空间异常增长排查”。在 Elastic AI Assistant / Agent 的自定义技能配置区核心字段大概是这样的skill: name: disk_space_growth_investigation description: | 当出现文件系统磁盘使用率过高、磁盘空间告警、日志报 No space left on device 时使用。 目标定位高占用目录、评估增长速率、判断是否需要立即扩容。 trigger: - 收到磁盘空间告警 - 用户提问中同时出现 disk / space / full / capacity - 日志检索命中 No space left steps: - task: query_node_disk_usage tool: esql_run_query params: query: FROM metrics-* | WHERE metricset.name filesystem | EVAL percent last(disk.used.pct) * 100 | STATS avg_usage avg(percent) BY host.name, mount.point | SORT avg_usage DESC - task: identify_top_writers tool: esql_run_query params: query: FROM logs-* | WHERE event.dataset system.syslog OR message LIKE *error* # 实际场景可替换为文件增长审计或者跟踪索引写入速率 depends_on: query_node_disk_usage - task: compute_growth_rate tool: stats_calculate params: metric: disk.used.bytes window: 6h depends_on: identify_top_writers decision: - if_avg_usage_gt: 80 then: 输出 TOP 目录清单并建议优先清理缓存/日志轮转 - if_avg_usage_lt: 80 then: 生成观察报告并提示持续监控这里有几个值得注意的细节Description 字段要写“触发线索”而不是空泛的功能描述。我见过很多配置把 description 写成“用于磁盘空间分析”结果 Agent 在用户说“我的机器写不了文件了”的时候根本没有调用它。正确做法是穷举真实用户会怎么表述这类问题把词面和语义变体都写进去。Steps 之间要显式声明依赖关系。上面用depends_on控制了执行顺序这是为了让 Agent 不提前乱跑。没有依赖关系时Agent 可能同时把两个查询丢出去拿回结果后对不上时间窗口分析结论就废了。Decision 块不要贪多。新手最容易犯的错是把所有可能的末梢情况都写成规则结果 Skill 配置本身变成了一坨无法维护的 JSON。我的原则是只写能覆盖 80% 情况的两三条主干决策剩下的交给 LLM 根据已有上下文做判断。触发条件的设计也需要特别留意。Elastic 的技能触发既可以是显式的用户输入匹配关键词也可以是隐式的AI Assistant 根据对话历史推断当前意图该用哪个技能。我强烈建议在初始阶段优先做显式触发把用户输入的关键词、告警标题的特征词都覆盖到每次漏触发都记下来补进 description 和 trigger 里。等积累了足够多的真实触发样本再考虑做更“聪明”的语义路由实测下来这样最稳。3.2 数据与工具准备没有好数据技能就是空中楼阁你给 Agent 定义了完美的技能路径但如果底层的 Tool 查不到干净、及时、结构一致的数据整个技能就只是花架子。这一步我建议按下面三个动作来准备。第一步梳理你的观测数据资产建立“字段词典”在配置 ES|QL 工具之前先用 Kibana 的 Data Views / Index Patterns 把常用数据源过一遍日志索引logs-、指标索引metrics-、APM 数据traces-apm*, metrics-apm*、SLO 数据.slo-*等。对每个数据源搞清楚核心字段的语义。比如日志类timestamp、log.level、service.name、host.name、message指标类metricset.name、system.cpu.total.pct、system.memory.actual.used.pct、disk.used.pctAPM 类transaction.duration.us、transaction.type、event.outcome、processor.event通用字段labels.*、tags、cloud.region、kubernetes.pod.name这个词典最后要固化成两样东西一是给 Tool 的入参校验规则防止 Agent 用错字段名二是注入到 Skill 配置里的“字段知识提示”。因为 LLM 对没有见过的字段名会在生成 ES|QL 时瞎编比如把disk.used.pct写成disk.usage_percent查询直接报错。提前把合法字段清单注入给 Agent能大幅降低这种低级错误。第二步验证每个 Tool 的真实返回结构我会在配置完工具后强制手动跑一轮“裸调用测试”不经过技能编排直接用 Agent 调用这个 Tool看看返回 JSON 的结构。目的有两个一是确认查询权限和数据通路没问题二是把返回字段的实际名称记录下来方便在 Skill 的后续步骤引用。这里有个很隐蔽的坑ES|QL 返回的字段名大小写、别名情况不一定跟你预期的一致。比如 STATS 之后如果用AVG(disk.used.pct)生成的列名可能是AVG(disk.used.pct)这种带括号的完整文本你在决策块里如果写if_avg_usage_gt就会匹配不上必须先把结果的列重命名为avg_usage。我早期就因为这个问题浪费了一整个下午排查“明明查询有结果Agent 却说拿不到数据”。第三步对小众、昂贵的操作增加确认环节比如自动清理索引、投递 webhook、修改存储策略这类有副作用的工具必须加一个独立于技能流程的“审批哨位”。做法很简单定义一个中间 Tool它不做实际修改只返回“待执行操作预览”由人类确认后才调用真正的写操作 Tool。在 Agent 层面可以将这类工具标记为requires_confirmation: trueElastic 的 AI Assistant 在调用前会自动弹确认框。千万别嫌这一步麻烦我见过不止一次因为技能误触发直接删了生产索引的事故。3.3 权限与安全给 Agent 的操作范围装上边界Agent 有了工具和技能之后权限模型就是最后的防线。我的做法是五个原则只读优先凡是用作“分析诊断类”的技能默认只授予读取权限。写操作类工具必须单独显式授权。数据空间隔离给不同业务线或不同环境建不同的 API Key限制 Agent 只能访问特定索引或数据视图。Elastic 的 API Key 支持索引级权限用起来很顺手。上下文最小化技能里注入的字段词典、索引清单只包含本次任务必需的数据源不要图省事把全平台的数据视图都塞进去。审计可追溯所有 Agent 发起的查询和操作都会在 Kibana 的审计日志中记录我会定时扫一遍看有没有异常查询模式比如半夜批量拉全量日志。敏感字段脱敏日志字段中如果有手机号、身份证、Token 等敏感内容在 Data View 层就做好字段级屏蔽确保 Agent 和最终用户都拿不到明文。如果你在 Elastic Cloud 上建议直接启用 Azure Private Link / AWS PrivateLink 之类的网络隔离并且不把 Agent 对公网开放只在可信 VPC 里调用。合规这块宁可做过头也不要留有口子。4. 实操过程搭建一个“SLO 异常归因”技能的全流程下面用我最近搭的一个“SLO 异常归因”技能当案例把从零到可用的完整流程走一遍。这个技能的目标是当某个服务 SLO 燃烧率报警Agent 能自动定位是哪个下游依赖变慢、哪个错误类型在增加、哪个部署版本引入回归。4.1 数据准备SLO 与 APM 关联字段确认先确认数据基础。SLO 数据在.slo-*索引里核心字段有slo.name、slo.time_window、slo.burn_rate、status。APM 数据在metrics-apm*和traces-apm*分布字段有service.name、service.environment、transaction.type、event.outcome、faas.id如果跑在函数上。我实际调研后发现一个问题SLO 索引里记录的是聚合后的指标并没有直接的请求链路 ID。所以要想做到“归因到具体依赖”不能只查 SLO 数据必须让 Skill 先定位“哪些服务在 SLO 下降的时间窗口内异常”再拿服务名去 APM 里查上下游依赖。这就是技能路径设计里最关键的“字段桥接”步骤——找到跨数据源连接的主键。对可观测性数据来说常用的桥接字段是service.nameservice.environmentlabels.deployment_id。如果这些字段在 SLO 和 APM 两边的命名不一致技能就会断链。我在实际项目里就碰到过SLO 里叫service_nameAPM 里叫service.name看似差不多但自动化查询里就是匹配不上。最后我写了一个系统映射表在工具层做了字段统一转换才解决。4.2 工具配置从 ES|QL 到结构化输出这一步需要为技能挂三个工具Tool 1查询指定 SLO 的当前燃烧率与历史窗口趋势工具名slo_get_burn_rate 入参slo_name字符串、window默认 1h 实现逻辑 FROM .slo-* | WHERE slo.name {slo_name} | WHERE timestamp NOW() - {window} | STATS avg_burn AVG(slo.burn_rate), worst_status MAX(status) BY slo.name, slo.time_window | SORT timestamp DESC | LIMIT 1 出参示例JSON { slo_name: checkout_service_availability, avg_burn_rate: 2.4, status: breaching, time_window: 30d, observed_at: 2025-01-15T14:30:00Z }Tool 2按服务查询时间窗口内的 APM 错误率和依赖延迟这个工具比起上面那个复杂一点它需要先查某个服务的时间序列指标再找它的下游依赖。为了不让 Agent 在每一步都调用两次 API我直接把两个查询封装成一个工具工具名apm_get_service_health_and_dependencies 入参service_name、environment、time_window 实现逻辑 1. 查询 metrics-apm* 中该服务的 throughput、error_rate、latency_p95 2. 查询 traces-apm* 中该服务作为 caller 的依赖列表 每条依赖附上 count、avg_duration、error_count 出参示例JSON { service: checkout, error_rate: 4.2, throughput: 380, dependencies: [ {target: payment-api, count: 120, avg_duration_ms: 840, error_rate: 8.6}, {target: inventory-api, count: 95, avg_duration_ms: 120, error_rate: 0.5} ] }把多个查询封装成一个工具的主要原因是缩短 Agent 的思考链路。如果每查一步数据都要生成一次工具调用Agent 容易在中间步骤出错把逻辑上强关联的查询打包Agent 的每一步都对应一个完整的语义环节可靠性显著提高。代价是工具函数里代码多一点、调试稍麻烦但对可观测性场景来说非常值得。Tool 3获取最近一次的部署事件工具名get_recent_deployments 入参service_name、time_window 实现逻辑 该工具对接我内部的一个部署事件表由 CI/CD 流水线写入 Elasticsearch 字段包括 release_id、deployed_at、git_commit、changed_components、deployer 出参示例JSON { deployments: [ { release_id: r-20250114-3, deployed_at: 2025-01-14T22:00:00Z, changed_components: [checkout-payment, retry-logic], git_commit: 8f2e1a0 } ] }这个工具不是 Elastic 自带的而是通过 Connector 接入的内部系统接口。实际操作中如果你没有现成的部署事件数据也可以直接从日志中提取比如检索关键字deploy、release、commit但效果会差很多。有条件的最好从 CI/CD 流程直接同步一份结构化的部署记录。4.3 技能编排路径规划与决策逻辑工具就绪后Skill 的编排如下skill: name: slo_breach_investigation description: | SLO 燃烧率过高、状态 breach、或用户投诉可用性下降时使用。 也适用于监控看板上 error_rate、latency 突增的归因排查。 steps: - task: get_burning_slo tool: slo_get_burn_rate params: slo_name: {slo_name} # 由触发上下文填充也支持枚举列出当前 breach 的 SLO - task: get_service_health tool: apm_get_service_health_and_dependencies params: service_name: {service_name} environment: production time_window: 1h depends_on: get_burning_slo - task: find_recent_deploy tool: get_recent_deployments params: service_name: {service_name} time_window: 24h depends_on: get_service_health - task: correlate_and_conclude tool: llm_analysis # 或直接让 Agent 汇聚前三步输出自行分析 depends_on: [get_service_health, find_recent_deploy] decision: - condition: 依赖列表中存在 avg_duration_ms 500 且 error_rate 5 的目标 action: 标注为“下游依赖异常”建议进一步检查调用的目标服务 - condition: 24h 内有部署记录且时间窗口与错误率上升起点接近 action: 标注为“疑似发布引入回归”建议联系部署负责人并对比 git_commit 变更 - condition: 无上述特征 action: 输出“基础设施类问题”候选建议检查节点指标、K8s 事件、网络指标从编排上可以看到整个排查链条其实还原了 SRE 的经典动作先确认 SLO 是不是真的破了再定位哪个服务受影响再看服务内部哪个依赖拖了后腿最后用“是不是刚发布过”来排序嫌疑。这个技能的核心价值不在于它有魔法而在于它把老师傅的排查路径标准化锁定了不可复现的临场发挥。4.4 参数选择里的计算逻辑这里单独说一个容易被忽略的点时间窗口怎么选。SLO 燃烧率的计算依赖窗口窗口太长会掩盖短时间抖动太短则会频繁误报。我在实际使用中选窗口会跑一组简单的计算验证该服务 SLO 目标可用性为 99.9%错误预算对应 30 天窗口约 43 分钟不可用时间。如果窗口设 1h燃烧率 1 意味着 1 小时内消耗了全部预算的 1/720因为 30 天一共有 720 小时即便持续 1 小时也只是消耗 0.14% 的预算触发归因没有意义。把窗口缩短到 5 分钟燃烧率会很容易超过 14.4稍有波动就告警又会疲劳轰炸。所以我的做法是归因技能的初始触发窗口设为 15 分钟因为 SLO 的燃烧率在这个尺度上放大明显能过滤掉缓慢漂移只捕捉真正需要立即处理的失守事件。后续检查历史趋势时再拉 24h 窗口做对比。这个参数不是拍脑袋定的而是根据错误预算消耗速率反推的大家可以根据自己服务的目标值重算一次不要直接抄。4.5 上线验证从模拟告警到真实故障技能配置完不能直接上生产我会分四步验证造数验证往测试环境写入一批模拟异常数据比如人为调慢某个依赖接口 1 秒、注入 5% 错误率看 Agent 能不能按预期路径定位到问题。历史回放选一次真实发生过的 SLO 失守事件把技能跑一遍对比当时 SRE 的排查结论。这一步能快速发现技能路径上缺失的环节。双盲比对让 Agent 和一名资深 SRE 同时看同一组告警数据输出各自的归因结论比对命中率。命中率低于 70% 的技能不上线。灰度切换先只在非核心服务的告警上启用 Agent 归因确认连续一周没有误判或漏判后再扩大到全服务。走完这套流程后这个技能才能算真正“可用”。我的经验是一个技能从上线到稳定至少需要经过三次真实告警的洗礼。第一次大概率会发现触发条件漏了某种表述第二次会发现某个工具返回格式在边界情况下解析失败第三次才真正稳定下来。这很正常不用灰心。5. 实测中的常见问题与排查技巧实录这部分我整理了在多个环境里踩过的高频问题按排查频率排序附上现象、原因和解决办法可以当一份速查表用。5.1 技能没有被触发或触发到了错误的技能现象用户描述“订单服务超时变多了”Agent 回复了一段通用的 APM 概念解释但完全没有调用磁盘或延迟排查技能。原因分析Skill 的 Description 里没有覆盖“超时”“变慢”“抖动”这类语义变体或者触发条件只写了字面匹配比如必须出现timeout而用户用词是 “slow” 和 “lag”。排查路径去 AI Assistant 的调试面板看每轮意图分类的置信度分数和命中的 Skill 列表。Elastic 的 assistant 会输出调用决策日志里面会显示“为什么没选某个 Skill”的近似评分。解决把触发词表扩展成同义词集合timeout/slow/lag/抱怨响应慢都加进 description 的最前面。同时在测试集里专门构造一批“口语化、非技术化”的输入来做回归避免只覆盖了文档里那种标准问法。5.2 查询执行了但返回空数据现象Step 1 查磁盘使用率返回空Agent 顺势给出“未发现异常”结论但用户看到的锅其实已经煮开了。原因分析索引名称错误、时间字段名不对、或者数据写入有延迟尤其是 metrics 索引的 rollover 策略查询窗口选在刚 rollover 的瞬间容易漏数据。排查路径直接在 Kibana Dev Tools 里拿生成后的 ES|QL 手动跑一遍对照返回的字段名和索引命中的逻辑。查询执行面板里通常能看到最终转换出来的 DSL 语句重点是检查字段名有没有被 LLM 幻觉改掉。解决工具层增加一个轻量“数据健康校验”ES|QL 返回COUNT 0时不直接返回空结果而是追加提示data_not_found_check让 Agent 重新换一个时间窗口或检查索引匹配。这个“空数据重试”的机制虽然简单但能把大量误判扼杀在摇篮里。5.3 工具调用成功但下游步骤数据串不上现象某个步骤拿到了依赖列表但后续决策块由于字段名不匹配始终走不进预设的if分支导致 Agent 每次都说“无法判断”。原因分析我在 3.2 节提到过的列名重命名问题STATS 聚合出来的列名默认是函数全名比如AVG(duration)而不是avg_duration还有一种是时间字段不同数据源的 timestamp 字段可能叫timestamp也可能叫timestamp解析不一致。排查路径打印工具实际返回 JSON 的 schema对照 Skill 决策块里引用的字段名。这个工作可以用一个脚本自动 diff。解决开发原则是“所有 ES|QL 查询里的 STATS 字段都必须显式 AS 别名”从源头杜绝默认列名的问题。如果你在 Elastic Cloud 上也可以借用 Field Capabilities API 来统一字段名的校验。5.4 Agent 生成过度自信的归因结论现象明明只查了两步数据Agent 就斩钉截铁说“是数据库连接池耗尽导致”但实际证据链并不完整。原因分析LLM 的补全倾向在作祟。当上下文里数据不足以得出结论时模型会脑补最可能的答案。这在通用场景下无伤大雅但在可观测性排查里错误的归因比不归因更危险。解决在 Skill 的全局配置里加一条硬约束决策块没有显式匹配到条件时Agent 只能输出“证据不足以归因建议补充查询”并列出缺失的数据项。同时在提示词层面把“只可分析已获取的数据禁止猜测”这句话作为系统级指令固化。实测下来加上这一条之后误报率能降一半。5.5 长会话中上下文截断导致技能后半段的决策失去前提现象在一个持续很久的排查会话里用户问了很多问题Agent 在前面已经拿到了“磁盘使用率 90%”的结果但因为上下文窗口截断后半段汇报时它突然说“未发现异常”。原因分析这是 LLM 应用的通病——上下文超长后早期关键信息被丢弃Agent 自己又没做“关键结论持久化”。解决有两个思路可以并行。一是在 Skill 内部增加一个memory_notes机制让 Agent 在每完成一个子步骤时把核心结论写入独立的短期记忆可以简单存到一个 index 里后续步骤从这个记忆里读取而不是依赖完整对话历史二是把窗口期的无关对话压缩成摘要只保留跟当前任务相关的数据快照。在 Elastic 里我倾向于用后一种因为 AI Assistant 本身具备会话摘要功能配置好压缩阈值就好。6. 关于 Agent Skills 设计与落地的一句话经验最后分享一点大实话。Agent Skills 最吸引人的地方是它把“可观测性运维经验”变成了可以复制、可以评审、可以演进的资产但相应地它也把原本只存在于人脑里的隐式判断暴露成了显式配置这要求你对“自己的排查套路”有比以往更清晰的认识。我在实际使用中最大的体会是技能的复杂度要克制。一开始总想做一个“万能诊断技能”把所有可能的故障类型都塞进去结果就是触发混乱、维护困难、Step 之间互相打架。后来改成“一个技能只管一类问题、管到极致”的哲学每个技能都做到路径短、决策准、输出直白好用程度反而大幅提升。给刚接触 Elastic Observability Agent Skills 的朋友一个可执行的起点先不做大而全的平台 AI选一个你最常被拉进群处理的告警场景比如日志里频繁出现某个错误码把排查步骤用文字写出来再一步步翻译成 Skills 配置。这个过程本身就是一次很有价值的“运维经验结构化的复盘”。另外再提一句Agent Skills 不是配完就一劳永逸的自动化。数据显示好的技能配置在持续迭代一年后其决策路径和最初版本相比基本是面目全非的——因为业务系统会变、数据字段会变、故障模式也会变。定期复盘技能的命中率和漏判率和定期 review 你手上其他告警规则一样应该成为一种例行动作。