Item 8: Use Skills for Progressive Knowledge Loading¶
问题¶
你想让 Agent 处理专业领域(比如财务报销),于是把所有规则塞进 Instructions:
agent = Agent(
client=client,
instructions="""
你是报销专家,请遵循以下规则:
# 国内出差报销
1. 机票:经济舱实报实销,高铁二等座...
2. 住宿:北上广深 600元/天,其他城市 400元/天...
3. 餐饮:50元/餐,一天不超过 150元...
# 国外出差报销
1. 机票:商务舱报销条件...
2. 住宿:参照外事差旅标准...
...(此处省略 500 行)
"""
)
问题来了:
- 每次调用都消耗大量 token——即使用户只问"我的机票能报吗"
- 上下文窗口被浪费——大量规则挤占了模型思考空间
- 更新困难——规则改了要改代码
深入解释¶
Skills 是 Agent Framework 的按需加载知识机制,灵感来自 Dynamic Scoping 和 lazy loading:
┌──────────────────────────────────────────────────────────┐
│ 传统方式:一次性加载所有知识 │
├──────────────────────────────────────────────────────────┤
│ Instructions: [5000 tokens 报销规则...] │
│ ↓ │
│ 每次请求都带 5000 tokens,不管用户问的是什么 │
└──────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────┐
│ Skills 方式:渐进式加载知识 │
├──────────────────────────────────────────────────────────┤
│ 第一步:Agent 知道有哪些 Skills(~100 tokens) │
│ "我有 expense-skill、travel-skill..." │
│ ↓ │
│ 第二步:用户问"机票报销标准" │
│ → 只加载 expense-skill 中的机票部分(~500 tokens) │
│ ↓ │
│ 第三步:用户问"国外差旅" │
│ → 加载 expense-skill 中的国外部分(~500 tokens) │
└──────────────────────────────────────────────────────────┘
心智模型:Skills 就像图书馆的借阅系统——不把所有书都放在你桌上(浪费空间),而是按需借阅,用完归还。
推荐做法¶
方法一:内联 Skill(简单场景)¶
from agent_framework import Agent, Skill, SkillsProvider
# 定义 Skill
expense_skill = Skill(
name="expense-rules",
description="处理报销政策咨询,包括机票、住宿、餐饮等报销标准",
)
# 创建 SkillsProvider
provider = SkillsProvider(skills=[expense_skill])
# 注入到 Agent
agent = Agent(
client=client,
name="ExpenseAgent",
instructions="你是一个报销助手。当用户询问报销相关问题时,加载对应的 skill。",
context_providers=[provider],
)
方法二:带资源的 Skill(复杂场景)¶
from agent_framework import Skill, SkillResource
expense_skill = Skill(
name="expense-rules",
description="处理报销政策咨询",
resources=[
SkillResource(
name="domestic-travel",
description="国内出差报销标准",
content="""
# 国内出差报销标准
## 机票
- 经济舱实报实销
- 高铁二等座实报实销
## 住宿
- 北上广深:600元/天
- 其他城市:400元/天
## 餐饮
- 50元/餐
- 日上限:150元
""",
),
SkillResource(
name="overseas-travel",
description="国外出差报销标准",
content="""
# 国外出差报销标准
## 机票
- 商务舱:需部门 VP 审批
- 经济舱:实报实销
## 住宿
- 参照外事差旅住宿标准
- 上限:100 USD/天
""",
),
],
)
方法三:SKILL.md 文件技能(团队共享)¶
examples/agent_skills/skills/expense/
├── SKILL.md # Skill 定义
└── resources/
├── domestic.md # 国内报销规则
└── overseas.md # 国外报销规则
好 vs 坏对比¶
# 坏做法:所有知识塞进 Instructions
agent = Agent(
client=client,
instructions="""
你是一个报销助手...
[10000 行报销规则]...
"""
)
# 每次调用消耗 10000+ tokens,即使只问一个问题
# 好做法:用 Skills 按需加载
provider = SkillsProvider(skills=[
expense_skill,
travel_skill,
it_skill,
])
agent = Agent(
client=client,
instructions="你是一个专业助手,有多个技能可用。",
context_providers=[provider],
)
# 用户问机票问题 → 只加载 expense-skill 的机票部分 (~500 tokens)
扩展讨论¶
加载阶段详解¶
| 阶段 | Token 消耗 | 说明 |
|---|---|---|
| Advertise | ~100 | 仅 Skill 名称和描述 |
| Load | 按需 | 用户问什么,加载什么 |
| Read resource | 按需 | 仅读取相关 resource |
与 RAG 的对比¶
| 特性 | Skills | RAG |
|---|---|---|
| 加载方式 | 整块加载 | 向量检索 |
| 适用场景 | 结构化知识(规则、手册) | 非结构化文档 |
| Token 控制 | 精确可控 | 依赖检索质量 |
| 更新方式 | Skill 版本化 | 向量索引重建 |
Skill 组合策略¶
# 按领域分组的大型 Skill
finance_skill = Skill(
name="finance",
description="财务相关知识",
resources=[
SkillResource(name="expense", content="..."),
SkillResource(name="budget", content="..."),
SkillResource(name="invoice", content="..."),
],
)
# 按场景分组的小型 Skill
# 用户问题匹配 → 选择性加载
企业级考虑¶
# 1. Skill 版本管理
expense_skill = Skill(
name="expense-rules",
version="2024-Q1", # 带版本号
content="...",
)
# 2. Skill 访问控制
from agent_framework import SkillAccessPolicy
policy = SkillAccessPolicy(
allowed_roles=["finance-team", "admin"],
denied_users=["intern-2024"],
)
# 3. Skill 变更追踪
# Skill 更新时记录变更日志,便于审计
Things to Remember¶
- Skills 解决的是"知识太多,一次性加载太贵"的问题
- Skills = 名称 + 描述 + 内容/资源,按需渐进加载
SkillsProvider管理多个 Skill,通过context_providers注入 Agent- 加载粒度:问什么加载什么,不是加载整个 Skill
- Advertise 阶段只消耗 ~100 tokens,告诉 Agent"你有哪些技能"
- Skills 适合结构化知识(规则、手册、流程),RAG 适合非结构化文档
- Skill 内容应该模块化拆分(按 topic 或场景),而不是做成一个大文件
- 生产环境中 Skill 应版本化管理,便于回滚和审计