用大模型批量生成 Python 差异化自媒体创意

用大模型批量生成 Python 差异化自媒体创意

做自媒体最怕的不是"不会写",而是写完一百篇之后,每一篇都长得像上一篇。大模型能帮你把"写一篇"变成"写一百篇",但前提是------你得先教会它什么叫"不一样"。否则你得到的就是一百篇换汤不换药的流水线废稿。

这篇文章讲一套可落地的方案:用 Python 驱动大模型,批量生成有结构差异化的自媒体创意,而不是批量生产垃圾。


一、核心问题:为什么直接调 API 生成的创意都"一个味"

你肯定试过直接让大模型"给我写 10 个科技类短视频选题",结果拿到的是:

  1. AI 如何改变未来
  2. 5 个你不知道的 Python 技巧
  3. 程序员必看的 3 本书
    ...

这玩意儿你自己也能想出来,而且你想出来的可能还更有趣。

**问题不在模型,在你的输入结构。**​ 大模型是"条件生成器",你给的条件越粗糙,它就越往训练数据里最常见的方向靠。要出差异化,得从三个维度下手:

维度 粗糙做法 差异化做法
角度 "写个 Python 教程" 限定叙事视角:新手踩坑 / 面试官视角 / 十年老码农吐槽
格式 "写篇文章" 限定输出结构:清单体 / 对话体 / 反转体 / 数据对比体
钩子 无约束 强制前 3 秒必须出现反常识断言或具体数字

下面这套方案,就是把这三个维度参数化,让 Python 替你做排列组合。


二、整体架构

复制代码
创意种子池(CSV/JSON)
      │
      ▼
差异化参数矩阵(角度 × 格式 × 钩子类型)
      │
      ▼
Prompt 模板引擎(Jinja2)
      │
      ▼
批量调用大模型 API(异步并发)
      │
      ▼
后处理:去重 / 打分 / 人工筛选

每一步都是可控的,每一步都能调。


三、创意种子池:别让模型"凭空想"

第一步最容易踩的坑:让模型自己决定写什么

"给我 20 个选题" → 模型从训练数据里抽最常见的 20 个 → 你得到一堆陈词滥调。

正确做法:你给它具体的种子 ,它负责加工和变形

复制代码
# seeds.py
SEEDS = [
    {"topic": "Python 虚拟环境", "audience": "转行学编程的文科生", "constraint": "必须提到 conda 和 venv 的恩怨"},
    {"topic": "FastAPI vs Flask", "audience": "有 2 年经验的创业公司后端", "constraint": "要带真实 QPS 对比数据"},
    {"topic": "爬虫被反爬", "audience": "刚被公司要求写爬虫的初级开发", "constraint": "必须包含一段真实报错信息"},
    {"topic": "SQLAlchemy 2.0", "audience": "还在用 Django ORM 的老项目维护者", "constraint": "要对比迁移成本"},
]

种子不需要多,20-50 条就够。关键是每条都有明确的受众具体的约束------这两个东西是差异化的锚点。


四、差异化参数矩阵

这是整套方案的核心。把"创意风格"拆成可枚举的维度:

复制代码
# dimensions.py
ANGLES = {
    "rookie_trap": "以新手第一次踩坑的视角,从错误认知写到恍然大悟",
    "war_story": "以'我去年在一个项目里遇到这个问题'的叙事口吻",
    "contrarian": "直接反驳一个广泛流传的观点,用数据或代码证明",
    "interviewer": "模拟技术面试官追问的节奏,层层递进",
    "tool_compare": "用表格/代码对比两个方案的真实差异,不站队",
}

FORMATS = {
    "listicle": "清单体,3-7 条,每条有独立小标题",
    "dialogue": "对话体,两个角色辩论或问答",
    "tutorial": "步骤体,从环境准备到运行结果逐步展示",
    "story": "叙事体,有时间线、有冲突、有结局",
    "thread": "Twitter 风格的线程体,每条短句,逻辑递进",
}

HOOKS = {
    "counterintuitive": "开头第一句必须是一个反直觉的事实断言",
    "specific_number": "开头第一句必须包含一个精确数字(不是'很多',是具体值)",
    "personal_failure": "开头第一句必须是'我搞砸了一次......'",
    "myth_bust": "开头第一句必须否定一个常见说法",
}

现在你有 5 × 5 × 4 = 100 种组合。同一个种子,走不同组合,出来的东西完全不同。


五、Prompt 模板引擎

用 Jinja2 把参数注入模板,而不是在代码里拼字符串:

复制代码
# prompt_builder.py
from jinja2 import Template

