1. Agent Skills 解决什么问题
随着 Agent 能处理的任务增加,System Prompt 往往会不断堆入数据库规范、报告格式、异常处理、部署步骤和代码模板。把所有任务说明常驻上下文会带来三个问题:
- 当前任务根本用不到的大量指令也会消耗 Token;
- 不相关规则相互干扰,模型更难判断本次应该遵守哪一组流程;
- 同一套操作说明散落在多个 Prompt 中,很难版本化、测试和复用。
Agent Skills 把任务专用知识组织成独立目录。Agent 启动时只看到用于发现的简要信息;确认相关后再读取完整说明,需要时才读取参考资料、模板或脚本。这就是渐进式披露的基本思想。
1.1 Agent Skills 是开放的目录格式
Deep Agents 遵循 Agent Skills Specification。一个 Skill 至少包含 SKILL.md,也可以携带脚本、参考资料和静态资源。
skills/
└── sales-summary/
├── SKILL.md
├── scripts/
│ └── summarize_sales.py
├── references/
│ └── schema.md
└── assets/
└── report-template.md
这种文件系统型能力包可以进入 Git,可以进行代码审查,也可以被不同 Agent Harness 读取。Deep Agents 提供的是发现、Prompt 注入和 Backend 集成;目录规范本身并不只属于某一个框架。
1.2 Skill 不是 Tool 插件
Skill 用说明和资源告诉模型“遇到什么问题、应该遵循什么流程”。Tool 则向模型暴露一个可调用的动作及参数 Schema。
Skill 目录里存在 scripts/summarize_sales.py,不代表 Agent 自动获得一个 summarize_sales Tool。模型必须先读取说明,再通过已有的 execute 或受控业务 Tool 运行它。
同样,Frontmatter 中出现 allowed-tools,也不会让 Deep Agents 自动隐藏其他 Tool。可调用能力仍由 Agent、SubAgent、Middleware、权限策略和执行环境决定。
一些教学实现会手写 Skill 列表、load_skill Tool、State 和 Middleware,再在加载 Skill 时动态加入 Tool。这有助于解释动态工具机制,但不能与 Deep Agents 原生 SkillsMiddleware 混为一谈。
1.3 Skill 与 Tool 怎样配合
以销售汇总为例:
Skill:何时使用、字段要求、执行顺序、失败条件、报告格式
Script:用确定性程序计算收入、成本和利润
execute:为模型提供运行程序的入口
Sandbox:决定脚本在哪个隔离环境执行
Skill 不能替代动作接口,Tool 也不适合承载几百行任务说明。两者组合后,模型负责选择流程,确定性代码负责计算,执行环境负责限制副作用。
2. 渐进式披露怎样工作
2.1 三层内容不是同时进入上下文

| 层级 | 内容 | 加载者 | 进入上下文的时机 |
|---|---|---|---|
| Level 1:Metadata | 规范层主要是 name 和 description | SkillsMiddleware | 配置的 Skill 被发现时 |
| Level 2:Instructions | 完整 SKILL.md 正文 | 模型调用 read_file | 模型判断任务可能匹配时 |
| Level 3:Resources | scripts/、references/、assets/ | 模型按说明读取或执行 | 完成任务确实需要时 |
“按需”不是强制状态机。模型可以不选择任何 Skill,可以同时读取多个 Skill,也可以读取说明后决定不运行脚本。
2.2 State Metadata、实际 Prompt 和规范参考要区分
本文的 sales-summary Frontmatter 是:
---
name: sales-summary
description: 汇总销售 CSV,校验 region、revenue、cost 列并输出收入、成本、利润和数据行数。用户要求销售汇总、区域业绩分析或核对利润时使用。
license: MIT
compatibility: Requires Python 3.12; uses only the Python standard library.
metadata:
author: deepagent-series
version: "1.0"
allowed-tools: read_file execute
---
这里存在三个不同表示:
- skills_metadata State 保存完整解析对象,包括路径、license、compatibility、任意 metadata 和 allowed_tools;
- SkillsMiddleware 根据当前版本的模板生成真实 System Prompt;
- skills-ref.to_prompt() 是规范参考库的渲染结果,不等于 Deep Agents Runtime Prompt。
P06 使用 RecordingChatModel 捕获实际模型请求,确认真实 Prompt 包含 Skill 名称、描述和当前版本渲染的 Allowed tools: read_file, execute,但不包含正文中的“不重新心算”。同时断言实际 Prompt 与 skills-ref.to_prompt() 不相等。
因此,不能拿参考库的字符串格式证明 Runtime 一定使用相同 Prompt,也不能把 State 中的所有字段都称为“始终发给模型”。
2.3 一个典型生命周期由谁负责

