
1. 项目概述agent-skills 是什么要解决什么问题不知道大家有没有这种感觉想用大模型做一个真正能干活的 Agent最头疼的不是“模型会不会回答”而是“让它稳定地干完一件事不跑偏、不半途而废、不答非所问”。我做过不少类似的需求比如让 Agent 帮我把散落在十几个表里的订单数据汇总、清洗、分类最后生成一份周报。用传统的多轮对话方式去调效果非常不稳定经常出现“问东答西”“中途断了重来”“同样一句话换个说法就不认了”的情况。后来我把整个处理流程抽成了一个又一个可复用的技能也就是项目标题里的 agent-skills整个系统的稳定性才真正立住了。agent-skills 的核心思路是把大模型从“什么都懂一点但什么都不精”的通用对话引擎改造为“手里攥着一套标准化工具包、按规则调用、按流程执行”的执行器。你可以把技能理解成给 Agent 准备好的“标准作业手册”每个技能都有明确的触发条件、输入参数、执行步骤、输出格式、以及校验规则。模型遇到对应场景时不会靠想象力自由发挥而是从技能库里选择最匹配的一项然后严格按照预定义的流程去跑。这个模式解决了几个非常实际的痛点。第一是上下文窗口瓶颈把复杂的任务逻辑从上下文搬到技能文件里不占宝贵的 token 空间。第二是行为稳定性技能封装了详细步骤和边界条件模型犯错的空间被大幅压缩。第三是复用性同样一个“用户意图分类”技能可以同时服务多个 Agent不用每次重新调一堆 prompt。适合谁来参考如果你正在做 Agent 应用开发、想把模型能力真正落地到业务流程里或者已经发现“提示词写得再长也管不住模型”的时候这套思路值得认真看看。这篇文章就是我对 agent-skills 从设计到落地全过程的一次复盘里面会讲清楚我怎么设计技能边界、怎么写描述和参数、怎么注册和测试以及那些踩过之后才明白的坑。2. 整体设计与核心思路拆解2.1 技能边界怎么划——按任务、按流程、还是按工具设计 agent-skills 时第一个要回答的问题不是“怎么写代码”而是“技能的粒度该多大”。这是整个框架的地基边界划得不好后面麻烦不断。我一开始犯过两个方向的错。第一个是技能粒度过细比如把“发送 HTTP 请求”“解析 JSON 字符串”都单独做成技能结果一个简单的查询操作要串四五个技能模型在技能之间的路由判断上频繁出错执行链路还特别长。第二个是粒度过粗把一个完整的“订单周报生成”做成一个大技能参数十几个内部逻辑几十步技能本身就成了一个黑盒模型很难准确理解入口和出口调试的时候也没法定位问题。后来我总结出一套比较适用的划法技能应该对应“一个可独立验证的业务动作”而不是“一个实现细节”或“一个完整业务流程”。比如“查询订单”“清洗地址”“计算订单金额汇总”“生成周报”“发送邮件通知”这些都是可以独立验证结果是否正确的动作每个技能有明确的输入输出结果能被单一标准评判。至于“完成订单周报”这种跨多个动作的流程应该放在上层编排逻辑里让 Agent 按顺序调用多个技能而不是把流程硬塞进一个技能里。还有个判断方法很好用如果一个技能需要两个以上“结果描述”才能说清楚它在干什么说明粒度划大了如果一个技能描述里出现“先”“然后”这种顺序词说明它可能混入了多个步骤该拆。按这个标准过一遍绝大多数粒度问题都能暴露出来。2.2 技能描述是关键怎么写才能让模型准确命中技能定义里自然语言描述的分量往往被低估。很多人写 description 就是一句话带过比如“查询订单”。实际上模型选择技能主要靠的就是这段描述和当前用户请求之间的语义匹配。描述写得太短模型区分不了相似技能写得太泛模型会拿这个技能去硬套不匹配的场景写得太玄模型直接理解不了。我现在的做法是每个技能描述包含以下几块信息做什么核心功能一句话、什么场景下用触发条件判断、什么场景下不要用负例约束非常管用、输入是什么、输出的关键字段有哪些。举个例子“查询订单”的技能描述我会写成当用户需要查看订单信息、物流状态、订单金额等数据时使用。 支持按订单号、用户ID、时间范围筛选。 如果用户询问的是退款、售后或商品库存不要使用本技能 请路由到对应技能。 输入参数order_id可选、user_id可选、start_time、end_time。 输出字段订单号、状态、金额、创建时间、收货人。表面看多写了几句但对模型的行为约束效果立竿见影。尤其是“不要用”的部分能直接压掉一大批误路由。实际测试下来加上负例之后技能路由准确率能从 70% 出头提到 92% 左右这个提升幅度比调什么采样参数都大。另外一个细节是描述里不要写“高级”“智能”“高效”这类形容词模型对这个没感知只会稀释有效信息。把工夫放在具体的场景描述和触发条件上比堆形容词有用得多。2.3 编排层面的控制流线性、条件还是循环技能本身只是一个可执行单元真正让 Agent 看起来“会思考”的是技能之上的编排控制流。这块的设计决定了你整个系统是“看起来聪明”还是“真的稳”。常见做法有三种。第一种是纯模型自由编排模型根据用户请求自己决定调用哪些技能、按什么顺序、是否重试。灵活度高但不确定性也最高适合原型验证不适合生产。第二种是外部流程引擎控制用 LangGraph 这类框架把流程固定成有向图技能按图执行模型只在关键分支上做选择。稳定但开发和维护成本高。第三种是折中方案把“技能链”抽成可配置的 JSON/YAML 模板模型按模板的步骤顺序执行模板保证主干稳定模型在叶节点做自由度较低的微决策。我实际项目里用得最多的是第三种。折中方案的做法是先定义一个技能的元信息文件里面列出该技能内部的可选路径和判断节点Agent 运行时先把用户的请求映射到技能技能内部再根据输入参数走对应的子流程。比如“退货处理”技能内部可以有三个分支仅退款、退货退款、换货模型负责根据用户描述选择分支但具体的执行步骤已经写死在分支里不存在模型自由发挥的空间。这种设计的底层逻辑是把“需要模型判断的地方”控制在最小范围内其他全部交给确定性逻辑。模型越少做选择系统越稳定。如果发现自己某个技能里到处是模型自由决策说明这个技能应该拆成多个更细的技能而不是指望模型每次都能选出最合理的下一步。3. 核心细节解析与实操要点3.1 技能清单Registry的定义与注册机制技能数量超过 10 个之后你就不能再靠“写一堆 if-else 把技能逻辑挂在聊天流程里”了得有一个统一的注册与调度中心。这个中心我习惯叫技能注册表Registry它的作用是把所有技能的名称、描述、入口函数、参数结构、状态信息集中管理Agent 路由时只查这张表不接触具体实现。注册表的数据结构不复杂核心字段大概是这样字段说明示例skill_name技能唯一标识order_querydisplay_name展示名称可中文订单查询description路由用自然语言描述用户查询订单信息时使用handler执行函数/接口引用order_query_handlerinput_schema输入参数的 JSON Schema{order_id: string}output_schema输出结构校验规则{orders: array}version技能版本号1.2.0enabled是否启用true注册机制上我自己比较推荐“声明式注册”也就是每个技能做一个 YAML 或 JSON 元信息文件启动时代码自动扫描目录并载入注册表。好处是新增技能不用动主流程代码团队成员只要按模板写好技能文件和实现函数注册表自动就有了。这样 Agent 系统的扩展性会好很多这也是 agent-skills 这类框架能扛住业务持续增长的关键原因。在实际代码层面注册器的核心逻辑大概这样import os import json from typing import Dict, Any, Callable SKILL_REGISTRY: Dict[str, Dict[str, Any]] {} def load_skills_from_directory(skills_dir: str): 扫描技能目录加载所有技能元信息。 for root, dirs, files in os.walk(skills_dir): for file in files: if not file.endswith(.json): continue path os.path.join(root, file) with open(path, r, encodingutf-8) as f: meta json.load(f) skill_name meta.get(skill_name) if not skill_name: print(f跳过无效技能文件: {path}) continue SKILL_REGISTRY[skill_name] { meta: meta, handler: get_handler(meta[handler_name]), enabled: meta.get(enabled, True), } def get_handler(handler_name: str) - Callable: 按名称从已注册函数表里取实现找不到时抛异常。 if handler_name not in HANDLER_MAP: raise ValueError(f未注册的 handler: {handler_name}) return HANDLER_MAP[handler_name]注册机制的设计要点是“元信息可解析、实现可调用、启动可校验”。哪些技能加载失败、哪些 handler 缺失必须在系统启动阶段就暴露出来而不是等用户请求打过来才报错。3.2 输入输出的 Schema 设计给技能装上“接口约束”如果说描述决定了模型能不能选对技能那 Schema 就决定了技能能不能正确执行。我接触过不少失败的 Agent 项目排查到最后往往不是模型不行而是技能内部对输入输出的假设不一致——上游传了个字符串下游在算整数上游少了必填字段下游直接抛异常。设计技能 Schema 时有几个原则是我坚持的。第一是“宁可多写约束不指望模型自觉”。所有参数的 type、required、enum、描述都写在 Schema 里模型生成完参数后还要做一次校验不合法就重新生成或报错。第二是“输入输出都定义”。很多项目只约束输入输出随便定义结果下游没法消费。第三是“枚举能写就写”如果某个参数只接受几个固定值直接把 enum 列出来能大幅减少模型瞎编的概率。下面是一个技能的输入 Schema 模板参考这个写基本不会出问题{ skill_name: order_query, input_schema: { type: object, properties: { order_id: { type: [string, null], description: 订单号完全匹配查询, minLength: 6, maxLength: 32 }, user_id: { type: [string, null], description: 用户ID精确匹配 }, start_time: { type: [string, null], format: date-time, description: 下单起始时间ISO8601 }, end_time: { type: [string, null], format: date-time, description: 下单结束时间ISO8601 } }, required: [] }, output_schema: { type: object, properties: { orders: { type: array, items: { type: object, properties: { order_id: {type: string}, status: {type: string, enum: [pending, paid, shipped, completed, cancelled]}, amount: {type: number, minimum: 0}, created_at: {type: string} }, required: [order_id, status, amount] } } }, required: [orders] } }在 Python 侧做校验可以用 Pydantic它定义模型类的方式和 JSON Schema 几乎一一对应还能直接返回可读的错误信息from pydantic import BaseModel, Field, ValidationError class OrderQueryInput(BaseModel): order_id: str | None Field(defaultNone, min_length6, max_length32) user_id: str | None None start_time: str | None None end_time: str | None None def validate_input(payload: dict) - OrderQueryInput: try: return OrderQueryInput(**payload) except ValidationError as e: raise ValueError(f输入参数校验失败: {e})参数校验通过之后还要处理“模型生成参数但没生成全”的兜底逻辑。比如查询订单时用户只提供了“查一下最近一个月的订单”start_time 有了但没有 end_time技能内部可以默认 end_time 为当前时间并在返回结果里给个提示。这种“默认值 提示”的策略比直接报错让用户重说体验好得多。3.3 技能通用执行模式调用、解析、校验、重试、返回定义好注册表和技术 Schema 之后就可以考虑技能执行的通用模式了。我采用的是“五步执行模式”每个技能走同一个框架调用模型生成参数 - 解析参数 - 校验参数 - 执行技能逻辑 - 后处理与格式化返回。第一步是模型生成参数。技术上通常做法是在提示词里告诉模型当前技能注册表里有哪些技能、各自参数是什么然后让模型输出一个 JSON 对象内容包括选择的技能名称和对应参数。有些场景还会让模型生成“为什么选择这个技能”的推理摘要虽然多一点 token 开销但调试时非常方便。第二步是解析参数。模型输出往往不是干净的 JSON可能包在 markdown 代码块里也可能夹杂解释性文字。解析器要去掉这些干扰提取真正的 JSON 对象。我见过不少人在这里直接抛异常然后整个链路崩溃。正确做法是写一个容错解析函数尝试多种解析方式实在不行再报错。第三步是校验参数也就是刚才说的 Pydantic 那段。第四步是执行技能逻辑调业务模块、外部 API 或数据库这一层是正常的工程代码不需要模型介入。第五步是后处理与格式化返回。技能执行完原始数据往往不能直接丢给用户要按输出 Schema 做映射缺字段补默认值多字段做裁剪。这一层还要统一错误码比如“无数据”“超时”“权限不足”这些状态要以结构化形式传给模型模型才能根据状态给用户更准确的反馈。整个模式的核心是“网关化”。技能本身只关心业务实现所有模型交互、参数处理、错误封装都交给通用执行器。把这层做扎实后面每新增一个技能工作量就只是写一个业务函数和一份元信息而不是再复制一段链路代码。3.4 自动化测试与回归技能加多了怎么保证不退化技能库扩充到几十个之后最大的风险不是单个技能坏掉而是新旧技能之间互相影响。改了一个共用函数的返回值格式所有依赖它的技能都跟着遭殃调整了一个技能的描述模型路由的准确率可能全局下降。应对这个问题只有一个办法——把测试当成技能开发的一部分而不是事后补救。我现在的做法是把测试分成三层。第一层是单元测试验证每个技能在给定合法输入时能返回符合输出 Schema 的结果给定非法输入时能返回正确的错误码。这一层不依赖模型跑得飞快每次提交代码都自动执行。第二层是集成测试模拟一条完整的“用户请求 - 模型路由 - 技能调用 - 格式化返回”链路重点验证技能描述和参数 Schema 是否让模型选得准、调得对。这一层需要真实调用模型成本和速度都有点吃紧所以放在每日任务而非每次提交。第三层是回归测试维护一批典型的用户请求样例和期望结果每次技能库更新之后全量跑一遍对比实际输出和期望结果的差异尤其关注“之前能选对的技能现在选错了没”。自动化测试的示例结构大概这样import pytest from skill_engine import execute_user_request pytest.mark.parametrize(user_input,expected_skill, [ (帮我查一下订单 20240815001 的状态, order_query), (这个订单我要退货, return_process), (最近一周所有订单的金额合计多少, order_summary), ]) def test_skill_routing(user_input, expected_skill): result execute_user_request(user_input) assert result.skill_name expected_skill每次新增技能或调整描述后先把新的用户意图样例加进回归集观察模型路由是否稳定再做批量回归。这个过程虽然繁琐但它能把“改坏了但没人发现”的风险降到最低。而且它带来的另一个好处是测试集本身会成为团队对“技能应该怎么被触发”的共同理解新成员上手也更快。4. 实操过程与核心环节实现4.1 从零搭一套最小技能框架核心文件与运行流程很多朋友问 agent-skills 落地要多少代码。我的经验是一个能跑通的最小框架大约只需要三类文件技能元信息文件JSON 或 YAML、技能实现模块Python 函数、以及一个执行引擎负责路由和调度。依赖方面只需要一个能调用大模型 API 的库和 Pydantic不会牵扯太多框架依赖。技能元信息刚才已经给过示例了现在看技能实现的示例# skills/order_query.py def order_query_handler(params: dict) - dict: order_id params.get(order_id) user_id params.get(user_id) start_time params.get(start_time) end_time params.get(end_time) # 这里是你的业务逻辑查数据库或调订单服务 orders query_orders( order_idorder_id, user_iduser_id, start_timestart_time, end_timeend_time, ) return { orders: orders, total_count: len(orders), }执行引擎里的核心路由逻辑是这样的接收用户请求构造系统提示词里面包含所有可用技能的描述和参数说明模型输出一个结构化 JSON指明技能名称和参数引擎解析并校验然后调用对应 handler最后返回格式化结果。一个非常精简的路由函数def route_to_skill(user_request: str) - dict: prompt build_router_prompt(SKILL_REGISTRY, user_request) response call_llm(prompt) # 调用大模型 parsed parse_llm_json(response) # 容错解析 skill_name parsed.get(skill_name) params parsed.get(params, {}) if skill_name not in SKILL_REGISTRY: raise ValueError(f模型选择了未注册技能: {skill_name}) handler SKILL_REGISTRY[skill_name][handler] result handler(params) return {skill: skill_name, result: result}整体跑通之后这个骨架就可以往里面加上下文管理、技能链编排、重试机制、日志追踪、权限控制。但核心的三类文件加上一个路由引擎就是 agent-skills 的全部底座了。先跑起来再逐步完善比一开始就铺一个庞大的框架要务实得多。4.2 完整流程把一个真实场景拆成技能、流程与校验为了让前面这些设计不悬空我用一个具体的场景把整个过程串一遍。假设业务方提了一个需求用户可以在对话里查询订单、申请退款、查看退款进度。不拆解的话这听起来就是一个大 Agent 功能。拆完技能之后会发现查询订单是一个技能申请退款是一个技能查看退款进度是一个技能它们共享一部分用户校验逻辑。第一步是写清需求边界。我需要明确“查询订单”的返回字段、筛选条件“申请退款”要满足什么前置条件比如订单已支付、未完成超过 7 天“查看退款进度”要拿到退款单号和状态。这步做完技能清单基本就定了。第二步是设计每个技能的元信息。这里最耗功夫的是描述部分。我给“申请退款”技能写的描述包含完整的触发判定和负例“当用户表示要退掉已购商品、发起了退款诉求、或询问如何申请退款时使用。用户仅咨询退款政策、询问能否退款的请先路由到售后咨询技能不要直接创建退款申请。”写完自己读一遍站在模型的角度模拟一下看看如果用户说“这个手机我不想要了”模型会不会选错。第三步是写实现函数。查询订单对接订单系统退款申请对接退款服务。每个函数都按输入 Schema 严格处理参数。实现细节不展开但有一个建议每个技能函数必须是“幂等或可重试”的尤其是退款申请这类会改变状态的技能。第一次调用因网络超时失败重试不能重复提交不然用户退两次款就麻烦了。第四步是接执行引擎然后跑回归测试。准备好的样例包括“查看订单 123456 的物流进度”应路由到物流查询或订单查询的物流字段、“我要退掉刚才那件衣服”应路由到退款申请、“退款到账要多久”应路由到售后咨询。把样例跑一遍观察路由结果和返回格式调整描述直到所有样例通过。4.3 技能库的版本管理与部署策略技能不是一个一次性开发完就再也不动的“静态库”它和普通代码一样需要版本管理。而且技能的改动往往不是“代码变了”这么简单——同一份代码换个描述路由准确率就不同了。所以我给技能库做了比普通代码库更细的版本管理。目前实践下来比较顺手的做法是技能库单独开一个 Git 仓库每个技能一个目录元信息文件里带 version 字段。任何对技能描述、Schema、实现的改动都必须递增版本号并在 commit message 里写明改动原因。发布时用一个 manifest 文件锁定当前生效的所有技能版本部署时严格按 manifest 加载。这样做的好处是“谁改了哪个技能、为什么改、版本是否一致”都一清二楚回滚也只需要切换 manifest。部署方面技能库建议打包成独立的服务或模块通过接口被 Agent 主服务调用。很多团队把技能库直接写死在主 Agent 进程里导致技能更新一次必须重新发布整个服务。拆成独立服务之后技能更新可以独立发布、灰度、回滚Agent 主进程完全不受影响。当然拆服务会增加一次网络调用的延迟需要在实际延迟要求和部署灵活性之间做个权衡。4.4 数据处理与异常封装的陷阱这条单独拎出来说是因为太多 Agent 项目死在“异常没处理”上。技能执行过程中会碰到各种非预期情况——外部 API 返回 500、数据库连接超时、上游返回的数据格式和文档里说的不一样。这些异常如果直接透传到 Agent 层模型收到一堆看不懂的报错就开始“胡言乱语”编补偿话术这是最糟糕的体验。我的做法是给每个技能定义一个标准异常处理层把所有异常都收敛为有限的错误类别错误类别含义给模型的反馈模板NO_AUTH用户无权操作您没有权限执行此操作请联系管理员NOT_FOUND目标数据不存在没有找到符合条件的记录TIMEOUT服务超时服务响应超时请稍后重试VALIDATION_ERROR参数不合法您提供的参数有误具体说明INTERNAL_ERROR内部异常或上游故障系统开小差了稍等再试这样模型拿到的永远是结构化、语义明确的错误状态它能据此给出正确的用户反馈而不是被原始异常信息带偏。我还要求每个技能在返回异常时附带一个可追踪的错误码用户反馈问题时可以直接靠这个码定位。这条经验帮我省掉了大量线上排查时间。5. 常见问题与排查技巧实录5.1 模型老是不调用技能直接自己“编答案”怎么办这是 Agent 开发里最常见的问题你已经定义了技能模型却自顾自地回答不触发任何技能调用。我分析下来原因基本集中在三点。第一是技能描述和用户请求之间的语义距离太远模型不觉得这个技能能帮上忙。解决方案在描述里加入更多用户表达的变体词比如“查物流”“包裹到哪了”“东西发出了吗”都加进去命中率立刻提升。第二是注册表里技能太多模型在这么多候选里有时候就“忘了”用。解决方案是给路由阶段做一次粗筛明显不相关的技能不放进 prompt。比如用户问的是天气退款技能就没必要出现在候选列表里。第三是模型不具备很强的工具调用能力这个跟模型本身有关需要选对模型或在提示词里给一个明确的“必须使用技能”的指令强约束。实操上我推荐一个调试技巧把路由 prompt 打印出来看看技能描述到底以什么形式出现在模型面前有没有被截断、有没有被其他内容挤掉。很多路由问题看一眼 prompt 就知道原因了。5.2 模型输出的 JSON 不标准、字段缺漏怎么办模型输出的内容经常不是合法的 JSON常见情况包括输出被 markdown 代码块包裹、JSON 里夹杂了注释、字段名大小写不一致、多了一个逗号。指望模型给出完美 JSON 不现实必须在解析层做容错。我自己维护了一个多层解析函数先按json代码块提取再尝试直接 json.loads失败的话用正则把注释和多余的尾逗号去掉再解析还不行就找离“{”最近和离“}”最近的区间做截取最后还失败就进入重试流程让模型带着错误信息重新生成参数。这一套下来绝大多数情况都能兜住。字段缺漏的问题则主要通过 Schema 校验Pydantic 的 default 和 required 机制处理。能设默认值的设默认值必须由用户确认的才设为必填。这样既保证了技能可以安静运行又不会因为少一个可选参数就直接罢工。5.3 技能之间互相干扰路由串了怎么办当技能数量多了以后你会发现有两个技能的描述在语义上有重叠。比如“订单查询”和“物流查询”都可能处理“我的订单到哪了”。这时候模型就会随机选一个带来不一致的体验。解决办法分两步。第一步是在描述中加入更精确的边界条件比如“订单查询只返回订单状态和金额物流轨迹请走物流查询技能”第二步是主动制造差异调整描述用词让模型更容易区分。我甚至试过给技能加“关键词标签”作为路由辅助信号但效果只能说一般因为模型不一定看关键词标签。更推荐的做法是发现两个技能经常互相串就想清楚它们是不是真的该合并成一个技能用内部参数做分支处理。技能合并有时候才是减少路由混淆的最优解。5.4 排查技能生产故障的标准动作线上环境技能调用失败时我的排查路径基本是固定的。首先查路由日志看模型到底选了哪个技能选错了还是选对了。然后看技能执行的入参日志确认参数是否合法。再查技能内部的业务调用日志定位是上游问题还是参数问题。为了这套排查能跑通我在执行引擎里加了几个关键的日志点每次请求都要记录用户原始输入、模型选择结果、解析后的参数、技能执行时长、返回结果摘要、错误信息。这套日志看起来“多余”但线上救命的往往就是这些细节。另一个调试神器是给每个技能增加一个“dry run”模式只输出技能计划的执行步骤和预计影响不真正执行业务逻辑。上线前跑一遍 dry run可以提前发现大量问题。6. 写在最后一些实操体会6.1 我的核心收获把模型当执行者而不是决策者回看我对 agent-skills 的整套实践最核心的认知变化是别再指望模型每一次都做出“正确决策”而是通过设计技能库把决策空间缩到模型不容易犯错的范围。这个理念贯穿了所有环节——技能粒度的划分、描述里的负例约束、Schema 的枚举限定、执行引擎的容错解析、异常的分类收敛。每一步本质上都是在“给模型的自由发挥上边界”。我见过太多失败的 Agent 项目问题都不是模型“不够聪明”而是开发者给了模型太多自由。模型能做十件事它就可能用第十一种方式来做而你没有定义那第十一种方式。把模型当作一个“严格按照操作手册执行、只有遇到边界情况才来找你确认”的执行者系统的稳定性和可维护性会好非常多。6.2 最后给大家的一个扩展方向如果读完这篇文章你打算试一把我建议不要直接照搬全套框架而是从一个最小的场景开始挑一个你日常需要反复做的业务动作把它定义成第一个技能写清楚描述和输入输出跑通路由和调用再加第二个技能。等你积累了几十个技能之后再回头审视整个运行机制的效果。这个循序渐进的过程会帮你建立对 agent-skills 很扎实的感知比一开始就搭一个庞大框架要好得多。我在实际使用中还有一个心得把技能库当成团队的“共同资产”来维护新增一个技能就像提交一篇文章一样必须有明确的目的、清晰的描述、完整的测试。这样的技能库会越来越像一个高质量的工具箱真正的价值会随着时间一点一点积累出来。