前阵子不是流行一句话嘛,大模型是大脑,工具是手脚。真到自己动手给 Agent 搭能力的时候才发现,光有大脑可不够,手脚得一根根手指头接上去,而且每根手指头怎么动、什么时候动、力气用多大,全是细节中的细节。这个项目代号叫agent-skills,核心就做一件事:为 Agent 建立一套可插拔、可调试、可观察的技能体系,让模型不再只是"会聊天",而是真正"能干活"。这篇文章我把从设计到实现再到踩坑的完整过程捋一遍,给正在做 Agent 落地的朋友一份真实可参考的实战记录。
1. 为什么需要一套"技能"而不是一组"提示词"
很多人一开始的想法是:既然大模型这么聪明,我把需求写清楚不就行了?让他自己生成代码、自己调函数、自己看日志,听起来很美。但真跑起来就会发现,纯靠提示词驱动 Agent,会遇到三个绕不过去的坎。
第一个坎是决策不稳定。同一个需求,Ask Agent 十次,他可能五次选择写脚本、三次选择调 API、两次干脆问你"请问您想怎么做"。不是模型不行,而是没有边界。技能体系本质上是给 Agent 划定了"你能干什么、不能干什么"的明确边界,把决策从"开放式命题"变成"有限选项的匹配"。
第二个坎是行为不可复核。提示词驱动的 Agent 在执行过程中经常自由发挥,用了哪个函数、传了什么参数、为什么这样处理,回头看全是一团黑盒。出问题别说回滚,连复现都难。而技能体系要求每个技能都有明确的输入输出、执行逻辑和错误处理,每一次调用都留下可追踪的日志,这才符合工程化的底线。
第三个坎是能力无法沉淀。你调了三天三夜、好不容易让 Agent 学会了一种业务处理流程,结果换个场景、换个项目,这些经验就全丢了,每次都要从头开始调。技能就不一样,它把"怎么做好一件事"固化成了代码和配置,一次封装、处处复用,这才是真正的资产积累。
所以我的判断很明确:Agent 的底层是模型,但 Agent 的上限是技能。提示词解决"说什么",技能解决"做什么"。agent-skills项目就是围绕这个判断做的整套实践,目标只有一个——让 Agent 稳定、可靠、可维护地把活干完。
1.1 技能、工具、插件、工作流:先厘清概念
我见过不少团队,四个人聊 Agent 架构,聊到一半才发现每个人说的"工具"根本不是一回事。这里先把我自己的定义放出来,后面的内容都基于这套划分。
| 概念 | 我的定义 | 典型例子 |
|---|---|---|
| 工具(Tool) | 最细粒度的原子操作,单个动作,无状态 | 发一个 HTTP 请求、读一个文件、执行一条命令 |
| 技能(Skill) | 面向一个完整任务目标的操作编排,可以组合多个工具,有输入输出约定,可复用 | 代码仓库巡检、自动化测试执行、发布前联检 |
| 插件(Plugin) | 一组相关技能/工具的打包分发单元,包含配置和元数据 | 某个云厂商的整套操作能力,打包成一个插件 |
| 工作流(Workflow) | 确定的、固定的流程编排,分支条件预先定义,不允许模型自由发挥 | 每天凌晨的数据清洗定时任务 |
这里我特别要强调的是,技能恰好站在"工具"和"工作流"之间的黄金位置。它比工具高一层,能编排多个原子操作;又比工作流灵活,把"什么时候调哪个工具、怎么组合"这部分决策留给模型。换句话说,技能是模型与工具之间最好的缓冲层:上面让模型做决策,下面隔离掉工具的复杂性,双方都不用去适应彼此的撕裂。
2. 技能设计的底层拆解:一个技能应该由什么构成
agent-skills走了不少弯路之后,最终沉淀出的技能结构包含四个核心部分:元信息、触发条件、执行逻辑、结果标准化。缺一个,技能的可用性和可维护性都会明显掉档。
元信息是技能的外衣,包括技能名称、描述、版本、作者。名称要短,描述要长而精准,因为对大模型来说,描述就是技能的门面,门面不吸引人、不明确,Agent 就不会在正确的时机选它。这一块我用一个词概括:"给自己看的是代码,给模型看的是描述"。描述写不好,技能再厉害也是深巷里的酒。
触发条件这件事特别值得念叨一下。大部分 Agent 框架默认是"模型自主决定是否调用技能",但实际场景里完全依赖模型自觉是不行的。我最后采用的做法是给每个技能标注两种触发模式,一种是"建议触发场景",写在描述里让模型自己判断;另一种是"强制前置依赖",由框架层在流程启动时检查,比如部署技能必须前置执行构建技能,这是流程刚性要求,不能交给模型临场决定。
执行逻辑相对直白,就是技能真正要干的活。但这里有个血泪教训:技能内部不要追求大而全的通用处理,要按场景预期收窄逻辑。比如一个"获取服务器信息"的技能,别想着适配所有操作系统,先明确支持 Linux,遇到 Windows 直接返回"不支持"并说明原因,比勉强跑一遍然后输出乱码强得多。
最后是结果标准化。技能跑完,返回给模型、返回给上层调度的数据格式必须稳定统一。我的做法是所有技能统一返回一个结构体,包含 status(成功/失败/部分成功)、data(业务数据)、error(错误信息)、meta(耗时、日志链接等元信息)。这是agent-skills里最不起眼但价值最高的设计——它让上层编排不用为每种技能写特殊的解析逻辑。
2.1 技能粒度:切粗了是摆设,切细了是灾难
技能粒度设计是agent-skills里最反复纠结的一个问题。一开始我走了两个极端,先是一股脑把功能切得特别细,"读文件"一个技能、"写文件"一个技能、"列出目录"一个技能——结果模型选技能选到崩溃,一个简单的任务要串五六个技能;接着又走了另一个极端,一个"自动化测试"大技能把所有事全包了——结果技能内部成了浆糊,改一个判断逻辑都要翻上百行代码。
试错之后我的结论是,技能的粒度要看任务的连贯性。如果一个任务的多个步骤放在一起有明确的先后顺序和共同目标,就应该是一个技能;如果步骤之间可能被单独复用,或者可能由不同的技能替代,就应该切分。举两个具体例子。
"代码仓库巡检"我做成一个完整技能,内部封装了拉取最新代码、运行静态检查、执行单元测试、收集覆盖率四个步骤。因为这几个步骤的目标只有一个——判断仓库健康状况,拆成四个独立技能不仅增加调度开销,还会引入中间状态管理的问题。
反过来,"获取数据库 Schema"和"生成 CRUD 代码"我切成了两个独立技能。因为获取 Schema 这个动作在写文档、做数据迁移、做查询优化等多个场景都会用到,它本身有独立价值;生成代码则是一个更大粒度的任务,可以在内部调用获取 Schema 作为子操作。
2.2 技能描述:这是写给模型看的说明书
技能描述怎么写,是agent-skills里提升效果最立竿见影的部分。我刚开始写描述跟写文档似的,"根据用户需求执行适当操作完成目标",结果模型在多个技能之间犹豫不决。后来我把描述改成了这种风格:
repo_inspect: 对指定代码仓库执行全面的健康巡检。 适合在以下场景调用: - 用户要求评估代码质量 / 准备发布前检查 / 想了解仓库状态 - 作为发布工作流的前置步骤 - 需要获取测试覆盖率、静态检查结果时 不适合的场景: - 用户只是问某个文件的内容(应该直接读文件) - 用户需要修改代码逻辑(应该走代码修改流程) 执行时会依次运行:拉代码 -> 静态检查 -> 单测 -> 覆盖率收集 总耗时通常在 10 分钟内,请提醒用户耐心等待这一改动让模型选对技能的比例显著提升。原理也简单:模型在匹配技能和用户意图时,本质上是在做语义匹配,描述里明确写清楚"适合什么场景、不适合什么场景、会做什么事、要多长时间",相当于直接给了模型一个填空题的答案选项,命中率自然高。我总结出一个公式:好描述 = 动词开头 + 目标说明 + 适用场景 + 不适用场景 + 预期行为 + 预期耗时。
3. 手把手实现一个技能:仓库巡检技能从零到上线
纸上谈兵够了,直接看代码。我用"代码仓库巡检"这个技能做完整拆解,把这个技能从定义到注册的全部过程走一遍。这个技能也是agent-skills项目里第一个真正落地的技能,后续很多技能都是照着它的骨架长出来的。
3.1 第一步:定义技能 Schema
所有技能的第一件事是定义 Schema,它同时是给模型看的接口说明、给框架看的注册信息、给调用方看的数据契约。仓库巡检技能的 Schema 长这样:
{ "name": "repo_inspect", "version": "1.2.0", "description": "对指定代码仓库执行全面的健康巡检,涵盖静态检查、测试执行与覆盖率收集", "tags": ["code-quality", "pre-release", "testing"], "parameters": { "type": "object", "properties": { "repo_path": { "type": "string", "description": "本地仓库的绝对路径" }, "branch": { "type": "string", "description": "要巡检的分支,默认当前分支", "default": "" }, "strict_mode": { "type": "boolean", "description": "严格模式,开启后静态检查告警也视为不通过", "default": false } }, "required": ["repo_path"] }, "timeout_seconds": 900, "allowed_actions": ["git_pull", "run_lint", "run_tests", "collect_coverage"] }注意几个细节。timeout_seconds我设成 900 秒,因为一次完整巡检包括拉代码、跑测试,太小的超时直接导致任务半途而废;allowed_actions是权限声明,框架层会依据这个字段拦截技能内部尝试执行的未授权命令,这是安全兜底,不然后面模型被提示词注入,技能就成了攻击通道。
parameters里每个字段都有 description,这也是写给模型看的。模型要根据用户的话去填这些参数,如果 description 写"分支名",模型不知道分支名可以从哪来,但写成"要巡检的分支,默认当前分支",模型就能在用户没说分支时直接留空。
3.2 第二步:实现技能核心逻辑
技能的实现我放在独立目录里,一个技能一个目录,做到技能内部高内聚、技能之间零依赖。核心代码挺直白:
# skills/repo_inspect/skill.py import subprocess import time from dataclasses import dataclass, field from typing import Optional @dataclass class SkillResult: status: str # success | failed | partial data: dict = field(default_factory=dict) error: Optional[str] = None meta: dict = field(default_factory=dict) def execute(repo_path: str, branch: str = "", strict_mode: bool = False) -> SkillResult: started = time.time() steps = [ ("pull_code", lambda: pull_code(repo_path, branch)), ("run_lint", lambda: run_lint(repo_path, strict_mode)), ("run_tests", lambda: run_tests(repo_path)), ("collect_coverage", lambda: collect_coverage(repo_path)), ] # 支持断点续跑:记录已完成的步骤,失败时返回部分结果 finished_steps = [] for step_name, step_func in steps: try: step_func() finished_steps.append(step_name) except Exception as e: return SkillResult( status="partial" if finished_steps else "failed", data={"finished_steps": finished_steps, "failed_step": step_name}, error=str(e), meta={"duration_ms": int((time.time() - started) * 1000)} ) return SkillResult( status="success", data={ "lint_summary": read_lint_summary(repo_path), "test_summary": read_test_summary(repo_path), "coverage": read_coverage(repo_path), }, meta={"duration_ms": int((time.time() - started) * 1000)} )这个实现有几个值得留意的设计决策。一是返回SkillResult而不是让函数内部自己抛异常或者直接打印日志,这样上层拿到的是一个结构化的结果对象,格式化展示、存日志、喂给模型都顺手。二是"断点续跑"思路,技能跑一半挂掉时,返回已完成步骤列表和失败步骤,模型可以基于这个信息判断"是否需要重试整个技能"还是"只重试失败的步骤",这个信息后续在编排里特别有用。
三是四个内部步骤各自独立成函数,每个函数内部都有 try/except 包裹并抛出带上下文的异常。比如run_lint失败,异常信息不是简单的"lint failed",而是"静态检查执行失败,退出码 1,输出见 /tmp/repo_inspect/lint_20250315.log"。这样 Agent 看到错误信息就知道下一步该去看日志。
3.3 第三步:注册与接入 Agent 运行时
技能实现的再完整,不注册进 Agent 的运行时也白搭。agent-skills的注册机制是在一个统一清单文件里声明技能路径,运行时动态加载:
# skills/registry.yaml skills: - name: repo_inspect entry: skills/repo_inspect/skill.py enabled: true - name: gen_crud_code entry: skills/gen_crud_code/skill.py enabled: true # ... 其他技能运行时启动时读取这个清单,动态导入每个技能的execute函数,并把技能的 Schema 注入到模型上下文里。这里我踩过一个坑,一开始把全部技能都一股脑注入,技能多了之后模型上下文暴涨,选择准确率反而下降。后面改成技能分组加载:基础技能常驻,领域技能按需加载。比如模型收到一个跟"发布"相关的任务提示词时,框架层先通过关键词匹配把发布相关的技能集注入,其他技能暂时不可见。用这种方式控制上下文规模,效果立竿见影。
接入之后,整个链路就是:用户说一句"帮我看看这个仓库能不能发版" -> 模型看到可用的repo_inspect技能,识别出意图,填入参数repo_path=/data/myproject-> 框架层解析模型输出,调用技能执行函数 -> 技能跑完返回SkillResult-> 框架把结果格式化后交回模型 -> 模型基于结果生成最终回复。整个链路中,模型负责"决策",技能负责"执行",框架负责"连接",三者各司其职,可替换性也最好。
4. 技能编排:从单技能到多技能协作
单个技能解决了"单点任务",但真实业务场景里,Agent 通常要连续完成多个任务。agent-skills在技能协作这部分的设计,核心就一句话:把技能当函数,把 Agent 当调度器。模型的角色不是"执行者"而是"调度者",负责拆解任务、决定调哪个技能、按什么顺序调、怎么处理技能返回的结果。
4.1 串行编排:发布前联检组合
串行是最常用也最简单的编排模式。我实际使用频率最高的一个组合是发布前联检,一共串了四个技能:
repo_inspect跑代码健康检查,确认测试和静态检查都通过config_check检查配置文件,确认环境变量、依赖版本、数据库连接串都正确db_migration_plan生成数据库迁移脚本的预演执行计划build_artifact构建发布产物并生成校验和
模型在收到"准备发布"的指令后,会按照技能描述中建议的顺序逐个调用。但要注意,这里不能完全靠模型自觉,我在框架层加了一个依赖检查:config_check依赖repo_inspect的检查结果,如果前者失败,后面的技能直接跳过,不给模型发挥"我觉得还能继续"的空间。这是流程刚性,不是建议。
# 伪代码示例:串行编排示意 pipeline = [ SkillCall("repo_inspect", {}), SkillCall("config_check", {}), SkillCall("db_migration_plan", {}), SkillCall("build_artifact", {}), ] result = SkillResult(status="success", data={}) for call in pipeline: result = orchestrator.execute(call.name, call.params) if result.status == "failed": result = SkillResult( status="failed", data={**result.data, "blocked_by": call.name}, ) break这里的核心心得是:串行编排必须显式定义失败短路逻辑。没有短路的话,模型可能会在第一步失败后继续往下走,最后提交了一个"带着已知缺陷的发布",这在真实场景里是绝对不可接受的。虽然这增加了编排代码的复杂度,但换来的是整体行为的确定性,值得。
4.2 并行编排与任务分解
有些技能的耗时大头在网络 IO 或外部系统等待,技能之间互不依赖,这时候并行执行能把整体耗时要缩短好几倍。
举一个真实例子。用户问"这几个服务最近一次发布后有没有异常",我拆成三个技能并行跑:log_anomaly_scan扫日志找异常模式、metric_review拉取监控指标找基线偏移、incident_record_query查历史故障记录。三个技能各自独立,没有依赖关系,并行跑,最后模型汇总三份结果形成一个综合判断。
并行编排实现上要注意的是资源限制和结果汇总顺序。资源限制是指系统同时只能跑 N 个技能,不能让用户一个问题触发 20 个技能并发,把机器打爆,我设置的默认上限是 5。结果汇总顺序则是说,并行跑完的结果给到模型之前,要按任务相关性排序,不能无序堆在一起,否则模型总结时的逻辑会很乱。
# 伪代码示例:并行编排示意 skills = [ SkillCall("log_anomaly_scan", {"service": service_name}), SkillCall("metric_review", {"service": service_name}), SkillCall("incident_record_query", {"service": service_name}), ] results = orchestrator.run_parallel(skills, max_concurrency=5) summary = model.analyze_results(results)4.3 状态管理:技能之间的数据怎么传
技能编排最难的不是调度,是状态管理。技能 A 的输出怎么变成技能 B 的输入?如果技能 B 执行失败退出,技能 A 的结果要不要回滚?
agent-skills的答案很朴素,但在我这儿很管用:技能之间不直接传对象,统一通过一个轻量的工作区来交换数据。每个技能执行前会从工作区读取它需要的前置产物,执行后把产物写回工作区。工作区本质是一个带版本管理的目录,每个文件都记录了"是哪个技能的哪个实例在什么时间写入的"。
这个设计的最大好处是可检查、可恢复。技能 B 启动时发现需要的中间产物不存在,或者版本不匹配,直接抛出"前置条件不满足"的错误,而不是用空数据硬跑然后在业务逻辑里崩溃。排查问题的时候,打开工作区目录,每个文件的进出记录一目了然,谁污染了数据一查便知,省去了一大半吵架时间。
5. 真实运行中踩过的坑和排查链路
任何框架看文档都光鲜,跑起来全是坑。这一章把agent-skills上线后遇到的高频问题、排查思路和修复方案完整记录一下,这些问题到现在还会偶尔冒出来,按下面的链路排查基本能解决九成。
5.1 技能超时:模型还等着回复,技能却悄悄挂了
第一个遇到的坑是技能超时。最开始把超时设得比较保守,比如仓库巡检设 120 秒,想着不会超才对。结果遇到一个大型 monorepo,光拉取代码就跑了三分钟,技能在拉代码阶段就被框架掐断了,返回一个"执行超时"的错误。模型拿到错误信息后,又尝试重新跑了一遍,结果还是超时,直接陷入死循环,用户看到的是 Agent 反复报错。
排查链路倒是不复杂:第一看技能日志,发现记录显示超时发生在git_pull阶段;第二看 git 操作日志,发现拉取的是一个巨大的历史仓库,而且工作区没有做浅克隆;第三确认根因是"技能对仓库大小没有预估,直接用默认的全量克隆策略"。
修复方案做了两层。第一层在技能内部,把 git 拉取改成--depth 1浅克隆,只取最新提交,几十倍的体积差异,时间立刻从三分钟降到十秒;第二层在框架层,把超时做成可配置的,技能 Schema 里声明预估超时,同时框架设置一个硬上限防止恶意长任务。通过这个问题的排查,我学到的原则是:框架层的超时是最后一道保险,不能当常规路径用,每个技能都要对自己的耗时负责,主动优化自己的执行策略。
5.2 上下文污染:上一次会话的脏结果混进了本次决策
第二个坑更隐蔽。Agent 在连续会话中会把历史对话内容一起塞给模型做上下文,这本是为了让对话连续,但如果上一次技能执行返回了一个报错信息,这个报错可能会被模型误以为是对当前问题的回答。
我遇到过的最典型的场景长这样:用户先问"这个服务怎么还没恢复",Agent 查了监控技能,返回"服务状态异常,错误率 32%"。然后用户又问"刚才那个问题查到原因了吗",本来应该触发新的日志分析技能,但模型看到上下文里已经有"错误率 32%"这个信息,直接从历史返回提取状态,没有调用任何技能,给了个回復:"错误率 32% 说明服务负载过高,建议扩容。"听起来像那么回事,但实际上根本没有查日志确认根本原因。
排查链路第一步是看 Agent 的"调用记录",看模型有没有触发技能调用。结果发现这次回答的调用记录是空,纯粹靠上下文回答。第二步检查上下文中注入的历史结果,发现上次技能执行的原始结果确实还在上下文窗口内。第三步确认 root cause 是框架层没有对技能的历史输出做时效性标记和清理策略。
修复方案是双管齐下。一是框架层在把历史技能结果注入上下文时,加上时间戳并标记"该结果来自 X 分钟前的技能调用,可能已过期";二是模型指令里明确要求:当问题涉及最新状态时,必须重新触发技能获取新数据,不能直接引用历史结果回答。做了这两处修改后,这类"拿旧数据回答新问题"的错误明显减少。
5.3 模型幻觉参数:给技能传了不存在的参数
技能定义得再清楚,模型有时候依然会"自由发挥"。有一次用户说"帮我把测试跑一下,重点看下 API 测试的覆盖情况",模型调用repo_inspect时自动填了一个api_coverage_only: true的参数,但技能的 Schema 里根本没有这个参数。框架层的处理是直接抛参数校验错误,然后模型收到错误后又换了个参数名重试,反复多次,把用户晾在那里等。
这个问题的根子在于框架层没有为模型提供"参数校验失败后的修正引导"。修复后的流程是:参数校验失败时,返回的错误信息不再只是一句"参数 api_coverage_only 非法",而是加上合规提示:"repo_inspect 支持的参数有 repo_path、branch、strict_mode。你是想限制 API 测试的覆盖范围吗?请使用 strict_mode=false 并额外调用 coverage_report 技能。"
这样一来,模型拿到错误信息后就知道"自己自作聪明了,应该用合规的参数重新表达用户意图",重试的成功率大大提高。这里我总结的心得是:错误信息不只是给人类看的,更是给模型看的。错误信息里包含的修正引导越具体,Agent 的自恢复能力就越强。
5.4 技能执行的权限边界:Agent 能跑的危险命令比想象中多
这是一个安全相关的坑,必须单独说。一开始我的技能模板里允许执行任意 shell 命令,想着反正跑在容器里,问题不大。后来有一次技能在解析外部输入时被塞入了一段恶意命令,因为技能内部直接拼接字符串执行,结果触发了容器里的一个敏感操作。那次之后我彻底重构了权限管理。
现在的权限模型是三层:第一层是框架层,明确规定 Agent 只能执行已注册技能内的操作,未注册的命令直接拒绝;第二层是技能层,每个技能的 Schema 里声明allowed_actions,框架启动时校验技能内部只能调用声明的动作,想执行 Shell 命令必须通过一个受限的run_command帮助函数,这个函数会校验命令白名单和参数合法性;第三层是沙箱层,技能运行在独立的容器里,没有宿主机挂载目录访问权限,也没有生产环境密钥。这套三层权限模型上线后,再也没有出现过 "Agent 跑飞" 的情况。
6. 技能体系上线之后:维护、复盘与持续演进
技能开发出来不是终点,能持续稳定地跑下去才是目标。agent-skills跑了一段时间后,我把精力分成了三块:可观测性建设、技能健康度评估、技能版本演进。
6.1 可观测性与日志体系
之前排查问题的经验反复告诉我一件事:没有日志,排查就是猜。所以后面我给整个技能体系架设了一个统一的日志规范。每个技能执行时都会输出一份结构化日志,包含执行时间、调用参数、每步耗时、结果状态、错误堆栈。日志收集到统一的展示面板上,可以直接看到每一个技能调用的完整链路。
这套日志体系最大的受益场景是"模型表现很蠢但我不知道蠢在哪"的时刻。以前遇到这种情况只能盲猜,现在打开追踪面板,一眼就能看到模型在这次任务里选了哪些技能、顺序对不对、哪个技能耗时长、哪个技能报错了、模型看到报错后又怎么处理。链路清晰了,问题定位就快多了。
6.2 技能健康度:不是所有技能都值得留
上线时间久了,技能会越加越多,有些技能可能一个月都没被调用一次,有些则天天被调用但成功率不到五成。我建立了一套简单的健康度评估机制:每个技能记录调用频率、成功率、平均耗时三个指标,每周汇总一次。调用频率低说明技能没有解决真实痛点,要么描述不吸引模型导致不被选中,要么场景本身就不需要技能化;成功率低说明技能的实现有 bug 或者和现实环境的兼容性差;平均耗时长则说明瓶颈可能在技能内部的串行步骤上。
根据这个评估,我会做三种处理:优化、下线、拆分。调用次数高但成功率低的技能优先优化;连续一个月零调用的技能直接下线,避免干扰模型的选择空间;耗时长的技能检查内部步骤是不是可以并行化。这套评估机制让技能库始终保持着健康状态,避免了"尸体技能"堆积导致的选择混乱。
6.3 技能版本与经验固化
技能也是有版本的。一个技能从开发到稳定,通常会经历三个版本阶段:V0 是先跑通功能,不管代码好看不好看,目标是让任务能完成;V1 是加固错误处理,把所有能想到的异常路径都覆盖到,把失败时的错误信息写得更细;V2 是性能优化和参数收敛,把明显不合理的耗时点修掉,去掉那些从来没被用过的参数,把接口收窄到够用但不过度设计。
我自己个人体会最深的一点是:技能的演进不能靠"想起来再改",要靠线上日志驱动。每次看到某个技能出错日志多,就去修一版,然后看看下一周的出错率降了多少。演进方向永远来自真实运行数据的反馈,而不是自己的凭空想象。这套"数据驱动迭代"的开发方式,让我后面维护技能库的精力投入少了很多,效果却比拍脑袋改要好太多。
agent-skills做到现在,真正让我觉得有成就感的不光是把 Agent 调得更会干活了,而是我批量复制这种"会干活"的能力越来越熟练了。每沉淀一个新技能,Agent 的能力边界就向外扩展一点,而扩展边界这件事本身,已经变成了一套有章法的工程流程,不是靠天吃饭的玄学。这份记录如果能给正在做同类事情的朋友一些参考,或者少踩几个我踩过的坑,那就挺值了。