| 阶段 | 执行方 | 是否固定 |
|---|---|---|
| 扫描配置 Source 的直接子目录 | SkillsMiddleware | 是 |
| 下载并解析 SKILL.md Frontmatter | SkillsMiddleware | 是 |
| 按名称处理多 Source 覆盖 | SkillsMiddleware | 是 |
| 更新 skills_metadata State | SkillsMiddleware | 是 |
| 将 Skill 列表加入模型请求 | SkillsMiddleware | 是 |
| 判断 Skill 是否相关 | 模型 | 否 |
| 调用 read_file 读取正文 | 模型 | 否 |
| 读取 references / assets | 模型 | 否 |
| 调用 Tool 或执行脚本 | 模型与 Runtime | 否 |
| 校验业务结果 | Skill 指令或应用 | 取决于实现 |
对于纯写作规范 Skill,流程可能在读取正文后结束;对于数据 Skill,模型还可能读取 Schema、执行程序并套用模板。不要把包含脚本的示例画成框架内部强制执行的十步 Workflow。
2.4 Metadata 何时重新扫描
deepagents==0.6.12 的准确条件不是“同一 Session 永远只加载一次”,而是:
if "skills_metadata" in state:
return None
只要传入 State 已经包含 skills_metadata,即使它是空列表,before_agent 也会跳过扫描。若 Checkpointer 在同一 Thread 保存了该 State,外观上就表现为跨 Turn 缓存。
更新磁盘 Skill 后,旧线程不一定立刻看到新 Metadata。可以创建新线程、显式清理对应 State,或者由应用实现受控刷新流程。没有 Checkpointer 的独立调用不应被笼统称为同一 Session。
3. 怎样设计高质量 Skill
3.1 目录职责与加载属性

