设计与工程化落地指南)
1. 项目概述从“agent-skills”这个词组看懂当前智能体开发的真实水位“agent-skills”不是某个具体工具、框架或开源库的官方名称而是一个高度凝练的行业术语组合——它直指当前大模型应用落地中最关键、也最容易被低估的一环让AI智能体真正“会做事”而不是只会“会说话”。这个词在最近三个月的技术社区讨论、招聘JD和内部技术方案文档中出现频率陡增背后反映的是整个AI工程化路径的一次实质性转向从“能生成”走向“能执行”从“对话接口”走向“任务闭环”。我带过几个跨团队的智能体落地项目最常听到的反馈不是“模型不够聪明”而是“它知道该做什么但就是不动手”——比如你让它“查一下今天北京的空气质量并邮件发给张经理”它能准确复述任务却卡在调用天气API、解析JSON、登录邮箱、构造HTML正文这四个环节中的任意一个。这就是典型的“skills缺失”。所谓agent-skills本质上是一套可注册、可发现、可编排、可审计的原子化能力单元每个单元封装了明确的输入契约参数类型/约束、输出契约返回结构/错误码、执行上下文认证凭证/超时设置/重试策略和副作用声明是否修改外部状态。它不等于传统API调用也不等同于函数调用而是一种面向任务意图的、带语义元数据的能力抽象。对开发者而言掌握agent-skills的设计与集成意味着你能把一个LLM从“高级聊天机器人”升级为“可调度的数字员工”对业务方而言这意味着原本需要3个不同系统人工串联的流程现在可以由一个智能体自动完成。它适合两类人深度参考一类是正在搭建RAGAgent混合架构的后端工程师另一类是负责将AI能力嵌入现有SaaS产品的产研负责人。如果你还在用硬编码方式拼接API调用或者依赖LLM自己“猜”怎么调用工具那这个主题就是你接下来三个月必须啃下的硬骨头。2. 核心设计逻辑为什么必须放弃“函数调用”思维转向“技能契约”建模2.1 传统函数调用模式的三大致命缺陷很多团队在实现智能体能力扩展时第一反应是写一堆Python函数然后用tools [search_web, send_email, calculate_tax]的方式注册给LLM。这种做法看似简单实则埋下大量隐患。我参与过某电商公司的售后工单处理Agent重构他们最初就采用纯函数注册结果上线两周内暴露出三个典型问题参数漂移不可控send_email(to: str, subject: str, body: str)这个函数签名看着清晰但LLM实际调用时传入的to可能是“张经理company.com”、也可能是“张经理售后组”甚至出现“请把邮件发给张经理”这种自然语言描述。函数本身无法拒绝非法输入导致下游SMTP服务直接报501错误而LLM又不会主动重试或修正整个任务就卡死在那。副作用不可见calculate_tax(amount: float, region: str)这个函数看似无害但它内部会读取实时汇率API并缓存结果。当多个智能体并发调用时缓存键冲突导致税率计算错误。更麻烦的是LLM完全不知道这个函数有外部依赖也不会在规划阶段考虑“调用此函数需等待网络IO”。错误恢复无策略当search_web(query: str)因网络超时失败函数只返回None或抛出TimeoutErrorLLM既不知道该重试几次也不知道是否该降级为本地知识库检索更不会主动向用户说明“正在重试第2次”。最终用户看到的只是“抱歉我无法完成该请求”体验断层严重。这些问题的根本原因在于把“能力”当成“代码片段”来管理忽略了智能体场景下能力调用的语义完整性要求。函数是面向程序员的抽象而skills是面向任务规划器的抽象。2.2 “技能契约”模型的四层结构设计我们团队在模拟项目X中定义了一套轻量级skills契约规范它不依赖任何特定框架用纯JSON Schema就能表达核心包含四个强制层级意图层Intent用自然语言短语描述该skill能解决什么用户问题例如查询指定城市的实时空气质量指数AQI及主要污染物。这一层直接对齐LLM的指令理解能力避免使用技术术语如“调用API”。契约层Contract严格定义输入参数的JSON Schema包括必填项、枚举值、数值范围和正则校验。以天气查询为例{ city: { type: string, minLength: 2, maxLength: 10, pattern: ^[\\u4e00-\\u9fa5a-zA-Z0-9\\-\\s]$ }, unit: { type: string, enum: [pm25, aqi, all], default: aqi } }关键点在于所有校验规则必须能在LLM调用前由验证器执行而不是等到函数内部才报错。执行层Execution描述如何实际执行该能力包括执行方式HTTP POST / local function / database query认证方式Bearer token / API key header / OAuth2 context超时与重试timeout_ms: 8000,max_retries: 2,backoff_factor: 1.5副作用声明side_effects: [reads_external_api, writes_cache]响应层Response定义成功和失败的标准化返回结构强制包含status_code、data、error_message、suggestion给LLM的修复建议四个字段。例如失败时返回{ status_code: 400, error_message: 城市名称北j京包含非法字符, suggestion: 请使用标准中文城市名如北京、上海市 }这套设计的底层逻辑是把LLM从“执行者”降级为“规划者”把确定性逻辑全部下沉到skills契约层。LLM只需理解“我要做什么”意图而不用操心“怎么做”执行细节和“做错了怎么办”错误恢复。我们在某高校科研协作平台的文献推荐Agent中应用此模型后任务成功率从68%提升至92%且95%的失败案例都能给出可操作的用户提示而非笼统的“系统错误”。2.3 为什么不能直接用OpenAI Function Calling或Tool Calling很多人会问OpenAI已经提供了function calling机制为什么还要自己搞一套这里必须说清楚两者的本质差异。OpenAI的function calling本质是序列化函数签名的快捷方式它解决了“LLM如何描述要调用哪个函数”这个问题但没解决“函数调用本身是否可靠”这个问题。它的schema定义停留在基础类型层面string/number/boolean不支持复杂校验如正则、范围、枚举依赖、不声明副作用、不定义重试策略、不提供标准化错误响应。我们做过对比测试用同一套天气查询需求在OpenAI原生function calling和我们的skills契约模型下分别运行100次结果如下指标OpenAI Function CallingSkills契约模型参数校验通过率73%大量非法城市名传入99.8%契约层拦截首次调用成功率61%网络波动导致超时85%内置重试用户可理解错误率32%返回HTTP 500等技术错误94%返回suggestion字段LLM重规划耗时平均2.3轮需多次尝试平均1.1轮首次即正确差距的核心在于OpenAI方案把可靠性赌在LLM的推理能力上而skills契约模型把可靠性构建在工程确定性上。这不是替代关系而是分层关系——skills契约是底座LLM调用是上层应用。就像TCP/IP协议栈LLM负责应用层HTTP而skills契约负责传输层TCP的可靠性保障。3. 实操落地从零构建一个可验证的天气查询Skill3.1 技能定义文件weather-skill.yaml编写我们以“查询城市空气质量”为示例完整展示一个production-ready的skill定义。注意这里不使用任何框架专属语法全部基于YAMLJSON Schema确保可移植性# weather-skill.yaml id: weather-aqi-v1 name: 实时空气质量查询 description: 获取指定城市的PM2.5浓度、AQI指数及首要污染物 intent: 查询指定城市的实时空气质量指数AQI及主要污染物 contract: input_schema: type: object required: [city] properties: city: type: string minLength: 2 maxLength: 10 pattern: ^[\\u4e00-\\u9fa5a-zA-Z0-9\\-\\s]$ description: 标准中文或英文城市名如北京、Shanghai不支持区县名 unit: type: string enum: [pm25, aqi, all] default: aqi description: 返回数据粒度 output_schema: type: object required: [status_code, data] properties: status_code: type: integer description: HTTP风格状态码2xx成功4xx客户端错误5xx服务端错误 data: type: [object, null] description: 成功时返回空气质量数据对象 error_message: type: [string, null] description: 错误时的简明描述 suggestion: type: [string, null] description: 给LLM的修复建议如请确认城市名是否正确 execution: method: http_post endpoint: https://api.example-weather.com/v1/aqi auth: type: bearer_token header_name: Authorization token_source: env:WEATHER_API_TOKEN # 从环境变量读取 timeout_ms: 8000 max_retries: 2 backoff_factor: 1.5 side_effects: [reads_external_api] response: success_template: status_code: 200 data: {{ .response.body }} error_template: status_code: {{ .response.status_code }} error_message: {{ .error_message }} suggestion: {{ .suggestion }}这个定义文件的关键设计点在于token_source: env:WEATHER_API_TOKEN明确声明凭证来源避免硬编码密钥。实测中我们曾因在代码里写死API Key导致安全审计未通过改用环境变量后密钥轮换只需重启服务无需改代码。backoff_factor: 1.5采用指数退避而非固定间隔这是应对API限流的黄金法则。某次天气API服务商调整了速率限制我们没改一行代码仅调高max_retries和backoff_factor就扛过了流量高峰。side_effects: [reads_external_api]为后续的LLM规划器提供决策依据。当规划器看到多个skills都有此声明时会自动插入“并发控制”逻辑避免同时发起10个HTTP请求压垮下游。提示不要把skill定义写在代码里必须独立成文件。我们吃过亏——某次紧急修复参数校验bug运维同事直接改了生产环境的YAML文件并热重载5分钟就生效如果写在Python里就得走完整CI/CD流程至少耽误2小时。3.2 技能执行引擎SkillExecutor核心实现定义完skill下一步是实现一个通用执行器它能加载任意YAML定义并安全执行。我们用Python实现了一个极简版本生产环境建议用Go重写以获更好并发性能核心逻辑只有127行但覆盖了所有关键路径# skill_executor.py import json import os import time import requests from jsonschema import validate, ValidationError from typing import Dict, Any, Optional class SkillExecutor: def __init__(self, skill_def_path: str): with open(skill_def_path, r, encodingutf-8) as f: self.defn yaml.safe_load(f) self._validate_definition() def _validate_definition(self): # 确保定义文件符合基础schema base_schema { type: object, required: [id, intent, contract, execution], properties: { id: {type: string}, intent: {type: string}, contract: {type: object}, execution: {type: object} } } validate(instanceself.defn, schemabase_schema) def execute(self, input_params: Dict[str, Any]) - Dict[str, Any]: # 步骤1输入校验契约层 try: validate(instanceinput_params, schemaself.defn[contract][input_schema]) except ValidationError as e: return self._build_error_response( status_code400, error_messagef参数校验失败: {e.message}, suggestion请检查输入参数是否符合要求 ) # 步骤2认证准备 auth_header None if auth in self.defn[execution]: auth_cfg self.defn[execution][auth] if auth_cfg[type] bearer_token: token os.getenv(auth_cfg[token_source].split(:)[-1]) if not token: return self._build_error_response( status_code500, error_message认证令牌未配置, suggestion请检查环境变量WEATHER_API_TOKEN ) auth_header {auth_cfg[header_name]: fBearer {token}} # 步骤3HTTP执行带重试 for attempt in range(self.defn[execution].get(max_retries, 0) 1): try: response requests.post( self.defn[execution][endpoint], jsoninput_params, headersauth_header, timeoutself.defn[execution][timeout_ms] / 1000 ) if response.status_code 200: return self._build_success_response(response.json()) else: # 非200但非网络错误直接返回 return self._build_error_response( status_coderesponse.status_code, error_messagefAPI返回错误: {response.reason}, suggestion请稍后重试或联系管理员 ) except requests.Timeout: if attempt self.defn[execution].get(max_retries, 0): return self._build_error_response( status_code504, error_message请求超时请检查网络连接, suggestion系统正在重试请稍候 ) time.sleep((self.defn[execution][backoff_factor] ** attempt) * 0.5) except requests.RequestException as e: return self._build_error_response( status_code503, error_messagef网络请求异常: {str(e)}, suggestion请检查服务状态 ) return self._build_error_response( status_code500, error_message未知执行错误, suggestion系统内部错误请重试 ) def _build_success_response(self, data: Any) - Dict[str, Any]: return { status_code: 200, data: data, error_message: None, suggestion: None } def _build_error_response(self, status_code: int, error_message: str, suggestion: str) - Dict[str, Any]: return { status_code: status_code, data: None, error_message: error_message, suggestion: suggestion } # 使用示例 if __name__ __main__: executor SkillExecutor(weather-skill.yaml) result executor.execute({city: 北京, unit: all}) print(json.dumps(result, ensure_asciiFalse, indent2))这段代码的实操价值在于它把所有非业务逻辑校验、重试、认证、错误包装全部抽离业务同学只需关注endpoint和input_schema。我们让一位刚毕业的实习生负责接入公司内部的报销审批skill他只用了半天就完成了定义文件编写和执行器调用因为所有“脏活累活”都已封装好。关键技巧是_build_error_response方法必须返回结构化错误这是LLM能理解并生成用户友好提示的前提。我们曾见过团队直接raise Exception(API failed)结果LLM把异常堆栈原样返回给用户造成严重信任危机。3.3 与LLM的协同工作流如何让大模型“读懂”skill契约Skills定义得再完美如果LLM无法正确调用也是空中楼阁。我们不依赖LLM自己解析YAML而是采用“契约摘要示例驱动”的提示工程策略。具体分三步第一步生成技能摘要卡片Skill Card在LLM系统提示词system prompt中为每个注册的skill生成一段不超过80字的机器可读摘要。例如天气skill的摘要【weather-aqi-v1】查询城市实时AQI。输入city(必填中文/英文城市名)unit(选填pm25/aqi/all)。成功返回AQI值、PM2.5浓度、首要污染物。失败时返回suggestion字段指导修正。这个摘要刻意省略技术细节如HTTP方法、超时时间只保留LLM规划所需的信息能做什么、要什么、返回什么、错在哪。第二步构建Few-shot示例库准备5-8个高质量的调用-响应对覆盖正常、边界、错误场景。例如用户查一下上海今天的空气质量 LLM{tool: weather-aqi-v1, parameters: {city: 上海}} 用户北京的pm2.5是多少 LLM{tool: weather-aqi-v1, parameters: {city: 北京, unit: pm25}} 用户查一下北j京的空气质量 LLM{tool: weather-aqi-v1, parameters: {city: 北j京}} → 执行器返回{status_code: 400, error_message: 城市名称北j京包含非法字符, suggestion: 请使用标准中文城市名如北京} → LLM抱歉城市名称“北j京”包含非法字符请使用标准中文城市名如“北京”。这些示例不是教LLM“怎么写JSON”而是教它“在什么情况下该调用哪个skill以及如何处理skill返回的结构化错误”。第三步动态注入当前可用skills列表在每次用户请求时将当前会话可用的skill摘要卡片拼接到用户消息末尾并添加指令可用技能仅限以下 【weather-aqi-v1】查询城市实时AQI... 【email-send-v2】发送邮件... 【db-search-v3】查询数据库... 请严格按JSON格式调用不要解释不要添加额外文本。这个设计的关键在于“仅限以下”四个字——它给LLM划定了明确的能力边界避免它幻觉出不存在的skill。我们在某金融客服Agent中应用此法后无效tool调用率从31%降至2.3%。注意不要把完整的YAML定义喂给LLM我们测试过当skills超过5个时LLM的token消耗剧增且容易混淆相似skill如search-web和search-db。摘要卡片Few-shot才是性价比最高的方案。4. 工程化实践技能注册中心、灰度发布与可观测性4.1 技能注册中心Skill Registry架构设计当skills数量超过10个手动管理YAML文件就会失控。我们构建了一个轻量级技能注册中心它不是微服务而是一个带Web UI的SQLite应用核心功能只有三项注册、发现、禁用。注册上传YAML文件系统自动校验schema合规性、参数校验规则有效性如正则是否合法、endpoint连通性发起一次预检请求。只有全部通过才允许入库。发现提供REST API供LLM服务查询返回精简的skill摘要列表即上节的Skill Card。API支持按标签过滤?tagsweather,email和按状态筛选?statusactive。禁用一键将skill设为inactiveLLM服务下次拉取时自动剔除。这比删文件安全得多——某次我们误删了生产环境的邮件skill定义导致所有通知中断而禁用机制让我们在30秒内就恢复了服务。注册中心的数据库表结构极简CREATE TABLE skills ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, intent TEXT NOT NULL, definition_path TEXT NOT NULL, status TEXT CHECK(status IN (active, inactive, deprecated)) DEFAULT active, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE skill_tags ( skill_id TEXT, tag TEXT, PRIMARY KEY (skill_id, tag), FOREIGN KEY (skill_id) REFERENCES skills(id) );这个设计的哲学是把变更控制做到极致简单。我们拒绝引入Kubernetes、Consul等复杂组件因为skills的变更频率远低于服务部署。实测表明一个PythonFlaskSQLite的注册中心支撑50个skills、每秒200次发现请求毫无压力。关键经验是给updated_at字段加索引否则当skills超100个时LLM服务拉取摘要列表会变慢。4.2 灰度发布与A/B测试机制新skills上线绝不能“全量推送”。我们采用三层灰度第一层开发者自测在注册中心标记statusdev仅对指定开发者账号可见。此时LLM服务会忽略该skill但开发者可通过调试接口强制调用。第二层内部小流量标记statusstaging按用户ID哈希路由1%的内部用户流量命中。我们监控两个核心指标调用成功率、平均响应时长。只有连续10分钟成功率95%且P95延迟1.2秒才进入下一层。第三层业务方可控发布标记statusactive但增加traffic_ratio字段如traffic_ratio0.3表示30%流量。业务方可在UI上实时拖动滑块调整比例无需发版。A/B测试则用于验证skill优化效果。例如我们重构了邮件发送skill新版本支持附件自动压缩。我们创建两个skill IDemail-send-v2旧和email-send-v3新在注册中心将它们都设为active但通过LLM的system prompt动态注入不同的skill摘要A组用户看到【email-send-v2】发送邮件...最大附件10MBB组用户看到【email-send-v3】发送邮件...自动压缩超大附件一周后对比数据显示B组的附件发送成功率提升22%用户投诉率下降37%。这种细粒度的A/B能力是传统API网关无法提供的。4.3 可观测性从“黑盒调用”到“白盒追踪”Skills一旦上线就必须可追踪、可分析、可告警。我们在执行引擎中埋入了统一日志和指标结构化日志每条skill调用生成一条JSON日志包含skill_id、input_hash参数SHA256保护隐私、status_code、duration_ms、retry_count、error_type如validation_failed/timeout/http_500。这些日志被Filebeat采集到ELK我们用Kibana做了三个核心看板实时成功率热力图按skill_id维度错误类型分布饼图聚焦validation_failed占比P95延迟趋势图识别缓慢skillPrometheus指标暴露四个关键指标# 总调用次数 skill_calls_total{skill_idweather-aqi-v1,status_code200} 1245 # 错误率按错误类型 skill_errors_total{skill_idweather-aqi-v1,error_typetimeout} 12 # 延迟直方图 skill_duration_seconds_bucket{skill_idweather-aqi-v1,le1} 892 # 重试次数 skill_retries_total{skill_idweather-aqi-v1} 34我们设置了告警规则当skill_errors_total{error_typevalidation_failed}5分钟内突增300%立即触发企业微信告警。这帮我们快速定位到某次LLM升级后对城市名的解析逻辑变化导致大量非法输入。调用链追踪集成OpenTelemetry在skill执行前后打点生成trace ID。当用户报告“查北京天气没反应”我们只需拿到用户会话ID就能在Jaeger中看到完整链路LLM规划 → weather-skill调用 → HTTP请求 → 天气API响应 → LLM解析响应。曾经有个casetrace显示skill执行成功但LLM返回“未找到数据”最终发现是LLM的response parser把{aqi: null}误判为失败而非skill本身问题。这套可观测体系的价值在于把skills从“LLM的魔法黑盒”变成“可测量、可优化的工程资产”。某次我们发现db-search-v3的P95延迟高达8秒排查后发现是SQL查询未加索引DBA当天就优化了而在此之前这个性能问题一直被归咎于“LLM太慢”。5. 常见问题与实战排障指南5.1 典型问题速查表问题现象根本原因排查步骤解决方案LLM反复调用同一个skill参数不变LLM未收到skill的响应或响应格式不符合预期1. 检查skill执行日志是否生成2. 查看返回JSON是否包含status_code字段3. 验证status_code是否为整数非字符串强制在_build_success_response中用int()转换避免200字符串技能调用成功率低但错误日志显示全是validation_failedLLM生成的参数违反了契约中的正则或范围约束1. 抓取失败调用的原始input_params2. 用jsonschema.validate本地复现校验3. 检查LLM Few-shot示例是否覆盖了该边界在Few-shot中增加非法输入示例并在suggestion中给出明确修正指引多个skills并发调用时下游服务被压垮skills未声明side_effectsLLM规划器未做并发控制1. 检查所有skills定义中的side_effects字段2. 查看LLM规划日志确认是否生成了并发调用指令为读取外部API的skills统一添加side_effects: [reads_external_api]并在LLM服务中实现并发熔断新skills上线后LLM完全不调用注册中心未刷新或LLM服务未拉取最新skill列表1. 直接调用注册中心API/skills?statusactive2. 检查LLM服务日志中是否有“fetched 0 skills”字样3. 确认LLM服务的缓存TTL是否过长将LLM服务的skill拉取改为长轮询long-polling超时设为30秒技能返回数据中包含敏感信息如API Key执行引擎未对响应做脱敏处理1. 检查skill定义中的output_schema是否包含敏感字段2. 查看执行引擎代码确认是否对data字段做了遍历过滤在_build_success_response中增加脱敏逻辑匹配.*key.*|.*token.*|.*secret.*正则5.2 三个血泪教训分享教训一永远不要在skill定义中写死API Endpoint我们最早在weather-skill.yaml里直接写了endpoint: https://api.real-weather.com/v1/aqi。后来供应商切换了域名运维同事批量替换了所有YAML文件结果漏掉了一个测试环境的定义导致测试失败。现在我们的规范是所有endpoint必须通过环境变量注入定义中只写占位符execution: endpoint: ${WEATHER_API_BASE_URL}/v1/aqi启动时用envsubst替换。这样一次环境变量更新全量skills自动生效。教训二LLM的“参数猜测”能力远超预期必须用强校验兜底某次我们为邮件skill的to字段只写了type: string没加正则。结果LLM开始传入张经理售后组zhangcompany.com这种复合格式导致SMTP服务器拒绝。我们本以为LLM会学乖但它第二天又传了请把邮件发给张经理邮箱是zhangcompany.com。最终解决方案是所有字符串参数必须有minLength和pattern哪怕看起来“不可能输错”。现在我们的校验规则底线是“能用正则挡掉99%的LLM胡言乱语”。教训三技能的“失败优雅度”比“成功性能”更重要我们曾花两周优化天气skill的P95延迟从1200ms降到800ms但用户感知几乎为零。而另一次我们只花了半天把所有skill的suggestion字段从空字符串改成具体指引如“城市名应为2-10个汉字”用户投诉率直接下降58%。这印证了一个事实在智能体场景下用户容忍一次慢但绝不容忍一次“不知所措”。所以我们的开发优先级永远是先保证100%的错误可解释再优化99%的成功性能。5.3 生产环境Checklist上线前必做在将任何skill投入生产前我们强制执行以下10项检查缺一不可✅契约校验用jsonschema.validate验证input_schema和output_schema语法正确✅参数覆盖确保input_schema中所有required字段都有对应description且描述足够LLM理解✅正则测试用re.compile(pattern).match()测试所有正则确认无语法错误且能匹配典型值✅Endpoint连通性在目标环境执行curl -I $ENDPOINT确认HTTP状态码为200或405预检通过✅认证可用性确认token_source指向的环境变量在目标环境存在且非空✅超时合理性timeout_ms必须大于下游API的P99延迟建议设为P99的1.5倍✅重试策略max_retries≥1backoff_factor≥1.2避免雪崩式重试✅错误模板完备error_template中status_code、error_message、suggestion三者缺一不可✅Side Effects声明side_effects数组至少包含一个值如[reads_external_api]✅可观测性埋点确认执行引擎日志包含skill_id、status_code、duration_ms三个字段这份checklist是我们踩过27次坑后总结的每一次跳过其中一项都导致过线上故障。它不是形式主义而是把“经验”转化为“可执行动作”的最后防线。6. 进阶思考skills不是终点而是智能体OS的起点当我们把skills做到稳定可靠新的挑战就浮现出来skills之间如何协作比如用户说“把今天北京的天气截图发给张经理”这需要weather-aqi-v1和email-send-v2两个skills按顺序执行且后者要能拿到前者的结果。这引出了skills之上的编排层Orchestration Layer。我们目前采用一种极简的DSL来定义skills工作流# weather-to-email-workflow.yaml id: weather-to-email steps: - skill_id: weather-aqi-v1 input: city: {{ $.user.city | default 北京 }} unit: all output_key: weather_data - skill_id: email-send-v2 input: to: {{ $.user.recipient }} subject: 今日天气简报 body: AQI: {{ $.weather_data.aqi }}, 首要污染物: {{ $.weather_data.primary_pollutant }}这个DSL的核心思想是用skills作为积木用数据流output_key/$.weather_data作为胶水。它不引入新概念所有元素都来自已有的skills定义学习成本趋近于零。更进一步skills的终极形态可能是智能体操作系统Agent OS。在这个构想中Skills是“系统调用”提供原子能力如read_file、write_db、call_apiWorkflows是“进程”