Skills 开发技巧 2:让大模型思考,让脚本执行

一个互联网技术玩家,一个爱聊技术的家伙。在工作和学习中不断思考,把这些思考总结出来,并分享,和大家一起交流进步。

这段时间我开发了好几个 Skills,可以逐步把工作中一些固定流程的事情让小龙虾来协助处理了,这里介绍一下其中3个skills,分别是项目管理晨会汇总、站点证书监控与自动续签、博客自动发布。做完之后回头看,有一些开发模式上的心得值得总结------大模型负责思考,脚本负责执行,这是我在这个过程中反复验证的核心理念。

一、三个真实案例,三份收益

1. 项目管理晨会汇总(pm-scrum)

这个 Skill 对接 TAPD,每天早上自动拉取当前迭代的需求、任务、风险,生成结构化日报发到企微。

收益

以前每天晨会前要手动打开 TAPD,点进迭代看板,挨个确认哪些完成了、哪些逾期了、哪些没人认领,整理成消息发出去,10 分钟起步。现在一句话触发,30 秒出报告。

更重要的是,风险识别变系统化了。以前靠肉眼扫,容易漏;现在脚本按规则(R1~R7)遍历每一条需求和子任务,无负责人、无排期、逾期未完成,一条都跑不掉。今天实际跑下来,迭代2 的 11 个需求里发现了 11 条高风险 + 6 条中风险,这要靠人工发现整理,至少得看10分钟。

关键实现 :迭代判断这里我踩了个坑------最初脚本直接取第一条 status=open 的迭代,结果拿到的是已经过期的迭代1(3月21日结束),数据完全错了。后来改成按日期范围判断:

go 复制代码
deffetch_iteration(wid:str):
today=date.today().isoformat()
iters=tapd_list("iterations_get",{"workspace_id":wid,"status":"open","limit":20})

# 过滤:今天必须在迭代的起止日期范围内
active=[
itforitiniters
ifit.get("startdate","")<=today<=it.get("enddate","")
]
ifactive:
active.sort(key=lambdax:x.get("startdate",""),reverse=True)
returnactive[0]

# 无活跃迭代:返回特殊标记 + 候选列表,由 LLM 给出建议
return{"_no_active":True,"candidates":[...]}

这段逻辑由 LLM 一次写好,之后每次执行结果完全确定,不再因为 LLM 判断方式的变化而漂移。


2. 站点证书监控与自动续签(site-health-monitor)

这个 Skill 每天定时检测 6 个站点的可用性和 SSL 证书有效期,发现证书快过期时自动 SSH 登录远程机器,用 certbot + dns-tencentcloud 插件续签,然后 nginx reload。

收益

今天跑了一次,发现 一个域名的证书只剩 5 天,已经触发了告警阈值(7天)。系统自动执行续签,整个过程:

go 复制代码
检测到证书预警(5天)
  → SSH 登录 IP:Port
  → sudo -s → export 腾讯云密钥
  → certbot certonly -a dns-tencentcloud -d 域名 
  → nginx -t → nginx -s reload
  → 证书更新

全程无人工干预,我是在看日志的时候才知道这件事发生了。

关键实现:续签这个流程如果让 LLM 直接执行,每次 SSH 命令的拼法、certbot 参数的组合都可能有细微差别,一旦某个参数不对证书就续不上。把它固化成脚本:

go 复制代码
defrenew_domain(ssh,domain:str,secret_id:str,secret_key:str,dry_run=False):
cmd=(
f"sudo -s -- sh -c '"
f"export TENCENTCLOUD_SECRET_ID={secret_id} "
f"TENCENTCLOUD_SECRET_KEY={secret_key}; "
f"certbot certonly -a dns-tencentcloud --agree-tos --non-interactive "
f"-d {domain} -d www.{domain}'"
)
rc,stdout,stderr=ssh_exec(ssh,cmd,dry_run=dry_run)
ifrc!=0:
raiseRuntimeError(f"certbot 失败: {stderr}")
returnstdout

每次执行的命令完全一致,报错信息清晰,LLM 只需要读结果、判断成功还是失败。


3. 博客自动发布(blog-writer)

这个 Skill 负责博客从写作到上线的完整流程:生成文章骨架 → 写正文 → 生成 Banner 图 → Hugo 验证 → 提交 PR → 等待 merge → push Gitee → SSH 登录远程机器执行 make 更新博客 → Playwright 截图验证首页。

收益

以前发一篇博客:本地写 → hugo 验证 → git commit → push → 登服务器拉代码 → make → 打开浏览器看效果,每次 15-20 分钟,而且这么多步骤都需要在不断地切换工具来操作,繁琐麻烦,现在是可以一气呵成了。

现在整个流程跑下来是全自动的,我只需要:①确认文章内容 ②merge PR。其他的由 Skill 完成,包括最后的截图验证------部署完自动打开浏览器截图发给我,眼见为实。

关键实现 :部署这步,服务器上的博客目录归 root 所有,ubuntu 用户没有写权限。如果让 LLM 决定怎么切换用户,每次可能用 sudo susudo -isudo -s 等不同方式,行为不稳定。固化到脚本:

go 复制代码
SSH_USER="ubuntu"
BLOG_DIR="/data/www/blog"

defdeploy(dry_run=False):
rc,stdout,stderr=ssh_run(
f"sudo -s -- sh -c 'cd {BLOG_DIR} && make 2>&1'",
dry_run=dry_run
)
ifrc!=0:
raiseRuntimeError(f"make 失败: {stderr}")
returnstdout

