什么是技能
技能是一组文件夹目录,每个文件夹包含一个或多个智能体可以使用的上下文文件:- 一个
SKILL.md文件,包含技能的指令和元数据 - 额外的脚本(可选)
- 额外的参考信息,如文档(可选)
- 额外的资源,如模板和其他资源(可选)
任何额外的资源(脚本、文档、模板或其他资源)必须在
SKILL.md 文件中引用,说明文件的内容和使用方式,以便智能体能够决定何时使用它们。技能的工作原理
当你创建深度智能体时,可以传入一个包含技能的目录列表。智能体启动时,会读取每个SKILL.md 文件的 frontmatter。
当智能体接收到提示时,它会检查在满足提示的过程中是否可以使用任何技能。如果找到匹配的提示,它会查看技能文件的其余内容。这种仅在需要时才查看技能信息的模式称为渐进式披露。
示例
你可能有一个技能文件夹,包含一个以特定方式使用文档站点的技能,以及另一个搜索 arXiv 预印本研究论文库的技能: skills/
├── langgraph-docs
│ └── SKILL.md
└── arxiv_search
├── SKILL.md
└── arxiv_search.py # 搜索 arXiv 的代码
SKILL.md 文件始终遵循相同的模式,以 frontmatter 中的元数据开头,后跟技能的指令。
以下示例展示了一个在收到提示时提供相关 LangGraph 文档指导的技能:
---
name: langgraph-docs
description: Use this skill for requests related to LangGraph in order to fetch relevant documentation to provide accurate, up-to-date guidance.
module: index.ts
---
# langgraph-docs
## Overview
This skill explains how to access LangGraph Python documentation to help answer questions and guide implementation.
## Instructions
### 1. Fetch the Documentation Index
Use the fetch_url tool to read the following URL:
https://docs.langchain.com/llms.txt
This provides a structured list of all available documentation with descriptions.
### 2. Select Relevant Documentation
Based on the question, identify 2-4 most relevant documentation URLs from the index. Prioritize:
- Specific how-to guides for implementation questions
- Core concept pages for understanding questions
- Tutorials for end-to-end examples
- Reference docs for API details
### 3. Fetch Selected Documentation
Use the fetch_url tool to read the selected documentation URLs.
### 4. Provide accurate guidance
After reading the documentation, answer the user's question using the relevant LangGraph docs you fetched.
In your response:
- Give a direct answer first.
- Include the minimum necessary context and any key steps or API names.
- Avoid quoting long passages. Paraphrase and link instead.
### 5. Provide the regular links for the used references
At the end of your response, include a **References** section listing the page URLs you used.
`llms.txt` uses Markdown link targets that typically end in `.md`. Use the helper from this skill module to resolve those into the actual page URLs before listing them as references.
```typescript
const { resolveLlmsUrl } = await import("@/skills/langgraph-docs");
// llms.txt uses Markdown link targets that typically end in `.md`.
// Convert those into the actual page URLs before fetching.
const llmsUrls = [
"https://docs.langchain.com/oss/langgraph/concepts.md",
"https://docs.langchain.com/oss/langgraph/concepts.md",
"https://docs.langchain.com/oss/langgraph/tutorials.md",
];
const pageUrls = [...new Set(llmsUrls.map(resolveLlmsUrl))];
pageUrls;
```
index.ts 中:
// index.ts
export function resolveLlmsUrl(url: string) {
return url.endsWith(".md") ? url.slice(0, -3) : url;
}
重要有关编写技能文件的约束和最佳实践,请参阅完整的 Agent Skills 规范。注意:
- 如果
description字段超过 1024 个字符,将被截断。 - 在深度智能体中,
SKILL.md文件必须小于 10 MB。超过此限制的文件在技能加载时会被跳过。
完整示例
以下示例展示了使用所有可用 frontmatter 字段的SKILL.md 文件:
---
name: langgraph-docs
description: Use this skill for requests related to LangGraph in order to fetch relevant documentation to provide accurate, up-to-date guidance.
license: MIT
compatibility: Requires internet access for fetching documentation URLs
metadata:
author: langchain
version: "1.0"
allowed-tools: fetch_url
module: index.ts
---
# langgraph-docs
## Overview
This skill explains how to access LangGraph Python documentation to help answer questions and guide implementation.
## Instructions
### 1. Fetch the documentation index
Use the fetch_url tool to read the following URL:
https://docs.langchain.com/llms.txt
This provides a structured list of all available documentation with descriptions.
### 2. Select relevant documentation
Based on the question, identify 2-4 most relevant documentation URLs from the index. Prioritize:
- Specific how-to guides for implementation questions
- Core concept pages for understanding questions
- Tutorials for end-to-end examples
- Reference docs for API details
### 3. Fetch selected documentation
Use the fetch_url tool to read the selected documentation URLs.
### 4. Provide accurate guidance
After reading the documentation, answer the user's question using the relevant LangGraph docs you fetched.
In your response:
- Give a direct answer first.
- Include the minimum necessary context and any key steps or API names.
- Avoid quoting long passages. Paraphrase and link instead.
### 5. Provide the regular links for the used references
At the end of your response, include a **References** section listing the page URLs you used.
`llms.txt` uses Markdown link targets that typically end in `.md`. Use the helper from this skill module to resolve those into the actual page URLs before listing them as references.
```typescript
const { resolveLlmsUrl } = await import("@/skills/langgraph-docs");
// llms.txt uses Markdown link targets that typically end in `.md`.
// Convert those into the actual page URLs before fetching.
const llmsUrls = [
"https://docs.langchain.com/oss/langgraph/concepts.md",
"https://docs.langchain.com/oss/langgraph/concepts.md",
"https://docs.langchain.com/oss/langgraph/tutorials.md",
];
const pageUrls = [...new Set(llmsUrls.map(resolveLlmsUrl))];
pageUrls;
```
用法
创建深度智能体时传入技能目录:- StateBackend
- StoreBackend
- FilesystemBackend
from urllib.request import urlopen
from deepagents import create_deep_agent
from deepagents.backends.utils import create_file_data
from langgraph.checkpoint.memory import MemorySaver
from deepagents.backends import StateBackend
from langchain_quickjs import CodeInterpreterMiddleware
checkpointer = MemorySaver()
backend = StateBackend()
skill_url = "https://raw.githubusercontent.com/langchain-ai/deepagents/refs/heads/main/libs/cli/examples/skills/langgraph-docs/SKILL.md"
with urlopen(skill_url) as response:
skill_content = response.read().decode('utf-8')
skills_files = {
"/skills/langgraph-docs/SKILL.md": create_file_data(skill_content)
}
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
backend=backend,
skills=["/skills/"],
checkpointer=checkpointer,
middleware=[CodeInterpreterMiddleware(skills_backend=backend)] # for interpreter skills
)
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "What is langgraph?",
}
],
# Seed the default StateBackend's in-state filesystem (virtual paths must start with "/").
"files": skills_files
},
config={"configurable": {"thread_id": "12345"}},
)
from urllib.request import urlopen
from deepagents import create_deep_agent
from deepagents.backends import StoreBackend
from deepagents.backends.utils import create_file_data
from langgraph.store.memory import InMemoryStore
from langchain_quickjs import CodeInterpreterMiddleware
store = InMemoryStore()
backend = StoreBackend()
skill_url = "https://raw.githubusercontent.com/langchain-ai/deepagents/refs/heads/main/libs/cli/examples/skills/langgraph-docs/SKILL.md"
with urlopen(skill_url) as response:
skill_content = response.read().decode('utf-8')
store.put(
namespace=("filesystem",),
key="/skills/langgraph-docs/SKILL.md",
value=create_file_data(skill_content)
)
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
backend=backend,
store=store,
skills=["/skills/"],
middleware=[CodeInterpreterMiddleware(skills_backend=backend)] # for interpreter skills
)
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "What is langgraph?",
}
]
},
config={"configurable": {"thread_id": "12345"}},
)
from deepagents import create_deep_agent
from langgraph.checkpoint.memory import MemorySaver
from deepagents.backends.filesystem import FilesystemBackend
from langchain_quickjs import CodeInterpreterMiddleware
# Checkpointer is REQUIRED for human-in-the-loop
checkpointer = MemorySaver()
backend = FilesystemBackend(root_dir="/Users/user/{project}")
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
backend=,
skills=["/Users/user/{project}/skills/"],
interrupt_on={
"write_file": True, # Default: approve, edit, reject
"read_file": False, # No interrupts needed
"edit_file": True # Default: approve, edit, reject
},
checkpointer=checkpointer, # Required!
middleware=[CodeInterpreterMiddleware(skills_backend=backend)] # for interpreter skills
)
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "What is langgraph?",
}
]
},
config={"configurable": {"thread_id": "12345"}},
)
list[str]
技能源路径列表。路径必须使用正斜杠指定,相对于后端的根目录。
- 如果省略,不加载任何技能。
- 使用
StateBackend(默认)时,通过invoke(files={...})提供技能文件。使用deepagents.backends.utils中的create_file_data()格式化文件内容;不支持原始字符串。 - 使用
FilesystemBackend时,技能从相对于后端root_dir的磁盘加载。
SDK 只加载你在
skills 中传入的源。它不会自动扫描 CLI 目录,如 ~/.deepagents/... 或 ~/.agents/...。有关 CLI 存储约定,请参阅应用数据。在 SDK 中模拟 CLI 源顺序
在 SDK 中模拟 CLI 源顺序
如果你想在 SDK 代码中实现 CLI 风格的分层,请按从低到高优先级的顺序显式传入所有所需的源:然后将该有序列表作为
[
"<user-home>/.deepagents/{agent}/skills/",
"<user-home>/.agents/skills/",
"<project-root>/.deepagents/skills/",
"<project-root>/.agents/skills/",
]
skills 传入创建智能体。源优先级
当多个技能源包含同名技能时,skills 数组中后列出的源的技能优先(后者优先)。这让你可以从不同来源分层技能。
# 如果两个源都包含名为 "web-search" 的技能,
# 来自 "/skills/project/" 的版本优先(最后加载)。
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
skills=["/skills/user/", "/skills/project/"],
...
)
子智能体的技能
使用子智能体时,你可以配置每种类型可以访问哪些技能:- 通用子智能体:当你向
create_deep_agent传入skills时,自动继承主智能体的技能。无需额外配置。 - 自定义子智能体:不继承主智能体的技能。在每个子智能体定义中添加
skills参数,指定该子智能体的技能源路径。
from deepagents import create_deep_agent
research_subagent = {
"name": "researcher",
"description": "Research assistant with specialized skills",
"system_prompt": "You are a researcher.",
"tools": [web_search],
"skills": ["/skills/research/", "/skills/web-search/"], # 子智能体专属技能
}
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
skills=["/skills/main/"], # 主智能体和通用子智能体获得这些
subagents=[research_subagent], # 研究员只获得自己的技能
)
智能体看到的内容
配置技能后,“技能系统”部分会被注入到智能体的系统提示词中。智能体使用这些信息遵循三步流程:- 匹配——当用户提示到达时,智能体检查是否有技能的描述与任务匹配。
- 读取——如果某个技能适用,智能体使用其技能列表中显示的路径读取完整的
SKILL.md文件。 - 执行——智能体遵循技能的指令并根据需要访问任何支持文件(脚本、模板、参考文档)。
在你的
SKILL.md frontmatter 中编写清晰、具体的描述。智能体仅根据描述决定是否使用某个技能——详细的描述能带来更好的技能匹配。使用技能执行代码
技能支持两种代码执行方式:- 使用解释器技能——当智能体需要从解释器代码中使用可复用的可导入辅助函数时。
- 在沙箱中执行技能脚本——当智能体需要安装依赖、运行测试、调用 CLI 或使用操作系统文件系统时。
使用解释器技能
解释器技能是向解释器暴露代码模块的技能。常规技能为智能体提供指令和上下文。解释器技能还为智能体提供可从解释器代码中调用的可导入函数。 这让你可以将领域特定的逻辑打包一次,并在智能体的工作空间中作为确定性的构建块使用。智能体可以导入经过测试的辅助函数并与工具、子智能体和运行时状态组合,而非要求模型从头重新创建解析器、评分器、规范化器、验证器或聚合程序。 当代码需要以下特性时,请使用解释器技能:- 可复用——跨提示、智能体或项目使用。
- 确定性——每次都需要相同的行为。
- 过于详细——不适合作为指令保留在模型上下文中。
- 在更大工作流中有用——如对搜索结果评分、规范化 API 响应、验证记录、分组行,或将数据转换为可报告的格式。
1
添加模块入口
在技能的
SKILL.md frontmatter 中添加 module 键。值是相对于技能目录的 JavaScript 或 TypeScript 文件路径。2
正常配置技能
创建智能体时,使用
skills 参数传入技能源路径。3
使用相同的后端
用与
SkillsMiddleware 加载技能文件相同的后端来配置解释器中间件。4
从解释器代码中导入
智能体使用
await import("@/skills/<name>") 导入辅助模块。skills/
`-- order-helpers/
|-- SKILL.md
`-- index.ts
---
name: order-helpers
description: Helper functions for normalizing and grouping order records.
module: index.ts
---
# order-helpers
Use this skill when order records need deterministic cleanup or aggregation.
Import these utilities into the REPL in order to interact with order data:
```typescript
const { groupByStatus } = await import("@/skills/order-helpers");
groupByStatus(...);
```
// skills/order-helpers/index.ts
interface Order {
id: string;
status: string;
}
export function groupByStatus(orders: Order[]) {
return orders.reduce((acc, order) => {
acc[order.status] = acc[order.status] ?? [];
acc[order.status].push(order);
return acc;
}, {});
}
from deepagents import create_deep_agent
from deepagents.backends import StateBackend
from langchain_quickjs import CodeInterpreterMiddleware
backend = StateBackend()
agent = create_deep_agent(
model="openai:gpt-5.4",
backend=backend,
skills=["/skills/"],
middleware=[CodeInterpreterMiddleware(skills_backend=backend)],
)
const { groupByStatus } = await import("@/skills/order-helpers");
const grouped = groupByStatus(orders);
grouped;
在沙箱中执行技能脚本
技能可以在SKILL.md 文件旁包含脚本,例如执行搜索或数据转换的 Python 文件。智能体可以从任何后端 读取 这些脚本,但要 执行 它们,智能体需要访问 shell——只有沙箱后端才提供。
当你使用 CompositeBackend 将技能路由到 StoreBackend 进行持久化,同时使用沙箱作为默认后端时,技能文件存在于 store 中而非沙箱中,而代码在沙箱中运行。为了让沙箱能够使用这些脚本,你必须使用自定义中间件在智能体启动前将技能脚本上传到沙箱中:
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="openai:gpt-5.4",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="anthropic:claude-sonnet-4-6",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="openrouter:anthropic/claude-sonnet-4-6",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="baseten:zai-org/GLM-5",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
import asyncio
from pathlib import Path
from typing import Any
from daytona import Daytona
from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState
from langchain_daytona import DaytonaSandbox
from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")
class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
"""Copy shared skill files from the store into the sandbox before each agent run."""
def __init__(self, backend: CompositeBackend) -> None:
super().__init__()
self.backend = backend
async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
store = runtime.store
files: list[tuple[str, bytes]] = []
for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
key = str(item.key)
if ".." in key or any(c in key for c in ("*", "?")):
msg = f"Invalid key: {key}"
raise ValueError(msg)
normalized = key if key.startswith("/") else f"/{key}"
# CompositeBackend routes paths and batches uploads to the right backend.
files.append((f"/skills{normalized}", item.value["content"].encode()))
if files:
await self.backend.aupload_files(files)
async def seed_skill_store(store: InMemoryStore) -> None:
"""Load canonical skill files from disk into the shared store namespace (run once at deploy).
You can retrieve skills from any source (local filesystem, remote URL, etc.).
"""
skills_dir = Path(__file__).resolve().parent / "skills"
for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
rel = file_path.relative_to(skills_dir).as_posix()
key = f"/{rel}"
await store.aput(
SKILLS_SHARED_NAMESPACE,
key,
create_file_data(file_path.read_text(encoding="utf-8")),
)
async def main() -> None:
store = InMemoryStore()
await seed_skill_store(store)
daytona = Daytona()
sandbox = daytona.create()
sandbox_backend = DaytonaSandbox(sandbox=sandbox)
backend = CompositeBackend(
default=sandbox_backend,
routes={
"/skills/": StoreBackend(
store=store,
namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
),
},
)
try:
agent = create_deep_agent(
model="ollama:devstral-2",
backend=backend,
skills=["/skills/"],
store=store,
middleware=[SkillSandboxSyncMiddleware(backend)],
)
finally:
sandbox.stop()
if __name__ == "__main__":
asyncio.run(main())
before_agent 钩子在每次智能体调用前运行,从共享命名空间读取技能文件并将它们上传到沙箱文件系统中。同步完成后,智能体可以使用 execute 工具像沙箱中的任何其他文件一样执行脚本。
有关还包含双向同步记忆的更完整示例,请参阅使用自定义中间件同步技能和记忆。
技能 vs. 记忆
技能和记忆(AGENTS.md 文件)服务于不同的目的:
| 技能 | 记忆 | |
|---|---|---|
| 用途 | 通过渐进式披露发现的按需能力 | 启动时始终加载的持久上下文 |
| 加载方式 | 仅在智能体判断相关时读取 | 始终注入系统提示词 |
| 格式 | 命名目录中的 SKILL.md | AGENTS.md 文件 |
| 分层 | 用户 → 项目(后者优先) | 用户 → 项目(合并) |
| 使用场景 | 指令针对特定任务且可能很大时 | 上下文始终相关时(项目规范、偏好) |
何时使用技能和工具
以下是使用工具和技能的一些通用指南:- 当有大量上下文时使用技能,以减少系统提示词中的 Token 数。
- 使用技能将能力打包成更大的操作并提供超出单个工具描述的额外上下文。
- 如果智能体无法访问文件系统,则使用工具。
连接这些文档 到 Claude、VSCode 等工具,通过 MCP 获取实时解答。