| 文件或目录 | 发现阶段自动读取 | 是否进入模型上下文 | 是否直接执行 |
|---|---|---|---|
| SKILL.md Frontmatter | 是 | Metadata 摘要 | 否 |
| SKILL.md Body | 否 | 模型按需读取 | 否 |
| scripts/ | 否 | 通常无需读取源码 | 需要 execute 或受控 Tool |
| references/ | 否 | 按需读取 | 否 |
| assets/ | 否 | 按需读取或直接复制 | 通常否 |
关键资源最好能从 SKILL.md 直接发现,但这是提高可靠性的工程建议,不是规范要求必须逐项列出目录里的所有文件。
3.2 Frontmatter 字段
| 字段 | 性质 | 说明 |
|---|---|---|
| name | 必填 | Skill 稳定标识,应与父目录名一致 |
| description | 必填 | 说明做什么以及什么时候使用 |
| license | 可选 | SPDX 名称或随 Skill 提供的许可说明 |
| compatibility | 可选 | 产品、运行时和依赖要求 |
| metadata | 可选 | 客户端自定义字符串映射 |
| allowed-tools | 实验字段 | 声明预批准 Tool,实际强制程度取决于 Agent 实现 |
在 Deep Agents 0.6.12 中,allowed-tools 被解析进 Metadata 并进入当前 Prompt 模板,但不会仅凭这一字段创建强制 Tool 白名单。它是模型可见信息,不是安全权限。
3.3 name 和 description 决定身份与路由
name 应保持短小、稳定并符合规范:
sales-summary good
Sales Summary bad
-sales-summary bad
sales--summary bad
description 是发现阶段最重要的自然语言路由信息。只写“处理销售数据”过于模糊;更好的描述同时包含任务、输入和触发场景:
description: >-
汇总销售 CSV,校验 region、revenue、cost 列并输出收入、成本、利润和
数据行数。用户要求销售汇总、区域业绩分析或核对利润时使用。
如果同时存在 sales-summary、sales-forecast 和 sales-anomaly-review,每个描述必须说明自己的输入、输出和不适用边界,否则模型会犹豫或选错。
3.4 正文应该写成可执行流程
一个可维护的 SKILL.md Body 通常包含:
- 使用条件与不适用条件;
- 输入字段和前置校验;
- 可复现的步骤;
- 何时读取哪个 supporting file;
- 失败时停止还是降级;
- 输出格式与验收标准;
- 不允许猜测或静默修复的边界。
本文 Skill 的关键指令是:
1. 阅读 references/schema.md,校验字段。
2. 运行 scripts/summarize_sales.py <csv-path>。
3. 使用 assets/report-template.md 输出报告。
4. 不重新心算脚本已经生成的数值。
5. 脚本失败时保留错误并停止生成结论。
3.5 长度限制要标明来源
| 规则 | 性质 | 来源 |
|---|---|---|
| name 最多 64 字符 | 规范约束 | Agent Skills Specification |
| description 最多 1024 字符 | 规范约束 | Agent Skills Specification |
| compatibility 最多 500 字符 | 规范约束 | Agent Skills Specification |
| SKILL.md 建议不超过 500 行 | 编写建议 | Agent Skills Specification |
| Body 建议低于 5000 Token | 编写建议 | Deep Agents Skills 文档 |
| 大于 10 MiB 的 SKILL.md 被跳过 | 版本实现 | deepagents==0.6.12 源码 |
| description / compatibility 过长时截断 | 版本实现 | deepagents==0.6.12 源码 |
运行时截断并不代表 Skill 符合规范。CI 中仍然应该使用严格校验器拒绝超限文件。10 MiB 只是防止异常文件拖垮发现过程的保护值,更不是“适合塞入上下文”的容量建议。
3.6 scripts:确定性逻辑应保持确定
本文脚本使用 Decimal 汇总金额,避免直接使用二进制 float 做财务计算:
from decimal import Decimal, InvalidOperation
def parse_money(value: str | None, *, row_number: int, column: str) -> Decimal:
normalized = (value or "").strip().replace(",", "")
if not normalized:
raise ValueError(f"row {row_number}: {column} must not be empty")
try:
amount = Decimal(normalized)
except InvalidOperation as exc:
raise ValueError(
f"row {row_number}: {column} must be a decimal number"
) from exc
if not amount.is_finite():
raise ValueError(f"row {row_number}: {column} must be finite")
return amount
当前业务口径明确规定:
- 空值、NaN、无穷大和非数字输入被拒绝;
- 负数允许表示退款或冲销;
- 重复行按独立业务记录计入,不自动去重;
- 所有记录必须使用同一货币与单位;
- 结果量化到两位小数。
脚本 CLI 还检查参数数量,以 JSON 输出结果。正式财务系统通常还需要币种字段、汇率日期、会计舍入规则和来源去重键。
3.7 references 与 assets
references/schema.md 保存字段口径、异常值和单位说明,避免把细节全部放入主文件。每个 Reference 应保持单一主题,并尽量只从 SKILL.md 深入一层,减少多次读取和路径迷失。
assets/report-template.md 是任务产物模板。某些 Asset 只需要复制,不需要全文进入 Prompt,例如 Word 模板、Logo、配置骨架或二进制样例。说明文件应写清何时使用、复制到哪里以及哪些占位符必须替换。
4. Skill Source 与 Backend
4.1 三种路径不能混用

