公司动态

从长文本到紧凑图:实现 /show-me 风格的 Agent Skill

📅 2026/8/29 9:55:29
从长文本到紧凑图:实现 /show-me 风格的 Agent Skill
实际使用 AI Agent 时模型输出长文本是非常常见的问题解释一个流程能写二十行对比两个方案能列满一屏。但人眼真正需要的往往是结构是能一眼看出节点关系、先后顺序和差异点的图形。/show-me 这类 agent skill 正是为这个场景出现的它以斜杠命令形式加载一套固定的渲染能力把复杂的结构化信息压缩成紧凑的视觉表示。这篇文章会从 agent skill 的概念讲起说明它和 MCP 到底有什么区别再动手实现一个最小可运行的 /show-me 风格渲染技能最后给出验证、排错和落地建议。1. 先理解 /show-me 是什么它解决 Agent 的什么问题1.1 Agent 输出长文本带来的阅读成本让 Agent 解释用户登录后请求如何流转默认回答往往是用户请求先到达 NginxNginx 根据路由规则把请求转发到网关服务网关完成认证和鉴权后再把请求分发到用户服务用户服务查询数据库后返回结果最后网关把响应汇总并返回给前端。这段描述信息量没问题但阅读成本很高。你需要先记住 Nginx、网关、用户服务、数据库四个节点再在脑子里把它们串成一张图。如果流程里有分支、重试、降级逻辑文本描述会成倍膨胀很容易漏掉关键依赖。换成紧凑视觉表示同样的信息变成下面这样┌──────────────────┐ │ Nginx │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ Gateway │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ User Service │ └──────────────────┘ │ ▼ ┌──────────────────┐ │ Database │ └──────────────────┘人眼扫描这张图只需要几秒节点关系、执行顺序、调用方向全都明确。这正是 /show-me 这类 agent skill 的核心价值在不依赖图片生成能力的文本通道里通过固定格式输出让复杂信息变得可扫读、可定位、可讨论。1.2 紧凑视觉表示到底包括哪些形式紧凑视觉表示并不是指高大上的图表渲染而是指在文本通道里用少量字符表达空间关系和结构信息。常见形式包括使用框线字符绘制的流程图和状态图。用|和-拼出来的结构表格。用字符长度表示数值大小的条形图。用缩进和分支符号表示的树形依赖关系。用标记符号标注差异点的对比清单。这些形式非常适合终端、聊天窗口、代码注释和 Markdown 文档。它们不依赖图片渲染服务也不会因为图片无法加载而丢失信息。更重要的是它们可以被固定成模板由代码生成而不是每次让模型自由发挥。2. Agent Skill 是什么它和普通工具调用的边界在哪里2.1 Skill 的组成与最小文件结构Agent Skill 可以理解为给 Agent 准备的可复用技能包。它不是简单的单一函数而是一个包含描述文件、脚本、模板、示例的目录。模型在对话中遇到匹配场景时会把整个技能包加载进来按里面定义的流程执行。一个典型的 Skill 目录结构如下show-me/ ├── SKILL.md ├── scripts/ │ └── render.py └── examples/ └── flow.jsonSKILL.md 是这个技能包的入口描述告诉模型这个技能解决什么问题、什么时候用、怎么用。scripts 目录存放真正执行逻辑的脚本。examples 目录提供示例输入帮助模型理解如何把用户意图转成脚本需要的参数。这种设计思路和普通工具调用有明显差别。普通工具调用是模型在运行期从一组预声明 API 里选择一个函数执行重点在调用;Skill 则把一段完整的工作方法打包包括提示词、命令约定、脚本和边界条件重点在复用整套能力。2.2 一次 Skill 调用的完整路径理解 Skill 最好的方式是看一次完整调用路径。用户在对话里输入触发表达例如画一下登录流程。Agent 框架把已注册的 Skill 描述注入上下文模型根据 SKILL.md 中的 description 判断当前需求是否匹配。匹配后模型或运行时把用户意图整理成脚本要求的输入结构例如一个 JSON。脚本在受控环境里执行返回文本结果。Agent 把脚本结果直接返回给用户而不是重新解释一遍。最后一步非常关键。如果 Agent 拿到脚本输出的流程图后因为想帮忙而重新表述表格会被改坏对齐会被破坏。所以 Skill 描述里通常要明确写一句脚本输出保持原样不要二次排版。这也是区分纯工具调用和Skill 工作流的细节差异。3. Agent Skill 和 MCP 的区别一张表说清定位差异3.1 MCP 解决的是连接标准化问题MCP即 Model Context Protocol是一种标准化的连接协议。它解决的是模型如何按统一方式访问外部工具和数据源的问题。一个 MCP Server 通过协议暴露工具、资源和提示MCP Client 负责连接并管理会话。这样模型就不需要为每个外部系统单独实现一套集成逻辑。可以这样理解MCP 关心的是连到哪里、怎么连、权限怎么控制而 Skill 关心的是拿到任务后按什么流程把活干完。两者的层次不同解决的问题也不同。3.2 一张表看清 Skill 与 MCP 的差异对比维度Agent SkillMCP定位可复用的能力包和执行流程模型与工具/数据源的标准连接协议基本组成SKILL.md、脚本、模板、示例Server、Client、协议定义触发方式斜杠命令或场景匹配后加载模型按协议发现并调用工具运行依赖通常本地目录即可依赖轻通常需要启动 Server 进程或远程服务权限边界以目录和脚本执行权限为主协议层面的工具发现和授权典型场景画流程图、整理表格、批量处理文件查数据库、调外部 API、操作 SaaS 工具维护重点提示词质量、脚本稳定性、模板覆盖Server 可用性、鉴权、数据格式兼容从这张表可以看出Skill 偏经验流程MCP 偏连接通道。Skill 可以完全离线运行适合把重复性工作固化成稳定产出MCP 则天然适合连接多个异构系统解决的是生态互通问题。3.3 两者不是二选一组合使用的典型结构实际项目中Skill 和 MCP 经常配合出现。一个常见结构是用户意图 │ ▼ Agent 框架 │ ├── Skill 层负责流程编排和渲染 │ │ │ └── /show-me 渲染脚本 │ └── MCP 层负责数据获取 │ ├── 数据库 MCP Server ├── 监控系统 MCP Server └── 内部 API MCP Server比如用户想把最近一小时的接口错误率画成图。MCP Server 负责从监控系统拉取数据/show-me 这类渲染 Skill 则负责把数据变成紧凑的文本条形图或表格。Skill 不直接接触数据源MCP 不直接负责版面呈现各管一段。4. 实现一个最小 /show-me 风格渲染 Skill4.1 目录设计把渲染逻辑和描述文件分开下面给出一个思路对齐 /show-me 能力定位的最小实现。你可以把它当成复刻这类技能的起点实际使用时要根据自己项目的目录和框架版本调整。先创建目录结构mkdir -p show-me/scripts show-me/examples然后把技能描述写入show-me/SKILL.md--- name: show-me description: 生成紧凑的文本视觉表示支持流程和表格两种类型。用户要求画流程图、整理对比表格、梳理结构时使用。 usage: /show-me flow 主题 /show-me table 行列数据 /show-me compare 方案A 方案B --- # show-me Skill 当用户需要把复杂信息转成可扫读的视觉结构时使用本技能。 执行步骤 1. 把用户意图整理成 JSON 输入。 2. 调用 scripts/render.pytype 指定 flow 或 table。 3. 把脚本输出原样返回不要重新排版。 注意 - 脚本输出就是最终结果禁止二次格式化。 - 如果输入信息不足先向用户确认节点或列名不要猜测。这里最重要的是description和usage。模型靠 description 判断是否触发技能靠 usage 学习命令格式。注意如果 description 写得过于宽泛模型会在不该触发时误触发如果写得太狭窄又会漏触发。4.2 核心脚本用 JSON 输入生成紧凑视觉输出渲染脚本只做一件事读取结构化 JSON输出文本图。不要把业务判断放进脚本里判断交给模型渲染交给代码。#!/usr/bin/env python3 show-me 最小渲染脚本把 JSON 描述转成紧凑文本图。 import json import sys from argparse import ArgumentParser def render_flow(nodes): 渲染竖向流程图节点之间用箭头连接。 if not nodes: return (empty flow) width max(len(n) 4 for n in nodes) parts [] for index, node in enumerate(nodes): top ┌ ─ * (width - 2) ┐ mid │ node.ljust(width - 4) │ bottom └ ─ * (width - 2) ┘ parts.append(\n.join([top, mid, bottom])) if index len(nodes) - 1: indent * ((width - 1) // 2) parts.append(indent │) parts.append(indent ▼) return \n.join(parts) def render_table(headers, rows): 渲染紧凑表格列宽按内容自适应。 if not headers or not rows: return (empty table) cols len(headers) widths [ max(len(headers[i]), max(len(str(r[i])) for r in rows)) 2 for i in range(cols) ] def fmt(cells): return ( | |.join( str(cells[i]).ljust(widths[i] - 2) for i in range(cols) ) | ) sep | |.join(- * widths[i] for i in range(cols)) | return \n.join([fmt(headers), sep] [fmt(r) for r in rows]) def main(): parser ArgumentParser(description__doc__) parser.add_argument( --type, choices[flow, table], defaultflow ) parser.add_argument(--data, requiredTrue, helpJSON 字符串) args parser.parse_args() try: data json.loads(args.data) except json.JSONDecodeError as exc: print(fJSON 解析失败: {exc}, filesys.stderr) return 2 if args.type flow: print(render_flow(data.get(nodes, []))) else: print(render_table(data.get(headers, []), data.get(rows, []))) return 0 if __name__ __main__: sys.exit(main())这段代码实现两个核心函数。render_flow根据节点列表生成竖向流程图框线宽度由最长节点决定。render_table根据表头和行数据生成 Markdown 风格表格列宽自动计算。main 函数统一处理 JSON 解析和错误返回脚本退出码可以用于后续自动化检查。4.3 接入 Agent 运行环境与本地验证不同 Agent 框架接入 Skill 的方式不同。常见做法是把show-me目录放到框架指定的 skills 目录框架启动时扫描注册也有的框架要求把 SKILL.md 内容维护在系统提示词里脚本单独存放模型需要时再触发执行。如果你使用的框架还不支持目录型 Skill可以退化成两步第一步把 SKILL.md 的说明段落放入系统提示词第二步在模型决定使用渲染时调用render.py。这种方式同样能验证技能逻辑后续再迁移到完整目录支持即可。先做本地验证不依赖 Agent 框架python3 scripts/render.py --type flow --data {nodes:[request received,validate token,load user,return data]}正常输出┌──────────────────┐ │ request received │ └──────────────────┘ │ ▼ ┌────────────────┐ │ validate token │ └────────────────┘ │ ▼ ┌────────────┐ │ load user │ └────────────┘ │ ▼ ┌─────────────┐ │ return data │ └─────────────┘再验证表格类型python3 scripts/render.py --type table --data {headers:[option,latency,cost,risk],rows:[[local cache,1ms,low,consistency],[redis,1-5ms,mid,ops],[sql query,5-20ms,high,slow]]}正常输出| option | latency | cost | risk | |---------------|---------|------|-------------| | local cache | 1ms | low | consistency | | redis | 1-5ms | mid | ops | | sql query | 5-20ms | high | slow |本地验证这一步很重要它能排除掉 Agent 框架的影响单独确认脚本本身可用。之后接入框架时如果出现问题可以二分定位是脚本问题还是框架调用问题。5. 运行验证输入、输出和异常分支都要测5.1 三组典型测试输入除了正常输入还要覆盖边界和异常情况否则技能在真实对话里会表现在意想不到的地方。第一类测试是正常场景。比如画一下用户登录流程模型应输出flow类型 JSON对比三种缓存方案模型应输出table类型 JSON。这类测试用来确认主流程可用。第二类测试是边界输入。空节点列表、只有一行数据的表格、列数不匹配的表格脚本都要有明确表现。示例python3 scripts/render.py --type flow --data {nodes:[]}预期输出(empty flow)退出码为 0。这样模型在接到空输入时不会拿到乱码而是能根据提示决定补充询问。第三类测试是异常输入。JSON 格式错误、缺少字段、--type传了不支持的值都应该有非零退出码和 stderr 错误信息。示例python3 scripts/render.py --type flow --data not-json预期输出JSON 解析失败: ...退出码为 2。Agent 框架可以据此判断调用失败而不是把一段错误文本当图形返回。5.2 判读输出是否合格的检查清单技能接入后要建立一套可复用的验收清单脚本直接执行退出码为 0stderr 无异常。输出在等宽终端和聊天代码块中都对齐。输出不包含多余反引号、转义反斜杠等会被 Markdown 误解的内容。Agent 拿到脚本结果后保持原样返回没有被重新排版。空输入、错误 JSON、缺少字段时有明确报错而不是静默失败。中文内容在终端中能正常显示、不乱码。表格列宽按显示宽度计算中文和英文混排不歪斜。其中最后两条在中文场景里很容易踩坑。下面的排错章节会详细展开。6. 常见问题排查从现象倒推到修复6.1 输入 /show-me 但没有任何反应现象用户在对话里输入/show-me flow ...Agent 完全忽略或者回复一段文字而不是图。可能原因依次检查技能目录没有被框架扫描到。SKILL.md 里的name或description与触发场景不匹配。模型在系统提示词里看不到技能注册信息。框架日志里根本没有触发记录。排查方式先确认技能目录位置和框架约定一致再直接问模型你现在能调用哪些技能看它列出的列表里有没有 show-me最后查看框架日志里是否存在技能加载失败的记录。注意不要在 SKILL.md 里写复杂的语法规则description 越直白模型越容易准确触发。6.2 脚本可用但输出对不齐现象单独执行render.py输出正常通过 Agent 返回后表格错位或框线断裂。常见原因有三个模型二次排版破坏了空格聊天窗口不是等宽字体中文显示宽度和英文不一致。模型二次排版是最隐蔽的一个。脚本输出的对齐依赖空格数量模型只要把某一行的空格重新调整整张图就废了。所以 SKILL.md 里要写死保持原样返回。字体问题建议在技能说明里提示用户切换到等宽字体或者在生成表格时减少对严格对齐的依赖。第三个问题需要更细致的宽度计算下面单独说明。6.3 中文字符和宽度计算问题现象中文节点绘图时框线上下左右对不齐看起来是阶梯状。原因是 Python 内置的len()函数把中文字符按长度 1 计算但绝大多数终端把中文按宽度 2 渲染。上面的示例只用了英文节点所以对齐正常换成收到请求这类中文节点框线宽度就偏小。问题现象常见原因检查方式处理建议中文节点框线错位字符显示宽度未按 CJK 计算在终端直接执行脚本观察使用wcwidth计算显示宽度中文乱码运行环境编码不是 UTF-8查看输出的字节序列设置PYTHONIOENCODINGutf-8列宽计算偏小len()不区分字符宽度打印每列计算值宽度计算函数抽象出来单独测试修复方式是在渲染函数里引入wcwidth库用它替代len()计算宽度。这个改动看起来小但对中文 Agent 场景是刚需建议在技能一开始就处理好而不是等用户反馈后再补。6.4 和 MCP 工具职责重复现象项目里同时有渲染 Skill 和某个 MCP ServerMCP 也能生成图表两个能力看似重复。排查时先明确各自边界。如果 MCP Server 提供的是数据查询能力只是返回了原始数据那么渲染仍然应该由 Skill 完成如果 MCP Server 本身就是一个专业的图表生成服务那么 Skill 可以退化为一个薄封装负责把用户意图转成 MCP Server 的调用参数。问题现象常见原因检查方式处理建议Skill 和 MCP 同时被触发两者 description 描述重叠查看调用日志收紧 Skill 的触发条件MCP 返回数据后 Skill 不渲染编排层未把数据传给脚本检查框架上下文明确数据流方向输出格式不一致多套渲染逻辑竞争对比历史输出固定一套渲染器MCP 只供数据7. 最佳实践与扩展方向让技能保持干净7.1 让 Skill 保持渲染器的纯粹性给 Agent 设计技能时最容易犯的错误是把判断逻辑也塞进脚本里。脚本一旦开始做语义判断就失去了确定性也难测试。推荐边界是模型负责把开放问题转成结构化输入代码负责把结构化输入稳定渲染成输出。理解意图是模型的长处精确排版是代码的长处。让两边各干各的技能才可靠。具体落地建议输入格式固定为 JSON字段含义写在 SKILL.md 里。渲染脚本不做业务推理只做格式转换。为每个渲染函数写单元测试覆盖正常、空输入、异常输入。模板样式集中管理不要散落在系统提示词里。7.2 接入生产环境前要补齐的能力学习环境里跑通脚本和生产环境接入 Agent 工作流中间还有一段距离。生产环境至少要考虑以下能力关注点开发/学习阶段生产环境要求配置来源脚本参数写死技能目录、JSON schema、风格配置外置日志直接打印记录触发时间、输入摘要、退出码、耗时权限本机执行限制脚本可访问的目录和环境变量回滚直接改文件技能包版本化可快速切换版本测试覆盖手动验证输出快照测试、回归测试资源消耗忽略超时、重试、输入长度上限7.3 扩展方向最小实现跑通后可以考虑以下扩展增加更多布局类型横向流程图、树形结构、环形依赖图。输入格式从 JSON 扩展为 YAML降低模型生成参数的难度。输出端增加终端配色或 HTML 片段适用不同展示场景。接入 MCP Server 获取真实数据再由 Skill 渲染成图形成完整数据链路。对超大结构做分段渲染避免一次输出超过聊天窗口限制。增加多语言宽度处理让中英文混排和纯中文内容都能对齐。对新手来说最有价值的练习不是把脚本写得多完善而是先想清楚一条边界哪些判断交给模型哪些判断交给代码。/show-me 这类技能给出了一个很好的答案——把渲染这种确定性工作交给代码把理解意图这种开放工作留给模型。明白这条边界之后你就能为自己的 Agent 设计出更多干净的技能包而不是每次让模型在长文本里自由发挥。