
先把话说在前面这篇聊的东西我并不是想说“提示工程已死”之类的空话而是想认真聊聊我在实际搭建智能体时踩过最深的一个坑——模型再聪明如果没有一套能真正跑起来的“技能库”兜底它在真实场景里就是个只会说不会做的摆设。agent-skills 这个项目名字字面意思是“智能体技能库”。但在我眼里它更像是给 LLM 这个超级大脑装上了一副合手的“手脚”——一套结构化、可复用、能真正落地的技能模块集合。这篇文章不打算做项目源码的逐行赏析而是换一个更实操的角度如果我想在你的业务里也搭一套类似的技能体系应该怎么设计、怎么实现、怎么避坑。开门见山先交代清楚这篇东西适合谁看适合那些已经看够了“智能体 Demo 演示”的人——产品经理、后端开发、以及正在做 RPA 或工作流自动化的技术人员。你对大模型 API 可能已经熟门熟路但你想知道如何把模型能力真正固化成一个又一个稳定可调用的“技能”而不是每次都在 prompt 里碰运气。如果你还没接触过 agent-skills 这类项目也没关系。我会从需求拆解开始聊到技能注册、参数协议、执行器设计最后给出一套你觉得靠谱的、可以直接抄作业的实现思路。1. 为什么智能体需要一套“技能体系”而不是一个“万能提示词”先别急着看代码我们先搞清楚一个本质问题一个智能体程序之所以能干活它到底在干什么从系统架构的角度看智能体的核心循环无非是“感知 → 决策 → 行动 → 再感知”。LLM 负责的是“决策”这一环它对上下文的理解和推理能力决定了一个智能体的“聪明程度”。但真正要完成一项任务必须要有“行动”这一环——比如调用数据库查询、执行代码、操作文件系统、发送网络请求。这些行动能力如果不显式地建模出来模型就算逻辑再强也只能在文本里打转。我见过不少团队的做法是把工具调用说明一股脑塞进 system prompt 里比如“当用户需要查天气时调用 get_weather(city) 接口当用户需要写文件时调用 write_file(path, content)”。这种做法在小规模验证时没问题但一旦技能数量超过 5 个或涉及参数嵌套结构的时候prompt 就会变得臃肿、模型开始产生工具幻觉、参数格式频繁传错整个链路就变得不可控了。而 agent-skills 的思路恰好是在做“结构化约束”这件事——它把每一个技能当成一个独立的对象拥有自己的名称、描述、参数协议、执行函数、返回规范。模型不是直接暴露在无数函数调用里而是通过一套注册机制来感知“我有哪些技能可用”再通过统一协议调用。这种设计带来的一个直接好处就是解耦。技能可以被单独测试、单独迭代甚至单独替换实现而不需要去动整个 prompt 结构。从更大的视角看这也是从“prompt engineering”走向“programmable agent”的过程。与其说是“让模型更听话”不如说是“把模型的意图理解能力装进一个受控的执行框架里”。思考的过程由模型负责动作的落地由框架负责。把每一步都放在可控的管线上才不会在复杂的真实任务里翻车。1.1 从“单体提示词”到“技能插槽”我给一个特别直观的类比单体提示词方案像是一个把所有工具都塞满的瑞士军刀但刀柄太大、很多场景下你根本握不稳而技能体系则更像一台带标准插槽的机器——你要用螺丝刀就把对应的模块插上你用完了可以换下一个。核心逻辑是模型不需要知道每个技能的实现细节它只需要知道“在当前场景我应该选用哪个插槽”。在一次真实的项目里我构造过一个“数据分析智能体”。它需要处理文件读取、表格清洗、统计计算、结果绘图、报告生成这几类操作。最初我也是把所有工具函数写进了 prompt 里结果模型经常混淆load_csv和read_excel的参数甚至有时候自己编造函数名。后来我把这些操作重新组织为独立的 skill 模块每个模块只暴露给模型一个精准的接口描述。效果立竿见影调用成功率和参数正确率都大幅上升而且调试时只需要定位某一个 skill 的内部逻辑不再需要从头到尾读那 3000 字的 prompt。1.2 这套体系解决了哪三类真实痛点顺着刚才的案例我归纳了三类最让我头疼的问题也是 agent-skills 这类技能库真正在解决的技能管理与扩展的问题当需要新增一个能力比如“查询用户余额”总不能每次都在那几条大 prompt 上做手术——新增一行、删掉一行牵一发动全身。技能独立注册之后新增能力就是新加一个模块的事。参数协议的一致性问题LLM 输出的是自然语言指令但程序需要的是结构化的参数。技能体系通过强制约束参数 JSON Schema能极大压缩模型传参时的“自由发挥空间”从而减少解析异常。执行结果落地的标准化问题技能执行完之后返回什么、状态怎么表示、错误怎么反馈这如果没定好标准下一步的推理就会收到一堆乱七八糟的结果智能体行为会变得极不稳定。当然我并不是说所有场景都一定要上技能库。如果你只是做一个简单问答机器人或者一次性的脚本任务直接上 prompt 反而更高效。但如果目标是一个真正要连续处理多步骤任务的生产级智能体那么一套结构化的技能体系绝对是必需品而且时间越往后你越会发现这套体系省下来的不是一点点烦恼而是大量的返工成本。2. agent-skills 核心设计一个技能模块到底长什么样现在我们来解剖一个具体的技能模块。有人说“写技能不就是写个函数吗”——这是对 agent-skills 的最大误解。一个能稳定运行的技能模块至少包含四层缺一层都会在关键时刻出问题。先看一段我在实际项目里抽象出来的技能模块骨架。我不是说 agent-skills 项目里就是这么写的但核心设计思路是类似的# skill.py from pydantic import BaseModel, Field class SkillInput(BaseModel): 技能参数协议模型将依据该协议生成调用参数 query: str Field(..., description检索关键词) top_k: int Field(5, description返回结果数量默认5) class SkillOutput(BaseModel): 技能返回协议模型将依据该结构理解执行结果 status: str Field(..., description执行状态: success / error) data: list[dict] Field(default_factorylist, description查询结果列表) class SearchSkill: name web_search description 检索公开互联网信息返回网页标题、链接和摘要 input_schema SkillInput output_schema SkillOutput async def execute(self, params: SkillInput) - SkillOutput: try: results await do_search(params.query, params.top_k) return SkillOutput(statussuccess, dataresults) except Exception as exc: return SkillOutput(statuserror, data[{error: str(exc)}])这段代码乍一看平平无奇但每个字段都是我反复打磨过的下面逐层拆开看。2.1 第一层技能描述层模型“看到”的说明书name和description这两个字段直接决定了模型会不会在合适的时机调用这个技能。这也是一开始我完全忽略的地方。我最初把 description 写得很随意比如“检索信息”结果模型经常在不需要搜索时乱调用或者在需要时想不起来用它。后来我才意识到description 本身就是在给模型写“使用说明”它必须包含三部分信息技能场景这个技能在什么场景下触发比如“当用户提到需要查找最新动态、实时数据或未公开的统计信息时”。输入描述它需要哪些关键参数参数分别是什么含义边界说明它不能做什么比如“不适用于访问内部数据库”。简单说description 写得好不好直接决定了意图识别的准确率。同样的一个搜索Skill如果 description 写得好在 100 次意图识别里都能稳定触发写得差可能一半的时候模型都无动于衷。这也是技能体系里唯一一个需要“用写文案的心态去写”的部分。2.2 第二层参数协议层用结构取代自由发挥SkillInput和SkillOutput是核心中的核心它本质上是模型与程序之间的“合同”。大模型对自然语言的自由度是必须的但工具调用这件事最希望的就是让模型在结构约束下做选择题而不是开放式写作。我强烈建议用pydantic这类库来声明参数结构而不是简单的 dict。原因有几点明确的类型提示模型能理解top_k是整数而不是字符串“5”。默认值兜底如果模型不传可选参数程序不会崩溃。描述即文档每个字段的 description 会直接参与 prompt 生成模型能据此学会填参。运行时校验传参错误能被第一时间拦截而不是到函数内部才发现。说白了参数协议设计得越严格模型的自由度就越低出错的概率也就越小。这不代表要限制能力而是要把自由留给“意图选择”而不是留给“参数格式”。2.3 第三层执行函数层程序真正干的活execute内部是真正的业务逻辑这里要注意一个原则技能执行过程中不允许出现“可能无限等待”的同步调用。这也解释了我为什么在骨架里用async def而不是普通函数。智能体的执行循环往往是串行的——模型推理一次、调用工具一次、拿到结果再推理一次。如果某个技能执行耗时长比如一次文件解析 30 秒整个链路就会被卡住。用异步化可以把这个技能的实现替换为“提交任务后立刻返回 task_id”再通过另一个技能轮询任务状态。这种方式在我处理长耗时任务比如需要调用外部大模型的技能时体验特别好。另外还有一点值得注意execute内部的异常处理。我设计的模式是“永不抛异常”——永远把异常包装成SkillOutput(statuserror)返回给模型。为什么因为模型需要知道“技能执行失败了”这件事本身而不是程序崩溃后让整个链路中断。模型看到 error 状态后可以自行决定是换一种方式执行、还是请用户修正输入、还是换个技能试试。这是智能体具备“纠错能力”的重要基础。2.4 第四层注册发现层让模型知道有哪些技能有了单个技能模块还差一个关键环节注册与发现。这就像是工具箱里的标签模型需要扫一眼就知道有哪些工具能用、每个工具是干什么的。我实现过一个最简版的注册中心# registry.py from typing import Dict, Type from skill import SearchSkill, FileReadSkill, CodeExecSkill class SkillRegistry: def __init__(self): self._skills: Dict[str, dict] {} def register(self, skill_cls): skill skill_cls() self._skills[skill.name] { name: skill.name, description: skill.description, input_schema: skill.input_schema, output_schema: skill.output_schema, handler: skill.execute } return skill_cls def list_skills_prompt(self) - str: 生成给模型看的技能清单 prompt lines [] for skill in self._skills.values(): schema_json skill[input_schema].model_json_schema() lines.append( f## {skill[name]}\n fDescription: {skill[description]}\n fInput Schema: {schema_json} ) return \n\n.join(lines) registry SkillRegistry() registry.register(SearchSkill) registry.register(FileReadSkill) registry.register(CodeExecSkill)模型每次进行意图决策时不需要知道全部技能实现逻辑只需要读取list_skills_prompt()生成的清单然后结合真实的用户请求输出结构化的调用意图——比如{skill: web_search, args: {query: xxx, top_k: 3}}。这就是技能体系相比“单块大 prompt”最本质的优越性把模型需要关注的信息压缩到最小、最有效。3. 实操从零到一搭建一个可运行的技能调度框架前面讲完了模块设计接下来我分享一次完整的实操过程如何从零到一搭建一个支持多技能调度、带记忆能力的智能体框架。这个框架足够简单、没有依赖重量级框架适合快速改造接入你自己的业务。3.1 环境准备与依赖选择先说明环境Python 3.11及以上需要openai或anthropic的 SDK根据你实际用的模型选择pydantic做参数校验asyncio做异步控制。安装很简单pip install openai pydantic anthropic我在实际项目中混用过多家大模型 API坦白说强推 OpenAI 的 function calling 格式因为在工具调用的结构化输出上它的稳定性确实优于其他家。但它有一点问题当你技能数量很多、参数很复杂时function calling 偶尔也会抽风尤其是嵌套参数。为了弱化这个问题我不建议直接依赖 SDK 内置的 tool calling而是在 prompt 层自己定义一个“小协议”——直接要求模型输出一个固定格式的 JSON 意图块。# prompt_builder.py def build_agent_prompt(user_request: str, skills_prompt: str, history: list[dict] | None None): system_prompt f 你是一个智能体调度器。你只能使用下面提供的技能完成任务。 可用技能如下 {skills_prompt} 当你认为需要调用某个技能时请严格输出以下 JSON 格式不要输出多余文字 {{skill: 技能名称, args: {{...}}}} 如果用户的需求不需要任何技能直接输出你的自然语言回复。 如果技能执行返回 error请尝试换一个技能或请用户补充信息。 messages [{role: system, content: system_prompt}] if history: messages.extend(history) messages.append({role: user, content: user_request}) return messages这算是一个“半显式”的意图协议——它不是 function calling但它比纯自然语言让模型“想想再用工具”要稳定得多。因为格式约束是显式的模型知道必须输出 JSON 块而不是在回复里夹带“好的我来为您搜索……”这种废话。3.2 核心循环决策、执行、再决策核心的调度循环我实现为run_agent_loop它会循环执行“模型决策→技能执行→结果反馈”这三个步骤直到模型认为任务完成。为了控制成本我设置了最大循环次数比如 5 次避免模型在某个死循环里转个不停。# agent_loop.py import asyncio, json from openai import AsyncOpenAI from registry import registry from prompt_builder import build_agent_prompt client AsyncOpenAI() async def call_llm(messages): resp await client.chat.completions.create( modelgpt-4o-mini, messagesmessages, temperature0, max_tokens2048, ) return resp.choices[0].message.content async def run_agent_loop(user_request: str, history: list[dict] | None None, max_steps: int 5): messages build_agent_prompt(user_request, registry.list_skills_prompt(), history) for step in range(max_steps): raw await call_llm(messages) # 尝试解析JSON意图块 try: start raw.find({); end raw.rfind(}) 1 intent json.loads(raw[start:end]) except (json.JSONDecodeError, ValueError): # 没有JSON块说明模型给出了最终答复 return raw skill_name intent.get(skill) args intent.get(args, {}) if skill_name not in registry._skills: messages.append({role: assistant, content: raw}) messages.append({role: user, content: 技能不存在请从可用技能中选择不要编造技能名。}) continue # 校验参数 skill_def registry._skills[skill_name] try: validated_args skill_def[input_schema](**args) except Exception as exc: messages.append({role: assistant, content: raw}) messages.append({role: user, content: f参数校验失败: {exc}请根据 input schema 重新输出。}) continue # 执行技能 result await skill_def[handler](validated_args) messages.append({role: assistant, content: raw}) messages.append({role: tool, content: result.model_dump_json()}) return 达到最大执行步数未能完成请求。这段代码很短但里面藏着几个关键的实践决策temperature0工具调用的场景完全不需要模型发挥创造力要保证输出稳定、可预测。JSON 解析时的rfind(})模型经常会在 JSON 前后夹带解释性文字所以不能直接用json.loads(raw)而是要切出 JSON 子串。这个细节实测能避免大量解析失败。错误反馈循环参数校验失败后不终止流程而是把报错信息回传给模型让它重新组织意图。这是智能体“自我纠错”能力的重要体现。3.3 用一个完整案例串起来带搜索与代码执行的技能空谈框架不够直白我来构造一个实际能跑通的多步任务案例。假设用户提出“请查询一下 Python 最近的稳定版本号并把版本号加上发布时间输出成一个 JSON 文件。”这个任务涉及两个技能web_search检索版本信息和write_file写入结果到文件。如果只有搜索技能模型可以回答版本号但无法落盘如果只有写文件技能模型没有内容可写。多技能协同正是智能体发挥价值的地方。对于一个真正的任务理想中的执行轨迹是这样的模型收到用户请求判断应先调用web_search输出意图 JSON。框架执行web_search返回包含版本号与发布日期的结构化结果。模型看到搜索结果提取关键信息再判断下一步要调用write_file输出第二个意图 JSON。框架执行write_file返回写盘成功状态。模型判断任务完成输出最终总结回复。整个流程看似简单但我实际写的时候发现陷阱不在“调用技能”而在“模型如何消化技能返回的结构化数据”。默认情况下模型返回的是一个SkillOutput里的 data 列表字段名是英文比如release_date、version_tag模型在总结时需要准确把这些字段映射到用户原始需求的中文表达中。为了不做错这一步我通常在SkillOutput的字段描述里给足上下文比如version_tag: str Field(..., description版本号字符串如 3.12.4)这样模型拿到的数据本身就自带“语义标签”总结时就不容易张冠李戴。3.4 给技能加“前置校验”与“二次确认”再分享一个我后来才加上的实践在某些高风险场景下执行技能前增加一个“二次确认”机制。比如delete_file这种不可逆操作或者execute_code这种可能产生持久化副作用的操作模型不应该直接执行而是要先向用户展示将要执行的参数得到用户确认后再继续。实现上我做了两种模式自动模式适用于风险低的技能搜索、读文件直接执行。确认模式适用于风险高的技能删文件、发邮件、写数据库先返回给用户一个确认提示同时把待调用的意图块存到会话上下文里。用户确认后再继续执行同一个意图块用户拒绝则反馈给模型。加了这一层智能体才不是那种“会乱跑的车”而是带刹车的车。这在生产环境里非常重要因为这个框架最终接的不只是玩具 Demo而是用户的真实数据。4. 常见坑与问题排查我在真实运行中踩过的雷这部分是我最想聊的。因为很多框架的“示例代码”都能跑通但一旦换成真实场景各种边界问题就会疯狂冒出来。我整理了在 agent-skills 类体系里最常遇到的五类问题每一个都是带着现场教训的。4.1 模型“编造技能名”就是不按注册表来症状模型在意图 JSON 里输出了一个并不存在技能名比如web_search_google_v2框架报“技能不存在”而中断。原因原因大多出在 prompt 里的“可用技能清单”描述不清晰或者模型受训练语料影响比如它见过其他类似的函数名产生了幻觉。还有一个可能是描述片段过长模型为了避免截断而自行压缩。解决我在registry.list_skills_prompt()里把技能名放到了最醒目的位置并且在描述开头统一加一句“注意你只能且必须使用上述技能名之一不要创造新名字。”另外每次解析失败时我把错误信息显式返回给模型相当于一次“就地纠正”。实测三轮以内模型就会回到正轨。4.2 参数类型正确但语义不对模型把字段填反了症状比如write_file有两个参数path和content模型输出的 path 是一个文件名content 却是文件路径结果写出了一个奇怪的空文件。原因这是我对字段 description 写得含糊造成的如果 description 里没有明确指出“path 是目标文件的完整路径content 是要写入的文本内容”模型很容易依赖词面猜测。解决痛定思痛后我把所有字段的 description 都写成“带例子和反例”的句子。比如path: str Field(..., description目标文件路径如 /tmp/demo.txt注意不要包含文件内容)。模型对“带例子”的描述理解准确度明显高了一截。4.3 技能执行成功但模型仍返回 error傻傻重试症状SearchSkill明明返回了 statussuccess 和 5 条数据但模型在下一轮仍然认为搜索失败甚至尝试调用同一个搜索技能三次。原因问题出现在返回结果的结构上。模型看到的data是一个纯列表里面元素是{title: ..., link: ...}它们没有被自然语义包裹起来。模型可能“读不懂”这是成功结果以为只是普通文本。解决我在SkillOutput里加了一个summary字段专门用来给模型生成一段人类可读的摘要。比如“已检索到 5 条结果前三条标题为xxx”。模型看到 summary 后可以迅速理解执行结果是成功还是失败。这个改动让多步任务的成功率提升非常显著。4.4 上下文越来越长模型开始“忘记”最初的用户目标症状在处理一个需要 4-5 步的复杂任务时系统 prompt 加上工具返回结果对话 token 数很快膨胀。跑到后面模型开始偏题不再围绕用户最初的请求做收尾动作甚至开始执行无关技能。原因这是典型的“注意力漂移”。LLM 对长上下文的注意力会衰减尤其是中间夹杂了大量工具返回数据时早期用户目标的重要性会被稀释。解决我采用的方案是在每轮工具调用后主动把“用户原始请求”压缩成一条精简的 reminder 再灌回去。比如每次执行完技能后我追加一条{role: system, content: 记住用户最初的需求是查询Python最新稳定版本号并写入JSON文件。请围绕此目标继续。}。这个“目标锚定”机制能有效防止模型跑偏。你也可以用摘要技术把历史记录先压缩但对于我目前的场景直接追加锚定文本就够了。4.5 技能执行过程中出现外部依赖异常框架无响应症状某个技能需要调用外部 HTTP 接口但外部接口超时或返回 500。框架卡在那里用户等了几分钟都没反应。原因我在最初实现execute时没有做超时控制。解决我封装了一个带超时控制的执行器。用asyncio.wait_for(skill_def[handler](validated_args), timeout10)10 秒超时后直接中止并返回一个“技能执行超时”的 error 结果给模型。模型收到后可以自行决定是否重试或请用户稍后再试。这一步很关键它保证不管外部系统怎么不稳定智能体整体都不会“僵死”。5. 扩展与进阶让技能库具备记忆、状态与组合能力很多人搭完基本的技能调度框架后以为万事大吉但真正进入生产环境会发现纯“无状态技能调用”是远远不够的。一个成熟的智能体技能体系一定要逐步加入以下几个维度的能力。5.1 技能级缓存与记忆无状态的技能每次执行都从头开始效率低不说还可能重复做同样的计算。我为技能体系加了两级缓存短期会话缓存同一轮对话里如果多个技能对同一外部资源发起请求比如连续两次查同一股票的行情后一次直接走缓存不用再打外部接口。长期记忆库对高频查询的技能比如“查询部门人员名单”定期把结构化结果摘要存下来减少重复计算成本。我实现的时候没有引入重型数据库直接用redis的 String 结构加上 JSON 序列化就够了。核心逻辑是每次技能执行前先检查缓存键命中则直接返回未命中则执行真实逻辑并回写缓存设置合理的 TTL。5.2 技能的“组合编排”能力单技能的简单调用只能覆盖简单的单步任务但真实业务里任务往往是“读一个文件 → 提取数据 → 调用分析 → 生成报告”这样的多步骤链条。如果每一步都要模型自己去想“下一步该用什么技能”不仅耗时还容易出错。更好的做法是把归组合的子技能固化成一个复合技能。class DataAnalysisPipelineSkill: name data_pipeline description 对上传的 CSV 文件执行完整数据分析流程包括读取、清洗、统计、生成图表 input_schema DataPipelineInput output_schema DataPipelineOutput async def execute(self, params): # 内部组合子技能不需要模型干预 raw_data await read_csv.execute(params.file_path) clean_data await data_cleaner.execute(raw_data) stats await data_statistics.execute(clean_data) chart_path await chart_generator.execute(clean_data) return DataPipelineOutput(statsstats, chart_pathchart_path)这种复合技能的好处是模型只关心调用一次data_pipeline至于内部是三步还是五步全部由程序决定。这样既保留了技能的稳定性又大大降低了模型的决策负担。5.3 技能工作流的可视化与人工审核这不是一个技术要求而是一个工程管理要求。我用一个开源的事件总线把智能体的每一步“决策日志”调用了哪个技能、传了什么参数、返回了什么结果统一记录下来然后给这些日志接了一个简单的 Web 面板。在面板上我可以回看任何一个会话的完整技能调用链甚至可以手动干预某个技能的执行参数。这个能力在真实项目里救了我的命。有一次模型在调execute_code时传了一个删除目录的命令如果没有人看到并干预后果不堪设想。所以即使技术上智能体可以全自动运行生产环境也一定要给“关键技能”留一个人工审核的口子。6. 未来的想象力从单个智能体到协作式技能网络最后我想跳出代码聊一点更宏观的观察。agent-skills 这类项目真正的价值不只是给单个智能体提供一套工具调用规范而是它在不经意间为智能体的“协作化”铺平了道路。试想这样的场景公司里有 20 个技能模块分属不同团队维护——销售团队维护客户查询技能运维团队维护日志分析技能财务团队维护账目报表技能。这些技能如果都注册到同一个中心化技能库里任何一个团队更新技能都可能影响所有使用方。但如果这套 skill 体系是去中心化的、基于标准协议的那么每个技能都可以独立部署、独立发布、独立升级。智能体在执行任务时按需去“技能市场”发现和调用它需要的能力——就像手机安装 App 一样。这正是我理解的 agent-skills 的长期愿景它不是一套硬编码的代码库而是一个“技能生态的雏形”。基于它你可以构建跨团队、跨组织的智能体能力共享网络让不同系统和智能体之间以统一的协议互相调用技能。把这个思路再往前推一步如果技能协议标准化到一定程度那么两个不同公司开发的智能体可以在彼此开放权限的前提下互相调用对方的能力模块——你调用我的“行业知识查询”我调用你的“客户行为分析”。这种松耦合的协作形态才是智能体真正摆脱“单机玩具”进化成“行业基础设施”的关键。我用了一年多的实际经验可以很负责任地说如果你现在准备做智能体商业化的东西技术的难点反而不在模型本身而在技能体系的稳定性和协作协议的设计上。谁先把这个底子打好谁就能在下一轮的智能体应用爆发里抢占先机。最后再分享一个小技巧。很多人在设计技能协议时总想着把参数做得“极其通用”希望一个技能能适配所有场景。根据我的经验这恰恰是最大的坑。技能越通用模型的选择成本就越高调参出错率也越高。更可靠的做法是让每个技能足够“小”且“专”——搜索就专搜索、写文件就专写文件不要搞一个万能“super skill”。如果发现一个技能被反复组合使用再考虑做复合技能。这个原则我在每个项目里都会写进技术规范的第一条。希望这篇分享能对你有所启发。如果你也在搭建自己的技能库或者踩过什么奇怪的坑欢迎随时交流毕竟这条路上大家都是摸着石头过河。