恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Agent技能系统设计:从函数封装到可编排的AI能力底座

  • 首页
  • 资讯中心
  • /
  • Agent技能系统设计:从函数封装到可编排的AI能力底座

相关资讯

all-MiniLM-L6-v2:句子向量模型原理、落地与避坑指南 2026/10/14 9:23:30
PyTorch ImageFolder实战:从目录结构到数据加载的避坑指南 2026/10/14 9:18:30
平板型永磁同步直线电机Maxwell仿真:反电动势、推力与磁阻力分离 2026/10/14 9:18:30

最新资讯

四层电梯PLC项目完整交付:程序逻辑、HMI联动与电气图纸复盘
OpenMontage照片马赛克制作:原理拆解与参数调优完整指南
OpenMontage实战:开源自动剪辑工具如何提升视频制作效率
TextING模型:轻量图建模实现归纳式文本分类
Electron + Vue 桌面应用项目结构详解与实战
数字序列模式识别与应用技术解析

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Agent技能系统设计:从函数封装到可编排的AI能力底座

发布时间:2026/10/14 9:23:30
Agent技能系统设计:从函数封装到可编排的AI能力底座 做Agent开发的朋友应该都有这个感受模型聪明归聪明但如果没有一套合适的手脚去联动外部系统它能做的事非常有限。我最近在重构自己的Agent项目时把散落的函数调用、外部服务和提示词模板统一收拢成了一个叫 agent-skills 的技能系统。说白了就是把Agent会干什么这件事显式地建模出来每个技能有名字、有语义描述、有参数规范、有可执行的实现再由一个注册中心统一管理。这套设计帮我解决了之前功能越多、调度越乱的窘境也让模型选工具、传参数、走流程的稳定性明显上了一个台阶。如果你的项目也卡在工具一多就乱或者Agent不会正确调用已有能力这层这篇内容应该对你有用。我会把整个 agent-skills 项目的设计思路、核心代码、编排方案、以及我在落地过程中踩过的坑全部拆开讲一遍。内容偏实践向适合已经跑过简单Agent流程、想进一步把工程化能力补齐的开发者。1. 先把 agent-skills 的整体设计思路讲清楚1.1 为什么功能一多Agent就开始犯晕很多Agent项目早期只有一两个功能比如查个数据库、发个通知。这种规模下你直接在提示词里告诉模型当你需要查数据库时调用xxx函数就够了逻辑简单直接。但功能一旦超过五个问题就接踵而至提示词越来越长模型开始抓不住重点工具间的边界模糊模型经常选错函数参数格式稍微复杂一点模型就开始自由发挥给出的参数跟函数签名对不上。我最早遇到的典型场景是项目里同时有查询订单状态和查询物流状态两个接口模型经常在三轮对话之内就把两者混淆。追问提示词、在函数名里加修饰词折腾了很久都收效甚微。后来我发现问题的根源不在于模型笨而在于我提供给模型的是一个个裸函数没有任何结构化描述告诉它这些函数分别适用于什么场景、参数的含义是什么、返回值长什么样。agent-skills 的思路就是把裸函数升级成带技能卡片的可执行单元。你可以把技能理解成一个标准化封装层外层是模型能读懂的自然语言描述和参数规范内层才是真正干活的函数实现。模型看到的不再是func_a(openid)这种代码而是一份清晰的需求说明它只需要按照说明提供参数剩下的交给技能系统去执行。1.2 技能系统的三层架构拆解我在 agent-skills 项目里把整体拆成了三层分别负责面向模型的语义层、面向调度的管理层和面向业务的功能层。第一层是技能注册中心负责登记所有技能的名称、描述、参数格式、标签分类和执行入口。它的核心价值是给模型提供一个菜单——每次对话开始前系统会把这份菜单作为可调用能力的列表交给模型菜单描述得清不清楚直接决定模型选得准不准。第二层是技能执行实体每个技能内部包含一个处理器和一套执行规则。处理器接收模型传入的参数执行实际业务逻辑执行规则负责参数校验、超时控制、异常捕获和返回结果格式化。没有这套机制技能执行时哪怕是随手一个参数类型错误都会让整个Agent流程当场崩溃。第三层是编排器负责把多个技能串联起来。真实业务很少是调用一个技能就结束更多是查数据-处理数据-写结果这种多步流程。编排器维护技能之间的输入输出关系让上一个技能的结果能自动作为下一个技能的输入参数同时处理中途失败的回退逻辑。这三层各司其职注册中心解决模型能否选对执行实体解决技能能否跑稳编排器解决多个技能能否协作。整个 agent-skills 项目的主干就是围绕这三层展开的。1.3 为什么不做成大而全的框架而是坚持轻量封装这个项目我一开始是有机会做成一个功能繁重的框架的——比如内置几十个通用技能、封装完整的对话管理和长短期记忆模块。但后来我主动砍掉了这些需求只保留技能注册、执行和编排的最小闭环。原因很简单Agent项目迭代速度极快今天用的提示词策略、底层模型、业务流程两周以后可能就完全换了样子。如果技能系统耦合了太多业务假设每次业务调整都要连带改动框架本身那这套系统很快就变成了技术债的源头。轻量封装意味着我把技能的标准格式定好把注册和调用的通信路径打通但不干涉具体技能内部的实现方式。业务团队想用一个Python函数、一个Shell命令还是一个HTTP请求来做技能执行体完全由他们自己决定。实际证明这个取向是对的。我后来在不同项目里复用了这套技能层底层模型从一家换到另一家技能定义几乎不用改动只有编排器的提示词部分需要微调。这就是只定协议、不做实现的红利。2. 技能建模把一个技能变成一段可被Agent理解的结构化描述2.1 技能描述的核心字段与设计要点在 agent-skills 里一个技能本质上是一个包含四类信息的结构化对象。第一个字段是技能名称要求短且唯一模型会直接拿这个名字去做调用决策所以不能起得像内部函数名好的命名是query_order_status这种一眼能看出用途的格式而不是qos_handle_v2这种缩写。第二个字段是自然语言描述这是给模型看的核心信息。描述要覆盖三个维度技能是干什么的、在什么场景下用、什么时候不要用。我用反面案例解释一下如果你只写用于查询订单模型面对用户想查一个月前买的手机的物流走到哪了这句话时依然可能选错。但如果写成查询订单状态适用于用户咨询订单当前的处理进度注意如果要查商品配送过程中的实时位置请使用查物流技能而非本技能模型的判断准确率会明显提升。说白了一份好的技能描述就是在帮模型划清边界感。第三个字段是参数规范我直接用JSON Schema格式来描述。JSON Schema的好处是既支持必填选填、字段说明、格式约束也方便在运行时做自动化校验。这里最容易被忽略的是参数示例。不要只写order_id: string而是给出一个真实示例order_id: AG-20250108-001模型对具体例子的理解能力远强于抽象类型描述。第四个字段是执行入口也就是真正的处理函数引用。在Python里就是一个可调用对象可以是普通函数、类方法也可以是包装了HTTP调用或Shell命令的闭包。2.2 技能分类基础技能、复合技能与应用技能随着技能数量变多我逐渐发现所有技能往一个注册中心里塞会劣化模型的选择效果——清单太长反而提高了选错概率。于是我把技能分成了三类管理。基础技能是最小粒度的能力单元比如发HTTP请求、读文件、执行计算、查数据库。这类技能通常只有一个职责不依赖其他技能是技能体系的地基。复合技能是由两个及以上基础技能按固定顺序组合而成的流程比如读取用户上传的表格并统计汇总就可以拆成读文件技能加数据统计技能。复合技能的好处是让模型不需要理解底层拆解逻辑只需要面对一个高层的业务动作。应用技能是面向特定业务场景的组合单元通常包含业务规则和状态判断比如自动生成对账单并发送邮件这种。它可能内部调用了十几个基础技能但对外只暴露一个口子。这个三级分类对模型调度和权限控制都很有帮助面向广泛开放式任务时基础技能清单就够了面向特定业务时给模型暴露复合技能和应用技能能减少无关干扰。2.3 注册中心实现从技能登记到运行时发现注册中心的实现并不复杂但有一个关键设计必须支持按场景过滤技能列表。先看基础的注册与发现实现。from dataclasses import dataclass, field from typing import Any, Callable, Dict, List, Optional dataclass class Skill: name: str description: str parameters: dict handler: Callable[..., Any] tags: List[str] field(default_factorylist) timeout: float 5.0 class SkillRegistry: def __init__(self): self._skills: Dict[str, Skill] {} def register(self, skill: Skill, replace: bool False): if skill.name in self._skills and not replace: raise KeyError(f技能 {skill.name} 已注册) self._skills[skill.name] skill def get(self, name: str) - Skill: if name not in self._skills: raise KeyError(f技能 {name} 不存在) return self._skills[name] def list_skills(self, tags: Optional[List[str]] None) - List[Dict]: result [] for skill in self._skills.values(): if tags and not set(tags).issubset(set(skill.tags)): continue result.append({ name: skill.name, description: skill.description, parameters: skill.parameters, }) return result这段代码的核心价值是模型拿到的技能清单不再是全量列表而是按当前场景过滤后的子集。比如在订单售后场景里只暴露订单类和退款类技能在用户资料维护场景里只暴露信息查询和修改类技能。过滤之后技能数量减少了选择准确率自然上升。有一个容易被忽略的细节技能注册时的依赖关系。如果某个技能内部依赖了另一个技能而没有在描述里声明编排器运行时才发现问题排查成本就会变高。我的做法是给复合技能增加一个依赖字段注册时自动检查依赖技能是否已存在。2.4 为什么不用裸函数而要做这层封装我在迭代中反复确认过直接从代码里导入函数给模型省事倒是省事但代价非常隐蔽。裸函数缺失了语义上下文模型不知道这个函数适合解决什么任务裸函数缺失了参数约束模型不知道该传什么、参数格式是什么裸函数还缺失了统一调用边界你很难在每次调用前做权限校验、在调用结束后做日志记录。技能封装相当于给每个能力加了一道翻译桥。它把程序员能看懂的代码签名翻译成模型能看懂的意图描述。这个翻译的精细程度直接等同于Agent能力的可用性。有一次我只是把技能描述里的一个关键词微调了一下模型对这个技能的命中率就从百分之三十提高到百分之九十。这充分说明封装层不是形式主义而是整个系统里性价比最高的投入。3. 从零实现一个 agent-skills 技能模块3.1 准备运行环境与项目结构我建议直接用Python 3.10起步核心依赖我刻意只用了标准库里的 dataclasses、typing 和 json这样任何人都能直接跑起来。如果你要部署成HTTP服务再加一个轻量Web框架即可。agent-skills/ ├── registry.py # 注册中心 ├── skill.py # 技能基础抽象 ├── skills/ # 具体技能实现 │ ├── file_info.py │ └── csv_stat.py ├── composer.py # 编排器 └── agent_loop.py # 与模型交互的主流程这个结构有两个好处技能与主流程解耦新增技能时完全不碰核心代码技能之间彼此独立方便单独测试。3.2 编写一个真实技能文件信息统计我拿一个非常实用的文件信息统计技能来演示完整过程。这个技能接收一个目录路径返回该目录下所有文件的名称、大小和最近修改时间。它很适合作为Agent在处理用户上传文件前的能力前置。import os from pathlib import Path from skill import Skill def gather_file_info(directory: str) - dict: base Path(directory).resolve() if not base.exists() or not base.is_dir(): raise FileNotFoundError(f目录不存在: {directory}) files [] total_size 0 for entry in base.iterdir(): if entry.is_file(): stat entry.stat() files.append({ name: entry.name, size_kb: round(stat.st_size / 1024, 2), mtime: int(stat.st_mtime), }) total_size stat.st_size files.sort(keylambda x: x[name]) return { directory: str(base), file_count: len(files), total_size_kb: round(total_size / 1024, 2), files: files[:100] } file_info_skill Skill( namegather_file_info, description( 统计指定目录下的文件信息返回文件名、大小和修改时间。 适用于用户需要了解某个文件夹里有什么文件、文件占用了多少空间。 注意该技能不递归子目录也不读取文件内容。 ), parameters{ type: object, properties: { directory: { type: string, description: 要统计的目录绝对路径例如 /home/user/data } }, required: [directory] }, handlergather_file_info, tags[file, local], timeout10.0 )这段代码展示的是技能定义的完整形态。参数部分用JSON Schema描述实际执行前可以用任何JSON校验库验证模型给的参数是否符合规范。返回结果我刻意加上取前100个文件的限制这是因为模型还要把技能结果读进上下文如果返回一万个文件信息上下文窗口直接就被撑爆了。这是一个在实战里非常重要、但新手很容易忽略的设计。3.3 技能注册与加载机制有了技能定义下一步是把它挂到注册中心。我建议把技能实例化动作集中到一个入口文件里而不是散落各处。# load_skills.py from registry import SkillRegistry from skills.file_info import file_info_skill from skills.csv_stat import csv_stat_skill def load_default_skills() - SkillRegistry: registry SkillRegistry() registry.register(file_info_skill) registry.register(csv_stat_skill) return registry注册时如果出现重名技能我选择直接抛异常而不是静默覆盖。这个设计源于一次真实教训某个技能在旧版本里返回的是字符串新版本改造后返回了字典但因为注册顺序写反模型一直在调用旧逻辑整整跑了两天才被日志发现。显式抛错可以把这类问题尽早暴露在开发环境里。3.4 把技能接入Agent主流程技能系统真正要服务的是Agent主流程。调用幻觉一个手写版本的Agent循环就能看清整个过程。# agent_loop.py import json from registry import SkillRegistry def run_agent_with_skills(user_request: str, registry: SkillRegistry, llm): # 第一步按场景过滤技能清单 tool_menu registry.list_skills(tags[default]) # 第二步递给模型让模型决定是否调用技能 decision llm.choose_tool( system请根据用户需求从技能清单中选择要调用的技能并生成JSON格式参数。, useruser_request, toolstool_menu ) if not decision or decision.get(name) is None: return llm.answer(user_request) # 第三步从注册中心取出技能执行体 skill registry.get(decision[name]) arguments decision.get(arguments, {}) # 第四步执行技能并捕获结果 try: result skill.execute(**arguments) except Exception as exc: return f技能执行失败{str(exc)} # 第五步把执行结果交回模型生成最终回答 return llm.answer_with_context( contextf技能 {skill.name} 返回结果{json.dumps(result, ensure_asciiFalse)}, original_requestuser_request )这里最关键的步骤是第二步要让模型学会先做计划再选技能。很多新手在第一步就把用户原话直接丢给模型调用工具结果模型看到帮我看看电脑里有什么大文件这种需求就直接凭空说出一堆不存在的文件。加上决策中必须包含技能名称和参数的约束之后模型才会老实按流程走。4. 技能的编排从一次调用到多步协作4.1 串行技能链与管道式数据传递真实业务里一个Agent任务往往需要连续调用多个技能。我以统计用户目录下最大的文件并复制到备份目录为例至少需要两步第一步是统计文件信息第二步是执行复制动作。这两个技能之间是严格的先后关系第二个技能的输入依赖第一个技能的输出。解决这个问题的方案是引入一个轻量编排器让技能的输出可以被后续技能引用。def run_skill_chain(chain, initial_args, registry): context initial_args for step in chain: skill registry.get(step[skill]) merged_args {**step.get(static_args, {}), **context} result skill.execute(**merged_args) key_name step.get(output_key, result) context[key_name] result return context这段实现里我特别设计了静态参数与动态上下文合并的机制。静态参数是编写编排配置时写死的东西比如目标备份目录动态上下文是上一个技能产出的结果比如要找出的最大文件名。合并时动态上下文会覆盖静态参数因为在前置执行结果里真实的运行数据优先级永远高于配置里的默认值。这种管道式编排最大的价值是清晰。整个链路的每一步都能从代码里直接读出来每一步的输入输出都有明确的key名出了问题你只需要看是哪一步断了。4.2 并行技能调用什么时候用、怎么合并结果并行调用是另一个常见的编排形态。当Agent需要同时获取多路信息时串行执行会把响应时间拉得很长。比如同时查询当前天气和用户所在地的空气质量这两个技能之间没有依赖关系完全可以并行执行。我用一个简单的执行器来支持并行调用。核心思路是给每个技能创建独立任务用标准库的并发工具统一等待结果。from concurrent.futures import ThreadPoolExecutor, as_completed def run_parallel(skills_args, registry, max_workers3): with ThreadPoolExecutor(max_workersmax_workers) as executor: futures {} for item in skills_args: skill registry.get(item[skill]) future executor.submit(skill.execute, **item[arguments]) futures[future] item[skill] results {} errors {} for future in as_completed(futures): name futures[future] try: results[name] future.result() except Exception as exc: errors[name] str(exc) return {results: results, errors: errors}并行执行有一个在真实项目里常见的坑并发度太高会导致下游系统过载。比如查询支付网关状态和查询用户积分余额这两个接口如果共用同一个基础服务并发一高就会出现限流。所以我一般保守地设置三到五个最大并发数而不是无脑打满。返回的errors字典也很重要某一路技能报错不应该拖垮整个任务只要主路径数据还在就可以先把部分结果交给模型兜底回答。4.3 编排失败时的降级与重试策略任何编排方案都会遇到技能执行失败的情况。我在 agent-skills 里的策略有两条能重试的先重试重试还失败的走降级路径。重试策略只适用于暂时性错误也就是网络超时、服务端过载这类问题。执行逻辑里我会把超时异常单独分类只有这类异常才触发重试。参数错误、权限不足这类确定性错误重试一百次也没用直接记录失败原因就好。降级策略则看场景如果主技能失败可以尝试调用一个能力相近的备用技能如果备用技能也没有就返回给模型一段结构化的失败信息让模型基于已有知识给用户一个保守回答。比如查天气接口挂了就提示当前无法获取实时天气数据而不是编造一个温度。这里还有一个关键心得技能执行返回信息要发给模型但是内部错误栈要不要发需要严格区分。我建议只把用户友好的错误信息交给模型详细堆栈留在日志里避免模型看到异常后产生幻觉式解释。5. 让技能系统更稳定性能、可靠性与权限边界5.1 技能调用超时与上下文长度控制技能执行最大的不稳定因素不是程序逻辑写错而是外部依赖迟迟不返回。我要求所有技能在注册时就带上预期时间注册中心统一加一个超时兜底机制默认五秒超时后立即终止并抛异常。复杂的统计类技能我会放宽到十五秒但不会更长。控制上下文长度同样重要。技能返回结果是要被模型读取并理解的这个结果越长模型处理得越慢还越容易丢失关键信息。我在设计技能返回协议时定了一条铁律任何技能返回给模型的数据必须尽量精简追求结论先行、明细可选。统计类技能默认只返回汇总数值和Top N明细需要完整明细时再额外传一个include_detail请求参数。这项设计让我在上下文长度受限的模型上也能跑通复杂技能链路。5.2 日志与可观测性设计技能系统的排错很大程度依赖日志。我在 agent-skills 项目里设计了两个维度的日志第一个维度是调用链日志记录每一次模型的技能选择请求、实际命中的技能名、参数内容、执行耗时、返回状态第二个维度是决策对比日志记录模型每次在技能清单里看到的所有选项。第二类日志尤其有价值。当你发现某个技能长期不被调用时排查方式就是去看决策对比日志里模型的思考过程往往能定位出是描述写得有歧义还是参数说明不清晰。有一次我发现模型总是把一个查天气技能和其它技能搞混翻日志后才知道是因为描述里都提到了当天这个字眼。把描述改成获取指定城市当前实时气象数据后问题立刻消失。5.3 权限校验与执行边界技能系统接入真实业务之后权限问题是绕不开的。我的处理方式是技能注册时增加一个permission_level属性执行器统一做校验。def safe_execute(skill, user_role, **kwargs): if skill.permission_level admin and user_role ! admin: raise PermissionError(当前用户无权调用该技能) return skill.execute(**kwargs)如果是给业务系统集成还可以接入更细的字段级权限。比如读用户信息技能里客服角色能看到手机号和地址运营角色只能看到用户ID和活跃时间。这个权限判断不放在技能内部而是放在执行层统一处理因为技能是可复用的直接改技能内部会污染通用逻辑。我在实际项目中曾因为权限判断散落在多个技能内部导致后面加一个审计需求时到处补代码所以这块一定要集中。6. 常见问题与排查技巧实录6.1 模型选错技能问题大多出在描述而不是模型现象是面对一个有歧义的需求模型调用了错误技能。我排查优先级第一是看两个技能的描述是否在关键场景上有盖章式的区分。比如订单查询和物流查询同时出现在默认技能列表里两者的描述都会提到订单如果没有明确物流查询用于查看配送轨迹模型就容易被带偏。6.2 参数格式对不上模型给的JSON不合法这是最频繁的报错之一。主要原因有两个参数Schema写得不规范模型不知道字段类型和格式模型本身偶尔会漏填必填字段或给出多层嵌套。我的对策是引入严格的JSON Schema校验在进入技能执行器之前先校验。import jsonschema def validate_args(skill, arguments): try: jsonschema.validate(instancearguments, schemaskill.parameters) except jsonschema.ValidationError as exc: raise ValueError(f技能 {skill.name} 参数校验失败{exc.message})同时我会把参数示例放进技能的parameters描述里模型看到order_id字段给出的示例AG-20250108-001时格式幻觉率大幅下降。6.3 技能冲突多个技能都能做同一件事当两个技能功能重叠时模型的选型就会随机化。我的处理方案是收敛功能把重复的技能合并成一个或者用tags把旧技能标记为不推荐在开放式任务中使用。一定要避免把两个几乎等价但名字不同的技能同时都挂在默认列表里这是确定性的大敌。6.4 调试经验给模型配一个技能沙盒我会建议你在开发阶段单独做一个命令行版本的Agent调试台这样测试技能时不需要走完整对话流程。核心做法是在本地启动一个最小化Agent进程手动输入一段用户请求然后输出模型选择的技能名、参数JSON、执行结果以及最终回答。这样每改造一个技能描述都能立刻看到效果不用等完整集成测试。我个人的另一条心得是对技能系统做回归测试不要只靠模型对话层面去验证。技能运行的效果最终要交给下游的数据测试比如对于文件统计类技能直接断言返回的文件数对不对、大小总和是否精确。技能层可以像普通函数一样做单元测试不要因为是给模型用的就跳过这一步。把技能函数的正确性提前锁死Agent层面的问题就能收敛到描述与选择这一个变量上排错效率会高出很多。这套 agent-skills 的项目我从零搭建、反复调整最终最明显的收益其实不是某个单点的性能提升而是一整套如何把AI能力工程化的心智模型。技能的封装让每个能力都有了清晰的入口和边界注册和编排让多个技能不再乱成一团日志和校验让每一个错误都能快速定位。如果你也在搞Agent开发我建议不用急着去抄一个现成的脚手架先把手头的三五个函数按照这个思路做一次技能化改造跑通一遍注册、调用、编排、排错的全流程之后遇到更复杂的业务往上叠加就行。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号