一、什么是Skills执行?——从函数调用到技能复用
在企业级AI Agent的开发中,Skills执行是连接“自然语言理解”与“实际业务操作”的关键桥梁。它的技术本质并不复杂:将一个完整的业务操作流程封装为可复用的技能单元,由AI根据用户意图自动匹配并执行。
理解Skills执行,可以从一个简单的类比切入。传统软件开发中,开发者将常用逻辑封装为函数,调用时传入参数、接收返回值。Skills执行沿用了这一思想,但封装的对象从“代码逻辑”升级为“业务操作序列”——一个Skill内部可能包含数据库查询、报表生成、邮件发送、状态更新等多个操作步骤,这些步骤被编排为一个整体,对外暴露统一的触发接口。
与单纯的Function Calling相比,Skills执行有三个显著区别:
| 封装粒度 | 单个API调用 | 多个操作步骤组成的完整业务流程 |
| 触发方式 | 模型决策后调用 | 自然语言意图匹配后触发 |
| 异常处理 | 调用方负责 | Skill内部自带异常处理策略 |
| 复用性 | 低,每个场景需重新编排 | 高,一次封装多处复用 |
简单来说,Function Calling解决的是“AI能不能调用工具”的问题,Skills执行解决的是“AI能不能完成一项完整的业务任务”的问题。前者是能力基础,后者是能力封装。
下面用一张流程图对比 Function Calling 与 Skills 执行的差异:
#mermaid-svg-THarEPK96I3dLTS5{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-THarEPK96I3dLTS5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-THarEPK96I3dLTS5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-THarEPK96I3dLTS5 .error-icon{fill:#552222;}#mermaid-svg-THarEPK96I3dLTS5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-THarEPK96I3dLTS5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-THarEPK96I3dLTS5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-THarEPK96I3dLTS5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-THarEPK96I3dLTS5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-THarEPK96I3dLTS5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-THarEPK96I3dLTS5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-THarEPK96I3dLTS5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-THarEPK96I3dLTS5 .marker.cross{stroke:#333333;}#mermaid-svg-THarEPK96I3dLTS5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-THarEPK96I3dLTS5 p{margin:0;}#mermaid-svg-THarEPK96I3dLTS5 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-THarEPK96I3dLTS5 .cluster-label text{fill:#333;}#mermaid-svg-THarEPK96I3dLTS5 .cluster-label span{color:#333;}#mermaid-svg-THarEPK96I3dLTS5 .cluster-label span p{background-color:transparent;}#mermaid-svg-THarEPK96I3dLTS5 .label text,#mermaid-svg-THarEPK96I3dLTS5 span{fill:#333;color:#333;}#mermaid-svg-THarEPK96I3dLTS5 .node rect,#mermaid-svg-THarEPK96I3dLTS5 .node circle,#mermaid-svg-THarEPK96I3dLTS5 .node ellipse,#mermaid-svg-THarEPK96I3dLTS5 .node polygon,#mermaid-svg-THarEPK96I3dLTS5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-THarEPK96I3dLTS5 .rough-node .label text,#mermaid-svg-THarEPK96I3dLTS5 .node .label text,#mermaid-svg-THarEPK96I3dLTS5 .image-shape .label,#mermaid-svg-THarEPK96I3dLTS5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-THarEPK96I3dLTS5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-THarEPK96I3dLTS5 .rough-node .label,#mermaid-svg-THarEPK96I3dLTS5 .node .label,#mermaid-svg-THarEPK96I3dLTS5 .image-shape .label,#mermaid-svg-THarEPK96I3dLTS5 .icon-shape .label{text-align:center;}#mermaid-svg-THarEPK96I3dLTS5 .node.clickable{cursor:pointer;}#mermaid-svg-THarEPK96I3dLTS5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-THarEPK96I3dLTS5 .arrowheadPath{fill:#333333;}#mermaid-svg-THarEPK96I3dLTS5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-THarEPK96I3dLTS5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-THarEPK96I3dLTS5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-THarEPK96I3dLTS5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-THarEPK96I3dLTS5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-THarEPK96I3dLTS5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-THarEPK96I3dLTS5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-THarEPK96I3dLTS5 .cluster text{fill:#333;}#mermaid-svg-THarEPK96I3dLTS5 .cluster span{color:#333;}#mermaid-svg-THarEPK96I3dLTS5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-THarEPK96I3dLTS5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-THarEPK96I3dLTS5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-THarEPK96I3dLTS5 .icon-shape,#mermaid-svg-THarEPK96I3dLTS5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-THarEPK96I3dLTS5 .icon-shape p,#mermaid-svg-THarEPK96I3dLTS5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-THarEPK96I3dLTS5 .icon-shape .label rect,#mermaid-svg-THarEPK96I3dLTS5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-THarEPK96I3dLTS5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-THarEPK96I3dLTS5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-THarEPK96I3dLTS5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Skills执行
Function Calling
单次调用
完整业务任务
用户指令
模型决策
调用单个API
返回结果
用户指令
意图识别
匹配Skill
按DAG执行多步骤
聚合结果返回
能力基础
能力封装
二、Skills执行的技术架构
一个完整的Skills执行系统包含三个核心组件:技能注册中心、技能匹配器和技能执行引擎。
#mermaid-svg-TQMT75zl6SbVn9xQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TQMT75zl6SbVn9xQ .error-icon{fill:#552222;}#mermaid-svg-TQMT75zl6SbVn9xQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TQMT75zl6SbVn9xQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TQMT75zl6SbVn9xQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TQMT75zl6SbVn9xQ .marker.cross{stroke:#333333;}#mermaid-svg-TQMT75zl6SbVn9xQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TQMT75zl6SbVn9xQ p{margin:0;}#mermaid-svg-TQMT75zl6SbVn9xQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster-label text{fill:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster-label span{color:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster-label span p{background-color:transparent;}#mermaid-svg-TQMT75zl6SbVn9xQ .label text,#mermaid-svg-TQMT75zl6SbVn9xQ span{fill:#333;color:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ .node rect,#mermaid-svg-TQMT75zl6SbVn9xQ .node circle,#mermaid-svg-TQMT75zl6SbVn9xQ .node ellipse,#mermaid-svg-TQMT75zl6SbVn9xQ .node polygon,#mermaid-svg-TQMT75zl6SbVn9xQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TQMT75zl6SbVn9xQ .rough-node .label text,#mermaid-svg-TQMT75zl6SbVn9xQ .node .label text,#mermaid-svg-TQMT75zl6SbVn9xQ .image-shape .label,#mermaid-svg-TQMT75zl6SbVn9xQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-TQMT75zl6SbVn9xQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TQMT75zl6SbVn9xQ .rough-node .label,#mermaid-svg-TQMT75zl6SbVn9xQ .node .label,#mermaid-svg-TQMT75zl6SbVn9xQ .image-shape .label,#mermaid-svg-TQMT75zl6SbVn9xQ .icon-shape .label{text-align:center;}#mermaid-svg-TQMT75zl6SbVn9xQ .node.clickable{cursor:pointer;}#mermaid-svg-TQMT75zl6SbVn9xQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TQMT75zl6SbVn9xQ .arrowheadPath{fill:#333333;}#mermaid-svg-TQMT75zl6SbVn9xQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TQMT75zl6SbVn9xQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TQMT75zl6SbVn9xQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TQMT75zl6SbVn9xQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TQMT75zl6SbVn9xQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TQMT75zl6SbVn9xQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster text{fill:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ .cluster span{color:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TQMT75zl6SbVn9xQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TQMT75zl6SbVn9xQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-TQMT75zl6SbVn9xQ .icon-shape,#mermaid-svg-TQMT75zl6SbVn9xQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TQMT75zl6SbVn9xQ .icon-shape p,#mermaid-svg-TQMT75zl6SbVn9xQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TQMT75zl6SbVn9xQ .icon-shape .label rect,#mermaid-svg-TQMT75zl6SbVn9xQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TQMT75zl6SbVn9xQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TQMT75zl6SbVn9xQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TQMT75zl6SbVn9xQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
匹配成功
匹配失败
用户自然语言指令
意图识别引擎
技能匹配器匹配目标Skill
技能执行引擎
返回澄清请求
技能注册中心获取Skill定义
按DAG执行操作序列
结果聚合与反馈
技能注册中心负责存储所有可用Skill的元数据:技能名称、能力描述、触发关键词、所需参数、执行步骤定义。当新Skill被创建时,它需要向注册中心完成注册,之后才能被匹配器发现。
技能匹配器的工作是在用户输入的自然语言指令与已注册的Skill之间建立映射。匹配逻辑通常基于意图识别结果——系统先解析出用户想做什么,再在注册中心中查找能力描述最匹配的Skill。匹配准确度取决于Skill名称和描述的清晰程度。
技能执行引擎负责按Skill内部定义的操作序列执行任务。它读取Skill的执行步骤定义,构建DAG(有向无环图),按拓扑顺序调度每个步骤,处理步骤间的依赖关系和异常回滚。
三、内置技能模板概览
企业级AI Agent平台通常预置一批高频场景的技能模板,降低用户的上手门槛。这些模板覆盖了大多数企业中的常见业务操作,可以直接启用,也可以作为自定义Skill的参考实现。
一个典型的技能模板包含以下要素:
{
"skill_name": "generate_sales_report",
"display_name": "生成销售报表",
"description": "查询销售数据,生成可视化报表,并邮件发送给指定收件人",
"trigger_keywords": ["销售报表", "销售日报", "销售周报", "业绩汇总"],
"required_params": ["time_range", "region", "recipient"],
"steps": [
{
"step_id": 1,
"action": "query_crm",
"params": {"region": "${region}"},
"depends_on": []
},
{
"step_id": 2,
"action": "query_erp",
"params": {"time_range": "${time_range}"},
"depends_on": [1]
},
{
"step_id": 3,
"action": "join_and_aggregate",
"params": {},
"depends_on": [1, 2]
},
{
"step_id": 4,
"action": "generate_chart",
"params": {"data_source": "step_3"},
"depends_on": [3]
},
{
"step_id": 5,
"action": "send_email",
"params": {"recipient": "${recipient}", "attachment": "step_4"},
"depends_on": [4]
}
],
"error_handling": {
"retry_count": 3,
"retry_interval_seconds": 30,
"fallback": "notify_admin"
}
}
这个JSON定义了一个完整的“生成销售报表”Skill。它包含5个操作步骤,步骤间存在严格的串行依赖,并配置了失败重试和降级策略。当用户说“生成上个月的销售周报发给张总”时,系统自动匹配到这个Skill,提取参数(时间范围=上月、收件人=张总),然后按DAG顺序执行。
“生成销售报表”Skill 的 5 个步骤构成一条严格的串行执行链,其 DAG 结构如下:
#mermaid-svg-XX1cEKH9UjnA4nYn{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XX1cEKH9UjnA4nYn .error-icon{fill:#552222;}#mermaid-svg-XX1cEKH9UjnA4nYn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XX1cEKH9UjnA4nYn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XX1cEKH9UjnA4nYn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XX1cEKH9UjnA4nYn .marker.cross{stroke:#333333;}#mermaid-svg-XX1cEKH9UjnA4nYn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XX1cEKH9UjnA4nYn p{margin:0;}#mermaid-svg-XX1cEKH9UjnA4nYn .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster-label text{fill:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster-label span{color:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster-label span p{background-color:transparent;}#mermaid-svg-XX1cEKH9UjnA4nYn .label text,#mermaid-svg-XX1cEKH9UjnA4nYn span{fill:#333;color:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn .node rect,#mermaid-svg-XX1cEKH9UjnA4nYn .node circle,#mermaid-svg-XX1cEKH9UjnA4nYn .node ellipse,#mermaid-svg-XX1cEKH9UjnA4nYn .node polygon,#mermaid-svg-XX1cEKH9UjnA4nYn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XX1cEKH9UjnA4nYn .rough-node .label text,#mermaid-svg-XX1cEKH9UjnA4nYn .node .label text,#mermaid-svg-XX1cEKH9UjnA4nYn .image-shape .label,#mermaid-svg-XX1cEKH9UjnA4nYn .icon-shape .label{text-anchor:middle;}#mermaid-svg-XX1cEKH9UjnA4nYn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XX1cEKH9UjnA4nYn .rough-node .label,#mermaid-svg-XX1cEKH9UjnA4nYn .node .label,#mermaid-svg-XX1cEKH9UjnA4nYn .image-shape .label,#mermaid-svg-XX1cEKH9UjnA4nYn .icon-shape .label{text-align:center;}#mermaid-svg-XX1cEKH9UjnA4nYn .node.clickable{cursor:pointer;}#mermaid-svg-XX1cEKH9UjnA4nYn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XX1cEKH9UjnA4nYn .arrowheadPath{fill:#333333;}#mermaid-svg-XX1cEKH9UjnA4nYn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XX1cEKH9UjnA4nYn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XX1cEKH9UjnA4nYn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XX1cEKH9UjnA4nYn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XX1cEKH9UjnA4nYn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XX1cEKH9UjnA4nYn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster text{fill:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn .cluster span{color:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XX1cEKH9UjnA4nYn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XX1cEKH9UjnA4nYn rect.text{fill:none;stroke-width:0;}#mermaid-svg-XX1cEKH9UjnA4nYn .icon-shape,#mermaid-svg-XX1cEKH9UjnA4nYn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XX1cEKH9UjnA4nYn .icon-shape p,#mermaid-svg-XX1cEKH9UjnA4nYn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XX1cEKH9UjnA4nYn .icon-shape .label rect,#mermaid-svg-XX1cEKH9UjnA4nYn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XX1cEKH9UjnA4nYn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XX1cEKH9UjnA4nYn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XX1cEKH9UjnA4nYn :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
步骤1query_crm查询CRM销售数据
步骤2query_erp查询ERP数据
步骤3join_and_aggregate关联聚合
步骤4generate_chart生成图表
步骤5send_email发送邮件
四、自定义技能开发:从需求到落地
内置模板覆盖的是通用场景,企业的真实需求往往更具体。此时需要开发自定义Skill。以下是完整的开发流程。
步骤1:定义技能边界
开发一个Skill之前,先明确三个问题:这个Skill解决什么业务问题?触发它的自然语言指令长什么样?它需要哪些输入参数?
以“客户跟进提醒”为例:
- 业务问题:每天定时检查CRM中超过7天未跟进的客户,生成清单并通知销售负责人
- 触发指令:“查一下超过7天没跟进的客户”“生成本周客户跟进清单”“哪些客户需要跟进了”
- 输入参数:跟进天数阈值(默认7天)、时间范围(默认本周)、通知对象(默认对应销售负责人)
步骤2:设计操作序列
将业务需求拆解为可执行的操作步骤,明确每个步骤的操作类型、目标系统和依赖关系。
以“客户跟进提醒”的步骤设计:
| 1 | 查询超过N天未跟进的客户 | CRM | 无 |
| 2 | 按客户等级和金额排序,筛选优先级前10 | 数据处理 | 步骤1 |
| 3 | 生成跟进建议清单 | 报表生成 | 步骤2 |
| 4 | 邮件发送给对应销售负责人 | 邮件系统 | 步骤3 |
| 5 | 更新CRM中的跟进状态标记 | CRM | 步骤2(与步骤3并行) |
步骤5与步骤3并行执行,互不阻塞——这是一个典型的DAG并行分支。
“客户跟进提醒”的步骤 3 与步骤 5 并行执行,其 DAG 结构如下:
#mermaid-svg-GM7o4k0vTBgsZh7F{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GM7o4k0vTBgsZh7F .error-icon{fill:#552222;}#mermaid-svg-GM7o4k0vTBgsZh7F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GM7o4k0vTBgsZh7F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GM7o4k0vTBgsZh7F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GM7o4k0vTBgsZh7F .marker.cross{stroke:#333333;}#mermaid-svg-GM7o4k0vTBgsZh7F svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GM7o4k0vTBgsZh7F p{margin:0;}#mermaid-svg-GM7o4k0vTBgsZh7F .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster-label text{fill:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster-label span{color:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster-label span p{background-color:transparent;}#mermaid-svg-GM7o4k0vTBgsZh7F .label text,#mermaid-svg-GM7o4k0vTBgsZh7F span{fill:#333;color:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F .node rect,#mermaid-svg-GM7o4k0vTBgsZh7F .node circle,#mermaid-svg-GM7o4k0vTBgsZh7F .node ellipse,#mermaid-svg-GM7o4k0vTBgsZh7F .node polygon,#mermaid-svg-GM7o4k0vTBgsZh7F .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GM7o4k0vTBgsZh7F .rough-node .label text,#mermaid-svg-GM7o4k0vTBgsZh7F .node .label text,#mermaid-svg-GM7o4k0vTBgsZh7F .image-shape .label,#mermaid-svg-GM7o4k0vTBgsZh7F .icon-shape .label{text-anchor:middle;}#mermaid-svg-GM7o4k0vTBgsZh7F .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GM7o4k0vTBgsZh7F .rough-node .label,#mermaid-svg-GM7o4k0vTBgsZh7F .node .label,#mermaid-svg-GM7o4k0vTBgsZh7F .image-shape .label,#mermaid-svg-GM7o4k0vTBgsZh7F .icon-shape .label{text-align:center;}#mermaid-svg-GM7o4k0vTBgsZh7F .node.clickable{cursor:pointer;}#mermaid-svg-GM7o4k0vTBgsZh7F .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GM7o4k0vTBgsZh7F .arrowheadPath{fill:#333333;}#mermaid-svg-GM7o4k0vTBgsZh7F .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GM7o4k0vTBgsZh7F .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GM7o4k0vTBgsZh7F .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GM7o4k0vTBgsZh7F .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GM7o4k0vTBgsZh7F .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GM7o4k0vTBgsZh7F .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster text{fill:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F .cluster span{color:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GM7o4k0vTBgsZh7F .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GM7o4k0vTBgsZh7F rect.text{fill:none;stroke-width:0;}#mermaid-svg-GM7o4k0vTBgsZh7F .icon-shape,#mermaid-svg-GM7o4k0vTBgsZh7F .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GM7o4k0vTBgsZh7F .icon-shape p,#mermaid-svg-GM7o4k0vTBgsZh7F .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GM7o4k0vTBgsZh7F .icon-shape .label rect,#mermaid-svg-GM7o4k0vTBgsZh7F .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GM7o4k0vTBgsZh7F .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GM7o4k0vTBgsZh7F .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GM7o4k0vTBgsZh7F :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
步骤1查询未跟进客户CRM
步骤2排序筛选Top10数据处理
步骤3生成跟进建议清单报表生成
步骤5更新CRM跟进状态CRM
步骤4邮件发送给销售负责人邮件系统
步骤3:实现技能逻辑
将操作序列转化为Skill定义文件。以下是一个简化版的自定义Skill定义示例:
from dataclasses import dataclass, field
from typing import List, Dict, Any, Optional
from enum import Enum
import json
class StepStatus(Enum):
PENDING = "pending"
RUNNING = "running"
SUCCESS = "success"
FAILED = "failed"
SKIPPED = "skipped"
@dataclass
class SkillStep:
"""Skill中的一个操作步骤"""
step_id: int
action: str
params: Dict[str, Any] = field(default_factory=dict)
depends_on: List[int] = field(default_factory=list)
status: StepStatus = StepStatus.PENDING
result: Optional[Any] = None
@dataclass
class SkillDefinition:
"""Skill的完整定义"""
skill_name: str
display_name: str
description: str
trigger_keywords: List[str]
required_params: List[str]
steps: List[SkillStep]
retry_count: int = 3
retry_interval: int = 30
class SkillRegistry:
"""技能注册中心:管理所有已注册的Skill"""
def __init__(self):
self._skills: Dict[str, SkillDefinition] = {}
def register(self, skill: SkillDefinition) –> None:
"""注册一个新的Skill"""
if skill.skill_name in self._skills:
raise ValueError(f"Skill '{skill.skill_name}' already registered")
self._skills[skill.skill_name] = skill
print(f"[SkillRegistry] Registered: {skill.display_name} "
f"(keywords: {skill.trigger_keywords})")
def find_by_intent(self, user_input: str) –> Optional[SkillDefinition]:
"""根据用户输入匹配最合适的Skill"""
best_match = None
best_score = 0
for skill in self._skills.values():
score = self._calculate_match_score(user_input, skill)
if score > best_score:
best_score = score
best_match = skill
return best_match if best_score > 0.5 else None
def _calculate_match_score(self, user_input: str, skill: SkillDefinition) –> float:
"""计算用户输入与Skill的匹配分数"""
matched_keywords = sum(
1 for kw in skill.trigger_keywords if kw in user_input
)
return matched_keywords / len(skill.trigger_keywords) if skill.trigger_keywords else 0
class SkillExecutor:
"""技能执行引擎:按DAG顺序执行Skill中的操作步骤"""
def __init__(self, registry: SkillRegistry):
self.registry = registry
def execute(self, skill_name: str, params: Dict[str, Any]) –> Dict[str, Any]:
"""执行指定Skill"""
skill = self.registry._skills.get(skill_name)
if not skill:
return {"status": "failed", "error": f"Skill '{skill_name}' not found"}
print(f"\\n[SkillExecutor] Executing: {skill.display_name}")
print(f"[SkillExecutor] Params: {json.dumps(params, ensure_ascii=False)}")
# 按拓扑顺序执行步骤
completed: Dict[int, Any] = {}
failed_steps: List[int] = []
while len(completed) + len(failed_steps) < len(skill.steps):
ready_steps = self._get_ready_steps(skill.steps, completed, failed_steps)
if not ready_steps:
# 有循环依赖或全部失败
break
for step in ready_steps:
result = self._execute_step(step, skill, params, completed)
if result.get("status") == "success":
step.status = StepStatus.SUCCESS
step.result = result.get("data")
completed[step.step_id] = result.get("data")
print(f" [Step {step.step_id}] {step.action} -> SUCCESS")
else:
step.status = StepStatus.FAILED
failed_steps.append(step.step_id)
print(f" [Step {step.step_id}] {step.action} -> FAILED: "
f"{result.get('error')}")
# 汇总执行结果
summary = {
"skill_name": skill_name,
"total_steps": len(skill.steps),
"completed_steps": len(completed),
"failed_steps": failed_steps,
"is_complete": len(failed_steps) == 0,
"results": completed,
}
print(f"[SkillExecutor] Summary: {json.dumps(summary, ensure_ascii=False)}")
return summary
def _get_ready_steps(self, steps: List[SkillStep],
completed: Dict[int, Any],
failed: List[int]) –> List[SkillStep]:
"""获取所有依赖已满足且未执行的步骤"""
ready = []
for step in steps:
if step.step_id in completed or step.step_id in failed:
continue
if step.status == StepStatus.PENDING:
if all(dep in completed for dep in step.depends_on):
ready.append(step)
elif any(dep in failed for dep in step.depends_on):
# 依赖失败,该步骤跳过
step.status = StepStatus.SKIPPED
return ready
def _execute_step(self, step: SkillStep, skill: SkillDefinition,
params: Dict[str, Any],
completed: Dict[int, Any]) –> Dict[str, Any]:
"""执行单个步骤(模拟)"""
# 实际系统中这里会调用对应的连接器或API
# 此处用模拟逻辑演示流程
print(f" [Step {step.step_id}] Executing {step.action} with "
f"params={step.params}")
# 模拟执行成功
return {
"status": "success",
"data": f"Result of {step.action}"
}
步骤4:注册与测试
Skill定义完成后,将其注册到技能注册中心,并通过测试指令验证匹配和执行效果:
# 创建客户跟进提醒Skill
follow_up_skill = SkillDefinition(
skill_name="customer_follow_up",
display_name="客户跟进提醒",
description="查询超过N天未跟进的客户,生成清单并通知销售负责人",
trigger_keywords=["跟进提醒", "未跟进", "跟进清单", "客户跟进"],
required_params=["days_threshold"],
steps=[
SkillStep(1, "query_unfollowed_customers", {"days": "${days_threshold}"}),
SkillStep(2, "rank_and_filter", {"top_n": 10}, depends_on=[1]),
SkillStep(3, "generate_followup_list", {}, depends_on=[2]),
SkillStep(4, "send_email", {"template": "followup_reminder"}, depends_on=[3]),
SkillStep(5, "update_crm_status", {"status": "pending_followup"}, depends_on=[2]),
],
retry_count=3,
retry_interval=30
)
# 注册Skill
registry = SkillRegistry()
registry.register(follow_up_skill)
# 测试匹配
user_input = "查一下超过7天没跟进的客户"
matched = registry.find_by_intent(user_input)
print(f"Matched skill: {matched.display_name if matched else 'None'}")
# 测试执行
if matched:
executor = SkillExecutor(registry)
executor.execute(matched.skill_name, {"days_threshold": 7})
这段代码演示了Skill从定义、注册、匹配到执行的完整流程。生产环境中的实现更复杂——需要真实的连接器调用、参数模板替换、DAG并行调度和断点恢复机制,但核心逻辑与示例一致。
五、技能调用的触发机制
Skills执行支持三种触发方式,分别对应不同的业务场景。
5.1 自然语言触发
用户在对话界面用日常语言描述需求,系统通过意图识别匹配对应的Skill。这是最常用的触发方式。
典型场景:销售主管说“生成昨天的销售日报”,系统匹配到“生成销售日报”Skill,自动执行查询→生成→发送的全流程。
技术要点:触发准确度取决于Skill的触发关键词配置和意图识别引擎的语义理解能力。关键词过窄会导致漏触发,过宽会导致误触发。
5.2 定时触发
按固定时间计划自动执行Skill,无需人工干预。适合日报、周报、巡检等周期性任务。
典型场景:每个工作日上午9:00自动执行“生成销售日报”Skill;每周一上午10:00自动执行“生成销售周报”Skill。
技术要点:定时触发需要任务编排引擎配合,在固定时间点调用Skill执行引擎,并将执行结果推送到指定渠道。
5.3 事件触发
当指定事件发生时自动执行Skill。适合异常监控、状态变更等场景。
典型场景:当CRM中新客户录入时,自动执行“客户欢迎邮件”Skill;当库存低于安全线时,自动执行“补货建议”Skill。
技术要点:事件触发需要系统具备事件监听能力——监听器检测到事件后,将事件上下文作为参数传递给对应的Skill执行引擎。
三种触发机制分别面向不同的业务场景,其整体架构如下:
#mermaid-svg-5K6NiRBec0bVerDK{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5K6NiRBec0bVerDK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5K6NiRBec0bVerDK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5K6NiRBec0bVerDK .error-icon{fill:#552222;}#mermaid-svg-5K6NiRBec0bVerDK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5K6NiRBec0bVerDK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5K6NiRBec0bVerDK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5K6NiRBec0bVerDK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5K6NiRBec0bVerDK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5K6NiRBec0bVerDK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5K6NiRBec0bVerDK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5K6NiRBec0bVerDK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5K6NiRBec0bVerDK .marker.cross{stroke:#333333;}#mermaid-svg-5K6NiRBec0bVerDK svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5K6NiRBec0bVerDK p{margin:0;}#mermaid-svg-5K6NiRBec0bVerDK .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5K6NiRBec0bVerDK .cluster-label text{fill:#333;}#mermaid-svg-5K6NiRBec0bVerDK .cluster-label span{color:#333;}#mermaid-svg-5K6NiRBec0bVerDK .cluster-label span p{background-color:transparent;}#mermaid-svg-5K6NiRBec0bVerDK .label text,#mermaid-svg-5K6NiRBec0bVerDK span{fill:#333;color:#333;}#mermaid-svg-5K6NiRBec0bVerDK .node rect,#mermaid-svg-5K6NiRBec0bVerDK .node circle,#mermaid-svg-5K6NiRBec0bVerDK .node ellipse,#mermaid-svg-5K6NiRBec0bVerDK .node polygon,#mermaid-svg-5K6NiRBec0bVerDK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5K6NiRBec0bVerDK .rough-node .label text,#mermaid-svg-5K6NiRBec0bVerDK .node .label text,#mermaid-svg-5K6NiRBec0bVerDK .image-shape .label,#mermaid-svg-5K6NiRBec0bVerDK .icon-shape .label{text-anchor:middle;}#mermaid-svg-5K6NiRBec0bVerDK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5K6NiRBec0bVerDK .rough-node .label,#mermaid-svg-5K6NiRBec0bVerDK .node .label,#mermaid-svg-5K6NiRBec0bVerDK .image-shape .label,#mermaid-svg-5K6NiRBec0bVerDK .icon-shape .label{text-align:center;}#mermaid-svg-5K6NiRBec0bVerDK .node.clickable{cursor:pointer;}#mermaid-svg-5K6NiRBec0bVerDK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5K6NiRBec0bVerDK .arrowheadPath{fill:#333333;}#mermaid-svg-5K6NiRBec0bVerDK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5K6NiRBec0bVerDK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5K6NiRBec0bVerDK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5K6NiRBec0bVerDK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5K6NiRBec0bVerDK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5K6NiRBec0bVerDK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5K6NiRBec0bVerDK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5K6NiRBec0bVerDK .cluster text{fill:#333;}#mermaid-svg-5K6NiRBec0bVerDK .cluster span{color:#333;}#mermaid-svg-5K6NiRBec0bVerDK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5K6NiRBec0bVerDK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5K6NiRBec0bVerDK rect.text{fill:none;stroke-width:0;}#mermaid-svg-5K6NiRBec0bVerDK .icon-shape,#mermaid-svg-5K6NiRBec0bVerDK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5K6NiRBec0bVerDK .icon-shape p,#mermaid-svg-5K6NiRBec0bVerDK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5K6NiRBec0bVerDK .icon-shape .label rect,#mermaid-svg-5K6NiRBec0bVerDK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5K6NiRBec0bVerDK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5K6NiRBec0bVerDK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5K6NiRBec0bVerDK :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
触发层
自然语言触发对话界面意图识别
定时触发任务编排引擎
事件触发事件监听器
技能匹配器
技能执行引擎
结果推送
六、异常处理与容错机制
Skill执行过程涉及多个系统调用,任何一个环节都可能失败。完善的异常处理机制是Skill从“Demo可用”到“生产可靠”的关键。
6.1 重试机制
对于网络超时、系统限流等可恢复性异常,自动重试是最常用的处理策略。重试参数通常在Skill定义中配置:
# 重试配置示例
retry_config = {
"max_retries": 3, # 最大重试次数
"retry_interval": 30, # 重试间隔(秒)
"backoff_multiplier": 2, # 退避倍数:30s → 60s → 120s
"retryable_errors": [ # 可重试的异常类型
"timeout", "rate_limit", "connection_error"
]
}
关键设计原则:重试只适用于可恢复性异常。对于业务逻辑错误(如参数非法、权限不足),重试无法解决问题,应直接降级或通知人工处理。
6.2 降级策略
当Skill的某个步骤连续失败时,需要配置降级方案。常见的降级策略包括:
- 跳过非关键步骤:如果失败的步骤不影响最终结果的核心价值,可以跳过继续执行
- 使用备选数据源:主数据源不可用时,切换到备选数据源
- 降级输出格式:无法生成可视化报表时,降级为结构化文本输出
- 通知人工介入:系统无法自动处理时,通知管理员手动处理
6.3 人工审批节点
对于涉及敏感操作的Skill(如发送外部邮件、修改财务数据),需要在执行路径中插入人工审批节点。Skill执行到审批节点时暂停,通知指定审批人确认后继续:
class ApprovalNode:
"""人工审批节点"""
def __init__(self, approver: str, message_template: str):
self.approver = approver
self.message_template = message_template
def execute(self, context: Dict[str, Any]) –> str:
"""暂停执行,发送审批请求,返回审批状态"""
message = self.message_template.format(**context)
print(f"[ApprovalNode] Sending approval request to {self.approver}: {message}")
# 发送通知,等待审批结果
# 返回 "approved" 或 "rejected"
return "approved" # 模拟审批通过
6.4 断点恢复
长链路的Skill执行中断后(如服务重启、网络中断),需要从上次成功的步骤继续执行,而非从头开始。这依赖任务状态持久化:
import json
import redis
class CheckpointManager:
"""检查点管理器:持久化Skill执行状态,支持断点恢复"""
def __init__(self, redis_client):
self.redis = redis_client
def save_checkpoint(self, execution_id: str, step_id: int,
step_result: Dict[str, Any]):
"""每个步骤执行后保存检查点"""
key = f"skill:checkpoint:{execution_id}"
self.redis.hset(key, f"step_{step_id}", json.dumps(step_result))
self.redis.expire(key, 3600)
def load_checkpoint(self, execution_id: str) –> Dict[int, Any]:
"""加载已完成的步骤结果,用于断点恢复"""
key = f"skill:checkpoint:{execution_id}"
data = self.redis.hgetall(key)
return {
int(k.replace("step_", "")): json.loads(v)
for k, v in data.items()
}
def clear_checkpoint(self, execution_id: str):
"""执行完成后清理检查点"""
self.redis.delete(f"skill:checkpoint:{execution_id}")
Skill 执行过程中的异常处理与容错决策流程如下:
#mermaid-svg-9rcSZeCfatYRlIol{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9rcSZeCfatYRlIol .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9rcSZeCfatYRlIol .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9rcSZeCfatYRlIol .error-icon{fill:#552222;}#mermaid-svg-9rcSZeCfatYRlIol .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9rcSZeCfatYRlIol .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9rcSZeCfatYRlIol .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9rcSZeCfatYRlIol .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9rcSZeCfatYRlIol .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9rcSZeCfatYRlIol .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9rcSZeCfatYRlIol .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9rcSZeCfatYRlIol .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9rcSZeCfatYRlIol .marker.cross{stroke:#333333;}#mermaid-svg-9rcSZeCfatYRlIol svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9rcSZeCfatYRlIol p{margin:0;}#mermaid-svg-9rcSZeCfatYRlIol .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-9rcSZeCfatYRlIol .cluster-label text{fill:#333;}#mermaid-svg-9rcSZeCfatYRlIol .cluster-label span{color:#333;}#mermaid-svg-9rcSZeCfatYRlIol .cluster-label span p{background-color:transparent;}#mermaid-svg-9rcSZeCfatYRlIol .label text,#mermaid-svg-9rcSZeCfatYRlIol span{fill:#333;color:#333;}#mermaid-svg-9rcSZeCfatYRlIol .node rect,#mermaid-svg-9rcSZeCfatYRlIol .node circle,#mermaid-svg-9rcSZeCfatYRlIol .node ellipse,#mermaid-svg-9rcSZeCfatYRlIol .node polygon,#mermaid-svg-9rcSZeCfatYRlIol .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9rcSZeCfatYRlIol .rough-node .label text,#mermaid-svg-9rcSZeCfatYRlIol .node .label text,#mermaid-svg-9rcSZeCfatYRlIol .image-shape .label,#mermaid-svg-9rcSZeCfatYRlIol .icon-shape .label{text-anchor:middle;}#mermaid-svg-9rcSZeCfatYRlIol .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9rcSZeCfatYRlIol .rough-node .label,#mermaid-svg-9rcSZeCfatYRlIol .node .label,#mermaid-svg-9rcSZeCfatYRlIol .image-shape .label,#mermaid-svg-9rcSZeCfatYRlIol .icon-shape .label{text-align:center;}#mermaid-svg-9rcSZeCfatYRlIol .node.clickable{cursor:pointer;}#mermaid-svg-9rcSZeCfatYRlIol .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9rcSZeCfatYRlIol .arrowheadPath{fill:#333333;}#mermaid-svg-9rcSZeCfatYRlIol .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9rcSZeCfatYRlIol .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9rcSZeCfatYRlIol .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9rcSZeCfatYRlIol .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9rcSZeCfatYRlIol .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9rcSZeCfatYRlIol .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9rcSZeCfatYRlIol .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9rcSZeCfatYRlIol .cluster text{fill:#333;}#mermaid-svg-9rcSZeCfatYRlIol .cluster span{color:#333;}#mermaid-svg-9rcSZeCfatYRlIol div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9rcSZeCfatYRlIol .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9rcSZeCfatYRlIol rect.text{fill:none;stroke-width:0;}#mermaid-svg-9rcSZeCfatYRlIol .icon-shape,#mermaid-svg-9rcSZeCfatYRlIol .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9rcSZeCfatYRlIol .icon-shape p,#mermaid-svg-9rcSZeCfatYRlIol .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9rcSZeCfatYRlIol .icon-shape .label rect,#mermaid-svg-9rcSZeCfatYRlIol .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9rcSZeCfatYRlIol .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9rcSZeCfatYRlIol .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9rcSZeCfatYRlIol :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
是
否
否
否
是
是
是
否
否
步骤执行失败
是否可恢复异常?
自动重试指数退避
重试成功?
继续执行
是否关键步骤?
跳过该步骤降级继续
是否需要人工审批?
暂停执行通知审批人
审批通过?
终止执行
断点保存通知管理员
七、总结
Skills执行是AI Agent从“对话能力”走向“任务执行能力”的关键技术环节。它将业务操作封装为可复用的技能单元,通过自然语言意图匹配触发,按DAG顺序执行操作序列,并在执行过程中处理异常和断点恢复。
掌握Skills执行的完整路径——从理解概念、使用内置模板、开发自定义技能,到配置触发机制和异常处理——是构建企业级AI数字员工的基础能力。对于正在评估AI Agent平台的技术决策者,建议用一条真实的业务指令做POC测试,观察Skill匹配的准确度、执行链路完整性和异常处理机制,这三项指标直接决定了技能调用能力能否在生产环境中稳定运行。
FAQ
Q:智能体执行任务怎么做?
A:智能体执行任务的核心机制是Skills执行。首先将业务操作封装为可复用的Skill,定义操作步骤、依赖关系和异常处理策略;然后在技能注册中心完成注册;最后通过三种方式触发——自然语言指令触发、定时触发或事件触发。用户用日常语言描述需求(如“生成本周客户跟进清单”),系统自动匹配对应Skill,按DAG顺序执行操作序列,并将结果返回给用户。实现这一能力需要三个核心组件:技能注册中心(存储Skill元数据)、技能匹配器(将自然语言指令映射到Skill)、技能执行引擎(按DAG调度执行操作步骤并处理异常)。
Q:AI工作流执行怎么配置?
A:AI工作流执行的配置分为四步。第一步,定义操作步骤——明确每个步骤的操作类型(查询、计算、生成、发送等)、目标系统和输入输出参数。第二步,配置依赖关系——用DAG描述步骤间的串行依赖和并行分支,例如步骤3依赖步骤1和步骤2的结果,步骤4和步骤5可以并行执行。第三步,设置触发条件——选择手动触发、定时触发(如每天9:00)或事件触发(如新数据录入时)。第四步,配置异常处理——为每个步骤设置重试次数、降级策略和人工审批节点。配置完成后,工作流执行引擎会按DAG拓扑顺序自动调度执行,并在执行日志中记录每个步骤的状态和结果。
网硕互联帮助中心




评论前必须登录!
注册