概述
本指南演示如何使用 Deep Agents 从零开始构建一个内容写作代理。 你构建的代理将:- 从
AGENTS.md和技能文件夹加载语音和工作流规则 - 将网络研究任务委托给一个具有
web_search功能的专用子代理 - 按照加载的技能起草博客或社交内容
- 使用 Gemini 生成封面或社交图片,并将文件保存在项目目录下
关键概念
本教程涵盖:先决条件
API 密钥:- Anthropic (Claude) 或其他提供商的 API 密钥
- 用于通过
gemini-2.5-flash-image进行图像生成的 Google (Gemini) 密钥 - 用于网络搜索的 Tavily(免费套餐)
- 用于跟踪的 LangSmith(可选)
设置
1
创建项目目录
mkdir content-builder-agent
cd content-builder-agent
2
安装依赖
pip install deepagents google-genai pillow pyyaml rich tavily-python langchain
uv init
uv add deepagents google-genai pillow pyyaml rich tavily-python langchain
uv sync
deepagents 固定到一个支持的范围(例如 >=0.3.5,<0.4.0),以匹配上游示例。3
设置 API 密钥
export ANTHROPIC_API_KEY="your_anthropic_api_key"
export GOOGLE_API_KEY="your_google_api_key"
export TAVILY_API_KEY="your_tavily_api_key" # 可选
export LANGSMITH_API_KEY="your_langsmith_api_key" # 可选
添加配置文件
该示例将行为保留在三种文件中:记忆、技能和子代理定义。1
添加 AGENTS.md
在项目根目录创建 要使此代理符合你自己的语气、支柱和格式规则,请更新
AGENTS.md。
当你稍后创建代理并指定此文件作为记忆参数的一部分时,它会被加载到系统提示中,以便品牌语音和研究期望适用于每次运行。# 内容写作代理
你是一家科技公司的内容作家。你的工作是创建引人入胜、信息丰富的内容,向读者介绍人工智能、软件开发和新兴技术。
## 品牌语音
- **专业但平易近人**:像一位知识渊博的同事那样写作,而不是教科书
- **清晰直接**:除非必要,避免使用术语;简单解释技术概念
- **自信但不傲慢**:分享专业知识,但不居高临下
- **引人入胜**:使用具体的例子、类比和故事来阐述观点
## 写作标准
1. **使用主动语态**:“代理处理请求”而不是“请求由代理处理”
2. **以价值为导向**:从对读者重要的内容开始,而不是背景介绍
3. **每段一个观点**:保持段落集中且易于浏览
4. **具体胜于抽象**:使用具体的例子、数字和案例研究
5. **以行动结尾**:每篇文章都应让读者知道下一步该做什么
## 内容支柱
我们的内容专注于:
- 人工智能代理和自动化
- 开发者工具和生产力
- 软件架构和最佳实践
- 新兴技术和趋势
## 格式指南
- 使用标题(H2、H3)来分隔长内容
- 在相关处包含代码示例(带语法高亮)
- 对于 3 项以上的列表使用项目符号
- 尽可能将句子保持在 25 个单词以内
- 在结尾包含明确的行动号召
## 研究要求
在撰写任何主题之前:
1. 使用 `researcher` 子代理进行深入的主题研究
2. 收集至少 3 个可信来源
3. 确定读者需要理解的关键点
4. 找到具体的例子或案例研究来阐述概念
AGENTS.md 中的文本。2
添加 subagents.yaml
创建一个名为 该文件稍后在创建深度代理时作为参数传递。
subagents.yaml 的文件。
然后添加以下文本,该文本描述了一个具有 Tavily 支持的 web_search 工具、Haiku 模型 ID 的 researcher 子代理,以及在从主代理委派时将发现保存到你指定路径的说明:# 子代理定义
# 这些由 content_writer.py 加载并与工具连接
researcher:
description: >
在撰写任何内容之前,请始终首先使用此代理研究任何主题。
搜索网络以获取当前信息、统计数据和来源。
委派时,告诉它主题和保存结果的文件路径
(例如,'Research renewable energy and save to research/renewable-energy.md')。
model: anthropic:claude-haiku-4-5-20251001
system_prompt: |
你是一个研究助理。你可以访问 web_search 和 write_file 工具。
## 你的工具
- web_search(query, max_results=5, topic="general") - 搜索网络
- write_file(file_path, content) - 保存你的发现
## 你的流程
1. 使用 web_search 查找有关该主题的信息
2. 使用特定查询进行 2-3 次有针对性的搜索
3. 收集关键统计数据、引文和示例
4. 将发现保存到任务中指定的文件路径
## 重要提示
- 用户会告诉你将文件保存到哪里 - 使用该确切路径
- 始终在你的发现中包含来源 URL
- 保持发现简洁但信息丰富
tools:
- web_search
3
添加技能
创建一个 接下来,创建 它们指示代理首先调用
skills/ 目录。每个技能是一个包含 SKILL.md 文件的文件夹,该文件具有 YAML frontmatter(name、description)和技能说明。创建 skills/blog-post/SKILL.md 并将以下文本复制到其中,其中包含有关创建长篇文章、为 SEO 优化内容以及生成封面图片的信息。---
name: blog-post
description: 撰写和构建长篇博客文章,创建教程大纲,并为 SEO 优化内容以及生成封面图片。当用户要求撰写博客文章、文章、操作指南、教程、技术文章、思想领导力文章或长篇内容时使用。
---
# 博客文章写作技能
## 首先研究(必需)
**在撰写任何博客文章之前,你必须委派研究:**
1. 使用 `task` 工具,设置 `subagent_type: "researcher"`
2. 在描述中,同时指定主题和保存位置:
```
task(
subagent_type="researcher",
description="Research [TOPIC]. Save findings to research/[slug].md"
)
```
示例:
```
task(
subagent_type="researcher",
description="Research the current state of AI agents in 2025. Save findings to research/ai-agents-2025.md"
)
```
3. 研究完成后,在写作前阅读发现文件
## 输出结构(必需)
**每篇博客文章必须同时包含文章和封面图片:**
```
blogs/
└── <slug>/
├── post.md # 博客文章内容
└── hero.png # 必需:生成的封面图片
```
示例:一篇关于“2025 年的 AI 代理”的文章 → `blogs/ai-agents-2025/`
**你必须完成两个步骤:**
1. 将文章写入 `blogs/<slug>/post.md`
2. 使用 `generate_image` 生成封面图片并保存到 `blogs/<slug>/hero.png`
**没有封面图片的博客文章是不完整的。**
## 博客文章结构
每篇博客文章应遵循以下结构:
### 1. 钩子(开头)
- 以引人入胜的问题、统计数据或陈述开始
- 让读者想要继续阅读
- 保持在 2-3 句话
### 2. 背景(问题)
- 解释为什么这个主题重要
- 描述问题或机会
- 与读者的经验联系起来
### 3. 主要内容(解决方案)
- 分为 3-5 个主要部分,使用 H2 标题
- 每个部分涵盖一个关键点
- 在有帮助的地方包含代码示例、图表或屏幕截图
- 对列表使用项目符号
### 4. 实际应用
- 展示如何应用这些概念
- 如果适用,包含分步说明
- 提供代码片段或模板
### 5. 结论和行动号召
- 总结关键要点(最多 3 个要点)
- 以明确的行动号召结尾
- 链接到相关资源
## 封面图片生成
撰写文章后,使用 `generate_cover` 工具生成封面图片:
```
generate_cover(prompt="A detailed description of the image...", slug="your-blog-slug")
```
该工具将图片保存到 `blogs/<slug>/hero.png`。
### 撰写有效的图片提示
使用以下元素构建你的提示:
1. **主题**:主要焦点是什么?具体而明确。
2. **风格**:艺术指导(极简主义、等距、扁平设计、3D 渲染、水彩等)
3. **构图**:元素如何排列(居中、三分法、对称)
4. **调色板**:特定颜色或情绪(温暖的大地色调、凉爽的蓝色和紫色、高对比度)
5. **灯光/氛围**:柔和的漫射光、戏剧性的阴影、黄金时段、霓虹灯辉光
6. **技术细节**:宽高比考虑、用于文本叠加的负空间
### 示例提示
**对于技术博客文章:**
```
等距 3D 插图,展示相互连接的发光立方体,代表 AI 代理,每个立方体都有微妙的电路图案。立方体通过发光的数据流连接。深海军蓝背景(#0a192f),带有电蓝色(#64ffda)和柔和的紫色(#c792ea)点缀。简洁的极简风格,顶部有大量负空间用于标题。专业的科技美学。
```
**对于教程/操作指南:**
```
干净的扁平插图,展示双手在键盘上打字,抽象的代码符号向上漂浮,变成灯泡和齿轮。温暖的渐变背景,从柔和的珊瑚色到浅桃色。友好、平易近人的风格。居中构图,留有文本叠加空间。
```
**对于思想领导力:**
```
人类轮廓与几何神经网络图案融合的抽象可视化。分割构图 - 左侧是有机的水彩纹理,过渡到右侧干净的矢量线条。柔和的鼠尾草绿和温暖的赤陶色调。沉思、前瞻的情绪。
```
## SEO 考虑
- 在标题和第一段中包含主要关键词
- 在全文中自然使用关键词 3-5 次
- 将标题保持在 60 个字符以内
- 撰写元描述(150-160 个字符)
## 质量检查清单
完成前:
- [ ] 文章已保存到 `blogs/<slug>/post.md`
- [ ] 主图已生成在 `blogs/<slug>/hero.png`
- [ ] 钩子在前 2 句话中抓住了注意力
- [ ] 每个部分都有明确的目的
- [ ] 结论总结了关键点
- [ ] 行动号召告诉读者下一步该做什么
skills/social-media/SKILL.md 并将以下文本复制到其中,其中包含有关起草社交媒体帖子和生成配套图片的信息:---
name: social-media
description: 起草引人入胜的社交媒体帖子,撰写钩子,建议标签,创建线程结构,并生成配套图片。当用户要求撰写 LinkedIn 帖子、推文、Twitter/X 线程、社交媒体说明、社交帖子或为社交平台重新利用内容时使用。
---
# 社交媒体内容技能
## 首先研究(必需)
**在撰写任何社交媒体内容之前,你必须委派研究:**
1. 使用 `task` 工具,设置 `subagent_type: "researcher"`
2. 在描述中,同时指定主题和保存位置:
```
task(
subagent_type="researcher",
description="Research [TOPIC]. Save findings to research/[slug].md"
)
```
示例:
```
task(
subagent_type="researcher",
description="Research renewable energy trends in 2025. Save findings to research/renewable-energy.md"
)
```
3. 研究完成后,在写作前阅读发现文件
## 输出结构(必需)
**每个社交媒体帖子必须同时包含内容和图片:**
**LinkedIn 帖子:**
```
linkedin/
└── <slug>/
├── post.md # 帖子内容
└── image.png # 必需:生成的视觉图
```
**Twitter/X 线程:**
```
tweets/
└── <slug>/
├── thread.md # 线程内容
└── image.png # 必需:生成的视觉图
```
示例:一篇关于“提示工程”的 LinkedIn 帖子 → `linkedin/prompt-engineering/`
**你必须完成两个步骤:**
1. 将内容写入适当的路径
2. 使用 `generate_image` 生成图片并保存在帖子旁边
**没有图片的社交媒体帖子是不完整的。**
## 平台指南
### LinkedIn
**格式:**
- 1,300 字符限制(约 210 个字符后显示更多)
- 第一行至关重要 - 让它成为钩子
- 使用换行符提高可读性
- 结尾使用 3-5 个标签
**语气:**
- 专业但个人化
- 分享见解和经验
- 提出问题以促进互动
- 使用“我”并分享经历
**结构:**
```
[钩子 - 一句引人入胜的话]
[空行]
[背景 - 为什么这很重要]
[空行]
[主要见解 - 2-3 个短段落]
[空行]
[行动号召或问题]
#hashtag1 #hashtag2 #hashtag3
```
### Twitter/X
**格式:**
- 每条推文 280 字符限制
- 更长的内容使用线程(使用 1/🧵 格式)
- 每条推文不超过 2 个标签
**线程结构:**
```
1/🧵 [钩子 - 主要见解]
2/ [支持点 1]
3/ [支持点 2]
4/ [示例或证据]
5/ [结论 + 行动号召]
```
## 图片生成
每个社交媒体帖子都需要一张引人注目的图片。使用 `generate_social_image` 工具:
```
generate_social_image(prompt="A detailed description...", platform="linkedin", slug="your-post-slug")
```
该工具将图片保存到 `<platform>/<slug>/image.png`。
### 社交图片最佳实践
社交图片需要在拥挤的动态中以小尺寸工作:
- **大胆、简单的构图** - 一个清晰的焦点
- **高对比度** - 在滚动时脱颖而出
- **图片中无文字** - 太小无法阅读,平台会添加自己的文字
- **正方形或 4:5 比例** - 适用于所有平台
### 撰写有效的提示
包含以下元素:
1. **单一焦点**:一个清晰的主题,而不是繁忙的场景
2. **大胆风格**:鲜艳的色彩、强烈的形状、高对比度
3. **简单背景**:纯色、渐变或微妙的纹理
4. **情绪/能量**:匹配帖子的语气(鼓舞人心、紧迫、深思熟虑)
### 示例提示
**对于见解/技巧帖子:**
```
单个发光的灯泡漂浮在深紫色渐变背景上,灯泡由相互连接的金色几何线条制成,柔和的光线向外辐射。极简、引人注目、高对比度。正方形构图。
```
**对于公告/新闻:**
```
由彩色几何形状制成的抽象火箭向上发射,留下一串粒子轨迹。明亮的珊瑚色和蓝绿色配色方案,背景为干净的白色。充满活力、喜庆的情绪。大胆的扁平插图风格。
```
**对于发人深省的内容:**
```
两个重叠的半透明圆圈,一个蓝色一个橙色,在中心形成一个发光的交集。代表协作或思想的交汇。深炭灰色背景,柔和空灵的光芒。极简主义且引人深思。
```
## 内容类型
### 公告帖子
- 以新闻开头
- 解释影响
- 包含链接或下一步
### 见解帖子
- 分享一个具体的学习
- 简要解释背景
- 使其可操作
### 问题帖子
- 提出一个真诚的问题
- 首先提供你的看法
- 保持专注于一个主题
## 质量检查清单
完成前:
- [ ] 帖子已保存到 `linkedin/<slug>/post.md` 或 `tweets/<slug>/thread.md`
- [ ] 图片已与帖子一起生成
- [ ] 第一行抓住了注意力
- [ ] 内容符合平台限制
- [ ] 语气符合平台规范
- [ ] 有明确的行动号召或问题
- [ ] 标签相关(非通用)
researcher 子代理,在 blogs/、linkedin/ 或 tweets/ 下编写 markdown,并调用 generate_cover 或 generate_social_image 生成图片。当你稍后创建代理并指定技能文件夹时,这些技能文件夹中 SKILLS.md 文件的 frontmatter 会被加载到系统提示中,以便代理在任务与技能描述匹配时可以使用该技能。构建脚本
在项目根目录创建content_writer.py。以下部分按顺序属于一个文件。
1
添加工具
研究子代理使用 Tavily 搜索。
博客和社交工作流使用 Gemini 图像生成。
稍后创建代理时,
load_subagents 函数读取 subagents.yaml 并将工具名称解析为这些装饰函数。import os
from pathlib import Path
from typing import Literal
import yaml
from langchain.tools import tool
EXAMPLE_DIR = Path(__file__).parent
@tool
def web_search(
query: str,
max_results: int = 5,
topic: Literal["general", "news"] = "general",
) -> dict:
"""搜索网络获取当前信息。
参数:
query: 搜索查询(需具体详细)
max_results: 返回结果数量(默认:5)
topic: "general" 用于大多数查询,"news" 用于时事新闻
返回:
包含标题、URL 和内容摘要的搜索结果。
"""
try:
from tavily import TavilyClient
api_key = os.environ.get("TAVILY_API_KEY")
if not api_key:
return {"error": "TAVILY_API_KEY 未设置"}
client = TavilyClient(api_key=api_key)
return client.search(query, max_results=max_results, topic=topic)
except Exception as e:
return {"error": f"搜索失败: {e}"}
@tool
def generate_cover(prompt: str, slug: str) -> str:
"""为博客文章生成封面图片。
参数:
prompt: 要生成的图片的详细描述。
slug: 博客文章的 slug。图片保存至 blogs/<slug>/hero.png
"""
try:
from google import genai
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.5-flash-image",
contents=[prompt],
)
for part in response.parts:
if part.inline_data is not None:
image = part.as_image()
output_path = EXAMPLE_DIR / "blogs" / slug / "hero.png"
output_path.parent.mkdir(parents=True, exist_ok=True)
image.save(str(output_path))
return f"图片已保存至 {output_path}"
return "未生成图片"
except Exception as e:
return f"错误: {e}"
@tool
def generate_social_image(prompt: str, platform: str, slug: str) -> str:
"""为社交媒体帖子生成图片。
参数:
prompt: 要生成的图片的详细描述。
platform: "linkedin" 或 "tweets"
slug: 帖子的 slug。图片保存至 <platform>/<slug>/image.png
"""
try:
from google import genai
client = genai.Client()
response = client.models.generate_content(
model="gemini-2.5-flash-image",
contents=[prompt],
)
for part in response.parts:
if part.inline_data is not None:
image = part.as_image()
output_path = EXAMPLE_DIR / platform / slug / "image.png"
output_path.parent.mkdir(parents=True, exist_ok=True)
image.save(str(output_path))
return f"图片已保存至 {output_path}"
return "未生成图片"
except Exception as e:
return f"错误: {e}"
def load_subagents(config_path: Path) -> list:
"""从 YAML 加载子代理定义并配置工具。
与 `memory` 和 `skills` 不同,深度代理默认不会从文件加载子代理。
此辅助函数将配置外部化,以便您可以在不修改 Python 代码的情况下编辑 YAML。
"""
available_tools = {
"web_search": web_search,
}
with open(config_path) as f:
config = yaml.safe_load(f)
subagents = []
for name, spec in config.items():
subagent = {
"name": name,
"description": spec["description"],
"system_prompt": spec["system_prompt"],
}
if "model" in spec:
subagent["model"] = spec["model"]
if "tools" in spec:
subagent["tools"] = [available_tools[t] for t in spec["tools"]]
subagents.append(subagent)
return subagents
2
创建代理
使用 create_deep_agent 创建深度代理时,传递记忆路径、技能目录、图像工具、来自 YAML 的子代理,以及一个以示例目录为根的 FilesystemBackend,以便像
./AGENTS.md 和 ./skills/ 这样的路径能正确解析。from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="google_genai:gemini-3.1-pro-preview",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="openai:gpt-5.4",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="anthropic:claude-sonnet-4-6",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="openrouter:anthropic/claude-sonnet-4-6",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="fireworks:accounts/fireworks/models/qwen3p5-397b-a17b",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="baseten:zai-org/GLM-5",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""创建一个由文件系统文件配置的内容写作代理。"""
return create_deep_agent(
model="ollama:devstral-2",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)
3
添加入口点
使用用户消息调用代理以验证代理是否正常工作:
import sys
from langchain.messages import HumanMessage
if __name__ == "__main__":
task = (
" ".join(sys.argv[1:])
if len(sys.argv) > 1
else "Write a blog post about how AI agents are transforming software development"
)
agent = create_content_writer()
result = agent.invoke(
{"messages": [HumanMessage(content=task)]},
config={"configurable": {"thread_id": "content-builder-demo"}},
)
for msg in result.get("messages", []):
if hasattr(msg, "content") and msg.content:
print(msg.content)
运行代理
文件系统后端可以读取、写入和删除
root_dir 下的文件。仅在专用目录中运行,并在发布前审查生成的内容。python content_writer.py
python content_writer.py Write a blog post about prompt engineering
LANGSMITH_API_KEY 后,你可以在 LangSmith 中检查运行情况。
输出
成功后,生成的工件将写入系统临时目录(在 macOS 和 Linux 上,通常在/tmp/ 下),而不是在你的项目文件旁边。
blogs/
└── prompt-engineering/
├── post.md
└── hero.png
research/
└── prompt-engineering.md
SKILL.md 中的技能说明。
完整代码
在 GitHub 上浏览完整的 content-builder-agent 示例,包括基于 Rich 的流式 UI。后续步骤
- 编辑
AGENTS.md以更改品牌语音和研究要求 - 在
skills/<name>/SKILL.md下添加技能以支持新的内容类型 - 在
subagents.yaml中添加子代理并在load_subagents中注册工具 - 阅读子代理、技能和自定义以进行更深入的配置
将这些文档通过 MCP 连接到 Claude、VSCode 等,以获取实时答案。

