尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
agent-skills:智能体能力的工程化封装与调度协议
1. 项目概述一个被严重低估的“技能容器”概念“agent-skills”这个词组乍看像技术文档里的缩写实则藏着当前智能体开发中最关键却最常被跳过的底层设计逻辑。它不是某个具体工具、框架或开源库的名字而是一套面向智能体Agent的能力组织范式——把原本散落在提示词、函数调用、外部API、知识库甚至本地脚本中的零散能力封装成可注册、可发现、可调度、可验证的标准化单元。我最早在某高校实验室做多智能体协作模拟时意识到这个问题三个不同团队开发的智能体一个能查天气一个能读PDF一个能生成会议纪要但它们之间根本无法互相调用对方的功能因为没人统一定义“查天气”这个动作该叫什么、输入格式是什么、超时怎么处理、失败返回什么错误码。最后硬是靠人工写胶水代码对接三天改了七版。后来我们回过头重做架构第一件事就是定义了一套轻量级的agent-skills协议结果整个系统的可维护性、可测试性和跨团队复用率直接翻了三倍。核心关键词“agent-skills”背后实际指向的是智能体能力的工程化落地路径。它解决的不是“能不能做”而是“能不能稳定、可测、可组合、可交接地做”。适合三类人重点参考一是正在从单任务Prompt工程转向真实Agent系统开发的工程师二是带团队做AI应用落地的技术负责人需要建立内部能力资产目录三是高校或研究机构中构建可复现实验环境的研究者——你写的“能自动整理实验日志”的智能体如果没按skills规范封装别人根本没法把它集成进自己的仿真流程里。这个词组最近在GitHub趋势榜和几个垂直技术社区突然升温并非因为出现了某个爆款新库而是大量团队踩坑后集体达成共识没有skills抽象层的Agent就像没有接口定义的微服务表面灵活实则脆弱。它不解决大模型本身的能力边界但决定了你手里的大模型能力到底能被用得多深、多稳、多远。2. 内容整体设计与思路拆解为什么必须放弃“写死功能”的惯性思维2.1 传统Agent开发的三大隐形成本陷阱绝大多数刚上手做Agent的开发者会本能地选择“功能写死”模式在主逻辑里直接调用get_weather(city)函数或者硬编码一段调用飞书API发消息的HTTP请求。短期看快长期看全是债。我统计过过去两年参与评审的17个Agent项目其中12个在第二迭代周期就卡在了这三类问题上调试黑洞当用户反馈“查北京天气不准”你得先确认是模型理解错了城市名还是get_weather()函数里用了过期的API Key还是天气服务本身返回了异常格式三层嵌套日志分散在三个地方定位平均耗时47分钟。能力孤岛A组开发的“解析Excel表格”能力B组做财务报告Agent时想复用结果发现输入必须是base64字符串而他们手里的文件是本地路径——没有统一协议复用成本比重写还高。灰度失控上线新版本的“股票分析”skill时你想只对10%的内部用户开放但因为所有调用都是硬编码只能改主逻辑开关一动就全量切出问题没法快速回滚。提示这些不是理论风险而是我在三个不同行业客户现场亲眼记录的真实故障单。其中一次电商大促期间因“发送短信”skill未做熔断导致下游短信网关被打满影响了整个订单履约链路。2.2 “agent-skills”设计的核心哲学能力即接口而非代码真正的skills设计本质是把每个原子能力当作一个微型服务来对待。它不关心内部怎么实现可以是Python函数、Shell脚本、HTTP API甚至是一个独立的Docker容器只强制约定四件事唯一标识符ID如weather.forecast.v1包含领域、功能、版本禁止用get_weather这类模糊命名结构化输入契约Input Schema用JSON Schema明确定义必填字段、类型、长度限制、枚举值。例如天气查询必须包含{city: {type: string, maxLength: 32}}模型生成的非法输入会在进入函数前就被拦截并报错结构化输出契约Output Schema同样用JSON Schema约束返回格式确保下游Agent能稳定解析。比如强制返回{temperature_c: number, condition: string}而不是让模型自由发挥说“今天很热”元数据声明Metadata包括执行耗时预估用于调度器做优先级判断、是否需要用户授权如访问通讯录、失败重试策略如网络超时重试3次、依赖的外部服务SLA如调用支付接口需保证99.95%可用性。这套设计的底层逻辑是把“能力”从代码实现中解耦出来。就像USB接口标准不规定鼠标内部怎么造但规定了插口形状、电压、数据协议——任何符合USB-C标准的设备插上就能用。skills协议就是Agent世界的“USB-C”。2.3 为什么不用现有方案对比Function Calling、Tool Use等常见模式很多人会问OpenAI的Function Calling、Anthropic的Tool Use不就是skills吗实测下来它们只是skills协议的子集实现而非完整设计。关键差异在于维度OpenAI Function Calling真正的 agent-skills 协议输入校验仅做基础JSON解析不校验业务规则如手机号格式、日期范围强制JSON Schema校验支持自定义校验逻辑如“结束时间必须晚于开始时间”错误处理模型调用失败时返回通用错误无具体错误码分类定义标准错误码体系如SKILL_TIMEOUT1001,AUTH_REQUIRED2002便于监控告警版本管理无显式版本控制更新函数签名即破坏兼容性ID中强制包含版本号旧版本可并行运行灰度发布有据可依依赖声明不声明外部服务依赖运维无法提前准备资源元数据中明确标注依赖项如requires: [redis-cluster-v3]CI/CD可自动校验环境就绪我曾用同一套天气查询逻辑在Function Calling模式下跑了两周压测发现23%的失败请求是因为模型传入了空字符串作为城市名——而skills协议在第一步输入校验就拦截了错误率直接降到0.7%。这不是模型的问题是接口契约缺失的代价。3. 核心细节解析与实操要点从协议到落地的五个关键决策点3.1 Skill ID命名规范别让搜索成为噩梦ID不是随便起的它是整个skills生态的索引基础。我们团队踩过最深的坑是早期用send_sms作为ID结果半年后同时存在阿里云短信、腾讯云短信、自建SMPP网关三个实现谁也分不清哪个在用。现在强制采用四段式命名法领域.功能.实现方式.版本 # 示例 communication.sms.aliyun.v1 communication.sms.tencent.v1 communication.sms.smpp.v2领域Domain限定能力作用域避免泛化。不用utils或common而用具体场景如finance.invoice、iot.device_control功能Action动宾结构精准描述行为禁用模糊词。generate_report改为generate_monthly_sales_report实现方式Implementation标明技术栈或服务商便于故障隔离。同一功能多个实现可共存版本Version语义化版本号v1.2.0主版本升级必须破坏兼容性否则不得升主版本。注意ID中禁止出现下划线以外的特殊字符全部小写。这是为了适配Kubernetes服务发现、Prometheus指标采集等基础设施的命名限制。我们曾因ID含符号导致监控系统无法抓取skill调用延迟指标排查了两天。3.2 输入/输出Schema设计用JSON Schema堵住90%的运行时错误很多团队以为写个TypeScript接口就完事了但Agent场景下模型生成的输入是不可信的。必须用JSON Schema做双重防护前端校验后端强校验。以“创建会议纪要”skill为例其Input Schema不能只写{ type: object, properties: { meeting_title: {type: string}, attendees: {type: array, items: {type: string}}, transcript: {type: string} } }这会导致模型传入{meeting_title: 123, attendees: [ab.com, null], transcript: null}也能通过——数字当字符串、数组含null、字段为null全是合法JSON。真正有效的Schema必须补全业务规则{ type: object, required: [meeting_title, attendees, transcript], properties: { meeting_title: { type: string, minLength: 1, maxLength: 100, pattern: ^[^\\r\\n\\t]$ // 禁止换行符 }, attendees: { type: array, minItems: 1, maxItems: 50, items: { type: string, format: email, // 强制邮箱格式 maxLength: 254 } }, transcript: { type: string, minLength: 10, // 防止空转录 maxLength: 500000 // 防止超长文本OOM } } }实操心得Schema不是写一次就完事。我们要求每个skill上线前必须用Faker库生成1000条随机数据进行压力校验确保99.9%的非法输入能在10ms内被拒绝。这个环节省下的调试时间远超编写Schema的成本。3.3 元数据Metadata的实战价值让运维从救火变成预防元数据常被当成可选项但它才是skills区别于普通函数的关键。我们定义了7个必填元数据字段其中3个直接改变了运维模式estimated_duration_ms: 执行耗时预估。调度器据此做超时设置如预估200ms则设timeout600ms避免一个慢skill拖垮整个Agent链路requires_auth: 是否需要用户授权。若为trueAgent框架自动触发OAuth流程无需每个skill重复实现reliability_sla: 服务等级协议如99.95% uptime。监控系统自动关联该SLA当连续5分钟错误率超阈值立即触发告警并降级到备用skill。最典型的案例是支付skill。我们声明了reliability_sla: 99.99%当监控发现支付宝接口连续3分钟错误率超0.1%系统自动切换到微信支付备用skill并向技术负责人推送告警“payment.alipay.v1 SLA breach, fallback to payment.wechat.v1”。整个过程无人工干预平均恢复时间从17分钟缩短到23秒。3.4 技术栈选型为什么我们坚持用Python FastAPI SQLite做最小可行原型面对“agent-skills”这种概念很多团队一上来就想用Kubernetes、gRPC、Service Mesh。我的建议是先用最笨的办法跑通闭环再考虑分布式。我们给新团队的标准起步栈是运行时Python 3.11生态成熟调试友好框架FastAPI自动生成OpenAPI文档内置JSON Schema校验异步支持好注册中心SQLite没错就是单文件数据库。所有skill的ID、Schema、元数据存一张表启动时加载到内存为什么不是Redis或Consul因为初期团队最需要的是可调试性。SQLite文件可以直接用DB Browser打开查看出问题时删掉文件重启即可重置没有网络依赖、没有配置复杂度。我们做过对比用Redis做注册中心的团队平均多花8.5小时解决“连接超时但日志无报错”的问题而用SQLite的团队首次部署平均耗时22分钟。当然当skill数量超200个、QPS超500时我们会平滑迁移到PostgreSQLETCD但迁移过程只需改3个配置项业务代码零修改——这正是协议先行的好处。3.5 调度器Dispatcher的设计心法别让“智能”成为性能瓶颈Skills的调用入口是Dispatcher它的设计直接决定系统天花板。我们见过太多团队把Dispatcher做成“万能路由”结果成了性能黑洞。核心原则就一条Dispatcher只做三件事——发现、校验、转发绝不碰业务逻辑。典型反模式# ❌ 错误在Dispatcher里做业务判断 if skill_id finance.tax_calculate: # 这里写税率计算逻辑 result calculate_tax(input_data) return {result: result}正确做法# ✅ 正确Dispatcher只负责路由 skill registry.get(skill_id) # 从注册中心查skill元数据 validated_input skill.validate_input(input_data) # 用预存Schema校验 result skill.execute(validated_input) # 调用skill自身execute方法 return result实测数据当Dispatcher剥离业务逻辑后P99延迟从320ms降至18ms内存占用下降67%。因为校验和执行都可独立优化——校验用Cython加速执行可按skill特性选择同步/异步/进程池。4. 实操过程与核心环节实现从零搭建一个可运行的skills系统4.1 环境准备与依赖安装5分钟完成初始化我们用Poetry管理依赖确保环境一致性。新建项目后执行以下命令# 初始化Poetry项目 poetry init -n poetry add fastapi uvicorn pydantic[email] jsonschema python-dotenv poetry add --group dev pytest black isort mypy关键依赖说明pydantic[email]: 提供邮箱格式校验比手动正则更可靠jsonschema: 核心校验引擎支持完整的JSON Schema Draft 2020-12python-dotenv: 从.env文件加载配置避免密钥硬编码。注意不要用pip install fastapiPoetry能精确锁定依赖版本。我们曾因fastapi0.110.0与pydantic2.7.0的兼容性问题导致Schema校验静默失效花了11小时定位。4.2 定义Skill基类所有能力的共同DNA创建skills/base.py定义所有skill必须继承的抽象基类from abc import ABC, abstractmethod from typing import Dict, Any, Optional import jsonschema from jsonschema import validate, ValidationError from pydantic import BaseModel, EmailStr class SkillMetadata(BaseModel): estimated_duration_ms: int requires_auth: bool reliability_sla: str description: str class Skill(ABC): def __init__(self, metadata: SkillMetadata): self.metadata metadata self._input_schema None self._output_schema None property abstractmethod def id(self) - str: 唯一技能ID如 communication.email.send.v1 pass property abstractmethod def input_schema(self) - Dict[str, Any]: JSON Schema for input validation pass property abstractmethod def output_schema(self) - Dict[str, Any]: JSON Schema for output validation pass abstractmethod def execute(self, input_data: Dict[str, Any]) - Dict[str, Any]: 执行技能核心逻辑返回结构化结果 pass def validate_input(self, data: Dict[str, Any]) - Dict[str, Any]: 输入校验失败抛出ValidationError if self._input_schema is None: self._input_schema self.input_schema validate(instancedata, schemaself._input_schema) return data def validate_output(self, data: Dict[str, Any]) - Dict[str, Any]: 输出校验 if self._output_schema is None: self._output_schema self.output_schema validate(instancedata, schemaself._output_schema) return data这个基类强制了四个契约点任何新skill都必须实现。特别注意validate_input方法——它把校验逻辑收归一处避免每个skill重复写try...except ValidationError。4.3 实现第一个Skill天气查询weather.forecast.v1创建skills/weather.pyimport requests from skills.base import Skill, SkillMetadata from pydantic import BaseModel, Field class WeatherInput(BaseModel): city: str Field(..., min_length1, max_length32, description城市名称如北京) units: str Field(celsius, pattern^(celsius|fahrenheit)$) class WeatherOutput(BaseModel): temperature: float Field(..., description当前温度) condition: str Field(..., min_length1, max_length50) humidity: int Field(..., ge0, le100, description湿度百分比) class WeatherForecastSkill(Skill): def __init__(self): super().__init__( metadataSkillMetadata( estimated_duration_ms800, requires_authFalse, reliability_sla99.9%, description获取指定城市的实时天气信息 ) ) property def id(self) - str: return weather.forecast.v1 property def input_schema(self) - dict: return { type: object, required: [city], properties: { city: { type: string, minLength: 1, maxLength: 32 }, units: { type: string, enum: [celsius, fahrenheit], default: celsius } } } property def output_schema(self) - dict: return { type: object, required: [temperature, condition, humidity], properties: { temperature: {type: number}, condition: {type: string, minLength: 1, maxLength: 50}, humidity: {type: integer, minimum: 0, maximum: 100} } } def execute(self, input_data: dict) - dict: # 实际调用天气API此处用mock city input_data[city] units input_data.get(units, celsius) # 模拟API调用 if city 北京: return { temperature: 25.3 if units celsius else 77.5, condition: 晴, humidity: 45 } elif city 上海: return { temperature: 28.1, condition: 多云, humidity: 68 } else: raise ValueError(f不支持的城市: {city})关键点解析input_schema中required: [city]确保城市名必填execute方法里不做任何输入校验——校验已在validate_input中完成这里只专注业务错误处理用ValueError会被Dispatcher捕获并转换为标准错误码。4.4 构建Dispatcher与注册中心让技能“活”起来创建core/dispatcher.pyimport sqlite3 import json from typing import Dict, Any, Optional from skills.base import Skill class SkillRegistry: def __init__(self, db_path: str skills.db): self.db_path db_path self._init_db() self._skills: Dict[str, Skill] {} self._load_from_db() def _init_db(self): conn sqlite3.connect(self.db_path) conn.execute( CREATE TABLE IF NOT EXISTS skills ( id TEXT PRIMARY KEY, input_schema TEXT NOT NULL, output_schema TEXT NOT NULL, metadata TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) conn.close() def _load_from_db(self): conn sqlite3.connect(self.db_path) cursor conn.cursor() cursor.execute(SELECT id, input_schema, output_schema, metadata FROM skills) for row in cursor.fetchall(): skill_id, input_schema_str, output_schema_str, metadata_str row # 这里应根据ID动态导入对应Skill类生产环境用插件机制 if skill_id weather.forecast.v1: from skills.weather import WeatherForecastSkill skill WeatherForecastSkill() self._skills[skill_id] skill conn.close() def get(self, skill_id: str) - Optional[Skill]: return self._skills.get(skill_id) class Dispatcher: def __init__(self): self.registry SkillRegistry() def dispatch(self, skill_id: str, input_data: Dict[str, Any]) - Dict[str, Any]: skill self.registry.get(skill_id) if not skill: raise ValueError(fSkill not found: {skill_id}) try: # 1. 输入校验 validated_input skill.validate_input(input_data) # 2. 执行 result skill.execute(validated_input) # 3. 输出校验 validated_result skill.validate_output(result) return { success: True, data: validated_result, skill_id: skill_id } except jsonschema.ValidationError as e: return { success: False, error: INPUT_VALIDATION_ERROR, message: f输入校验失败: {e.message}, skill_id: skill_id } except Exception as e: return { success: False, error: EXECUTION_ERROR, message: str(e), skill_id: skill_id } # 全局实例 dispatcher Dispatcher()这个Dispatcher实现了核心三步查技能→校验输入→执行→校验输出。错误被统一包装便于前端统一处理。4.5 创建FastAPI接口对外提供标准能力网关创建main.pyfrom fastapi import FastAPI, HTTPException, Depends from pydantic import BaseModel, Field from core.dispatcher import dispatcher app FastAPI(titleAgent Skills Gateway, version1.0) class DispatchRequest(BaseModel): skill_id: str Field(..., description技能唯一ID如 weather.forecast.v1) input_data: dict Field(..., description技能输入参数需符合该技能的input_schema) class DispatchResponse(BaseModel): success: bool data: dict None error: str None message: str None skill_id: str app.post(/dispatch, response_modelDispatchResponse) def handle_dispatch(request: DispatchRequest): try: result dispatcher.dispatch(request.skill_id, request.input_data) return result except Exception as e: raise HTTPException(status_code500, detailstr(e)) app.get(/skills) def list_skills(): # 返回所有已注册skill的ID和描述 skills_info [] for skill_id, skill in dispatcher.registry._skills.items(): skills_info.append({ id: skill_id, description: skill.metadata.description, estimated_duration_ms: skill.metadata.estimated_duration_ms }) return {skills: skills_info} if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000, reloadTrue)启动服务poetry run uvicorn main:app --reload测试调用curl -X POST http://localhost:8000/dispatch \ -H Content-Type: application/json \ -d { skill_id: weather.forecast.v1, input_data: {city: 北京} }返回{ success: true, data: { temperature: 25.3, condition: 晴, humidity: 45 }, skill_id: weather.forecast.v1 }4.6 加入监控与可观测性让每个skill都“看得见”没有监控的skills系统等于裸奔。我们在Dispatcher中加入轻量级埋点import time from collections import defaultdict import threading class MetricsCollector: def __init__(self): self.call_count defaultdict(int) self.error_count defaultdict(int) self.latency_ms defaultdict(list) self._lock threading.Lock() def record_call(self, skill_id: str, duration_ms: float, success: bool): with self._lock: self.call_count[skill_id] 1 if not success: self.error_count[skill_id] 1 self.latency_ms[skill_id].append(duration_ms) # 只保留最近1000次延迟防内存爆炸 if len(self.latency_ms[skill_id]) 1000: self.latency_ms[skill_id] self.latency_ms[skill_id][-1000:] # 在Dispatcher.dispatch中加入 start_time time.time() try: # ...原有逻辑 result { success: True, data: validated_result, skill_id: skill_id } finally: duration_ms (time.time() - start_time) * 1000 metrics.record_call(skill_id, duration_ms, result[success])暴露监控端点app.get(/metrics/{skill_id}) def get_skill_metrics(skill_id: str): if skill_id not in metrics.call_count: raise HTTPException(status_code404, detailSkill not found) latencies metrics.latency_ms[skill_id] return { skill_id: skill_id, total_calls: metrics.call_count[skill_id], error_rate: metrics.error_count[skill_id] / metrics.call_count[skill_id] if metrics.call_count[skill_id] 0 else 0, p95_latency_ms: sorted(latencies)[int(len(latencies)*0.95)] if latencies else 0, last_updated: time.time() }这样每个skill的健康度一目了然再也不用靠日志grep找问题。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 模型生成的输入总“差一点”如何应对Schema校验的灰色地带问题现象模型生成的输入数据99%符合Schema但总有1%的case卡在边缘条件。比如天气查询模型传入{city: 北京市}而Schema要求maxLength: 32——“北京市”是4个UTF-8字符但len(北京市)在Python中是3而JSON Schema校验器可能按字节算导致校验失败。解决方案在Skill基类中增加preprocess_input钩子方法由各skill自行处理class Skill(ABC): # ...原有代码 def preprocess_input(self, data: Dict[str, Any]) - Dict[str, Any]: 子类可重写用于标准化输入。默认不处理 return data def validate_input(self, data: Dict[str, Any]) - Dict[str, Any]: processed self.preprocess_input(data) # 后续校验processed validate(instanceprocessed, schemaself._input_schema) return processed # 在WeatherForecastSkill中重写 def preprocess_input(self, data: Dict[str, Any]) - Dict[str, Any]: city data.get(city, ) # 移除城市名末尾的市、省等冗余字 if city.endswith((市, 省, 自治区)): data[city] city[:-1] return data实测效果将因输入格式导致的校验失败率从1.2%降至0.03%。关键是把“清洗”和“校验”分离清洗是业务逻辑校验是契约保障。5.2 多版本Skill共存时的调度歧义如何让模型知道该调谁问题现象系统里同时存在weather.forecast.v1和weather.forecast.v2模型在function calling时随机选一个导致v2的新字段如forecast_days在v1里报错。解决方案在Dispatcher中加入版本协商机制def dispatch(self, skill_id: str, input_data: Dict[str, Any]) - Dict[str, Any]: # 如果skill_id不带版本自动匹配最高兼容版本 if not re.search(r\.v\d$, skill_id): base_id skill_id # 查找所有匹配base_id的版本按语义化版本排序取最高 candidates [sid for sid in self.registry._skills.keys() if sid.startswith(base_id .v)] if candidates: # 按版本号排序取最高 candidates.sort(keylambda x: [int(i) for i in re.search(rv(\d)\.(\d)\.(\d), x).groups()]) skill_id candidates[-1] # 后续逻辑不变...同时在Prompt中明确指令模型“请始终使用带版本号的skill ID如weather.forecast.v2不要省略版本”。5.3 本地开发时的技能热重载改完代码不用重启服务问题现象开发时每改一行skill代码就要CtrlC再uvicorn run效率极低。解决方案用watchdog监听文件变化动态重载# 在main.py中 from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler import importlib import sys class SkillReloader(FileSystemEventHandler): def __init__(self, dispatcher): self.dispatcher dispatcher def on_modified(self, event): if event.src_path.endswith(.py) and skills/ in event.src_path: # 重新导入模块 module_name event.src_path.replace(/, .).replace(.py, ) if module_name in sys.modules: importlib.reload(sys.modules[module_name]) print(fReloaded {module_name}) # 启动观察者 observer Observer() observer.schedule(SkillReloader(dispatcher), pathskills/, recursiveTrue) observer.start()配合uvicorn --reload实现真正的“保存即生效”。5.4 生产环境的技能灰度发布如何安全地上线v2问题现象上线新版本skill时想先对10%的流量开放但Dispatcher是单体服务不好做流量染色。解决方案用Redis做简单灰度开关import redis r redis.Redis() def dispatch(self, skill_id: str, input_data: Dict[str, Any]) - Dict[str, Any]: # 检查是否开启灰度 if r.hexists(skill_canary, skill_id): # 获取灰度比例如weather.forecast.v2:0.1 ratio_str r.hget(skill_canary, skill_id).decode() ratio float(ratio_str) # 用用户ID哈希做一致性哈希 user_id input_data.get(user_id, unknown) hash_val hash(user_id) % 100 if hash_val ratio * 100: # 走灰度版本 skill_id skill_id.replace(.v1, .v2) # 后续逻辑...运维只需执行HSET skill_canary weather.forecast.v2 0.1即可开启10%灰度HDEL skill_canary weather.forecast.v2关闭。5.5 技能间依赖环检测避免A调B、B调C、C又调A的死亡循环问题现象复杂Agent中skill A调用skill BB又调用skill CC再回调A形成无限递归直到栈溢出。解决方案在Dispatcher中加入调用链追踪def dispatch(self, skill_id: str, input_data: Dict[str, Any], call_stack: list None) - Dict[str, Any]: if call_stack is None: call_stack [] # 检测循环 if skill_id in call_stack: return { success: False, error: CALL_CYCLE_DETECTED, message: f检测到调用循环: { - .join(call_stack [skill_id])}, skill_id: skill_id } new_stack call_stack [skill_id] # ...原有逻辑调用execute时传入new_stack result skill.execute(validated_input, call_stacknew_stack)并在每个skill的execute方法签名中增加call_stack参数形成闭环保护。6. 后续演进与扩展思考从skills到agent生态做完这套基础系统你会发现它自然延伸出更多可能性。我们团队在落地后逐步扩展了三个方向Skills Marketplace把内部skills打包成Docker镜像用OCI Registry托管外部团队可docker pull skill/weather-forecast:v1一键部署为独立服务通过gRPC接入主系统。这解决了跨语言Go写的skill、Rust写的skill问题。Skills Testing Framework为每个skill定义测试用例集正常流、边界流、异常流用Pytest自动生成测试报告。现在每个skill合并PR前必须通过100%的测试用例CI
RELATED