TEMPLATE = Template("""
你是一个有 10 年经验的科技自媒体创作者,擅长把技术话题写得让人停不下来。

## 任务
基于以下种子信息,生成一篇{{ platform }}平台的创意大纲。

## 种子信息
- 核心话题:{{ seed.topic }}
- 目标受众:{{ seed.audience }}
- 硬性约束:{{ seed.constraint }}

## 风格要求
- 叙事角度:{{ angle_desc }}
- 内容格式:{{ format_desc }}
- 开头钩子:{{ hook_desc }}

## 输出结构
请按以下结构输出(严格遵循):

1. **标题**:1 个主标题 + 2 个备选(每个标题必须包含具体技术名词,禁止泛泛而谈)
2. **开头钩子**:2-3 句话,必须命中指定的钩子类型
3. **核心内容大纲**:3-5 个要点,每个要点包含:
   - 要点标题
   - 一句话说明
   - 是否需要代码示例(是/否)
4. **结尾互动**:一个能引发评论区讨论的问题

## 限制
- 不要写"大家好""今天我们来聊聊"这类废话开头
- 不要用"首先、其次、最后"这种结构词
- 代码示例必须是真实可运行的,不能是伪代码
""")

def build_prompt(seed, angle_key, format_key, hook_key, platform="小红书"):
    return TEMPLATE.render(
        seed=seed,
        angle_desc=ANGLES[angle_key],
        format_desc=FORMATS[format_key],
        hook_desc=HOOKS[hook_key],
        platform=platform,
    )

模板里的限制条件很重要------它们是在对抗模型的"默认写作习惯"。没有这些约束,模型会退化成最安全的平庸输出。


六、批量异步调用

asyncio + aiohttp 并发调用,别一个一个串行等:

复制代码
# batch_generate.py
import asyncio
import aiohttp
import json
from itertools import product
from seeds import SEEDS
from dimensions import ANGLES, FORMATS, HOOKS
from prompt_builder import build_prompt

API_URL = "https://api.openai.com/v1/chat/completions"
API_KEY = "sk-..."

async def call_llm(session, prompt, max_retries=3):
    payload = {
        "model": "gpt-4o",
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.85,  # 创意任务需要一定随机性
        "max_tokens": 1500,
    }
    
    for attempt in range(max_retries):
        try:
            async with session.post(API_URL, json=payload, headers={
                "Authorization": f"Bearer {API_KEY}"
            }) as resp:
                result = await resp.json()
                return result["choices"][0]["message"]["content"]
        except Exception as e:
            if attempt == max_retries - 1:
                return f"ERROR: {e}"
            await asyncio.sleep(2 ** attempt)

async def batch_generate():
    # 生成参数组合(每个种子取 3 种不同组合,避免全量爆炸)
    combos = []
    keys = list(product(
        list(ANGLES.keys()),
        list(FORMATS.keys()),
        list(HOOKS.keys())
    ))
    
    for seed in SEEDS:
        # 每个种子随机选 3 种组合
        import random
        selected = random.sample(keys, min(3, len(keys)))
        for angle, fmt, hook in selected:
            combos.append((seed, angle, fmt, hook))
    
    print(f"共 {len(combos)} 个创意待生成")
    
    async with aiohttp.ClientSession() as session:
        tasks = []
        for seed, angle, fmt, hook in combos:
            prompt = build_prompt(seed, angle, fmt, hook)
            tasks.append(call_llm(session, prompt))
        
        results = await asyncio.gather(*tasks)
    
    # 保存结果
    with open("creative_output.jsonl", "w", encoding="utf-8") as f:
        for (seed, angle, fmt, hook), content in zip(combos, results):
            f.write(json.dumps({
                "seed": seed["topic"],
                "angle": angle,
                "format": fmt,
                "hook": hook,
                "content": content
            }, ensure_ascii=False) + "\n")
    
    print("完成!结果已写入 creative_output.jsonl")

if __name__ == "__main__":
    asyncio.run(batch_generate())

跑一次,几百个差异化创意就出来了。


七、后处理:去重与打分

批量生成最大的问题是有些组合出来的东西确实很烂。需要自动过滤一轮:

复制代码
# postprocess.py
import json
from collections import Counter

def score_creative(item):
    """简单打分,过滤明显低质的"""
    content = item["content"]
    score = 0
    
    # 有具体技术名词加分
    tech_keywords = ["Python", "API", "SQL", "Docker", "FastAPI", "Django", "Flask"]
    found = sum(1 for kw in tech_keywords if kw.lower() in content.lower())
    score += min(found * 2, 10)
    
    # 有代码块加分
    if "```" in content:
        score += 5
    
    # 有具体数字加分
    import re
    numbers = re.findall(r'\d+', content)
    if len(numbers) >= 3:
        score += 3
    
    # 标题包含"如何""为什么""vs"等加分
    title_indicators = ["如何", "为什么", "vs", "VS", "对比", "避坑"]
    if any(ind in content[:200] for ind in title_indicators):
        score += 3
    
    # 太短减分
    if len(content) < 300:
        score -= 5
    
    return score