| 路径类型 | 示例 | 使用位置 |
|---|---|---|
| Backend 虚拟路径 | /skills/sales-summary/SKILL.md | read_file、ls、glob 等文件 Tool |
| LocalShell 工作目录相对路径 | skills/sales-summary/scripts/summarize_sales.py | LocalShellBackend.execute |
| 远程 Sandbox 内绝对路径 | /workspace/skills/sales-summary/scripts/summarize_sales.py | Sandbox execute |
当 FilesystemBackend(root_dir=ROOT, virtual_mode=True) 处理 read_file(“/skills/…”) 时,/skills/ 是 Backend 虚拟路径。Shell 不会自动执行同样的路径映射。
本文 LocalShell 测试之所以能运行下面的命令,是因为 root_dir 同时是 Shell cwd:
python skills/sales-summary/scripts/summarize_sales.py data/sales.csv
远程 Sandbox 中必须先同步 Skill 和数据,再使用环境内部的真实路径:
python /workspace/skills/sales-summary/scripts/summarize_sales.py \
/workspace/data/sales.csv
不要把 Backend VFS 路径直接交给 Shell,也不要假设 virtual_mode=True 会重写命令字符串。
4.2 SDK Source 与产品级自动发现
create_deep_agent(…, skills=[…]) 只加载显式配置的 Source。Deep Agents Code 或 CLI 可以有自己的用户级、Agent 级和项目级发现目录,但目录名称与优先级属于具体产品版本。
SDK 文章不应笼统宣称某四个目录永远会被扫描。需要模拟 CLI 分层时,应依据目标 CLI 版本取得 Source 顺序,再显式传给 skills。
4.3 Source 覆盖、Label 与非法 Skill
多个 Source 按声明顺序加载,解析成功的同名 Skill 使用 last-one-wins:
skills=[
"/skills/base/",
"/skills/team/",
"/skills/project/",
]
P06 不仅测试 project 覆盖 base,还测试了后置 Source 中同名但缺少 description 的非法 Skill。0.6.12 会跳过非法项,因此早先加载的有效 Skill 保留,不会被无效内容覆盖。
以下 API 结论严格限定在 deepagents==0.6.12:
create_deep_agent(..., skills: list[str] | None)
SkillsMiddleware(..., sources: Sequence[str | tuple[str, str]], system_prompt=...)
直接构造 SkillsMiddleware 时可以用 (path, label) 区分同名来源;Label 影响当前 System Prompt 的来源展示,不改变覆盖顺序。create_deep_agent 的 skills 参数仍只接受 list[str]。自定义 Prompt 必须传入包含规定占位符的 system_prompt,或者编写版本特定 Middleware,并不是任意模板都能直接使用。
4.4 State 中已有 Metadata 时跳过扫描
State 是否包含 skills_metadata 才是缓存条件。磁盘文件变化、相同 thread_id 或新的 Run 本身都不是完整判断依据。
加载失败可能包含源路径或 Backend 错误等不可信文本。0.6.12 的实现会把错误放入
这只是当前版本的防御性渲染,不等于加载错误“绝对安全”。应用仍应限制 Source、目录名和错误内容,不应把警告当成新的模型指令。
4.5 StateBackend 必须注入完整 Skill
默认 StateBackend 没有 Host 目录,需要在输入 State 中提供文件,并使用 create_file_data() 生成 Backend v2 数据结构:
from deepagents.backends.utils import create_file_data
files = {
"/skills/sales-summary/SKILL.md": create_file_data(skill_text),
"/skills/sales-summary/scripts/summarize_sales.py": create_file_data(script_text),
"/skills/sales-summary/references/schema.md": create_file_data(schema_text),
"/skills/sales-summary/assets/report-template.md": create_file_data(template_text),
}
result = agent.invoke({
"messages": [{"role": "user", "content": "汇总销售数据"}],
"files": files,
})
本文的 build_state_skill_files() 读取并注入全部四个文件,测试实际 Agent Prompt 能发现 Skill。
0.6.12 的 files State 使用 Delta reducer:新路径与旧路径合并,同名路径由右侧更新覆盖,值为 None 表示删除。初始化时重复注入正式路径可能覆盖线程里的已有版本,因此输入文件集合也需要版本、哈希和覆盖测试。大文件或跨线程共享库不适合长期放在 State 中。
4.6 StoreBackend 必须注入 Store 和可信身份
from dataclasses import dataclass
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StateBackend, StoreBackend
from langgraph.store.memory import InMemoryStore
@dataclass(frozen=True)
class TenantContext:
tenant_id: str
user_id: str
store = InMemoryStore()
backend = CompositeBackend(
default=StateBackend(),
routes={
"/skills/": StoreBackend(
namespace=lambda runtime: (
runtime.context.tenant_id,
runtime.context.user_id,
"skills",
),
)
},
)
agent = create_deep_agent(
model=model,
backend=backend,
skills=["/skills/"],
context_schema=TenantContext,
store=store,
)
本地程序必须显式传 store=InMemoryStore() 或持久 Store;部署平台可以由 Runtime 提供托管 Store。InMemoryStore 只适合测试和单进程开发,不提供跨进程持久化。
身份应由应用认证层写入 Runtime Context,不能从用户 Prompt 提取。P06 在同一个 Agent 和 Store 中写入 tenant A 与 tenant B 的不同描述,分别调用后捕获真实 System Prompt,确认两个 Namespace 不会串读。
Composite 会在转交 /skills/ route 时移除前缀,因此 Store 中的 Key 是 /sales-summary/SKILL.md,而不是再次保存 /skills/sales-summary/SKILL.md。
4.7 Skill 可读不等于脚本可执行
StateBackend、StoreBackend 和普通 FilesystemBackend 可以让文件 Tool 读取 Skill,但不一定提供可执行 Shell。
例如:
/skills/ -> StoreBackend
default -> Daytona Sandbox
模型可以从 Store 读取脚本源码,但 Daytona 文件系统中并没有这个文件。应用必须将经过验证的 Skill 版本同步到 Sandbox,只读挂载或上传到固定路径,再执行 Sandbox 内路径。
这也是为什么“拒绝内置 write_file”不能证明脚本不会被复制。只要仍允许任意 execute,Shell 就可以通过 cp、重定向或 Python 写文件。强边界应该来自只读挂载、内容哈希、签名、固定入口包装器和隔离执行环境。
5. Skill、SubAgent、Tool 与 Memory 怎样选择
5.1 四种机制解决的问题不同