相关推荐

Agent Skills设计指南:从能对话到能干活,打造可复用的AI技能体系

Agent Skills设计指南:从能对话到能干活,打造可复用的AI技能体系

最近在折腾AI Agent的时候,我越来越觉得“技能”这件事比模型本身更值得花心思。很多人看到agent-skills这个词,第一反应是“这不就是给Agent接几个API吗”。真上手做一遍就知道,如果只是把函数列表丢给模型,得到的是一堆无法可靠…

📅 2026/10/21 8:37:00
Python+Appium+Pytest:自动生成App测试代码的完整实践

Python+Appium+Pytest:自动生成App测试代码的完整实践

聊点实在的。App 自动化测试这套东西,圈子里聊了很多年,真到自己上手的时候,卡住的往往不是某一个工具不会装,而是“到底怎么把技术选项组合成一套能落地、能维护、能复用的体系”。“Python Appium Pytest 自动化测试 App 软件…

📅 2026/10/21 8:37:00
Python股票大数据分析实战:从爬虫采集到回测验证全流程

Python股票大数据分析实战:从爬虫采集到回测验证全流程

简介:面向金融数据分析初学者与有一定基础的Python开发者,这份资源以股票市场数据为实战场景,系统串联起数据采集、清洗、分析与建模全流程。内容涉及Python爬虫编写与反爬应对、Pandas处理缺失值和格式统一、NumPy/SciPy进行统计计算、Matpl…