def deduplicate(results):
    """基于标题去重(简单版)"""
    seen_titles = set()
    unique = []
    for item in results:
        # 提取标题(假设第一行是标题)
        first_line = item["content"].split("\n")[0][:30]
        if first_line not in seen_titles:
            seen_titles.add(first_line)
            unique.append(item)
    return unique

# 加载并打分
results = [json.loads(line) for line in open("creative_output.jsonl", encoding="utf-8")]
results = deduplicate(results)

scored = [(score_creative(r), r) for r in results]
scored.sort(key=lambda x: x[0], reverse=True)

# 输出 top 20
for score, item in scored[:20]:
    print(f"[{score}分] {item['seed']} | {item['angle']} | {item['format']}")
    print(f"  {item['content'][:100]}...")
    print()

八、一个真实输出示例

种子:{"topic": "Python 虚拟环境", "audience": "转行学编程的文科生", "constraint": "必须提到 conda 和 venv 的恩怨"}

参数组合:contrarian + story + personal_failure

模型输出(节选):

标题:我搞砸了 conda 和 venv 之后,才明白虚拟环境不是"可选技能"

开头钩子:我搞砸了一次部署,原因是我在 conda 环境里装了包,却用 venv 跑代码------这两个东西根本不该同时存在,但我花了 3 天才搞清楚。

核心内容大纲

  1. conda 不是包管理器,是环境模拟器 ------ 一句话说明区别,配 conda list vs pip list 输出对比
  2. venv 的干净与残酷 ------ 它只管 Python 包,不管系统依赖,所以轻量但"裸"
  3. 我犯的错误:混用 ------ 展示 which python 在不同环境下的输出差异
  4. 文科生的生存方案:只选一个 ------ 给出决策树:数据科学→conda,Web 开发→venv

结尾互动:你第一次遇到"包装了但 import 报错"是什么时候?

这个东西你直接拿去就能写。而且它和"5 个 Python 技巧"那种稿子完全不是一个物种


九、进阶:让差异化更进一步

1. 平台适配层

同一个创意,小红书需要"情绪 + 干货 + 排版",B 站需要"节奏 + 反转 + 视觉提示",掘金需要"深度 + 代码 + 架构图"。加一层平台参数:

复制代码
PLATFORMS = {
    "xiaohongshu": "每段不超过 5 行,必须有用 emoji 分隔,语气像朋友安利",
    "bilibili": "需要设计'悬念点'和'笑点'的位置标记,适合口播节奏",
    "juejin": "需要完整的代码块和架构说明,语气专业克制",
}

2. 系列化生成

不要一个一个孤立选题,让模型一次性生成系列内容

复制代码
SERIES_TEMPLATE = """
基于话题"{{ topic }}",生成一个 5 篇的系列大纲,要求:
- 每篇之间有逻辑递进关系(不能简单并列)
- 第 1 篇是"钩子篇",用最低门槛吸引最广泛受众
- 第 5 篇是"深度篇",留住核心粉丝
- 每篇标注预估阅读/观看时长
"""

3. 竞品差异化注入

把你竞品的标题喂进去,让模型刻意避开

复制代码
AVOID_PATTERNS = [
    "不要出现'终极指南'、'完整教程'、'一文搞懂'这类标题党词汇",
    "不要和以下已有标题雷同:{competitor_titles}",
]

十、关键认知

这套方案的本质不是"让 AI 替你创作",而是把你脑子里的创意维度显式化、参数化,然后用工具批量探索组合空间

  • 模型负责生成和变形
  • 你负责定义什么是"好"和什么是"不同"
  • Python 负责把探索空间放大 100 倍

最终产出的 100 个创意里,可能只有 10 个能用。但问题是------你自己想 10 个差异化创意可能要花一周,而这套流程跑一次只要 20 分钟

省下来的时间,你用来打磨那 10 个真正好的创意,才是正经事。

相关推荐
繁星蓝雨8 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J9 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
杜大哥9 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk5559 小时前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python
yurenpai(27届找实习中)9 小时前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18
shmily麻瓜小菜鸡9 小时前
JS/TS 易踩坑知识点 — 模块化与工程化类
开发语言·javascript·ecmascript
geovindu10 小时前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
郝学胜-神的一滴10 小时前
C++11 工程级应用 09:告别无谓拷贝,解锁高性能移动语义
开发语言·数据结构·c++·vscode·软件工程·visual studio
MC皮蛋侠客11 小时前
OPC UA 系列(一):标准全景与 Python 最小闭环——让第一条设备数据流动起来
开发语言·python·opcua
SamChan9012 小时前
PDF翻译后的格式完整性校验:用Python自动比对译文与原文档的表格与段落结构
开发语言·python·ai·pdf·机器翻译