1. 先搞明白OpenCode技能系统到底解决什么问题
1.1 从“靠提示词碰运气”到“给模型发工具手册”
刚开始接触OpenCode的时候,我的用法很简单:在会话里把需求描述得特别详细,让模型一步步执行。遇到能干的模型,效果还不错;遇到状态一多、任务一长的场景,模型就开始“自由发挥”——明明上一步刚说过要用某个脚本,下一步却自己另起炉灶;明明要求输出JSON,结果夹带一堆解释文本。问题不在于模型笨,而在于我把“怎么做”的说明全塞在临时对话里,模型只能靠上下文猜我的意图,猜得准全靠运气。
OpenCode的技能系统就是为这个问题设计的。它的核心思路是:把一套固定流程、固定脚本、固定输出格式提前封装成一个“技能模板”,模型在会话里根据任务描述自动扫描技能清单,发现合适的就加载使用。换句话说,我不需要每次都把步骤讲一遍,只需要让模型知道“有这么一个技能,它的工作流程是什么,遇到什么情况该调用它”。
这个设计尤其适合三类场景:一类是重复性极高的固定操作,比如批量重命名文件、格式化代码、扫描日志中的异常;一类是需要保证输出格式稳定的任务,比如生成指定结构的报表、提取特定字段;还有一类是需要多个步骤串联的流程,比如拉取数据、清洗、分析、输出结果。
1.2 技能模板的运行链路:模型扫描、匹配、加载、执行
技能系统的工作方式可以理解为一个“自动派单”的过程。OpenCode在启动会话时,会从配置的技能目录里读取所有技能模板,每个模板的核心是一份SKILL.md文件,文件里用结构化格式写清楚技能的用途和操作步骤。当你在对话中提出一个需求,模型会先检索有哪些技能与当前需求相关,然后读取匹配技能的全文,按其中的指令执行。
这个机制和普通的“提示词模板”有本质区别。提示词模板只是把一段文字插入对话,模型看到什么就临时理解什么;技能模板则多了一个“注册—扫描—匹配—加载”的链路,模型不是被动接收文字,而是主动查找技能库。就好比一个是临时抓个人来干活,一个是去工具库里拿对应的专用工具。
所以,基础技能模板的真正价值,在于“结构化的可复用性”。你写一次,以后所有会话都能自动命中这个技能,不需要重复维护一大段提示词。这个前提是:模板本身的字段要写得足够规范,目录位置要放对,脚本要能被正确调用。接下来我按自己的实操过程把这些细节逐一拆开讲。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. OpenCode基础技能模板的结构拆解:SKILL.md、scripts与assets
2.1 技能目录的三种组织方式
OpenCode的技能存放没有强制规定唯一路径,但社区和官方文档里最常用的是三种组织方式,我逐一试过,分别说一下适用场景。
第一种,全局技能目录。放在用户主目录下的.config/opencode/skills/或者OpenCode指定的全局配置目录里,适合那种“不管在哪个项目里都需要用”的技能,比如日志分析、代码格式化、通用工具函数。
第二种,项目级技能目录。放在当前项目的.opencode/skills/下,适合和业务强绑定的技能,比如这个项目的专属构建脚本、数据库迁移脚本、接口联调工具。项目级技能的好处是跟着Git仓库走,团队成员clone下来就能用,不需要每个人都手动配置。
第三种,自定义路径。在OpenCode的配置文件(比如opencode.json)里通过skills字段指定多个目录路径,路径之间用数组组织。这个方式灵活性最高,可以把不同来源的技能分门别类,比如第三方下载的技能包放一个目录,自己写的放另一个目录,互不干扰。
我自己项目的习惯是:全局技能目录放通用型技能,项目级目录放业务型技能,自定义路径基本不常用,除非需要挂载一个外部技能包。你如果刚开始接触,先建全局技能目录就够了,后面有需要再加项目级。
2.2 SKILL.md里的frontmatter和正文到底怎么写
技能的核心文件是SKILL.md,它的格式分为两部分:YAML格式的frontmatter字段区和markdown正文区。这个结构类似很多静态站点生成器里文章头部信息的写法,OpenCode会先解析frontmatter来获取技能的元信息,再把正文当作模型操作的说明书。
frontmatter里最关键的字段是name和description,这两个直接决定了技能能否被正确匹配。name是技能的唯一标识,要求简短且能代表功能,比如log-scanner或者code-formatter。description是给模型看的“检索索引”,必须写清楚这个技能处理什么任务、在什么场景下用、大概怎么做、输出什么格式。注意,description不是给人看的,你的措辞要尽量贴近模型理解任务的方式,写得太抽象会导致模型扫不到这个技能。
举个例子,我写过的一个日志扫描技能,description最初写的是“扫描日志文件中的异常信息”,测试时发现模型经常不匹配这个技能,改成“当用户需要分析日志文件、查找报错信息、统计错误码频次时使用此技能,输入为日志文件路径和过滤关键词,输出为结构化错误报告”之后,命中率立刻提高。原因是后者包含了触发条件、输入形式、输出形式三个维度,模型扫描时更容易判断“当前对话和这个技能匹配”。
正文部分就是给模型看的操作手册,用自然语言描述执行步骤,格式可以灵活,但我建议固定用“目标、输入、步骤、输出、注意事项”五段式结构。这样做的好处是:模型读取时路径非常清晰,不容易漏掉关键步骤;你自己维护时也方便增删内容,不会越改越乱。
2.3 scripts目录与执行权限:模型“动手”的入口
SKILL.md描述的是“怎么做”,真正动手干活的是脚本文件。OpenCode支持在技能目录下建一个scripts目录,把各种可执行脚本放进去,模型在读取SKILL.md后如果需要执行命令,就会去这个目录里找对应的脚本。
脚本类型没有限制,Python、Shell、Node.js都行,但有几个细节需要特别注意。第一,脚本要加上可执行权限,Linux和macOS下用chmod +x script.py,Windows下要确保脚本能被当前解释器直接调用。第二,脚本内部尽量使用相对路径或者从参数接收路径,不要硬编码绝对路径,否则项目一换位置就废了。第三,脚本的输入输出最好遵循“标准输入输入—标准输出输出”的原则,参数通过命令行传,结果通过stdout输出,这样模型解析结果时最省事,也最不容易出错。
除了scripts目录,有些技能还会带assets目录,存放技能运行所需的静态资源,比如配置文件模板、参考样例、数据字典。assets目录不是必须的,但如果你希望技能输出的结果总带一个固定表头,或者需要预设某些字段的可选值,把模板文件放在assets里让模型读取,比在SKILL.md里写一大段文字更稳定。
这里必须强调一个容易被忽略的问题:模型执行脚本时,工作目录可能不是你项目所在的目录,而是OpenCode启动时指定的某个工作目录。所以脚本里涉及文件路径时,要么用绝对路径,要么在SKILL.md正文里明确要求模型“先切换到项目根目录再执行脚本”。我早期写技能时忽略了这一点,导致脚本一直报找不到文件,排查了半天才发现是路径基准问题。
3. 从零建一个基础技能模板:完整操作Step by Step
3.1 环境准备:先解决“opencode不是可运行程序”的问题
新建技能之前,先把OpenCode本身装好、跑通。这一步看起来简单,但我在Windows机器上踩过一个经典坑:在PowerShell里输入opencode,系统直接报“无法将“opencode”项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。这个提示的意思是系统找不到opencode的可执行文件,常见原因有两个——安装路径没有加入PATH环境变量,或者安装过程中被安全软件拦截了。
如果你遇到这个报错,按下面的顺序排查:
- 先确认opencode有没有装上,在PowerShell里执行
Get-Command opencode -ErrorAction SilentlyContinue,如果返回空,说明命令确实不在PATH里。 - 找到opencode的可执行文件位置。大部分安装方式会放在npm全局目录、Homebrew目录或专门的bin目录下,用
where.exe opencode(Windows)或which opencode(macOS/Linux)查看。 - 如果文件存在但命令找不到,把它的所在目录加入系统PATH。Windows可以在“系统属性—环境变量”里编辑PATH,加一行目录路径然后重新打开终端;macOS/Linux则在
~/.zshrc或~/.bashrc里加一行export PATH="/具体路径:$PATH",然后source一下。 - 如果文件不存在,重新运行安装命令,并注意安装日志有没有提示“安装成功”字样。
把命令跑通之后,再执行opencode进入会话界面,确认能正常对话,再进行下一步。这一步不能跳过,因为技能系统调试的过程中会频繁用到命令行,命令本身不工作,后面一切都白搭。
3.2 定义一个日志扫描技能:从目录创建到SKILL.md落地
我这里用“日志扫描技能”作为示例,带你完整走一遍基础技能模板的创建流程。这个技能的需求是:输入一个日志文件路径和关键词列表,输出每个关键词的出现次数以及匹配行示例。
先在全局技能目录下建一个名为log-scanner的子目录(具体目录请按你OpenCode的实际安装情况定位),然后创建SKILL.md文件,内容如下:
yaml复制---
name: log-scanner
description: 当用户需要分析日志文件、查找错误信息、统计关键词出现次数或提取日志样本时使用此技能。输入为日志文件路径和可选的关键词列表。输出为结构化统计报告。
---
markdown复制# 日志扫描技能
## 目标
统计日志文件中指定关键词的出现次数,并输出匹配行示例。
## 输入
- 日志文件路径
- 关键词列表(逗号分隔,可选;未指定则默认扫描 ERROR、WARN、Exception)
## 执行步骤
1. 确认日志文件存在且可读,如果文件不存在,直接向用户反馈错误,不要自行猜测路径。
2. 调用 scripts/log_scanner.py 脚本,第一个参数为日志文件路径,第二个参数为关键词列表。
3. 脚本输出JSON格式结果,字段包括 total_lines、keyword_stats、sample_lines。
4. 将结果整理成markdown表格输出给用户。
## 输出格式
| 关键词 | 出现次数 | 示例行 |
|--------|----------|--------|
| ERROR | 12 | [xxx] ERROR ... |
## 注意事项
- 如果文件超过50MB,先询问用户是否需要只扫描前10000行。
- 不要修改原始日志文件。
- 扫描结束提醒用户结果基于全量日志或截断日志。
注意frontmatter的三个反引号之间是YAML,正文从# 日志扫描技能开始,不要混在一起。OpenCode解析时严格区分这两个区域,格式写错会导致技能无法识别。
3.3 编写scripts/log_scanner.py脚本并设置执行权限
技能描述写好了,接下来写真正的扫描脚本。脚本要求:能接收命令行参数、返回JSON格式结果、处理文件不存在等异常情况。
python复制#!/usr/bin/env python3
import json
import sys
from collections import defaultdict
def main():
if len(sys.argv) < 2:
print(json.dumps({"error": "缺少日志文件路径参数"}))
sys.exit(1)
log_path = sys.argv[1]
keywords_arg = sys.argv[2] if len(sys.argv) > 2 else "ERROR,WARN,Exception"
keywords = [k.strip() for k in keywords_arg.split(",") if k.strip()]
try:
with open(log_path, "r", encoding="utf-8", errors="ignore") as f:
lines = f.readlines()
except FileNotFoundError:
print(json.dumps({"error": f"文件不存在: {log_path}"}))
sys.exit(1)
except Exception as e:
print(json.dumps({"error": f"读取文件失败: {str(e)}"}))
sys.exit(1)
keyword_stats = defaultdict(int)
sample_lines = defaultdict(list)
total_lines = len(lines)
for line in lines:
lowered = line.lower()
for keyword in keywords:
if keyword.lower() in lowered:
keyword_stats[keyword] += 1
if len(sample_lines[keyword]) < 3:
sample_lines[keyword].append(line.strip()[:200])
result = {
"total_lines": total_lines,
"keyword_stats": {k: keyword_stats[k] for k in keywords},
"sample_lines": {k: sample_lines[k] for k in keywords}
}
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
写完脚本后,给它加上可执行权限。Linux和macOS下执行chmod +x scripts/log_scanner.py,Windows下如果直接用Python解释器运行,不需要额外权限,但要注意命令里明确写python scripts/log_scanner.py,而不是直接写脚本路径。
这个脚本的设计有几个刻意的考虑:一是所有结果都走stdout输出JSON,方便模型解析;二是对关键词做了大小写不敏感处理,避免日志里大小写不一导致统计不准;三是每个关键词最多保留3条示例行,避免输出过大撑爆模型上下文。这些细节看起来小,但实际用起来非常影响体验。
3.4 把技能注册进OpenCode配置并验证调用
技能目录和脚本都准备好了,最后一步是让OpenCode知道这个技能存在。如果OpenCode默认扫描全局技能目录,你只需确认目录结构正确即可;如果默认不扫描,需要编辑配置文件(通常是opencode.json),在skills字段里加上技能目录的路径。
配置文件示例:
json复制{
"skills": [
"~/.config/opencode/skills"
]
}
这里的路径写的是技能目录的根路径,OpenCode会遍历下面所有子目录,寻找每个子目录里的SKILL.md文件。
配置完成后,重启OpenCode会话,然后在对话里发出一个自然语言请求,比如“帮我看一下server.log里ERROR出现了多少次,顺便提取几个示例行”。如果技能系统生效,模型应该会自动检索到log-scanner技能并调用它。如果模型只是直接回答而没有调用脚本,可能是技能没被识别,或者description写得不到位,可以参考第2.2节里的方式优化。
我自己测试时习惯先手动给一句话确认技能已加载——“列出你当前可用的技能”,如果模型能说出log-scanner,说明技能注册成功;如果它说不知道,优先检查目录路径、SKILL.md的frontmatter格式、配置文件里的路径是否正确。
4. 技能被调用时的幕后机制:描述匹配、参数槽位与指令约束
4.1 description是“索引”,正文是“说明书”
很多人在写SKILL.md时容易陷入一个误区:把description当成给“人”看的摘要,写得很文艺、很抽象。但description真正服务对象是模型,它在会话中扫描技能清单时看的不是正文,而是这个字段。所以description一定要像搜索引擎的关键词组合一样,把触发场景、输入形态、输出形态都写进去。
我对比过几种写法,效果非常直观。写法A:“扫描日志文件中的错误信息”,模型偶尔能命中,但经常在用户需求表达得比较隐晦时不匹配。写法B:“当日志文件需要分析、查找ERROR/Exception/Malformed等错误关键词、统计错误次数、查看错误上下文时使用此技能。输入可为文件路径或上一轮对话中的日志内容。输出为包含统计次数和示例行的报告。”改成写法B之后,几乎用户一说“日志”相关内容,模型都会优先考虑这个技能。
这背后的原因是:模型扫描技能列表时,类似于一种语义匹配,它会把当前对话意图和每个技能的description做相似度判断。description里包含的关键词越多、覆盖的场景越广,匹配率越高。但也不是越长越好,太长的description会占用上下文token,还可能让模型认为这个技能什么都能干,结果什么都不敢用。我建议控制在3到4句话,覆盖“什么场景用”“输入什么”“输出什么”三个维度就够。
4.2 参数占位符与解析:模型如何填充技能模板
SKILL.md正文里经常需要让模型填入具体参数,比如文件路径、关键词列表、输出目录。这些参数怎么传递给脚本?答案很简单:让模型根据用户对话内容提取参数,然后在执行命令时手动拼上去。
但这带来一个常见问题:模型提取参数时可能“想当然”。比如用户说“看下昨天的日志”,日志文件路径是logs/app-20250101.log,模型可能会猜成logs/app.log,或者在路径里加上不存在的目录。这时候你只在SKILL.md里写“获取日志文件路径”是不够的,必须写清“如果用户未明确提供路径,先向用户确认,不要猜测”。
参数解析的另一个细节是参数顺序和格式。脚本里我规定第一个参数是文件路径,第二个参数是关键词列表,那么在SKILL.md正文里就要明确写“调用时第一个参数为文件路径,第二个参数为逗号分隔的关键词列表,不要改变顺序”。模型遵循长指令的能力比很多人想象中强,前提是你把指令写得足够具体。
补充一点:如果脚本需要接收一个JSON字符串作为参数,要提醒模型用单引号包裹整个JSON,或者把参数写到临时文件再传给脚本,否则命令行解析会出问题。这类“参数传递边界”是技能模板最容易翻车的地方,宁可多写一句注释,也不要指望模型自己懂。
4.3 在模板里给模型立“边界约束”:什么时候不许用
技能模板不只是告诉模型“怎么做”,还要告诉模型“什么时候不做”。我在SKILL.md的注意事项部分,通常固定加几条负面约束,比如:
- 如果输入文件不存在,不要自行猜测路径,直接向用户反馈错误。
- 如果用户的需求和本技能描述不完全匹配,不要强行调用,先向用户确认。
- 如果脚本执行的输出包含敏感信息,不要展示完整内容,只展示摘要。
负面约束的作用是给技能画一个边界,防止模型在语义模糊的情况下乱用技能。这一点在技能数量变多之后尤其重要——当你的技能库里同时有code-formatter、code-linter、code-documenter,模型如果分不清边界,很容易一个请求触发两个甚至三个技能,输出结果反而混乱。
我自己的经验是:每写一个新技能,都会在注意事项里至少写三条“如果……则不要……”句式,并在测试时故意用模糊需求去试探模型是否能正确区分。如果发现它经常误调用,就回头强化description和注意事项的表述。
5. 技能不生效的常见排错链路:从命令找不到到参数解析失败
5.1 “opencode不是可运行程序”:命令层面的排查
前文提过Windows下最常见的报错“无法将‘opencode’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”,这里再展开一条完整排查链路。
首先,确认终端类型。Windows下如果你在用PowerShell,命令识别和cmd不完全一样,有时候你在cmd里装好了,但PowerShell因为PATH没有刷新,仍然找不到。解决方法不是重装,而是重启终端或者执行$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")手动刷新当前会话的PATH。
其次,检查是否安装了多个版本。有些情况下系统里存在两个OpenCode可执行文件,一个旧版本在一个新版本,PATH里排在前面的不是你想用的那个。执行Get-Command opencode | Format-List Source可以查看实际调用的路径,如果发现不对,调整PATH顺序或删除旧版本。
最后,确认安装代理和镜像源的问题。很多用户安装OpenCode时会走镜像源或者代理工具,如果下载不完整,可执行文件可能损坏,导致命令存在但运行报错。遇到这种情况,最简单的办法是卸载之后换官方源重装,装完先执行opencode --version确认版本号能正常输出。
5.2 技能没出现在会话里:目录位置、frontmatter字段、命名冲突
如果你在对话中向模型询问“可用技能”,它说没有,或者你自己明显感到技能没被加载,优先检查三个点。
第一,目录位置是否正确。OpenCode默认扫描的技能目录路径一般固定在配置文件或全局设置里,你把技能放在其他位置是不会被扫描的。先执行OpenCode相关命令或者查看配置,确认技能目录的绝对路径。
第二,SKILL.md的frontmatter格式是否正确。YAML字段名拼错、冒号后面没加空格、三个反引号被误删,都会导致解析失败。你可以打开SKILL.md,重点检查name和description这两行,看冒号后是否有一个英文空格。
第三,命名冲突。技能库中已经存在同名的name字段时,新技能可能被忽略。排查方式是逐个列出技能目录下所有SKILL.md文件,确认没有重复的name。
5.3 脚本执行报错:权限、换行符、相对路径
技能能加载但执行时报错,这个问题出在脚本本身或脚本与系统的兼容性上。最常见的是三种情况。
权限问题:Linux/macOS上直接执行脚本文件时提示Permission denied,解决办法是chmod +x。Windows上如果脚本是.py文件,但系统没有把.py文件关联到Python解释器,也会报错,解决方案是在命令里显式调用python。
换行符问题:在Windows上编写脚本,保存为CRLF换行格式,然后放到Linux服务器或macOS上运行,可能出现/usr/bin/env python3\r这种奇怪的解释器路径报错。解决方案是用文本编辑器把换行符改成LF,或者在脚本第一行不要写env方式,而是用完整路径。
相对路径问题:脚本内部用了./data/input.log这种相对路径,但是模型执行脚本时的工作目录不是脚本所在目录,导致文件找不到。解决方案是在脚本里基于__file__判断目录:script_dir = os.path.dirname(os.path.abspath(__file__)),然后拼出资源的绝对路径。
5.4 参数被模型“想当然”地填错:怎么从源头规避
这是所有技能排错里最隐蔽也最难发现的问题。脚本本身没有bug,技能也加载了,但输出结果完全不对,打开日志一看,原因往往是模型把参数传错了。
举个例子,你的技能要求第一个参数是日志文件路径,第二个参数是关键词列表。模型可能因为用户说了一句“重点看ERROR和数据库连接失败的报错”,就自动把“数据库连接失败”也塞进关键词列表,甚至把用户没有明确给出的路径猜了一个。这种“好心办坏事”的情况,靠事后修脚本很难解决,必须在SKILL.md正文里做硬性约束。
我在基础模板里通常固定加一段“参数确认规则”:
markdown复制## 参数确认规则
- 对于用户未明确提供的参数,不要自行猜测或设置默认值。
- 如果参数不完整,先向用户询问缺失项,比如文件路径、关键词列表。
- 只有所有必要参数都明确之后,才允许执行脚本。
加上这段之后,模型“想当然”的概率会大幅降低。因为模型在遵循明确指令时,比我们想象中要“听话”得多,怕的就是你没写清楚边界,它只能自由发挥。
6. 让基础模板长出进阶能力:参数校验、多脚本与错误反馈
6.1 模板内置校验:让技能学会拒绝“脏数据”
基础技能模板跑通之后,你可以往“健壮性”方向进阶。第一个值得加的是参数校验。这个校验不是写在脚本里,而是写在SKILL.md正文中,让模型在执行前先判断数据是否合理。
举例,日志扫描技能里可以加一条:“如果传入的文件扩展名不是.log或.txt,提示用户确认文件类型后再执行。”这个逻辑看起来很简单,但能防止很多低级问题。还有:“如果用户要扫描的关键词超过20个,提示用户减少关键词数量,避免输出内容过长。”
校内验不是替代脚本里的try-catch,而是把“检查数据合理性”的职责前移给模型。脚本里的异常处理负责的是“出了错怎么办”,模型负责的是“错误发生前怎么避免”。两者结合,技能才靠谱。
另外,如果技能的使用者不只你一个人,建议在模板里加一个“版本”字段,以便后续升级时追踪。在frontmatter里加version: 1.0.0,同时在配置文件里记录每个技能的使用次数,时间长了可以统计出哪些技能是高频使用的,哪些长期闲置,方便优化。
6.2 从单脚本到多脚本:查询、执行、回滚分离
当技能逻辑变得复杂时,一个脚本“一把梭”会很难维护。比如做个数据库相关技能,直接一个脚本完成“查询连接串—执行SQL—备份数据—返回结果”,不仅脚本代码臃肿,一旦某一步失败,整个流程都乱了。
我的做法是把流程拆成多个脚本,在SKILL.md正文里定义好调用顺序和条件。举一个例子,一个“数据库变更”技能可以包含以下脚本:
| 脚本文件 | 职责 | 调用时机 |
|---|---|---|
| check_schema.py | 检查表结构是否匹配预期 | 执行变更前 |
| execute_change.py | 执行变更SQL | check通过后 |
| rollback_change.py | 回滚变更 | execute失败或用户要求回滚 |
SKILL.md正文里相应写清楚:
markdown复制## 执行流程
1. 调用 scripts/check_schema.py,传入变更描述文件,检查结构是否匹配。
2. 如果check通过,调用 scripts/execute_change.py 执行变更。
3. 如果execute返回失败码,调用 scripts/rollback_change.py 执行回滚。
这样设计的好处是每个脚本职责单一、容易测试,模型也能根据中间结果决定下一步动作,而不是只能一把梭执行完整个流程。
6.3 错误反馈信息设计:让模型看到“为什么失败”
脚本执行失败时,模型会读取stdout和stderr,但它只能看到文本,不理解上下文的含义。所以脚本里的错误输出必须带上足够的信息,帮助模型判断如何处理。
我的统一错误输出格式是:
json复制{
"error": true,
"error_code": "FILE_NOT_FOUND",
"message": "日志文件不存在: /path/to/server.log",
"suggestion": "请检查路径是否正确,或向用户确认文件位置"
}
其中error_code要定义成机器可读的简短字符串,message是人读的详细信息,suggestion是给模型的下一步行动建议。这样模型在读到错误时,不需要自己反复猜测,而是能根据suggestion直接给出合理回复,甚至自动重试。
这个设计看起来只是输出格式的小调整,实际上能显著提升技能在复杂场景下的表现。因为模型处理异常的能力有限,你能在错误信息里给出明确的“下一步”,它就少了很多有风险的自由发挥。所有脚本统一遵循这套错误输出规范后,不同技能之间的错误处理逻辑也保持一致,维护成本更低。
最后分享一下我最近的体会:OpenCode技能系统的门槛不在“写脚本”,而在“设计模板”这件事上。一个基础技能模板好不好用,取决于SKILL.md里的描述够不够精确、脚本的输入输出规不规范、边界约束写得够不够清楚。先从一个日志扫描这样的简单技能开始跑通全流程,比一开始就追求复杂的多步骤技能要稳妥得多。等你对模型的匹配习惯、脚本调用方式都熟悉了,再逐步加大难度,扩充技能库,会顺手很多。