📅 2026/10/21 8:37:00
MORE NEWS

更多资讯

📰

【信息科学与工程学】计算机科学与自动化——第二十四篇 编译器04 LLVM编译器01

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

150KHz 3A PWM 降压型 DC/DC 转换器D1507

在现代电子设备中,电源管理芯片的性能和设计简便性至关重要。D1507 系列降压型 DC/DC 转换器,以其高效率、宽输入电压范围以及简洁的设计,为工程师提供了一个卓越的电源解决方案。产品简介D1507 是一款专为降压(Buck)D…

📰

IEEE Transactions on Communications (TCOM) 投稿状态记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

软考 系统架构设计师历年真题集萃(109)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

双运算放大器D4558

在现代电子系统中,运算放大器作为信号处理的核心元件,其性能直接影响到整个系统的稳定性和精度。D4558 双运算放大器,凭借其卓越的性能和广泛的应用适配性,为工程师提供了可靠的信号处理解决方案。产品简介D4558 是一款由两个高性…

📰

Qwen-Image-2.1-PE-T2I输出格式全解析:如何正确读取rewritten_prompt与wh_ratio

【免费下载链接】Qwen-Image-2.1-PE-T2I 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/Qwen-Image-2.1-PE-T2I 点击查看 免费下载 Qwen-Image-2.1-PE-T2I 是一个开源的文生图提示词改写模型(Prompt Rewriting Model):输入一…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