1. 项目概述这不是一个“技能库”而是一套可插拔的智能体行为引擎“agent-skills”这个名称乍看像某个开源项目的子模块或是某篇论文里被轻描淡写带过的术语。但在我过去三年深度参与多个智能体Agent系统落地项目的过程中这个词反复出现在架构评审会、调试日志和凌晨三点的 Slack 消息里——它从来不是附庸而是决定一个 Agent 能不能真正“做事”的分水岭。核心关键词就藏在这四个字母里agent智能体、skills技能。它指的不是大模型本身的知识储备而是让模型能调用外部工具、感知真实环境、执行具体动作的一整套可注册、可编排、可验证的行为能力单元。比如当用户说“把上周销售数据导出成 Excel 并发给张经理”真正的难点不在于理解这句话而在于调哪个 API 获取数据用什么库生成 Excel邮箱地址从哪查附件大小超限怎么处理这些就是 skills 要解决的问题。它适合三类人正在从 Prompt 工程转向真实 Agent 开发的工程师需要评估 Agent 产品落地边界的业务方以及想搞懂“为什么我的 ChatGPT 插件总在关键步骤卡住”的技术决策者。它不教你怎么写 prompt而是告诉你当 prompt 失效时该往哪个方向补上真实的能力。我第一次意识到 skills 的重要性是在某高校实验室做的一个跨平台文档协同 Demo。当时模型能完美总结 PDF 内容但一到“把这份总结同步到指定 Notion 数据库并打上‘已审阅’标签”这一步就彻底失联。我们花了两天排查模型输出格式最后发现根本问题在于没有定义一个notion_update_pageskill更没做字段映射校验和权限 token 的自动轮换。后来我们把所有操作抽象成 7 个原子 skillfetch_pdf_content、extract_text、call_llm_for_summary、search_notion_db、create_notion_page、update_notion_page、send_email_with_attachment。每个 skill 都有独立的输入 schema、错误重试策略、超时控制和日志埋点。结果是整个流程的失败率从 68% 降到 4.2%而且每次报错都能准确定位到是search_notion_db的 API 返回了空数组而不是笼统的“LLM 调用失败”。这说明“agent-skills”本质是一套面向生产环境的行为契约体系——它用结构化接口约束了模型的“幻想空间”把不可控的自然语言输出锚定在可控的程序执行路径上。2. 整体设计与思路拆解为什么必须放弃“万能函数”拥抱“技能插槽”很多人初学 Agent 开发时会本能地写一个巨无霸函数比如execute_action(action_name, **kwargs)然后在里面用 if-elif 堆砌所有可能的操作。我试过也踩过坑。在某公司内部知识库项目中我们最初用这种方式集成了 12 个功能查文档、改权限、发通知、调 BI 接口、生成图表……代码行数不到 500但上线一周后运维告警里全是KeyError: chart_type和TimeoutError: waiting for BI response。问题不在逻辑而在设计哲学这种“大杂烩”模式把协议层、执行层、错误处理层全搅在一起导致任何一个微小变更比如 BI 接口加了个新参数都得动全局函数测试成本指数级上升。后来我们彻底重构核心就一条每个 skill 必须是一个独立、自洽、可验证的最小执行单元。这直接决定了整个系统的可维护性和扩展性。为什么非得这么麻烦因为真实世界里的操作天然具备三个刚性特征异构性、状态依赖、失败常态。异构性指不同系统 API 差异巨大——Notion 用 OAuth2内部 CRM 用 JWT老系统甚至要模拟表单 POST状态依赖指操作有严格顺序比如“删服务器”前必须“备份数据”而“备份”又依赖“确认磁盘空间足够”失败常态则是现实——网络抖动、限流、下游服务降级都是家常便饭。一个 skill 如果不显式声明自己的前置条件precondition、副作用side effects、重试策略retry policy和降级方案fallback那它在复杂流程里就是一颗定时炸弹。我们最终采用的方案是“三层契约模型”第一层是Schema 层用 JSON Schema 定义 skill 的输入/输出结构强制类型检查和必填项校验第二层是执行层每个 skill 封装一个独立函数只做一件事且必须返回标准 Result 对象含 success、data、error、log第三层是治理层由统一的 Skill Registry 管理所有 skill 的注册、启停、版本灰度和调用链追踪。这个设计不是为了炫技而是为了解决一个实际问题当业务方突然要求“给所有导出 Excel 的操作加水印”你能在 5 分钟内定位到generate_excel这个 skill修改其内部实现而不影响send_email或fetch_data的任何一行代码。这才是工程化的起点。提示不要试图用装饰器或元编程“自动注册”所有函数。我们早期用register_skill装饰器结果发现调试时 IDE 找不到函数引用单元测试 mock 困难CI 流程里还因 import 顺序问题偶发注册失败。现在坚持手动注册在skills/__init__.py里显式调用registry.register(send_email, send_email_skill)。看似多写两行但所有 skill 的入口、版本、作者都在一个文件里可查新人上手成本直降 70%。3. 核心细节解析与实操要点从定义到验证一个 skill 的完整生命周期定义一个真正可用的 skill远不止写个函数那么简单。以最常用的web_searchskill 为例它的表面功能是“搜索网页”但实际要覆盖的细节远超想象。我们先看一个典型失败案例某团队直接封装了 SerpAPI 的 Python SDK函数签名是def web_search(query: str) - List[str]。上线后用户反馈“搜不到最新新闻”排查发现是 SerpAPI 默认只返回前 10 条且缓存时间长达 24 小时。这暴露了 skill 设计的致命缺陷没有暴露关键控制参数也没有声明能力边界。一个合格的web_searchskill 至少要包含以下要素3.1 输入 Schema 的严谨设计我们用 Pydantic V2 定义输入模型from pydantic import BaseModel, Field from typing import Optional, List class WebSearchInput(BaseModel): query: str Field(..., description搜索关键词支持引号精确匹配) num_results: int Field(5, ge1, le50, description返回结果数量1-50) time_range: Optional[str] Field( None, description时间范围如 q1_20242024年第一季度, past_week, past_month ) site_filter: Optional[str] Field( None, description限定域名如 github.com支持通配符 *.org ) safe_search: bool Field(True, description是否启用安全搜索过滤成人内容)注意这里每个字段都有明确的业务含义和约束ge1, le50而非简单int。time_range的枚举值不是硬编码而是从配置中心动态加载方便后续对接不同搜索引擎。这种设计让 LLM 在规划planning阶段就能准确生成符合要求的参数避免因参数越界导致 skill 直接崩溃。3.2 执行逻辑的防御性编程真正的难点在函数体内。我们不会直接调用serpapi.search()而是构建一个带熔断和降级的执行链def web_search(input: WebSearchInput) - SkillResult: # 步骤1前置校验防止无效请求 if len(input.query.strip()) 2: return SkillResult.failure(query too short, minimum 2 chars) # 步骤2构建请求参数适配多引擎 params { q: input.query, num: input.num_results, safe: active if input.safe_search else off } if input.time_range: params[tbs] time_range_to_tbs(input.time_range) # 转换为 Google tbs 格式 # 步骤3熔断器调用基于 Redis 记录失败率 try: with circuit_breaker(serpapi, failure_threshold3, timeout60): response serpapi_client.search(params) except CircuitBreakerOpenError: # 降级用本地缓存 关键词匹配精度低但可用 cached cache.get(fsearch:{input.query}) if cached: return SkillResult.success(cached[:input.num_results]) return SkillResult.failure(service unavailable, fallback failed) # 步骤4结果清洗统一输出结构 results [] for item in response.get(organic_results, [])[:input.num_results]: results.append({ title: item.get(title, ), url: item.get(link, ), snippet: item.get(snippet, )[:200] # 截断防爆内存 }) return SkillResult.success(results)这里的关键是每个 skill 都要自己处理超时、熔断、降级、数据清洗。我们曾遇到过 SerpAPI 返回 500 但 SDK 未抛异常的情况导致 skill 卡死。后来在serpapi_client封装层加了强制timeout30和raise_for_status()才彻底解决。这再次证明skill 不是胶水代码而是有独立生命体征的“数字工人”。3.3 输出验证与可观测性埋点Skill 的输出必须可验证。我们要求所有 skill 的SkillResult.success(data)中data必须是 JSON-serializable 的 dict/list且通过预设的输出 Schema 校验class WebSearchOutput(BaseModel): results: List[Dict[str, str]] Field(..., min_items0, max_items50) # 每个 result 必须有 title/url/snippet validator(results) def validate_result_fields(cls, v): for r in v: if not all(k in r for k in [title, url, snippet]): raise ValueError(each result must have title, url, snippet) return v同时每个 skill 调用都会记录结构化日志logger.info(skill_executed, skill_nameweb_search, input_hashhashlib.md5(str(input.dict()).encode()).hexdigest(), duration_mselapsed_ms, result_sizelen(results), statussuccess if success else failed )这些日志被接入 ELK运营同学能直接查“过去 24 小时web_search的平均耗时和失败率”而不用翻代码。这才是 production-ready 的 skill。4. 实操过程与核心环节实现从零搭建一个可运行的 skills 系统现在我们动手搭建一个最小可行系统MVP目标让一个 LLM 能通过调用 skills 完成“查询北京今日天气并发送短信提醒”。整个过程分为四步环境准备、skill 编写、Agent 集成、流程编排。所有代码基于 Python 3.11不依赖任何商业框架确保你能完全掌控每个环节。4.1 环境准备轻量但完备的依赖栈我们刻意避开 LangChain 等大框架选择更底层的组合LLM 接口openai1.0.0兼容 OpenAI 兼容 API如 Ollama、vLLM技能注册与调度pydantic2.0.0Schema 验证、tenacity8.0.0重试、cachetools5.0.0本地缓存HTTP 客户端httpx0.24.0异步友好支持 HTTP/2配置管理pydantic-settings2.0.0环境变量优先的配置创建pyproject.toml[build-system] requires [hatchling] build-backend hatchling.build [project] name agent-skills-mvp version 0.1.0 dependencies [ openai1.0.0, pydantic2.0.0, tenacity8.0.0, cachetools5.0.0, httpx0.24.0, pydantic-settings2.0.0, ] [project.optional-dependencies] dev [pytest7.0.0, black23.0.0]安装后初始化项目结构agent-skills-mvp/ ├── main.py # Agent 主流程 ├── skills/ │ ├── __init__.py # 技能注册入口 │ ├── weather.py # 天气查询 skill │ └── sms.py # 短信发送 skill ├── config/ │ └── settings.py # 配置管理 └── utils/ └── registry.py # 技能注册中心4.2 编写两个核心 skillweather 和 sms先看skills/weather.py。我们用和风天气免费 API需申请 keyimport httpx from pydantic import BaseModel, Field from tenacity import retry, stop_after_attempt, wait_exponential from typing import Optional, Dict, Any class WeatherInput(BaseModel): city: str Field(..., description城市名称如 北京) unit: str Field(c, description温度单位c 或 f) class WeatherOutput(BaseModel): city: str temperature: float condition: str humidity: int wind_speed: float # 使用 httpx.AsyncClient 复用连接池 weather_client httpx.AsyncClient( base_urlhttps://devapi.qweather.com/v7/weather/, timeouthttpx.Timeout(10.0, connect5.0) ) retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min1, max10), reraiseTrue ) async def get_weather(input: WeatherInput) - Dict[str, Any]: # 步骤1通过城市名获取 location_id和风要求 loc_resp await httpx.get( https://geoapi.qweather.com/v2/city/lookup, params{key: YOUR_KEY, location: input.city} ) loc_resp.raise_for_status() loc_data loc_resp.json() if not loc_data.get(location): raise ValueError(fcity {input.city} not found) location_id loc_data[location][0][id] # 步骤2获取实时天气 weather_resp await httpx.get( fnow, params{key: YOUR_KEY, location: location_id} ) weather_resp.raise_for_status() data weather_resp.json()[now] return { city: input.city, temperature: float(data[temp]), condition: data[textDay], humidity: int(data[humidity]), wind_speed: float(data[windSpeed]) } # Skill 包装函数符合统一接口 async def weather_skill(input: WeatherInput) - SkillResult: try: raw_data await get_weather(input) # 输出验证 output WeatherOutput(**raw_data) return SkillResult.success(output.model_dump()) except Exception as e: return SkillResult.failure(fweather api error: {str(e)})注意retry装饰器放在get_weather上而非 skill 函数上因为重试应针对 HTTP 请求而非整个 skill 逻辑比如输入校验失败不该重试。weather_client是全局复用的避免频繁创建连接。再看skills/sms.py用阿里云短信服务需开通from aliyunsdkcore.client import AcsClient from aliyunsdkcore.auth.credentials import AccessKeyCredential from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest import json class SmsInput(BaseModel): phone: str Field(..., description手机号11位数字) content: str Field(..., description短信内容不超过 500 字) # 阿里云客户端单例 sms_client AcsClient( akYOUR_AK, secretYOUR_SECRET, region_idcn-hangzhou ) def sms_skill(input: SmsInput) - SkillResult: try: # 校验手机号 if not input.phone.isdigit() or len(input.phone) ! 11: return SkillResult.failure(invalid phone number format) # 构建短信请求阿里云要求模板 request SendSmsRequest.SendSmsRequest() request.set_accept_format(json) request.set_SignName(测试签名) # 替换为你的签名 request.set_TemplateCode(SMS_123456) # 替换为你的模板ID request.set_PhoneNumbers(input.phone) # 阿里云模板变量格式{code:1234}我们简单处理 request.set_TemplateParam(json.dumps({content: input.content})) response sms_client.do_action_with_exception(request) resp_json json.loads(response) if resp_json.get(Code) OK: return SkillResult.success({message_id: resp_json.get(MessageId)}) else: return SkillResult.failure(fsms send failed: {resp_json.get(Message)}) except Exception as e: return SkillResult.failure(fsms client error: {str(e)})这里的关键是skill 必须处理领域特定的协议细节。阿里云要求模板 ID、签名、JSON 参数格式这些都不能丢给 LLM 去猜必须在 skill 内部封装好。4.3 Agent 集成让 LLM 学会“调用技能”核心是main.py中的 Agent 循环。我们不用复杂推理框架用最朴素的“Thought-Action-Observation”循环import asyncio from openai import AsyncOpenAI from skills import registry # 导入注册中心 from utils.registry import SkillRegistry # 初始化技能注册中心 skill_registry SkillRegistry() # 手动注册技能见 2.2 节提示 skill_registry.register(get_weather, weather_skill) skill_registry.register(send_sms, sms_skill) # 初始化 LLM 客户端 client AsyncOpenAI( base_urlhttp://localhost:11434/v1, # Ollama 本地服务 api_keyollama ) async def run_agent(user_query: str): # Step 1: LLM 规划生成 tool call plan_prompt f 你是一个智能助手能调用以下技能 - get_weather(city: str, unit: str): 查询指定城市天气 - send_sms(phone: str, content: str): 发送短信 用户请求{user_query} 请按 JSON 格式输出你要调用的技能例如 {{tool: get_weather, params: {{city: 北京, unit: c}}}} 或多个技能按顺序 [{{tool: get_weather, ...}}, {{tool: send_sms, ...}}] 只输出 JSON不要任何解释。 plan_response await client.chat.completions.create( modelqwen2:7b, # 本地模型 messages[{role: user, content: plan_prompt}], temperature0.0 ) plan_json plan_response.choices[0].message.content # Step 2: 解析并执行技能 try: plan json.loads(plan_json) if isinstance(plan, dict): plan [plan] # 统一为列表 observations [] for action in plan: tool_name action.get(tool) params action.get(params, {}) if not tool_name or tool_name not in skill_registry.skills: observations.append(fERROR: unknown tool {tool_name}) continue # 调用 skill skill_func skill_registry.skills[tool_name] try: # 同步 skill 用 run_sync异步用 await if asyncio.iscoroutinefunction(skill_func): result await skill_func(**params) else: result skill_func(**params) obs fSUCCESS: {tool_name}({params}) - {result.data} observations.append(obs) except Exception as e: observations.append(fERROR: {tool_name}({params}) - {str(e)}) # Step 3: LLM 总结将 observations 转为自然语言回复 summary_prompt f 用户请求{user_query} 技能执行结果 {\n.join(observations)} 请用简洁中文总结结果不要提技术细节。如果全部成功直接给出答案如果有失败说明原因。 summary_response await client.chat.completions.create( modelqwen2:7b, messages[{role: user, content: summary_prompt}], temperature0.3 ) return summary_response.choices[0].message.content except json.JSONDecodeError: return 无法理解请求请重试。 except Exception as e: return f系统错误{str(e)} # 运行示例 if __name__ __main__: result asyncio.run(run_agent(查询北京今日天气并发送短信提醒我)) print(result)这个 Agent 的精妙之处在于它把“技能调用”变成了 LLM 的一个标准输出格式任务而不是靠复杂插件机制。我们用专门的 prompt 引导 LLM 输出 JSON再由 Python 解析执行。这样做的好处是调试极其简单——你可以把plan_prompt单独喂给 LLM看它是否能正确生成{tool: get_weather, params: {city: 北京}}。如果不行问题就在 prompt 或模型能力而不是框架 bug。4.4 流程编排超越线性调用的技能协作真实场景中skills 往往需要协作。比如“订会议室”需要查日历空闲 → 创建事件 → 发邀请邮件 → 同步到 IM。我们设计了一个轻量编排器WorkflowEnginefrom typing import List, Dict, Any, Callable class WorkflowStep: def __init__(self, skill_name: str, params: Dict[str, Any], next_on_success: str None, next_on_failure: str None): self.skill_name skill_name self.params params self.next_on_success next_on_success self.next_on_failure next_on_failure class WorkflowEngine: def __init__(self, registry: SkillRegistry): self.registry registry async def execute(self, steps: List[WorkflowStep]) - Dict[str, Any]: context {} # 共享上下文用于参数传递 for step in steps: # 动态解析参数中的上下文变量如 {weather.temperature} resolved_params self._resolve_context(step.params, context) try: skill_func self.registry.skills[step.skill_name] if asyncio.iscoroutinefunction(skill_func): result await skill_func(**resolved_params) else: result skill_func(**resolved_params) # 将结果存入上下文供后续步骤使用 context[f{step.skill_name}_result] result.data # 根据结果跳转 if result.success and step.next_on_success: continue # 下一步 elif not result.success and step.next_on_failure: # 跳转到失败分支 pass except Exception as e: context[f{step.skill_name}_error] str(e) break return context # 使用示例天气短信工作流 workflow WorkflowEngine(skill_registry) steps [ WorkflowStep( skill_nameget_weather, params{city: 北京}, next_on_successsend_sms ), WorkflowStep( skill_namesend_sms, params{phone: 13800138000, content: 北京今日天气{get_weather_result.temperature}°C{get_weather_result.condition}} ) ] result await workflow.execute(steps)这里{get_weather_result.temperature}的语法糖是我们在_resolve_context中实现的 Jinja2 风格模板渲染。它让 skills 能自然串联而无需 LLM 去记忆中间结果。这才是“agent-skills”作为行为引擎的真正威力——它把复杂的多步协作降维成清晰的、可配置的、可审计的流程图。5. 常见问题与排查技巧实录那些只有踩过才知道的坑在十几个项目中我们整理出一份高频问题清单每一条都来自血泪教训。这些问题往往不会出现在官方文档里但会实实在在卡住你的进度。5.1 LLM 总是生成不存在的 skill 名称现象Agent 日志显示ERROR: unknown tool get_weater拼错或send_email_v2版本号乱加。根因LLM 的输出受训练数据影响对 skill 名称的“拼写稳定性”极差。我们曾统计过同一 prompt 下Qwen2-7B 有 12% 的概率把get_weather写成get_weatcher。解决方案建立 skill 别名映射表在注册时为每个 skill 添加常用别名skill_registry.register(get_weather, weather_skill, aliases[weather, forecast, check_weather])在解析前做模糊匹配用difflib.get_close_matches(tool_name, list(skill_registry.skills.keys()), n1, cutoff0.7)找最接近的合法名称。最关键的是在plan_prompt末尾强制加一句“可用技能名必须严格从以下列表中选择[get_weather, send_sms]不允许任何变体。” 这招实测将错误率从 12% 降到 0.3%。5.2 技能执行超时但 LLM 却认为“成功”现象web_searchskill 设置了 30 秒超时但 LLM 的总结里写“已搜索完成”而实际上返回了空结果。根因skill 的SkillResult没有区分“业务成功”和“技术成功”。HTTP 超时抛出httpx.TimeoutExceptionskill 捕获后返回failure但 LLM 的总结 prompt 没有要求它识别failure状态。解决方案统一错误分类在SkillResult中增加error_type字段如network_timeout、api_rate_limit、validation_error。强化总结 prompt在summary_prompt中明确指令“如果任何步骤返回 ERROR请在总结开头用【警告】标出并说明具体原因。”添加兜底校验在 workflow 引擎中对failure结果自动注入一条 observationOBSERVATION: CRITICAL FAILURE in {step.skill_name} - {result.error}确保 LLM 无法忽略。5.3 多技能并发调用时共享资源冲突现象当同时运行 5 个send_smsskill 时阿里云返回Throttling错误但日志里只看到一个失败其他 4 个还在继续发。根因技能是无状态的但下游服务如短信网关有全局配额限制。每个 skill 独立调用不知道兄弟进程的存在。解决方案引入分布式限流器用 Redis 实现令牌桶所有send_smsskill 调用前先redis.eval(LUA_SCRIPT, 1, sms_quota, 1)申请令牌。更优雅的做法在sms_skill内部封装一个SmsRateLimiter单例用threading.Lock控制本地并发再配合 Redis 做集群协调。经验之谈不要在 Agent 层做限流必须下沉到 skill 内部。因为 Agent 可能被多个实例部署而 skill 是唯一能感知“自己在做什么”的实体。5.4 技能输出数据过大导致 LLM 上下文爆炸现象fetch_documentskill 返回一篇 5000 字的 PDF 文本LLM 的summary_prompt直接超出上下文长度报错context_length_exceeded。根因skill 的职责是“执行”不是“决策”。它不应该假设 LLM 能处理多大体积的数据。解决方案强制输出截断在 skill 的SkillResult.success(data)前加入if isinstance(data, str) and len(data) 2000: data data[:2000] ...内容过长已截断提供多粒度输出为fetch_documentskill 增加output_level参数summary200字摘要、key_points5个要点、full_text全文但需显式声明风险。终极方案用 RAG 替代全文传输。skill 只返回文档 ID 和向量库中的 chunk IDsLLM 通过retrieve_chunksskill 按需拉取——这才是 scalable 的设计。5.5 生产环境技能调用链路黑盒无法快速定位故障点现象用户投诉“订会议室失败”但日志里只有Agent finished with error找不到是日历查询失败还是邮件发送失败。根因缺乏端到端的 trace ID 和结构化日志。解决方案统一 trace ID 注入在 Agent 入口生成trace_id str(uuid.uuid4())并透传给每个 skill 调用。标准化日志格式所有 skill 日志必须包含trace_id、skill_name、input_hash、duration_ms、status。可视化看板用 Grafana Loki 搭建技能健康度看板监控每个 skill 的 P95 延迟、错误率、成功率趋势。我们曾靠这个看板发现notion_update_page的错误率在每天上午 10 点飙升最终定位到是 Notion 的 rate limit 重置时间点。注意不要用print()或logging.info()打日志必须用结构化 logger如structlog确保每条日志能被机器解析。我们曾因日志格式不统一导致运维同学花 3 小时手动 grep 200MB 日志文件。6. 技能治理与演进如何让 skills 系统持续健康生长一个 skills 系统上线只是开始真正的挑战在于长期治理。我们总结了一套“技能健康度”评估框架每月对所有 skill 进行扫描指标包括可用性uptime、准确性output_schema_conformance、效率p95_latency、健壮性failure_recovery_rate、可维护性code_churn_rate。其中code_churn_rate代码变更频率是个反直觉但极有价值的指标——如果一个 skill 的代码每月修改超过 3 次说明它的契约Schema设计有问题总在打补丁健康的 skill 应该稳定运行半年以上只做小修小补。6.1 技能版本灰度与回滚机制当要升级web_searchskill 以支持新搜索引擎时绝不能一刀切。我们的做法是双注册模式skill_registry.register(web_search, new_search_skill, versionv2)和skill_registry.register(web_search, old_search_skill, versionv1)。流量染色在 Agent 的plan_prompt中加入当前环境标识envprod-canaryLLM 生成时带上{tool: web_search, version: v2}。自动降级如果v2的错误率连续 5 分钟超过 5%系统自动将所有v2请求路由到v1并触发告警。这套机制让我们在某次升级 SerpAPI 到 Google Custom Search 时零用户感知完成了迁移。6.2 技能市场与跨团队复用skills 最大的价值在于复用。我们建立了内部“技能市场”一个静态网站展示所有已注册 skill 的文档、Schema、调用示例、SLA 承诺如get_weatherP95 800ms。每个 skill 的 README.md 都包含适用场景如“适用于实时性要求不高的天气概览不适用于气象预警”。已知限制如“仅支持中国城市海外城市需用get_weather_international”。性能基线表格形式展示不同输入下的耗时city北京vscity纽约。这避免了重复造轮子。某业务线曾想开发自己的短信 skill看到市场里已有send_sms支持阿里云/腾讯云双通道且 SLA 99.95%立刻放弃。6.3 技能的“退休”与“归档”技术债最大的来源不是写新代码而是不敢删旧代码。我们规定退休条件一个 skill 连续 9
阅读完成 · 觉得有帮助?