二、核心理念:为什么要「让脚本执行」

从上面三个案例可以看出两个共同点。

1. 不确定性收敛

大模型的输出是概率采样,每次都有细微差异。用同样的 prompt 让它生成 curl 命令,今天可能是 -H "Content-Type: application/json",明天可能多个空格或者参数顺序不同,大多数时候没问题,但在关键执行路径上,这种不确定性是隐患。

把执行逻辑写成脚本,LLM 的不确定性就被限制在「生成代码」这一步。

代码一旦写好,每次执行路径完全确定:

go 复制代码
LLM(不确定)→ 生成脚本(一次)→ 脚本执行(永远确定)

而不是:

go 复制代码
LLM(不确定)→ 执行命令(每次都可能不同)

2. Token 消耗优化

脚本执行不消耗 Token,只有 LLM 处理文本才消耗。

以项目管理报告为例,如果让 LLM 直接用工具一条条拉 TAPD 数据、处理 JSON、过滤风险项,全程 LLM 参与,单次消耗 8000+ Token。

改成脚本后:脚本负责数据拉取和处理,LLM 只读最终结果生成报告,单次降到 2000 以内,降幅超过 75%。

数据处理、格式转换、条件判断,这些都是「确定性工作」,没有理由让 LLM 参与。

3. 只告诉大模型它不知道的

这是我在写 SKILL.md(技能说明文档)时总结出的另一个原则。

最开始我习惯在 SKILL.md 里写得很详细,把所有背景知识都加进去,结果文档越来越长,每次触发 Skill 都要把一大段已知知识塞进上下文,Token 消耗居高不下,LLM 反而容易在细枝末节上绕圈子。

后来我意识到:大模型对通用知识已经有很强的基础,不需要你教它怎么写 Python、怎么用 SSH、怎么解析 JSON。它真正需要的,是它无法凭通用知识推断的部分

  • 你的项目的具体路径(/data/www/blog

  • 你的服务器的端口(2330 而不是默认的 22

  • 你的业务规则(证书剩余 7 天触发告警,不是 30 天也不是 3 天)

  • 你踩过的坑(sudo -s 而不是 sudo su,因为目录归 root 所有)

这些是大模型无法从互联网训练数据里学到的,必须显式告诉它。而「Python 怎么读文件」、「certbot 的基本用法」这类通用知识,写进 SKILL.md 纯粹是浪费 Token。

一个简单的判断标准:如果这条信息在 Stack Overflow 上能搜到,就不用写;如果只存在于你的系统或你的决策里,就必须写。

这个原则用在 Skill 开发上,让我的 SKILL.md 从动辄 500 行压缩到 100 行以内,触发时的上下文更小,LLM 更聚焦,执行更准确。

反例:脱敏规则

今天有一个很典型的反例,印证了这个原则。

我在给 blog-writer Skill 增加「敏感信息脱敏」功能时,第一反应是把所有脱敏类型整理成一张详细的表格写进 SKILL.md------IP 地址用 <YOUR_IP> 替换、Token 用 <API_KEY> 替换、数据库连接串用 <DSN> 替换......洋洋洒洒列了十几行。

写完之后想了想:这些信息大模型早就知道。IP、Token、密码、API Key 是敏感信息,是业界常识,Stack Overflow 上随便一篇安全文章都会提到。我把它写进 SKILL.md,不过是在用大模型自己的知识教大模型,纯属浪费 Token。

最终版本是一句话:

生成博客正文时,对所有敏感信息进行脱敏处理,替换为语义明确的占位符。

大模型看到这句话,凭通用知识就能正确执行------该脱敏什么、怎么替换,它都清楚。十几行表格能做到的事,一句话就够了。

SKILL.md 的核心原则:只写大模型不知道的(你的系统配置、你的业务规则、你踩过的坑),其余的信任它的通用能力。


三、执行循环:自愈式开发模式

基于这个理念,我现在写 Skill 的标准流程是:

go 复制代码
Task      明确目标
  ↓
Plan      LLM 规划步骤,设计数据流
  ↓
Code      LLM 生成 Python 脚本
  ↓
Execute   脚本执行,返回结果或报错
  ↓
Feedback  LLM 分析输出,报错则定位修复,重回 Code

Feedback 这步是关键。脚本非零退出不是终点,而是 LLM 的信号:「出错了,看 stderr 修。」这个自愈循环让 Skill 在遇到环境变化时能自己恢复,而不是崩掉让人工干预。

go 复制代码
defmain():
try:
ctx=plan(args)
result=execute(ctx)
print(result)# LLM 读取此输出做 Feedback
sys.exit(0)
exceptExceptionase:
print(f"ERROR: {e}",file=sys.stderr)
sys.exit(1)# 非零退出码 → 触发 LLM 自愈循环

四、总结

三个 Skill 开发下来,这套模式给我的最直接感受:

  • 稳:关键路径行为确定,不会因为 LLM 今天"心情不好"而执行出不同结果
  • 省:Token 消耗降了一半以上,对话更快,成本更低
  • 好调试:脚本可以独立运行,加日志、加单测,不用把整个 Agent 跑起来验证一个逻辑

大模型的脑子很灵,但需要一双确定的手。脚本就是那双手。

在 AI Agent 开发中,这个分工越清晰,系统越健壮。