主题
构建内容构建器智能体
构建一个具备品牌记忆、技能、子智能体和图像生成能力的内容写作智能体
概述
本指南演示如何使用 Deep Agents 从零开始构建一个内容写作智能体。
你将构建的智能体将:
- 从
AGENTS.md和技能文件夹中加载语气与工作流规则 - 借助
web_search将网络研究委派给专门的子智能体 - 按照已加载的技能起草博客或社交内容
- 使用 Gemini 生成封面或社交图片,并将文件保存到项目目录下
本教程中的代码接入了图像生成工具和文件系统后端,因此智能体可以在项目目录下读取和写入文章、研究笔记和图像。如需完整可运行的项目,请参见 content-builder-agent 示例。
核心概念
本教程涵盖:
前置条件
API 密钥:
- Anthropic (Claude) 或其他服务商的 API 密钥
- Google (Gemini),用于通过
gemini-2.5-flash-image生成图像 - Tavily 用于网络搜索(免费套餐)
- LangSmith 用于追踪(可选)
Python 3.11 或更高版本。
设置
Create project directory
bash
mkdir content-builder-agent
cd content-builder-agentInstall dependencies
bash
pip install deepagents google-genai pillow pyyaml rich tavily-python langchainbash
uv init
uv add deepagents google-genai pillow pyyaml rich tavily-python langchain
uv sync在你自己的项目中将 deepagents 固定到受支持的版本范围(例如 >=0.3.5,<0.4.0),以与上游示例保持一致。
Set API keys
bash
export ANTHROPIC_API_KEY="your_anthropic_api_key"
export GOOGLE_API_KEY="your_google_api_key"
export TAVILY_API_KEY="your_tavily_api_key" # Optional
export LANGSMITH_API_KEY="your_langsmith_api_key" # Optional添加配置文件
该示例将行为保存在三类文件中:记忆、技能和子智能体定义。
Add AGENTS.md
在项目根目录下创建 AGENTS.md。 当你稍后创建智能体并将该文件指定为 memory 参数的一部分时,它会被加载到系统提示词中,从而让品牌语气和研究预期适用于每一次运行。
markdown
# Content Writer Agent
You are a content writer for a technology company. Your job is to create engaging, informative content that educates readers about AI, software development, and emerging technologies.
## Brand Voice
- **Professional but approachable**: Write like a knowledgeable colleague, not a textbook
- **Clear and direct**: Avoid jargon unless necessary; explain technical concepts simply
- **Confident but not arrogant**: Share expertise without being condescending
- **Engaging**: Use concrete examples, analogies, and stories to illustrate points
## Writing Standards
1. **Use active voice**: "The agent processes requests" not "Requests are processed by the agent"
2. **Lead with value**: Start with what matters to the reader, not background
3. **One idea per paragraph**: Keep paragraphs focused and scannable
4. **Concrete over abstract**: Use specific examples, numbers, and case studies
5. **End with action**: Every piece should leave the reader knowing what to do next
## Content Pillars
Our content focuses on:
- AI agents and automation
- Developer tools and productivity
- Software architecture and best practices
- Emerging technologies and trends
## Formatting Guidelines
- Use headers (H2, H3) to break up long content
- Include code examples where relevant (with syntax highlighting)
- Add bullet points for lists of 3+ items
- Keep sentences under 25 words when possible
- Include a clear call-to-action at the end
## Research Requirements
Before writing on any topic:
1. Use the `researcher` subagent for in-depth topic research
2. Gather at least 3 credible sources
3. Identify the key points readers need to understand
4. Find concrete examples or case studies to illustrate concepts若要此智能体遵循你自己的语气、内容支柱和格式规则,请更新 AGENTS.md 中的文本。
Add subagents.yaml
创建一个名为 subagents.yaml 的文件。 然后添加以下 txt,它描述了一个 researcher 子智能体,该子智能体带有一个由 Tavily 支持的 web_search 工具、一个 Haiku 模型 id,以及将研究发现保存到你从主智能体委派时所指定路径的指令:
yaml
# Subagent definitions
# These are loaded by content_writer.py and wired up with tools
researcher:
description: >
ALWAYS use this first to research any topic before writing content.
Searches the web for current information, statistics, and sources.
When delegating, tell it the topic AND the file path to save results
(e.g., 'Research renewable energy and save to research/renewable-energy.md').
model: anthropic:claude-haiku-4-5-20251001
system_prompt: |
You are a research assistant. You have access to web_search and write_file tools.
## Your Tools
- web_search(query, max_results=5, topic="general") - Search the web
- write_file(file_path, content) - Save your findings
## Your Process
1. Use web_search to find information on the topic
2. Make 2-3 targeted searches with specific queries
3. Gather key statistics, quotes, and examples
4. Save findings to the file path specified in your task
## Important
- The user will tell you WHERE to save the file - use that exact path
- Always include source URLs in your findings
- Keep findings concise but informative
tools:
- web_search稍后创建 deep agent 时,该文件会作为参数传入。
Add skills
创建一个 skills/ 目录。每个技能都是一个文件夹,其中包含一个 SKILL.md 文件,带有 YAML frontmatter(name、description)以及该技能的说明。
创建 skills/blog-post/SKILL.md,并将以下文本复制进去,其中包含关于撰写长篇文章、针对 SEO 优化内容以及生成封面图片的信息。
md
---
name: blog-post
description: Writes and structures long-form blog posts, creates tutorial outlines, and optimizes content for SEO with cover image generation. Use when the user asks to write a blog post, article, how-to guide, tutorial, technical writeup, thought leadership piece, or long-form content.
---
# Blog Post Writing Skill
## Research First (Required)
**Before writing any blog post, you MUST delegate research:**
1. Use the `task` tool with `subagent_type: "researcher"`
2. In the description, specify BOTH the topic AND where to save:task( subagent_type="researcher", description="Research [TOPIC]. Save findings to research/[slug].md" )
Example:task( subagent_type="researcher", description="Research the current state of AI agents in 2025. Save findings to research/ai-agents-2025.md" )
3. After research completes, read the findings file before writing
## Output Structure (Required)
**Every blog post MUST have both a post AND a cover image:**blogs/ └── <slug>/ ├── post.md # The blog post content └── hero.png # REQUIRED: Generated cover image
Example: A post about "AI Agents in 2025" → `blogs/ai-agents-2025/`
**You MUST complete both steps:**
1. Write the post to `blogs/<slug>/post.md`
2. Generate a cover image using `generate_image` and save to `blogs/<slug>/hero.png`
**A blog post is NOT complete without its cover image.**
## Blog Post Structure
Every blog post should follow this structure:
### 1. Hook (Opening)
- Start with a compelling question, statistic, or statement
- Make the reader want to continue
- Keep it to 2-3 sentences
### 2. Context (The Problem)
- Explain why this topic matters
- Describe the problem or opportunity
- Connect to the reader's experience
### 3. Main Content (The Solution)
- Break into 3-5 main sections with H2 headers
- Each section covers one key point
- Include code examples, diagrams, or screenshots where helpful
- Use bullet points for lists
### 4. Practical Application
- Show how to apply the concepts
- Include step-by-step instructions if applicable
- Provide code snippets or templates
### 5. Conclusion & CTA
- Summarize key takeaways (3 bullets max)
- End with a clear call-to-action
- Link to related resources
## Cover Image Generation
After writing the post, generate a cover image using the `generate_cover` tool:generate_cover(prompt="A detailed description of the image...", slug="your-blog-slug")
The tool saves the image to `blogs/<slug>/hero.png`.
### Writing Effective Image Prompts
Structure your prompt with these elements:
1. **Subject**: What is the main focus? Be specific and concrete.
2. **Style**: Art direction (minimalist, isometric, flat design, 3D render, watercolor, etc.)
3. **Composition**: How elements are arranged (centered, rule of thirds, symmetrical)
4. **Color palette**: Specific colors or mood (warm earth tones, cool blues and purples, high contrast)
5. **Lighting/Atmosphere**: Soft diffused light, dramatic shadows, golden hour, neon glow
6. **Technical details**: Aspect ratio considerations, negative space for text overlay
### Example Prompts
**For a technical blog post:**由相互连接、发光的立方体(代表 AI 智能体)构成的等距 3D 插画,每个立方体带有细微的电路图案。立方体之间由发光的数据流相连。深海军蓝背景(#0a192f),点缀电光蓝(#64ffda)和柔和紫(#c792ea)。简洁极简风格,顶部留有大量负空间用于放置标题。专业的科技美学。
**For a tutorial/how-to:**简洁的扁平插画:双手在键盘上打字,抽象的代码符号向上飘浮,并转化为灯泡和齿轮。从柔和珊瑚色到浅桃色的暖色渐变背景。友好、平易近人的风格。居中构图,留有空间用于叠加文字。
**For thought leadership:**人类侧脸剪影与几何神经网络图案相融合的抽象可视化。分割式构图——左侧为有机水彩纹理,过渡到右侧干净的矢量线条。柔和的鼠尾草绿与温暖的陶土色配色。沉思、前瞻的氛围。
## SEO Considerations
- Include the main keyword in the title and first paragraph
- Use the keyword naturally 3-5 times throughout
- Keep the title under 60 characters
- Write a meta description (150-160 characters)
## Quality Checklist
Before finishing:
- [ ] Post saved to `blogs/<slug>/post.md`
- [ ] Hero image generated at `blogs/<slug>/hero.png`
- [ ] Hook grabs attention in first 2 sentences
- [ ] Each section has a clear purpose
- [ ] Conclusion summarizes key points
- [ ] CTA tells reader what to do next接下来,创建 skills/social-media/SKILL.md,并将以下文本复制进去,其中包含关于起草社交媒体帖子和生成配套图像的信息:
md
---
name: social-media
description: Drafts engaging social media posts, writes hooks, suggests hashtags, creates thread structures, and generates companion images. Use when the user asks to write a LinkedIn post, tweet, Twitter/X thread, social media caption, social post, or repurpose content for social platforms.
---
# Social Media Content Skill
## Research First (Required)
**Before writing any social media content, you MUST delegate research:**
1. Use the `task` tool with `subagent_type: "researcher"`
2. In the description, specify BOTH the topic AND where to save:task( subagent_type="researcher", description="Research [TOPIC]. Save findings to research/[slug].md" )
Example:task( subagent_type="researcher", description="Research renewable energy trends in 2025. Save findings to research/renewable-energy.md" )
3. After research completes, read the findings file before writing
## Output Structure (Required)
**Every social media post MUST have both content AND an image:**
**LinkedIn posts:**linkedin/ └── <slug>/ ├── post.md # The post content └── image.png # REQUIRED: Generated visual
**Twitter/X threads:**tweets/ └── <slug>/ ├── thread.md # The thread content └── image.png # REQUIRED: Generated visual
Example: A LinkedIn post about "prompt engineering" → `linkedin/prompt-engineering/`
**You MUST complete both steps:**
1. Write the content to the appropriate path
2. Generate an image using `generate_image` and save alongside the post
**A social media post is NOT complete without its image.**
## Platform Guidelines
### LinkedIn
**Format:**
- 1,300 character limit (show more after ~210 chars)
- First line is crucial - make it hook
- Use line breaks for readability
- 3-5 hashtags at the end
**Tone:**
- Professional but personal
- Share insights and learnings
- Ask questions to drive engagement
- Use "I" and share experiences
**Structure:**[钩子 - 1 句引人注目的话]
[空行]
[背景 - 为什么这很重要]
[空行]
[核心见解 - 2-3 段简短文字]
[空行]
[行动号召或问题]
#hashtag1 #hashtag2 #hashtag3
### Twitter/X
**Format:**
- 280 character limit per tweet
- Threads for longer content (use 1/🧵 format)
- No more than 2 hashtags per tweet
**Thread Structure:**1/🧵 [钩子 - 核心见解]
2/ [支撑论点 1]
3/ [支撑论点 2]
4/ [示例或证据]
5/ [结论 + CTA]
## Image Generation
Every social media post needs an eye-catching image. Use the `generate_social_image` tool:generate_social_image(prompt="A detailed description...", platform="linkedin", slug="your-post-slug")
The tool saves the image to `<platform>/<slug>/image.png`.
### Social Image Best Practices
Social images need to work at small sizes in crowded feeds:
- **Bold, simple compositions** - one clear focal point
- **High contrast** - stands out when scrolling
- **No text in image** - too small to read, platforms add their own
- **Square or 4:5 ratio** - works across platforms
### Writing Effective Prompts
Include these elements:
1. **Single focal point**: One clear subject, not a busy scene
2. **Bold style**: Vibrant colors, strong shapes, high contrast
3. **Simple background**: Solid color, gradient, or subtle texture
4. **Mood/energy**: Match the post tone (inspiring, urgent, thoughtful)
### Example Prompts
**For an insight/tip post:**一只发光的灯泡悬浮在深紫色渐变背景上,灯泡由相互连接的金色几何线条构成,柔和的光线向外散发。极简、醒目、高对比度。正方形构图。
**For announcements/news:**由色彩缤纷的几何形状构成的抽象火箭向上发射,拖着一条粒子尾迹。明亮的珊瑚色与青绿色配色,背景为干净的白色。充满活力、庆祝的氛围。大胆的扁平插画风格。
**For thought-provoking content:**两个相互重叠的半透明圆形,一个蓝色一个橙色,在中心形成一个发光的交叠区域。象征协作或思想的交汇。深炭灰色背景,柔和空灵的光晕。极简而富有沉思感。
## Content Types
### Announcement Posts
- Lead with the news
- Explain the impact
- Include link or next step
### Insight Posts
- Share one specific learning
- Explain the context briefly
- Make it actionable
### Question Posts
- Ask a genuine question
- Provide your take first
- Keep it focused on one topic
## Quality Checklist
Before finishing:
- [ ] Post saved to `linkedin/<slug>/post.md` or `tweets/<slug>/thread.md`
- [ ] Image generated alongside the post
- [ ] First line hooks attention
- [ ] Content fits platform limits
- [ ] Tone matches platform norms
- [ ] Has clear CTA or question
- [ ] Hashtags are relevant (not generic)它们指示智能体先调用 researcher 子智能体,将 markdown 写入 blogs/、linkedin/ 或 tweets/ 下,并调用 generate_cover 或 generate_social_image 来生成图像。
当你稍后创建智能体并指定技能文件夹时,这些技能文件夹中 SKILLS.md 文件的 frontmatter 会被加载到系统提示词中,这样当任务与某个技能描述匹配时,智能体就能使用该技能。
构建脚本
在项目根目录下创建 content_writer.py。以下各节按顺序属于同一个文件。
Add tools
researcher 子智能体使用 Tavily 搜索。 博客和社交工作流使用 Gemini 图像生成。 稍后创建智能体时,load_subagents 函数会读取 subagents.yaml,并将工具名称解析为这些被装饰的函数。
python
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:
"""Search the web for current information.
Args:
query: The search query (be specific and detailed)
max_results: Number of results to return (default: 5)
topic: "general" for most queries, "news" for current events
Returns:
Search results with titles, URLs, and content excerpts.
"""
try:
from tavily import TavilyClient
api_key = os.environ.get("TAVILY_API_KEY")
if not api_key:
return {"error": "TAVILY_API_KEY not set"}
client = TavilyClient(api_key=api_key)
return client.search(query, max_results=max_results, topic=topic)
except Exception as e:
return {"error": f"Search failed: {e}"}
@tool
def generate_cover(prompt: str, slug: str) -> str:
"""Generate a cover image for a blog post.
Args:
prompt: Detailed description of the image to generate.
slug: Blog post slug. Image saves to 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"Image saved to {output_path}"
return "No image generated"
except Exception as e:
return f"Error: {e}"
@tool
def generate_social_image(prompt: str, platform: str, slug: str) -> str:
"""Generate an image for a social media post.
Args:
prompt: Detailed description of the image to generate.
platform: Either "linkedin" or "tweets"
slug: Post slug. Image saves to <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"Image saved to {output_path}"
return "No image generated"
except Exception as e:
return f"Error: {e}"
def load_subagents(config_path: Path) -> list:
"""Load subagent definitions from YAML and wire up tools.
Unlike `memory` and `skills`, deep agents do not load subagents from files by default.
This helper externalizes configuration so you can edit YAML without changing Python code.
"""
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 subagentsCreate the agent
使用 create_deep_agent 创建 deep agent 时,传入记忆路径、技能目录、图像工具、来自 YAML 的子智能体,以及一个以示例目录为根的 FilesystemBackend,以便诸如 ./AGENTS.md 和 ./skills/ 这样的路径能够正确解析。
python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="google_genai:gemini-3.6-flash",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="openai:gpt-5.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),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="anthropic:claude-sonnet-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),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="openrouter:z-ai/glm-5.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),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="fireworks:accounts/fireworks/models/glm-5p2",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="baseten:zai-org/GLM-5.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),
)python
from deepagents import create_deep_agent
from deepagents.backends import FilesystemBackend
def create_content_writer():
"""Create a content writer agent configured by filesystem files."""
return create_deep_agent(
model="ollama:north-mini-code-1.0",
memory=["./AGENTS.md"],
skills=["./skills/"],
tools=[generate_cover, generate_social_image],
subagents=load_subagents(EXAMPLE_DIR / "subagents.yaml"),
backend=FilesystemBackend(root_dir=EXAMPLE_DIR),
)Add an entry point
用用户消息调用智能体,以验证智能体是否正常工作:
python
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)运行智能体
WARNING
文件系统后端可以读取、写入和删除 root_dir 下的文件。请只在专用目录中运行,并在发布前审查生成的内容。
在项目目录中,你可以不传参数调用智能体,也可以将提示词作为参数传入:
bash
python content_writer.pybash
python content_writer.py Write a blog post about prompt engineering设置 LANGSMITH_API_KEY 后,你可以在 LangSmith 中检查运行。
输出
成功时,生成的产物会写入系统临时目录下(在 macOS 和 Linux 上通常位于 /tmp/ 下),而不是你的项目文件旁边。
text
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中注册工具 - 阅读子智能体、技能和自定义以进行更深入的配置