给 AI 系统增加能力时,开发者很容易把每个新需求都包装成一个子智能体。这样做在演示阶段很直观,却可能引入额外的提示词、状态、路由和可观测性成本。Azure 首席工程师 Kishorekumar Pattabiraman 给出的判断方向更务实:围绕复用性、简单性和长期维护成本,决定一项能力应该成为 Skill、子智能体,还是普通工具与确定性代码。
先判断任务需要“能力”还是“自主性”
Skill 更接近一段边界清晰、可重复调用的能力,例如格式转换、字段提取、文档分类或调用某个业务 API。它通常接收结构化输入,执行相对稳定的步骤,再返回结构化结果。
子智能体则适合承担具有独立目标的工作单元。它可能需要维护自己的上下文,在多个工具之间选择,分解任务,并根据中间结果调整下一步动作。例如,“调查一次生产事故并形成初步报告”可能涉及查询日志、核对发布记录、关联告警和整理证据,这已经不只是一次函数调用。
可以用三个问题快速筛选:
- 输入和输出是否能够稳定定义?如果可以,优先考虑 Skill 或普通函数。
- 是否需要多步推理、工具选择和局部状态?如果需要,子智能体更有价值。
- 这项能力能否被多个工作流复用?如果能,应把通用部分从智能体提示词中抽出来,形成独立 Skill。
关键不在于任务听起来是否“智能”,而在于它是否真的需要一个独立决策循环。确定性的校验、计算和权限判断不应交给语言模型自由发挥。
一张工程决策表
| 场景特征 | 更合适的实现 | 原因 |
|---|---|---|
| 固定输入输出、单一职责 | 普通函数或 API 工具 | 成本低,测试和排错最直接 |
| 需要模型理解,但步骤稳定 | Skill | 易复用,也容易嵌入不同工作流 |
| 有独立目标,需要多步规划 | 子智能体 | 可以隔离上下文、工具和决策过程 |
| 流程固定,只有少数节点需要模型 | 工作流编排 | 避免让模型控制整个流程 |
| 涉及权限、金额或合规判定 | 确定性服务加人工审批 | 降低不可预测行为带来的风险 |
Skill 与子智能体也不是互斥关系。一个负责事故调查的子智能体,可以调用“查询日志”“读取部署记录”“生成时间线”等多个 Skill。这样的分层让通用能力保持稳定,同时把任务级规划留给子智能体。
可以这样实践:用配置显式记录选择理由
下面是一个可改造的最小示例。这里作出明确假设:skill 表示一次受控调用,sub_agent 表示允许执行有限次数的多步任务;示例并非特定 Azure API,而是一种可运行的本地设计方法。
先创建 capabilities.yaml:
capabilities:
- name: extract_invoice_fields
kind: skill
reusable: true
stateful: false
max_steps: 1
owner: finance-platform
- name: investigate_service_incident
kind: sub_agent
reusable: false
stateful: true
max_steps: 6
owner: sre
- name: approve_refund
kind: deterministic_workflow
reusable: true
stateful: true
max_steps: 3
owner: payments
human_approval: true
如果环境中没有 YAML 解析库,可用下面的纯 Python 版本直接运行相同的决策逻辑。将它保存为 decision.py,然后执行 python decision.py:
from dataclasses import dataclass
from enum import Enum
class Approach(str, Enum):
FUNCTION = "function_or_api"
SKILL = "skill"
SUB_AGENT = "sub_agent"
WORKFLOW = "deterministic_workflow"
@dataclass(frozen=True)
class TaskProfile:
stable_io: bool
needs_model_understanding: bool
needs_planning: bool
needs_local_state: bool
reusable: bool
high_risk: bool
def choose_approach(task: TaskProfile) -> Approach:
if task.high_risk:
return Approach.WORKFLOW
if task.needs_planning or task.needs_local_state:
return Approach.SUB_AGENT
if task.needs_model_understanding or task.reusable:
return Approach.SKILL
if task.stable_io:
return Approach.FUNCTION
return Approach.SKILL
tasks = {
"extract_invoice_fields": TaskProfile(
stable_io=True,
needs_model_understanding=True,
needs_planning=False,
needs_local_state=False,
reusable=True,
high_risk=False,
),
"investigate_incident": TaskProfile(
stable_io=False,
needs_model_understanding=True,
needs_planning=True,
needs_local_state=True,
reusable=False,
high_risk=False,
),
"approve_refund": TaskProfile(
stable_io=True,
needs_model_understanding=False,
needs_planning=False,
needs_local_state=True,
reusable=True,
high_risk=True,
),
}
for name, profile in tasks.items():
print(f"{name}: {choose_approach(profile).value}")
预期输出为:
extract_invoice_fields: skill
investigate_incident: sub_agent
approve_refund: deterministic_workflow
这段代码不是为了自动替代架构评审,而是把判断条件变成团队可讨论、可测试的对象。真实项目还可以加入延迟预算、单次调用成本、允许使用的工具、数据敏感级别和失败后的降级策略。
可维护性取决于边界是否清楚
子智能体数量增加后,系统会出现新的运维问题:谁负责路由,状态保存在哪里,调用链如何追踪,失败是否重试,以及不同智能体的提示词升级会不会改变整体行为。因此,每个子智能体都应有明确的目标、工具白名单、最大步骤数、超时和终止条件。
Skill 也不能只是散落的提示词片段。一个可维护的 Skill 至少应定义:
- 输入与输出的数据结构;
- 允许调用的外部服务;
- 超时、重试和幂等策略;
- 成功率、延迟与成本指标;
- 面向典型输入和异常输入的评测集。
当多个子智能体复制相同提示词或工具调用代码时,通常意味着应该提取 Skill。反过来,如果一个 Skill 开始维护长上下文、反复选择工具并自行规划步骤,它可能已经越过边界,应升级为子智能体。
落地时从最简单的实现开始
架构选择可以遵循一条保守路径:普通代码能解决,就使用普通代码;需要模型理解且能力可复用,再封装为 Skill;只有在任务确实需要独立目标、多步规划和局部状态时,才引入子智能体。
上线前可检查以下事项:
- 是否能清楚说明引入子智能体而不是 Skill 的必要性;
- 通用能力是否已经从任务级提示词中抽离;
- 每个决策循环是否有步骤、成本和时间上限;
- 高风险操作是否由确定性规则或人工审批兜底;
- 是否能按 Skill、子智能体和完整工作流分别追踪质量;
- 失败时是否能够定位到具体工具、提示词或路由节点。
真正可持续的 AI 架构,不取决于系统中有多少智能体,而取决于每一层是否承担了恰当的责任。Skill 提供可复用能力,子智能体处理有边界的自主任务,普通代码则守住确定性和风险控制。