当前位置: 首页 > news >正文

Agent Skills:从技能文档到行为契约的工程化实践

1. 从“技能文件”到“智能体行为中枢”:Agent Skills 不是功能列表,而是决策逻辑的载体

你第一次在 GitHub 上看到一个 LLM 应用项目里有个叫skill.md的文件,点开发现里面既不是 Python 代码,也不是 JSON 配置,而是一段段带标题、带参数说明、带示例输入输出的 Markdown 文本——你下意识觉得:“这不就是个文档?”
结果跑起来才发现,整个 Agent 的能力边界、它能接什么任务、怎么拆解用户一句话、甚至什么时候该调用外部 API、什么时候该查本地数据库,全由这个看似“静态”的文件驱动。

这就是Agent Skills的真实定位:它不是功能说明书,而是智能体(Agent)的行为契约执行协议。它定义了“这个 Agent 能做什么”,但更关键的是,它定义了“这个 Agent 在什么条件下、以什么方式、带着什么上下文去做”。

我最早在 Hermes Agent 和 DeepSeek Agent 的开源仓库里系统接触这套设计。当时团队正为一个金融投研助手做能力扩展,原方案是每加一个新功能就硬编码一个函数、注册一个路由、写三份测试——两周加了 7 个技能,光是函数签名对齐就出了 3 次类型错误。后来我们把所有技能抽象成统一结构,用skill.md描述,再通过一个轻量解析器注入运行时,新增技能从“改代码→提 PR→等 CI→上线”压缩成“写好 skill.md → git push → 自动加载”。

关键词Agent Skills在当前技术语境中,已悄然完成一次语义跃迁:

  • 2023 年初:指代 LLM 提示词工程中“让模型学会某类任务”的技巧集合(如“Chain-of-Thought 技巧”“Self-Consistency 技巧”);
  • 2023 年中后期:随 LangChain、LlamaIndex 等框架成熟,开始指向可注册、可发现、可编排的“工具函数”(Tool),此时skill.md尚未出现,开发者靠 Python docstring 或 YAML 注册;
  • 2024 年至今skill.md成为事实标准,它把“技能”从“一段可执行代码”升维为“一个可验证、可版本化、可跨框架复用的行为单元”。它不绑定语言(Python/JS/Rust 均可实现)、不绑定框架(LangChain / LlamaIndex / 自研调度器均可消费)、甚至不绑定执行环境(本地 CLI / Web API / 移动端 SDK 都能加载)。

为什么是 Markdown?不是 JSON?不是 YAML?
这不是格式偏好问题,而是工程权衡:

  • 人类可读性优先:产品经理要确认“这个技能是否覆盖了客服场景的退换货流程”,他不该被缩进和引号困扰;
  • Git 友好git diff能清晰显示“新增了 refund_policy 参数”“修改了超时阈值”,而 JSON/YAML 的 diff 常因空格、顺序、引号风格失效;
  • 编辑门槛低:实习生用 Typora 写完就能提交,无需装 VS Code 插件或学 Schema 语法;
  • 可扩展性强:用 HTML 注释<!-- @skill:finance/stock_price -->或自定义 frontmatter---\nscope: internal\nversion: 1.2\n---,Markdown 解析器可轻松提取,JSON/YAML 则需预定义全部字段。

提示:别被skill.md的后缀迷惑——它本质是技能元数据描述协议.md是载体,不是约束。你完全可以用skill.yamlskill.json,只要解析器支持。但社区选择.md,是因为它天然平衡了机器可解析性与人可维护性,这是过去十年 DevOps 工具链反复验证过的路径(参考:Dockerfile、Makefile、README.md 的成功逻辑)。

我见过太多团队卡在第一步:把skill.md当作文档写,结果开发时发现“文档里写的参数名和代码里不一致”“示例输出格式和实际返回不匹配”“没写错误码,线上报错只能看日志”。根本原因在于,他们没意识到:skill.md是契约,不是注释;是接口定义,不是使用说明。

举个真实案例:同花顺 Pine Script 团队曾向我们咨询如何将技术指标封装为 Agent Skill。他们原有指标是.pine文件,含大量内置函数(ta.sma()request.security())。我们没让他们重写逻辑,而是设计了一个skill.md描述协议:

# SMA 计算(简单移动平均) ## 描述 基于指定周期计算收盘价的简单移动平均值,支持多时间周期合成(如 5 分钟 K 线计算 20 周期 SMA)。 ## 输入参数 | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| | `symbol` | string | 是 | — | 股票/期货代码,如 `SH600519` | | `period` | integer | 是 | — | 移动平均周期,范围 2–200 | | `timeframe` | string | 否 | `"1D"` | K 线周期,支持 `"1D"`, `"1H"`, `"5M"` | ## 输出格式 ```json { "value": 185.32, "timestamp": "2024-06-15T14:30:00Z", "source": "tushare" }

示例调用

{ "symbol": "SZ000001", "period": 30, "timeframe": "1H" }

错误码

含义排查建议
E_SKILL_001symbol 格式错误检查代码是否含交易所前缀(如SZ/SH
E_SKILL_002period 超出范围周期必须为整数且 2≤period≤200
这个文件交付后,前端工程师直接按 `input parameters` 表格写表单,后端工程师按 `output format` 写 DTO,测试同学按 `example call` 写自动化用例,运维按 `error codes` 配监控告警。**一份文件,四端对齐,零歧义落地。** 这才是 `Agent Skills` 的起点:它让 LLM 应用开发,从“拼凑提示词 + 调用函数”的手工作坊模式,走向“定义契约 + 实现协议 + 自动编排”的工业化生产模式。 ## 2. 解剖 `skill.md`:六个不可省略的字段,缺一不可的工程闭环 很多团队写完第一个 `skill.md` 就急着跑通 Demo,结果上线后三天内收到 17 个工单,核心问题高度集中:83% 的报错源于 `skill.md` 中某个字段缺失或描述模糊。这不是偶然——`skill.md` 的六个核心字段,每个都对应一个工程环节的输入/输出契约。漏掉任何一个,都会在下游引发雪崩式返工。 下面我以一个真实金融风控技能为例,逐字段拆解其设计逻辑、常见错误及补救方案。这个技能名为 `risk_score_v2`,用于实时评估用户贷款申请风险等级。 ### 2.1 名称与唯一标识:`# Risk Score v2` 不是标题,而是服务发现键 `skill.md` 的第一行 `# Risk Score v2` 看似只是 Markdown 标题,实则是整个技能的**全局唯一标识符(UID)**。它参与三个关键环节: - **注册阶段**:Agent 框架扫描目录时,将 `#` 后文本作为 `skill_id` 存入技能注册中心; - **调用阶段**:LLM 生成的 JSON Action 中,`"name": "Risk Score v2"` 必须与之完全匹配(大小写、空格、标点均敏感); - **监控阶段**:Prometheus 指标 `agent_skill_duration_seconds{skill_id="Risk Score v2"}` 依赖此 ID 聚合。 常见错误: - ❌ 使用中文或特殊符号:`# 用户风险评分(V2)` → 解析器报错 `Invalid skill_id: contains non-ASCII chars`; - ❌ 版本混用:`# Risk Score` 和 `# Risk Score v2` 并存 → 框架无法区分新旧版本,调用随机失败; - ❌ 缺少版本号:`# Risk Score` → 运维无法判断线上运行的是 v1 还是 v2,故障排查无从下手。 正确做法: - ✅ 采用 `kebab-case` 命名:`# risk-score-v2`(推荐)或 `# risk_score_v2`; - ✅ 版本号强制显式:`v1`/`v2`/`v2.1`,禁止 `latest` 或 `stable`; - ✅ 在文件顶部添加注释说明变更:`<!-- @changelog: v2 adds 'income_source' field, drops 'employment_years' -->`。 > 注意:`skill_id` 与文件名无关。你可以把 `risk-score-v2.md` 改名为 `credit-assessment.md`,只要 `#` 行不变,技能功能完全不受影响。这保证了“语义稳定,物理可变”的工程灵活性。 ### 2.2 描述字段:用“用户视角”写,而非“开发者视角” `## 描述` 字段常被写成技术说明:“调用内部风控模型 API,输入用户基础信息,返回风险分值”。这毫无价值。真正有效的描述,必须回答用户(LLM 或终端使用者)的三个问题: - **我能用它解决什么具体问题?**(场景锚定) - **它需要我提供哪些明确信息?**(输入预期) - **它会给我什么确定结果?**(输出承诺) 反例(开发者视角): > “封装了 XGBoost 模型推理服务,调用 `/api/v1/risk/predict` 接口。” 正例(用户视角): > “当用户提交贷款申请时,根据其身份证号、月收入、负债总额、近 6 个月征信查询次数,实时返回 0–100 的风险评分(分数越高,违约概率越大),并标注高风险维度(如‘负债过高’‘征信查询频繁’)。适用于信贷审批、额度初筛、贷后预警等场景。” 这个描述的价值在于: - **LLM 可理解**:它能据此判断何时该调用此技能(如用户说“帮我看看这个客户的贷款风险”); - **产品经理可验收**:对照“适用于信贷审批…”,确认是否覆盖业务需求; - **法务可审计**:明确“实时返回”“0–100 分”,符合金融监管对响应时效与结果可解释性的要求。 ### 2.3 输入参数:表格即契约,字段即接口 `## 输入参数` 是 `skill.md` 中最易出错也最关键的字段。它必须是一个**严格定义的 Markdown 表格**,且每一列都有不可妥协的含义: | 字段 | 类型 | 必填 | 默认值 | 说明 | |------|------|------|--------|------| - **字段**:参数名,必须与后端函数签名、API 请求体 key、数据库字段名**完全一致**(包括大小写)。例如 `user_id` 不能写成 `userId`; - **类型**:仅限基础类型 `string`/`integer`/`number`/`boolean`/`array`/`object`,禁用 `list`/`dict`/`json` 等模糊表述; - **必填**:`是` 或 `否`,禁用 `可选`/`recommended`; - **默认值**:若为 `否`,此处必须填具体值(如 `""`、`0`、`false`)或 `—`(表示无默认); - **说明**:用“用户能懂的语言”解释,**禁止技术术语堆砌**。例如: - ❌ “`max_retries`: 重试次数上限,类型 int,用于幂等性保障”; - ✅ “`max_retries`: 如果网络请求失败,最多自动重试几次(填 0 表示不重试)”。 真实教训:我们曾因 `## 输入参数` 表中 `default_value` 列写 `null`,导致前端 JS 解析时 `JSON.stringify({a: null})` 生成 `{a: null}`,而后端 Python FastAPI 将 `null` 解析为 `None`,触发非空校验失败。最终补救方案是在表格下方加一行: > **类型转换说明**:`null` 在 JSON 中等价于 `None`(Python)、`undefined`(JS)、`nil`(Ruby),各语言实现需确保一致性。 ### 2.4 输出格式:JSON Schema 的轻量替代方案 `## 输出格式` 字段必须提供**完整、可复制的 JSON 示例**,且该示例需满足: - 包含所有字段(即使某些字段在特定条件下为空); - 数值类型用真实数字(`185.32`,非 `{{value}}`); - 字符串用真实样例(`"SH600519"`,非 `"symbol_placeholder"`); - 时间戳用 ISO 8601 格式(`"2024-06-15T14:30:00Z"`); - **禁止任何模板语法**(如 Handlebars `{{ }}`、Jinja2 `{% %}`)。 为什么不用 JSON Schema?因为 Schema 太重: - 开发者要学 `$ref`、`oneOf`、`additionalProperties`; - LLM 很难准确解析复杂 Schema 并生成合规 JSON; - 前端表单生成器(如 React JSON Schema Form)对嵌套 Schema 支持不稳定。 而一个精心设计的 JSON 示例,就是最直观的 Schema: ```json { "risk_score": 68, "risk_level": "high", "high_risk_factors": ["debt_to_income_ratio > 0.7", "credit_inquiries_last_6m > 5"], "recommendation": "建议人工复核,暂缓自动审批", "timestamp": "2024-06-15T14:30:00Z", "version": "v2.1" }

这个示例隐含了:

  • risk_score是整数(68);
  • risk_level是枚举值("high"暗示还有"low"/"medium");
  • high_risk_factors是字符串数组;
  • recommendation是非空字符串;
  • timestamp是 ISO 格式字符串;
  • version是语义化版本字符串。

LLM 在生成调用参数时,会本能地模仿这个结构。这比写 20 行 Schema 更高效、更鲁棒。

2.5 示例调用:覆盖 80% 的边界场景

## 示例调用不是“教你怎么用”,而是穷举高频、典型、有代表性的输入组合。一个合格的示例表,应覆盖:

  • 正常主流程(占 50%);
  • 关键边界值(如period: 2period: 200,占 30%);
  • 常见异常输入(如symbol: ""period: -1,占 20%)。

反例(只给一个理想情况):

{ "symbol": "SH600519", "period": 30 }

正例(结构化覆盖):

场景输入 JSON说明
主流程{"symbol": "SZ000001", "period": 30}A 股主板股票,30 日均线
小盘股适配{"symbol": "BJ836077", "period": 10}北交所股票,10 日均线(最小周期)
多周期合成{"symbol": "SH600519", "period": 60, "timeframe": "1H"}基于 1 小时 K 线计算 60 周期 SMA
错误输入{"symbol": "", "period": 30}symbol 为空,触发 E_SKILL_001
越界输入{"symbol": "SH600519", "period": 300}period 超出 200,触发 E_SKILL_002

这些示例直接成为:

  • 单元测试的test_data
  • LLM 微调的 few-shot prompt;
  • 前端表单的默认值来源;
  • 客服培训的模拟话术库。

2.6 错误码:把“未知错误”变成“可运营事件”

## 错误码字段是skill.md的安全阀。没有它,所有异常都 fallback 到Internal Server Error,运维只能看日志大海捞针。一个规范的错误码表,必须包含三列:

| 码 | 含义 | 排查建议 |

  • :全局唯一错误码,格式E_SKILL_{三位数字}(如E_SKILL_001),便于日志聚合与告警;
  • 含义:一句话说明错误本质,不暴露技术细节(如不说“Redis 连接超时”,而说“外部数据源暂时不可用”);
  • 排查建议:给一线运维/开发的可操作指引,避免“请联系管理员”这类无效话术

真实案例:某次risk-score-v2E_SKILL_003(“用户征信数据获取失败”),排查建议写的是:

“1. 检查CREDIT_API_URL环境变量是否配置;2. 执行curl -I $CREDIT_API_URL/health确认服务存活;3. 查看credit-apiPod 日志中timeout关键字。”

这条建议让 SRE 同学 3 分钟内定位到是 Kubernetes Service DNS 解析失败,而非重启整个风控服务。

提示:错误码必须与后端代码中的raise SkillError('E_SKILL_001')严格一致。我们强制要求 CI 流程扫描所有skill.md中的错误码,并与代码库中的SkillError枚举校验,不匹配则构建失败。这是防止“文档与代码脱节”的最后一道防线。

3. 从skill.md到可运行技能:解析器、执行器与生命周期管理

写好skill.md只完成了 30% 的工作。剩下 70%,是让它真正活起来:被发现、被加载、被调用、被监控。这背后是一套轻量但严谨的运行时机制,我称之为Skills Runtime Layer。它不依赖 LangChain 或 LlamaIndex,而是用不到 300 行 Python 代码实现,已在多个生产环境稳定运行 18 个月。

3.1 解析器:把 Markdown 变成可编程对象

解析器的核心任务,是把skill.md的六个字段,转换成内存中可操作的Skill对象。关键设计原则是:不做任何假设,只做严格校验

以下是我们解析## 输入参数表格的 Python 伪代码逻辑(已简化,生产环境使用mistune解析器):

def parse_input_params(md_content: str) -> List[SkillParam]: # 1. 提取表格内容(正则匹配 | 字段 | 类型 | ... |) table_lines = extract_table(md_content, "## 输入参数") params = [] for line in table_lines[1:]: # 跳过表头 cols = [c.strip() for c in line.split('|') if c.strip()] if len(cols) < 5: raise ParseError(f"Input param table row malformed: {line}") field, type_, required, default, desc = cols[:5] # 2. 严格类型校验 if type_ not in ["string", "integer", "number", "boolean", "array", "object"]: raise ParseError(f"Invalid type '{type_}' for field '{field}'") # 3. 必填校验:'是'/'否' 二值化 is_required = required.strip() == "是" # 4. 默认值处理:'—' → None, '""' → "", '0' → 0 default_val = parse_default_value(default) params.append(SkillParam( name=field, type=type_, required=is_required, default=default_val, description=desc )) return params

这个解析器的威力在于:

  • 零容忍错误:任何表格格式偏差、类型不合法、必填项缺失,都在加载时抛出明确错误,绝不静默失败
  • 类型即契约parse_default_value函数确保"0"被转为整数0"false"被转为布尔False"null"被转为None,杜绝字符串"0"传给期望int的函数;
  • 可调试:错误信息包含具体行号和原始内容,开发者一眼定位问题。

我们曾用此解析器扫描 200+ 个开源skill.md,发现 63% 存在至少一处解析错误(如type列写int而非integerrequired列写Y/N而非是/否)。这证明:没有解析器的skill.md,只是漂亮文档;有解析器的skill.md,才是可执行契约。

3.2 执行器:隔离、超时、重试的黄金三角

skill.md定义了“做什么”,执行器决定“怎么做”。它必须解决三个核心问题:

  • 隔离性:一个技能崩溃,不能拖垮整个 Agent;
  • 可控性:必须有硬性超时,防止单个技能卡死整个流程;
  • 韧性:对网络抖动等瞬态错误,应自动重试而非立即失败。

我们的执行器采用“沙箱进程 + 硬超时 + 指数退避”策略:

def execute_skill(skill: Skill, input_data: dict) -> dict: # 1. 输入校验:用 Pydantic 模型验证 input_data 是否符合 skill.input_schema try: validated_input = skill.input_model(**input_data) except ValidationError as e: return {"error": "E_SKILL_004", "message": f"Input validation failed: {e}"} # 2. 启动子进程执行(隔离崩溃) proc = subprocess.Popen( [sys.executable, "-m", "skills.risk_score_v2", json.dumps(validated_input)], stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=skill.timeout_seconds # 硬超时,OS 层面 kill ) try: stdout, stderr = proc.communicate() if proc.returncode != 0: # 3. 重试逻辑:仅对特定错误码重试(如网络超时、503) if "ConnectionTimeout" in stderr.decode() or "503" in stderr.decode(): return retry_with_backoff(execute_skill, skill, input_data, max_retries=2) else: return {"error": "E_SKILL_005", "message": "Skill process crashed"} return json.loads(stdout.decode()) except subprocess.TimeoutExpired: proc.kill() return {"error": "E_SKILL_006", "message": "Execution timed out"}

这个设计的关键细节:

  • 子进程隔离risk_score_v2.py是独立脚本,即使它import tensorflow导致内存爆炸,也不会影响主 Agent 进程;
  • OS 级超时timeout=skill.timeout_seconds传递给subprocess.Popen,由操作系统强制终止,比 Pythonthreading.Timer更可靠;
  • 智能重试:只对可恢复错误(网络、服务临时不可用)重试,对KeyErrorTypeError等编程错误绝不重试,避免掩盖 bug。

经验:skill.md中的timeout_seconds字段必须显式声明。我们规定:所有技能默认超时 5 秒,金融类技能(如实时风控)不得超过 2 秒,数据分析类技能(如批量报告)可设为 30 秒。这个值写在skill.md里,而非代码中,确保 SLA 可审计、可变更。

3.3 生命周期管理:注册、发现、热更新、灰度发布

一个技能从编写到上线,需经历完整生命周期。skill.md是它的“出生证”,而生命周期管理是它的“操作系统”。

注册与发现

Agent 启动时,扫描skills/目录下所有*.md文件,用解析器加载为Skill对象,存入内存注册中心。同时,它向 Consul 注册一个健康检查端点/skills/{skill_id}/health,返回{"status": "up", "version": "v2.1"}。这样,Kubernetes 的ServiceMonitor或 Prometheus 就能自动发现所有技能实例。

热更新

我们不重启 Agent 来更新技能。当skill.md文件被inotify监听到修改时:

  • 解析器重新加载该文件;
  • 执行器切换到新版本的input_schematimeout_seconds
  • 旧版本技能进程继续处理完正在运行的请求,新请求全部路由到新版本。
    这实现了真正的“零停机更新”。某次紧急修复E_SKILL_002的边界校验,从修改skill.md到全量生效,耗时 12 秒。
灰度发布

对高风险技能(如资金转账),我们支持按user_id哈希灰度:

# skills/risk-score-v2.md --- canary: enabled: true traffic_percent: 5 hash_field: "user_id" ---

执行器读取此配置,对 5% 的user_id哈希值落在指定区间的请求,调用新版本技能,其余调用旧版本。效果立竿见影:新版本上线 2 小时内,通过对比灰度组与对照组的E_SKILL_003错误率,确认修复有效,随即全量。

提示:生命周期管理的精髓,在于把skill.md从“静态文件”变成“活的配置”。它应该像 Kubernetes ConfigMap 一样,可版本化、可审计、可回滚。我们要求所有skill.md必须纳入 GitOps 流水线,每次git push都触发 CI 对解析、校验、冒烟测试的全流程验证。

4. Agent Skills 的实战陷阱:那些skill.md里不会写,但会让你彻夜难眠的问题

skill.md很容易,让skill.md在生产环境稳定运行很难。过去一年,我帮 12 个团队排查过 Agent Skills 相关故障,87% 的根因不在代码,而在skill.md的“隐性约定”被打破。以下是三个最痛、最隐蔽、文档里绝不会提的陷阱,附真实复现步骤与根治方案。

4.1 陷阱一:<script>标签的幽灵依赖——当skill.md里藏着未声明的 JS 模块

搜索热词中反复出现failed to load module script: expected a javascript-or-wasm module scriptthe lang attribute of <script> is missing,这绝非偶然。根源在于:部分团队把skill.md当作“富文本编辑器”,在里面插入<script>标签来实现动态逻辑。

例如,一个stock-alert.md技能,为了“让用户自定义价格阈值”,写了这样的 HTML 片段:

<!-- 在 skill.md 的 '描述' 或 '示例' 区域 --> <div id="alert-config"> <input type="number" id="price-threshold" value="100"> <button onclick="saveConfig()">保存</button> </div> <script> function saveConfig() { const threshold = document.getElementById('price-threshold').value; // 发送配置到后端... } </script>

问题在哪?

  • skill.md解析器只处理###、表格、代码块,对<script>标签完全无视
  • 当前端页面渲染skill.md时(如用marked.js),这段 JS 会被执行,但它依赖的saveConfig函数在全局作用域不存在(因为skill.md是纯文本,不打包 JS);
  • 浏览器控制台报错ReferenceError: saveConfig is not defined,但技能本身仍能调用——错误被掩盖,直到某天用户反馈“配置按钮点不动”。

根治方案:零容忍<script>标签

  • CI 流程加入grep -r "<script>" skills/检查,命中即失败;
  • 所有交互逻辑移出skill.md,放入独立的前端组件(如 Vue 的SkillConfig.vue),通过skill_id动态挂载;
  • skill.md中的“示例”只保留纯 JSON,禁用任何 HTML/JS。

教训:skill.md的使命是定义契约,不是实现 UI。把它当作文档,UI 就交给专业前端;把它当作页面,你就亲手埋下不可维护的雷。

4.2 陷阱二:SKILL.mdskill.md的大小写战争——Linux 与 Windows 的文件系统鸿沟

热词中codebuddy无法导入skill.md高频出现,真相令人哭笑不得:开发者在 Windows 上用 VS Code 创建文件,命名为SKILL.md(全大写),Git 提交后,在 Linux 生产服务器上ls skills/看不到它——因为 ext4 文件系统区分大小写,SKILL.mdskill.md

更隐蔽的是:

  • VS Code 的文件树显示SKILL.md,但右键“在终端中打开”却进入skill.md目录;
  • git status显示modified: SKILL.md,但git add SKILL.md报错pathspec 'SKILL.md' did not match any files
  • Agent 解析器遍历skills/*.md,永远找不到SKILL.md,技能“凭空消失”。

根治方案:文件系统无关化

  • CI 流程强制标准化:find skills/ -name "*.md" -exec rename 's/(.*)\/(.*)\.md/$1\/\L$2.md/' {} \;(将所有.md文件名转小写);
  • Git 配置core.ignorecase = false,让 Windows Git 客户端也区分大小写;
  • 解析器升级:glob.glob("skills/*.[mM][dD]"),同时匹配skill.mdSKILL.MD

我们曾因此故障停服 47 分钟。现在,CI 流水线第一行就是:

# 检查大小写混乱 if find skills/ -regex ".*\.[Mm][Dd]" | grep -q "[A-Z]"; then echo "ERROR: Uppercase .md files detected! Rename to lowercase." exit 1 fi

4.3 陷阱三:<script setup>的无声吞噬——Vue3 组合式 API 如何悄悄吃掉你的skill.md内容

热词中vscode 中的vue script内容自动给我清理没有了thymeleaf 无法使用layui的script模版{{,指向同一个底层问题:现代前端框架的模板编译器,会主动解析并“优化” Markdown 中的<script>标签,导致内容丢失。

例如,一个>```python import requests # 调用技能 resp = requests.post( "https://api.example.com/skills/data-export-v1", json={"query": "SELECT * FROM users WHERE active=true"} ) print(resp.json())

表面看是代码块,但某些 Vue3 项目配置了 `markdown-it-container` 插件,会把所有 `<script>` 标签内的内容当作“待执行 JS”,在构建时尝试解析——而 `requests.post(...)` 不是合法 JS,编译直接失败,整个 `skill.md` 被清空。 **根治方案:三层防御** - **第一层(作者端)**:禁用 `skill.md` 中所有 `<script>` 标签,代码示例一律用 Markdown 代码块(```python); - **第二层(CI 端)**:`grep -r "<script>" skills/` + `grep -r "```.*\n.*requests\." skills/` 双重扫描; - **第三层(运行时)**:解析器对 `## 示例调用` 字段做白名单校验,只允许 JSON/Python/Shell 代码块,拒绝任何含 `<script>` 的 HTML。 > 最后一个经验:所有陷阱的共性,是把 `skill.md` 当作“自由文本”,而忽略了它作为**契约文件**的严肃性。它应该像 API Swagger 文档一样,接受 OpenAPI Spec 的严格校验。我们已将 `skill.md` 解析器集成到 SonarQube,任何字段缺失、类型错误、格式违规,都会在
http://www.jsqmd.com/news/1071486/

相关文章:

  • OpenCLAW飞书云原生集成:零代码AI能力嵌入工作流
  • Wireshark抓包分析核心:OSI分层过滤与TCP三次握手精解
  • MATLAB实现数独求解器:融合回溯法与候选数法的算法实践
  • 国产大模型落地实战:从智能体编排到全栈国产化适配
  • 密码掩码设计全解析:从安全原理到前端实现的最佳实践
  • Sora内测申请实战指南:从资格获取到高效应用全解析
  • MPC860 ATM调度与中断机制:硬件原理与实战配置详解
  • MPC8641D PCIe控制器错误捕获与配置空间访问机制详解
  • 教学辅助问答系统:基于SpringBoot+Vue的知识引擎设计
  • 长上下文大模型在金融招股书理解中的实战突破
  • Llama4应用构建:基于DLAI范式的可监控生产流水线
  • 从实战视角解析学生方程式大赛:线控刹车标定与数据采集系统应用
  • MPC8572E DMA控制器工作模式详解:从基础到高级的性能优化实践
  • CTF实战:从流量分析到AES解密的Misc综合解题思路
  • 用 Nacos 3.2 构建企业级 Skills Registry
  • 安卓APP逆向实战:从静态分析到动态验证的完整流程解析
  • 科学计算代码现代化重构:从Python 2祖传算法到可维护工程实践
  • MATLAB eigshow 交互式学习:特征值与奇异值分解的几何可视化
  • IoT数据分析实战:从传感器数据到智能决策的完整指南
  • GUIDE跨控件数据访问:从原理到实践的MATLAB GUI开发指南
  • 20行Rust实现AI代码Agent骨架:基于A3S模型的轻量执行环
  • 挖矿木马攻击路径转向:Redis、Docker等非Web服务漏洞防御实战
  • Hermes Agent Linux安装指南:轻量级AI智能体运行时部署实战
  • SVG矢量图形原理、应用与前端开发实战指南
  • OpenClaw浏览器自动化实现微信公众号全自动运营
  • 大模型技术解析:从算法原理到微调部署实战指南
  • DeepSeek V4 实质是工程成熟度代号:R1模型+协议网关的本地AI开发落地实践
  • Linux内核堆溢出漏洞CVE-2022-0995深度剖析与复现
  • Metasploit实战:SSH弱口令爆破原理、自动化检测与防御策略
  • ASTER框架:基于VAE和LLM的时间序列异常检测新方法