使用 Skill 时,如何解决上下文窗口限制问题?
在 Agent 系统里,Skill 是非常好用的能力封装。
比如:
PDF 处理 Skill
Excel 分析 Skill
网页抓取 Skill
代码生成 Skill
图片识别 Skill
邮件编写 Skill
每个 Skill 里面可能包含说明文档、使用规则、示例、脚本、参数定义、异常处理方式。
问题也随之来了:
Skill 内容一多,大模型的上下文窗口很容易被撑爆。
所以使用 Skill 时,不能简单粗暴地把所有内容一次性塞给大模型。
更好的做法是:
少量识别
按需加载
局部读取
结果压缩
外部存储
这篇文章就聊聊:使用 Skill 时,如何解决上下文窗口限制问题。
一、上下文窗口到底是什么?
可以把大模型的上下文窗口理解成一张工作台。
用户问题、历史对话、系统提示词、Skill 文档、工具返回结果,都会被放到这张工作台上。
模型只能看到工作台上的内容。
但这张工作台不是无限大的。
如果内容太多,就会出现几个问题:
放不下
成本升高
响应变慢
重点被稀释
模型容易抓错重点
所以,上下文窗口限制并不只是“Token 不够”的问题。
它更像是一个注意力管理问题。
内容越多,模型越难知道到底什么最重要。
二、为什么 Skill 特别容易占上下文?
因为 Skill 通常不是一句话,而是一包资料。
一个完整 Skill 可能长这样:
metadata.yaml
skill.md
examples/example-1.md
examples/example-2.md
scripts/helper.py
docs/faq.md
docs/best-practices.md
如果每次执行任务都把这些内容全部加载进去,就会非常浪费。
比如用户只是说:
帮我提取 PDF 里的标题
结果系统把 PDF Skill 的全部说明、全部示例、全部脚本都塞给模型。
这就像用户只想查一个单词,你却把整本字典都搬上桌。
能用,但很笨。
三、核心思路:不要一次性全部加载
解决上下文窗口限制的核心原则是:
模型当前需要什么,就只给什么。
不要让模型背完整个 Skill。
而是让模型先知道:
有哪些 Skill
哪个 Skill 可能相关
这个 Skill 怎么用
当前任务需要哪一小段说明
所以使用 Skill 时,最重要的不是“能不能加载”,而是“加载多少”。
四、第一招:只加载 Skill 元数据
第一层只加载最轻的信息。
比如:
name: pdf-processor
description: 处理 PDF 文件,支持文本提取、表格识别、表单解析
这一层通常只有几十个 Token。
它的作用是让模型知道:
系统里有这个能力
这个能力大概能解决什么问题
如果用户任务和 PDF 无关,就不继续加载 PDF Skill。
这样可以避免每次对话都把所有 Skill 文档塞进上下文。
这一层解决的是:
先识别,不展开。
五、第二招:任务匹配后再加载主文档
如果用户任务和某个 Skill 匹配,再加载它的主文档。
比如用户说:
帮我从 PDF 合同里提取甲方、乙方和金额
这时系统判断需要 PDF Skill,于是加载:
skill.md
主文档里一般放核心规则:
# PDF 处理 Skill
## 能力
- 提取文本
- 提取表格
- 提取表单字段
## 使用方式
当用户上传 PDF,并要求解析、提取、总结内容时使用。
## 输出格式
优先输出结构化 JSON。
注意,这里仍然不是加载全部资料。
只是加载最核心的使用说明。
这一层解决的是:
确定要用,再看说明。
六、第三招:需要细节时再加载附件
有时候主文档不够。
比如用户要求:
从 PDF 表单里提取字段,并按照指定 JSON 格式返回
这时可能需要加载示例:
examples/extract-form.md
如果需要执行复杂解析,还可能加载脚本说明:
scripts/pdf-helper.py
但是这些附件不应该默认加载。
只有当任务真的需要时才加载。
这就是渐进式引用:
元数据 → 主文档 → 示例 → 脚本
它解决的是:
先少后多,按需展开。
七、第四招:长文档切片,只取相关片段
有些 Skill 的主文档本身就很长。
比如一个 Excel Skill 可能包含:
读取表格
清洗数据
生成图表
透视分析
公式处理
异常值检测
导出文件
如果用户只问:
帮我生成一个柱状图
那就没必要加载“公式处理”和“透视分析”的内容。
更好的方式是把 Skill 文档切成多个小片段:
chunk_1:读取表格
chunk_2:数据清洗
chunk_3:生成图表
chunk_4:公式处理
chunk_5:导出文件
任务来了之后,只检索最相关的片段。
比如这次只加载:
chunk_3:生成图表
chunk_5:导出文件
这就像查书时只翻相关章节,而不是从第一页读到最后一页。
这一招解决的是:
文档可以很长,但每次只读相关部分。
八、第五招:把示例压缩成规则
示例很有用,但也很占 Token。
比如一个 Skill 里有 10 个完整示例,每个示例几百行。
如果全部塞进上下文,窗口很快就爆了。
更好的做法是先把示例沉淀成简短规则。
例如原始示例很多:
示例 1:提取合同字段
示例 2:提取发票字段
示例 3:提取简历字段
示例 4:提取报名表字段
可以压缩成规则:
字段提取任务优先输出 JSON。
字段不存在时返回空字符串。
金额字段保留原文单位。
日期字段统一为 yyyy-MM-dd。
不要编造 PDF 中不存在的信息。
这样模型不一定需要看完整示例,也能知道该怎么做。
这一招解决的是:
用规则代替大量样例。
九、第六招:给上下文做预算
上下文窗口虽然有限,但可以规划。
比如一次模型调用前,可以给不同内容分配预算:
系统提示词:10%
用户当前问题:15%
历史对话:15%
Skill 文档:30%
工具返回结果:20%
输出格式要求:10%
当然,实际系统里不一定真的按百分比写死。
但一定要有这个意识:
不是谁先来谁占位置,而是谁重要谁留下。
比如当前任务强依赖 Skill,那就减少历史对话。
如果当前任务强依赖用户上传文件,那就减少示例内容。
如果工具返回内容太长,就先摘要再放进去。
上下文管理的本质是排序:
最相关的留下
最不相关的丢掉
太长的先压缩
可以外部存的不要塞进来
十、第七招:中间结果放外部,别都塞回模型
很多人做 Agent 时容易犯一个错误:
每一步工具结果都完整塞回上下文。
比如 PDF 有 100 页,解析后得到几万字。
如果全部塞回模型,窗口肯定爆。
更合理的做法是:
完整内容放外部存储
上下文里只放摘要和引用地址
需要具体内容时再读取局部片段
比如:
{
"fileId": "pdf_001",
"summary": "这是一份采购合同,包含甲方、乙方、金额、交付时间等信息",
"sections": [
{
"title": "合同主体",
"range": "page 1-2"
},
{
"title": "付款条款",
"range": "page 5-6"
}
]
}
模型不需要一开始看到全文。
它先看到摘要和结构。
如果后面要查付款条款,再读取第 5 到 6 页。
这一招解决的是:
上下文里放索引,不放全文。
十一、第八招:工具执行和模型理解分开
Skill 里经常会带脚本。
比如:
scripts/pdf-helper.py
scripts/excel-analyzer.py
scripts/image-cleaner.py
这些脚本不一定要完整塞给模型。
模型真正需要知道的是:
这个脚本能做什么
输入是什么
输出是什么
什么时候调用
而不是脚本每一行怎么写。
所以可以给模型看接口说明:
工具名:extract_pdf_text
输入:PDF 文件路径
输出:按页拆分的文本
用途:当用户要求读取 PDF 内容时使用
真正执行脚本的事情交给后端。
这样可以大幅减少上下文占用。
这一招解决的是:
模型负责决策,工具负责执行。
十二、第九招:历史对话也要裁剪
上下文窗口不只被 Skill 占用,历史对话也会占很多。
如果用户连续聊了很多轮,系统不能每次都把全部历史塞进去。
更好的做法是:
保留最近几轮
保留关键决策
压缩旧对话
丢弃无关闲聊
例如把长历史压缩成:
用户正在实现一个 Agent 工作流系统。
已讨论过图构建、参数传递、Function Calling、OpenAI 兼容协议。
当前正在讨论 Skill 的上下文管理。
这样模型仍然知道背景,但不用读取所有历史原文。
这一招解决的是:
历史要保留结论,不保留废话。
十三、第十招:输出格式单独保留,不能被挤掉
很多任务失败,不是模型能力不行,而是输出格式要求被上下文挤没了。
比如用户要求输出 JSON。
如果上下文太长,模型可能最后忘了格式要求。
所以建议把输出格式要求放在靠近末尾的位置,并保持简短明确。
比如:
请只输出 JSON,不要输出解释。
字段包括:name、amount、date。
缺失字段返回空字符串。
这类规则很短,但非常关键。
不要让它被大量 Skill 文档淹没。
这一招解决的是:
重要格式要求,要靠近最终调用位置。
十四、完整流程可以这样设计
使用 Skill 时,一个比较稳的上下文管理流程是:
用户提出任务
↓
加载 Skill 元数据
↓
匹配相关 Skill
↓
加载 Skill 主文档
↓
判断是否需要示例或脚本
↓
按需加载局部片段
↓
裁剪历史对话
↓
压缩工具返回结果
↓
构造最终 Prompt
↓
检查 Token 是否超限
↓
调用大模型
重点不是某一个技巧,而是整条链路都要控制上下文。
十五、一个简单例子
用户说:
帮我从这个 PDF 合同里提取甲方、乙方、金额和签署日期。
不要这样做:
加载全部 PDF Skill 文档
加载全部示例
加载全部脚本
加载 PDF 全文
加载完整历史对话
然后一起塞给模型
更好的做法是:
1. 通过元数据匹配到 PDF Skill
2. 加载 PDF Skill 主文档
3. 只加载“字段提取”相关片段
4. PDF 先由工具解析
5. 上下文里只放相关页面文本
6. 输出格式明确要求 JSON
7. 字段不存在返回空字符串
最终给模型的内容可以很精简:
任务:从合同文本中提取字段。
字段:
- 甲方
- 乙方
- 金额
- 签署日期
规则:
- 只根据文本提取,不要编造
- 找不到返回空字符串
- 只输出 JSON
合同相关片段:
……
这样上下文更短,模型也更容易抓住重点。
十六、最终总结
使用 Skill 时,解决上下文窗口限制的核心不是“换一个更长上下文的模型”。
更重要的是做好上下文管理。
可以总结成一句话:
Skill 不要一次性全量加载,而要按任务相关性逐步加载、局部读取、压缩表达。
常用方法包括:
只加载元数据
匹配后加载主文档
需要时加载附件
长文档切片检索
示例压缩成规则
工具结果先摘要
中间数据放外部
历史对话做裁剪
输出格式单独保留
调用前检查 Token
如果再压缩成一个架构原则,就是:
上下文窗口里只放当前任务最需要的内容。
Skill 系统真正成熟的标志,不是 Skill 文档写得有多大,而是系统能不能在正确的时候,加载正确的一小部分。
这样既能节省 Token,又能减少干扰,还能让模型更稳定地完成任务。