先说结论
感知让 Agent 看见世界,记忆让 Agent 记住过去,规划让 Agent 决定下一步做什么。
没有规划的 Agent,就像没有大脑的机器人 --- 感知到信息也不知道怎么用,记住了偏好也不知道什么时候该用。
在 self-media-agent 项目里,规划体现在三个层面:
| 规划层面 | 决定什么 | 对应代码 | 当前状态 |
|---|---|---|---|
| 选题规划 | 写什么主题 | topic/generator.py |
LLM 生成,但与热点脱节 |
| 流水线编排 | 以什么顺序执行 | pipeline/runner.py |
固定 7 步,静态 |
| 风格进化策略 | 用什么风格写 | persona/style_learner.py |
被动触发,用户修改后才学习 |
当前项目是静态规划 --- 步骤固定、顺序固定、策略固定。 这够用但不够聪明。真正的 Agent 应该能动态规划:根据热点自动选题、根据质检分数决定是否重写、根据发布数据主动调整风格。
一句话:规划是 Agent 智能的核心体现,当前项目迈出了第一步(静态规划),离动态规划还有距离。
一、选题规划:从赛道到选题
当前实现:LLM 根据赛道生成选题
topic/generator.py 的 TopicGenerator --- 给 LLM 赛道信息,让它生成结构化选题:
ini
class TopicGenerator:
async def generate_topics(self, persona: PersonaConfig, count: int = 10) -> list[Topic]:
niche_name = NICHE_NAMES.get(persona.niche, persona.niche)
system_prompt = self.prompt_mgr.get_template("topic_generation")
user_prompt = (
f"赛道:{niche_name}\n"
f"人设类型:{persona.persona_type}\n"
f"文案风格:{persona.writing_style}\n"
f"内容形式:{persona.content_format}\n"
f"请生成 {count} 个高潜力选题。"
)
result = await self.llm.generate_json(
system_prompt=system_prompt,
user_prompt=user_prompt,
schema=TopicList, # ← 结构化输出
temperature=0.8,
)
topics = [
Topic(persona_id=persona.id, title=item.title, category=item.category, ...)
for item in result.topics
]
return topics
选题规划的输入输出:
css
输入:PersonaConfig(赛道、人设类型、风格、内容形式)
↓ LLM 规划
输出:list[Topic](10 个结构化选题,含分类和预估流量)
选题池管理:规划结果的存储
生成的选题不直接用,先存入选题池 (topic/pool.py),按状态管理:
ruby
class TopicPool:
def add(self, topic: Topic) -> Topic: # 添加到池
def list_pending(self, persona_id: str) -> list: # 列出待使用
def mark_used(self, topic_id: str) -> Topic: # 标记已使用
def mark_skipped(self, topic_id: str) -> Topic: # 标记已跳过
选题状态流转:
objectivec
生成 → PENDING(待使用)→ USED(已使用)/ SKIPPED(已跳过)
选题池的作用:规划结果不一次性消费完,可以攒一批选题,按需取用。避免每次生成都要调 LLM。
踩坑:选题与热点脱节
项目在 api/schemas.py 里定义了 use_hotspot 参数:
ini
class TopicGenerateRequest(BaseModel):
persona_id: str
count: int = Field(default=10, ge=1, le=50)
use_hotspot: bool = False # 是否使用热点数据
但这个参数在选题生成的实现里并没有被使用。 热点抓取(hotspot/)和选题生成(topic/generator.py)是两个独立模块,没有自动串联。
当前流程(手动):
arduino
用户手动执行 sma hotspot → 看到热点列表 → 手动挑选 → 手动传入 sma run --topics "xxx"
理想流程(自动):
Agent 自动抓热点 → 自动分析筛选 → 自动结合人设生成选题 → 自动生产内容
差距就是规划模块的进化方向 --- 从"人规划"到"Agent 规划"。
二、流水线编排:固定顺序的执行规划
第4篇详细讲过 pipeline/runner.py 的 7 步流水线。从规划的角度看,流水线就是一种静态规划 --- 步骤和顺序在代码里写死:
python
async def _generate_single(self, persona, topic, output_dir):
# 2a: 生成标题 ← 第1步:规划好了,先写标题
# 2b: 生成正文 ← 第2步:规划好了,再写正文
# 2c: 排版 ← 第3步:规划好了,然后排版
# 2d: 四维质检 ← 第4步:规划好了,然后质检
# 2e: 构建对象 ← 第5步:规划好了,打包成对象
# 2f: 存储 ← 第6步:规划好了,存起来
# 2g: 导出文件 ← 第7步:规划好了,导出
静态规划的特点
| 特点 | 当前项目 | 动态规划的未来 |
|---|---|---|
| 步骤顺序 | 固定写死在代码里 | 根据内容类型动态调整 |
| 步骤数量 | 固定 7 步 | 某些步骤可跳过或重复 |
| 分支决策 | 只有 short_video vs text 一个分支 |
根据质检分数决定是否重写 |
| 错误处理 | 失败跳过,不重试 | 失败后根据原因决定重试策略 |
静态规划够用但不够聪明
够用的地方:步骤固定 → 可预测、可测试、可维护。对于"生成文章"这种标准化流程,静态规划完全够用。
不够聪明的地方:
ini
# 当前:质检不过也照常输出
quality_result = await self.quality_orchestrator.check_and_fix(persona, formatted)
formatted = quality_result.final_content
quality_score = quality_result.total_score
# ← 如果 quality_score < 0.5,应该重写,但当前直接输出了
动态规划应该做的:
python
# 未来:质检不过 → 自动重写
for attempt in range(max_retries):
quality_result = await self.quality_orchestrator.check_and_fix(persona, formatted)
if quality_result.total_score >= 0.8:
break # 通过,跳出循环
# 没通过 → 把质检反馈注入 Prompt,重新生成
user_prompt += f"\n\n# 上次质检问题\n{quality_result.violations}\n请改进。"
formatted = await self.body_gen.generate_text(persona, topic, title)
这就是静态规划和动态规划的核心区别:静态规划不根据中间结果调整后续步骤,动态规划会。
三、风格进化策略:从被动学习到主动进化
第6篇讲过 style_learner.py 的三层风格进化。从规划的角度看,风格进化是一种"策略规划" --- 决定用什么风格写。
当前的风格进化策略:被动触发
用户修改文章
↓(被动触发)
提取偏好 → 合并到 style_preferences
↓(手动触发)
汇总偏好 → 生成 style_profile
↓(自动注入)
下次生成时 build_style_hint 注入 Prompt
"被动"体现在:Agent 不会主动发现问题、不会主动建议修改、不会主动调整风格。所有进化都由用户的行为触发。
风格进化的完整流程
style_learner.py 实现了从提取到注入的完整链路:
python
class StyleLearner:
# 1. 从单次修改提取偏好
async def extract_preference_from_revision(self, revision) -> list[dict]:
"""LLM 从修改前后 diff + 修改建议中提取结构化偏好"""
# 输入:revision(原文 + 修改后文 + 建议)
# 输出:[{"category": "语气", "value": "活泼", "confidence": 0.8}]
# 2. 合并去重偏好
@staticmethod
def _merge_preferences(preferences: list[dict]) -> list[dict]:
"""相同 category+value 合并,confidence 取最高,count 累加"""
# 3. 汇总生成画像
async def build_profile(self, persona) -> tuple[str, list[dict]]:
"""收集所有修改记录 → 逐条提取偏好 → 合并 → LLM 生成画像"""
# 4. 写入人设
def apply_to_persona(self, persona, profile, preferences) -> PersonaConfig:
"""model_copy 更新 style_profile + style_preferences"""
# 5. 构建注入 Prompt 的风格提示
@staticmethod
def build_style_hint(persona) -> str:
"""画像 + 偏好提醒 → 注入生成 Prompt"""
5 个方法,构成风格进化的完整规划链:
sql
extract(提取)→ merge(合并)→ build_profile(汇总画像)→ apply(写入)→ inject(注入)
被动 vs 主动
| 被动进化(当前) | 主动进化(未来) | |
|---|---|---|
| 触发 | 用户修改后提取 | Agent 生成后自评 |
| 发现 | 用户说"太正式" | Agent 对比历史偏好发现偏差 |
| 建议 | 不建议 | 主动建议"这段可以更活泼" |
| 调整 | 下次生成注入 | 当前生成实时调整 |
主动进化示例(未来):
ini
# 生成后自评:对比当前输出和历史偏好
current_style = analyze_style(generated_body)
if current_style.tone != persona.preferred_tone:
# 发现偏差 → 自动调整
user_prompt += f"\n注意:你的风格偏好是{persona.preferred_tone},请调整。"
generated_body = await regenerate(user_prompt)
当前项目做不到这一点 --- 因为没有"风格自评"的规划步骤。 这是规划模块的进化方向。
四、静态规划 vs 动态规划:差距在哪
把三个层面综合起来看:
当前:静态规划
┌─────────────┐
│ 静态规划 │
└──────┬──────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ 选题规划 │ │ 流水线编排 │ │ 风格进化 │
│ LLM生成 │ │ 固定7步 │ │ 被动触发 │
│ 不结合热点│ │ 不根据结果 │ │ 不主动自评 │
│ │ │ 调整后续 │ │ │
└─────────┘ └──────────┘ └──────────┘
三个"不" :
- 不结合热点 --- 选题和热点是两个独立模块,没有自动串联
- 不根据结果调整 --- 质检不过也照常输出,不重写
- 不主动自评 --- 风格进化全靠用户触发,Agent 不主动发现问题
未来:动态规划
┌─────────────┐
│ 动态规划 │
└──────┬──────┘
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│ 选题规划 │ │ 流水线编排 │ │ 风格进化 │
│ 热点→选题 │ │ 质检不过 │ │ 生成后自评 │
│ 自动串联 │ │ →自动重写 │ │ 主动调整 │
│ 相关度排序 │ │ 根据分数 │ │ 对比历史 │
│ │ │ 调整策略 │ │ 偏好 │
└─────────┘ └──────────┘ └──────────┘
三个"变" :
- 热点→选题自动串联 ---
use_hotspot=True时,先抓热点再生成选题,按相关度排序 - 质检不过自动重写 ---
quality_score < 0.8时,把违规项注入 Prompt 重新生成 - 生成后主动自评 --- 对比当前输出和历史偏好,发现偏差主动调整
为什么当前是静态的?
不是不能做,是权衡后的选择:
| 动态规划的代价 | 当前项目的权衡 |
|---|---|
| 每次质检不过要重写 → LLM 调用翻倍 | 成本 ×2,当前选择不重写 |
| 主动自评需要额外 LLM 调用 | 成本 +1,当前选择不自评 |
| 热点自动串联增加复杂度 | 先做简单的,热点手动触发够用 |
静态规划是 V1 的合理选择 --- 先跑通,再优化。 动态规划是 V2/V3 的方向。
五、规划模块在项目中的位置
把规划放在 Agent 四大模块的框架里看:
感知(第5篇)→ 获取热点
↓
记忆(第6篇)→ 提供人设 + 风格偏好
↓
规划(本篇) → 决定选题 + 执行顺序 + 风格策略
↓
行动(第8篇)→ 生成标题 + 正文 + 质检 + 排版
规划是感知和行动之间的桥梁 --- 感知提供信息,记忆提供偏好,规划决定怎么用这些信息和偏好来指导行动。
当前项目的规划链路:
ini
# pipeline/runner.py --- 规划的入口
async def run(self, persona_id, topics=None, topic_count=10):
# ━━━ 感知 + 记忆 → 规划输入 ━━━
persona = self.persona_mgr.get(persona_id) # 记忆:人设 + 风格偏好
# ━━━ 规划:决定写什么 ━━━
if topics:
topic_list = [Topic(...) for t in topics] # 用户规划:手动选题
else:
topic_list = await self.topic_gen.generate_topics(persona, topic_count) # AI 规划:自动选题
# ━━━ 规划:决定怎么写 ━━━
for topic in topic_list:
content = await self._generate_single(persona, topic, output_dir)
# _generate_single 内部:标题→正文→排版→质检→存储→导出
# 风格偏好通过 build_style_hint 自动注入正文生成
两处规划决策:
- 写什么 ---
topics有值则用户规划,无值则 AI 规划 - 怎么写 ---
_generate_single的 7 步顺序 + 风格偏好注入
当前都是静态的 --- 决策逻辑写死在代码里,不根据运行时信息动态调整。
踩坑总结
| 坑 | 根因 | 修复方向 |
|---|---|---|
| 选题与热点脱节 | use_hotspot 参数定义了但没实现 |
实现热点→选题自动串联 |
| 质检不过照常输出 | 流水线不根据中间结果调整 | 质检不过时自动重写 |
| 风格进化全靠手动 | 没有主动自评的规划步骤 | 生成后对比历史偏好自评 |
| 选题生成不结合热点 | 两个模块独立,无串联 | 规划层串联:热点→分析→选题 |
| 流水线步骤固定 | 静态规划,不动态调整 | 根据内容类型/质检结果动态调整步骤 |
| 风格画像手动触发 | build_profile 需要手动调用 | 定期自动汇总,或每次修改后增量更新 |
经验总结
- 规划决定"做什么"和"以什么顺序做" --- 选题规划决定主题,流水线编排决定执行顺序,风格策略决定写作风格
- 当前项目是静态规划 --- 步骤固定、顺序固定、策略固定,够用但不够聪明
- 静态规划的三 个"不" --- 不结合热点、不根据结果调整、不主动自评,这是动态规划的进化方向
- 静态规划是 V1 的合理选择 --- 先跑通再优化,动态规划的代价是 LLM 调用翻倍,当前权衡后选择不做
- 规划是感知和行动的桥梁 --- 感知提供信息,记忆提供偏好,规划决定怎么用这些来指导行动
下篇预告
下一篇讲 行动模块:Agent的手 --- 行动是 Agent 的产出,多策略行动(图文/脚本/笔记)+ 质检作为行动的一部分 + 修改再生成的行动闭环。