| 机制 | 最适合保存或执行什么 | 是否按需进入上下文 | 是否独立上下文 |
|---|---|---|---|
| Skill | 可复用流程、领域规则、参考资料和脚本 | 是 | 否 |
| Tool | 参数化动作和外部系统能力 | Tool Schema 常驻 | 否 |
| Memory | 用户偏好、项目约定和跨会话事实 | 取决于实现 | 否 |
| SubAgent | 独立角色、模型、Tool、上下文和并行任务 | 委派时 | 是 |
需要“如何完成”时考虑 Skill,需要“执行动作”时使用 Tool,需要“长期记住”时使用 Memory,需要“上下文隔离或角色分工”时使用 SubAgent。
5.2 Skills 与 Multi-Agent 的取舍
Skills 与 Multi-Agent 的关键差异不是哪个更高级,而是任务是否需要独立上下文、独立角色和独立工具配置。
同一个 Agent 偶尔需要 PDF、销售和发布流程时,可以配置多个互不重叠的 Skill;如果任务需要 Analyst 生成结论、Reviewer 独立质疑并返回结构化审核结果,SubAgent 更合适。
两者也可以组合:主 Agent 使用规划 Skill,Analyst SubAgent 使用数据分析 Skill,Reviewer 使用审核规范 Skill。
5.3 general-purpose 与自定义 SubAgent
Deep Agents 自动添加的 general-purpose SubAgent 会继承主 Agent Skills。普通自定义 SubAgent不会自动继承,必须显式配置:
researcher = {
"name": "researcher",
"description": "执行资料研究",
"system_prompt": "只做资料检索和证据整理。",
"tools": [web_search],
"skills": ["/skills/research/"],
}
如果用自定义同名 general-purpose 替换默认实例,也不能继续假设自动继承,应检查并显式设置其 Skills。
每个带 Skills 的 Agent 图都有自己的 SkillsMiddleware 和 Metadata State。即使主 Agent 与 SubAgent 共享同一个文件 Backend,也不代表它们自动共享已加载 State。
5.4 按角色预配置 Source
SKILLS_BY_ROLE = {
"engineering": ["/skills/code-review/", "/skills/testing/"],
"data": ["/skills/sql-analysis/", "/skills/visualization/"],
"support": ["/skills/ticket-triage/", "/skills/runbook/"],
}
这属于静态配置分流,不是 Middleware 在一次 Run 中动态改变 Source。生产应用通常在启动阶段构造并缓存少量角色 Agent,避免每个请求重复装配图与 Middleware。
真正按 Runtime Context 解析 Source 时,可以编写受控 Middleware 或使用 StoreBackend Namespace。动态逻辑必须从认证 Context 读取身份,不能信任 Prompt 自报角色。
6. 安全、治理与发布
6.1 allowed-tools 不是强制权限
Agent Skills 规范把 allowed-tools 定义为实验性的预批准 Tool 声明。Deep Agents 0.6.12 会展示它,但不会自动变成强制 Tool allowlist。
安全边界必须由实际 Tool 集合、SubAgent 配置、Middleware、FilesystemPermission、Backend Policy 和 Sandbox 建立。不能因为 Skill 写了:
allowed-tools: read_file execute
就认为其他 Tool 已经不可调用,或者 execute 只能执行 Skill 目录下的程序。
6.2 第三方 Skill 是供应链输入
导入第三方 Skill 等同于同时导入 Prompt、代码、模板和依赖声明。至少要检查:
- 仓库来源、提交哈希和发布签名;
- SKILL.md 是否包含越权、外传或关闭安全检查的指令;
- scripts 内容哈希与静态扫描结果;
- 依赖 Lockfile、SBOM 和安装脚本;
- 网络访问域名和数据出口;
- 需要的 Tool、目录、密钥与资源配额;
- 运行产物的路径、类型与哈希。
不要允许 Skill 在运行时安装任意最新版本依赖。依赖应由镜像或锁文件提前固定。
6.3 正式 Source 只读,Agent 只生成 Draft
推荐分层:
/skills/system/ 平台维护,只读
/skills/team/ 审核后发布,只读消费
/skills/user/ 受用户身份与审批约束
/draft-skills/ Agent 可以生成草稿
Agent 不应直接修改正式 Source。草稿发布流程至少经过规范校验、代码测试、安全扫描、内容哈希、独立审批和原子发布。
只拒绝 write_file 不足以实现只读,因为 execute 可以绕过文件 Tool。只读必须落实在 Store 授权、对象版本、只读挂载或独立发布服务中。
6.4 规范校验器的真实命令
本文安装的是:
skills-ref==0.1.1
console script: agentskills -> skills_ref.cli:main
因此本地实际命令是:
.venv_deepagent/bin/agentskills validate \
p06_skills/skills/sales-summary
结果:
Valid skill: p06_skills/skills/sales-summary
Agent Skills 网站或其他版本可能展示 skills-ref validate。Console Script 名称应通过安装版本的 entry point 或 pip show -f skills-ref 核实,不能把某个版本的命令写成永久规范。
6.5 版本字段、哈希和发布策略
metadata.version 只是任意字符串 Metadata,不会自动提供 SemVer 解析、依赖求解、升级或兼容性管理。生产团队需要自行定义:
- Skill 版本号与兼容范围;
- 目录或包发布方式;
- SKILL、脚本和依赖锁的内容哈希;
- 发布签名和审批记录;
- 回滚与废弃策略;
- Agent / Runtime 兼容矩阵。
当前 summarize_sales.py 发布文件的 SHA-256 是:
b5674a03d16a6df6adcb1c994717c42dc45610ed52b830137c4bdc7e126da3ad
哈希需要与代码一起更新。在线执行时还应记录“实际执行文件”的哈希;只记录仓库文件哈希不能证明远程 Sandbox 中运行的是同一内容。
6.6 可观测性记录什么
Skill 激活并不是名为 skill 的独立 Tool Call。观测时应关联:
- 当前 State 加载的 Skill 名称、路径和来源;
- skills_load_errors 以及被截断的告警数量;
- 模型是否调用 read_file 读取 SKILL.md;
- 是否读取 references 与 assets;
- 实际执行的 Tool、命令、退出码与脚本哈希;
- 输入、输出和 Sandbox 产物哈希;
- 每次模型请求与整个 Agent Loop 的 Token、延迟和错误。
日志和 Trace 不能记录密钥或敏感文件全文。
7. 测试、真实模型结果与故障排查
7.1 当前离线验证
cd source/_posts/deepagent/examples
.venv_deepagent/bin/python -m ruff check p06_skills
.venv_deepagent/bin/pytest p06_skills -q
.venv_deepagent/bin/agentskills validate \
p06_skills/skills/sales-summary
实际结果:
All checks passed!
..................... [100%]
21 passed
Valid skill: p06_skills/skills/sales-summary
覆盖范围包括:
- Agent Skills 规范校验;
- skills-ref 与真实 Runtime Prompt 的区别;
- Metadata 加载与 State 缓存条件;
- 有效 Source 覆盖和非法后置 Source;
- Decimal 金额、缺失列、空值、非有限值和 CLI JSON;
- 完整 StateBackend Skill 文件集合;
- Runtime Store 注入与多租户 Prompt 隔离;
- Source Label、加载告警转义与截断;
- 锁定版本 agentskills Console Script;
- 原生 Skills Agent 离线构造。
7.2 真实模型端到端记录
仓库中的 live_result.json 保存了一次真实在线记录:
| 项目 | 值 |
|---|---|
| 模型端点 | 第三方 OpenAI 兼容网关 www.juaiapi.com/v1 |
| 模型 ID | gpt-5.5,属于网关别名,不作为官方模型身份保证 |
| Deep Agents | 0.6.12 |
| Backend | LocalShellBackend |
| Host 环境继承 | 关闭 |
| 延迟 | 15397 ms |
| 累计输入 Token | 35518 |
| 累计输出 Token | 224 |
实际 Tool 顺序为:
[
{"name": "read_file", "file_path": "/skills/sales-summary/SKILL.md"},
{"name": "read_file", "file_path": "/skills/sales-summary/references/schema.md"},
{"name": "read_file", "file_path": "/skills/sales-summary/assets/report-template.md"},
{
"name": "execute",
"command": "python skills/sales-summary/scripts/summarize_sales.py data/sales.csv"
}
]
结果是 3 行、收入 300、成本 190、利润 110,与本地确定性断言一致。
35518 个输入 Token 是整个多轮 Agent Loop 的累计值,不是一次三行 CSV 请求的单轮 Prompt。每次模型调用都会再次携带 System Prompt、Tool Schema 和不断增长的消息历史;四次 Tool Call 加最终回答导致累计输入明显放大。优化方向包括缩短全局 Prompt、减少无关 Tool Schema、避免无必要读取模板、使用更窄的 SubAgent,以及检查网关 Usage 统计语义。
本次记录只证明那次请求真实发生了四个 Tool Call。它不能证明所有模型都会按同一顺序工作,也不能证明 allowed-tools 形成强制权限,不能证明 LocalShell 没有其他副作用,更不能证明渐进式披露一定节省总 Token。
本轮将脚本改成 Decimal 并为后续 live_demo 增加执行前后 SHA-256 检查,但当前工作区没有在线凭据,因此没有把旧记录伪装成新代码的在线复验。当前 Decimal、哈希和租户隔离改动只完成了离线测试。执行前后哈希相同也只能发现持久修改,不能代替只读挂载和 Sandbox。
7.3 LocalShell 验证边界
在线记录中的 inherit_env=False 只减少环境变量泄露,不提供 OS 级隔离。LocalShellBackend 仍然拥有完整 Host Shell 能力,可以复制、改写或删除可访问文件。
处理不可信 Skill 必须使用隔离 Sandbox、只读 Skill 挂载、受控入口和网络策略。write_file deny 只能限制内置文件 Tool,不能限制 Shell。
7.4 常见错误
| 现象 | 原因 | 处理方式 |
|---|---|---|
| Agent 没发现 Skill | Source 未传入或 Backend 路径错误 | 检查 skills、root 和 Prompt Trace |
| 看到 Metadata 但不读取正文 | description 与任务不匹配 | 补充做什么、何时使用和关键词 |
| read_file 成功但脚本路径失败 | 把 VFS 路径当成 Shell 路径 | 按执行环境映射实际路径 |
| State Skill 激活后缺文件 | 只注入了 SKILL.md | 注入完整 scripts/references/assets 集合 |
| StoreBackend 缺少 Store | Agent 没传 store= | 本地注入 Store,部署核对平台 Runtime |
| 租户看到别人的 Skill | Namespace 身份不完整或来自 Prompt | 使用可信 Runtime Context 并做隔离测试 |
| 修改 Skill 后仍显示旧描述 | State 已含 skills_metadata | 新线程或受控刷新 Metadata State |
| 后置非法 Skill 没有覆盖 | 解析失败后被跳过 | 查看日志并运行规范校验器 |
| 自定义 SubAgent 看不到 Skill | 自定义实例不自动继承 | 在 SubAgent 中显式配置 skills |
| 禁止 write_file 后仍能写文件 | execute 绕过文件 Tool | 存储层只读或禁止任意 Shell |
| 校验器命令不存在 | Console Script 随版本不同 | 检查包 entry points |
8. 总结
Agent Skills 是开放的文件系统型能力包规范。它用 Metadata、完整说明和 supporting resources 三层披露,把任务专用上下文从全局 Prompt 中移出。SkillsMiddleware 负责 Source 扫描、Frontmatter 解析、State 更新和 Prompt 注入;是否选择 Skill、读取哪些资源和执行什么动作仍然由模型与 Runtime 决定。
Skill 不会自动注册 Tool,allowed-tools 也不是 Deep Agents 0.6.12 中的强制安全白名单。尤其要区分 Backend 虚拟路径、LocalShell 工作目录路径和远程 Sandbox 内路径;禁止 write_file 无法阻止 Shell 写文件。
生产环境应把正式 Skill Source 设为只读,通过 Runtime Context 和 Store Namespace 隔离租户,并为脚本建立 Decimal 等确定性计算、依赖锁、内容哈希、签名、Sandbox、网络限制、发布审批和可观测性。StateBackend、StoreBackend 和 FilesystemBackend 只是不同的存储边界,执行安全仍然需要单独设计。
下一篇将比较 Skills 与 Memory,继续解释哪些信息应该按需加载、哪些项目约定应该始终进入上下文,以及如何使用 Offloading、Summarization 和长期 Store 控制上下文成本。