perf: 全链路 LLM 提示词优化 + 微信公众号配图改为 base64 内联
- research.py: 提示词增加热点关联/独特观点储备/市场价值判断/平台搜索趋势 - outline.py: 增加反常识洞察/平台推荐优化/每章市场价值产出要求 - writer.py _expand_section: 增加独特观点/热点时效/专业简洁/平台推荐友好要求 - writer.py _optimize_title: 各平台补充搜索热词利用/情绪感召/参考真实爆款语气 - writer.py wechat配图: placeholder.jpg 改为 base64 内联 SVG(带相机图标+提示文案),预览可渲染,复制即用
This commit is contained in:
+24
-15
@@ -51,7 +51,7 @@ class Outliner:
|
|||||||
cases_summary = self.research_notes[:2000] if self.research_notes else "暂无研究笔记"
|
cases_summary = self.research_notes[:2000] if self.research_notes else "暂无研究笔记"
|
||||||
|
|
||||||
if HAVE_LLM:
|
if HAVE_LLM:
|
||||||
prompt = f"""你是一个资深内容编辑,擅长设计读者爱看+搜索引擎友好+平台愿意推荐的文章结构。
|
prompt = f"""你是一个资深内容编辑,擅长设计读者爱看+搜索引擎友好+平台愿意推荐+有市场传播力的文章结构。
|
||||||
|
|
||||||
## 选题信息
|
## 选题信息
|
||||||
标题:{title}
|
标题:{title}
|
||||||
@@ -68,25 +68,34 @@ class Outliner:
|
|||||||
- 5-8章,每章2-4个要点
|
- 5-8章,每章2-4个要点
|
||||||
- 结构要有递进:要么认知升级型,要么问题解决型
|
- 结构要有递进:要么认知升级型,要么问题解决型
|
||||||
- 把独特视角和受众痛点融入各章,不单独列
|
- 把独特视角和受众痛点融入各章,不单独列
|
||||||
- 每章标题自带信息量,不要「引言」「总结」这类通用标题
|
- 每章标题自带信息量+好奇心,不要「引言」「总结」这类通用标题
|
||||||
|
- 开头要有"钩子"(hook)抓住读者,结尾要有可转发/收藏的总结
|
||||||
|
|
||||||
### 数据要求
|
### 数据与热点
|
||||||
- **全文使用的数据必须为2025-2026年最新数据**,禁用2024年及之前过时数据
|
- **全文使用2025-2026年最新数据**,禁用过时数据
|
||||||
- 每个观点尽量配最新的数据或案例支撑
|
- 每个观点尽量配最新的国内外热点事件/数据/政策来佐证
|
||||||
|
- 体现当前行业正在讨论的核心议题
|
||||||
|
|
||||||
|
### 独特风格
|
||||||
|
- 有自己的判断和立场,不是搬运观点
|
||||||
|
- 每章至少一个"反常识"或"很少有人提"的洞察
|
||||||
|
- 避免同质化表达、老生常谈
|
||||||
|
|
||||||
### SEO
|
### SEO
|
||||||
- H2/H3标题自然包含用户搜索时会用的短语
|
- H2/H3自然包含用户搜索时会用的短语
|
||||||
- 确保大纲覆盖2-3个高价值搜索词
|
- 确保大纲覆盖2-3个高价值搜索词,包含1个长尾词
|
||||||
|
- 每章标题对用户搜索意图有回应
|
||||||
|
|
||||||
### 价值
|
### 市场价值
|
||||||
- 每章回答读者「所以呢?」——为什么对他有用
|
- 读完每章读者能拿走一个实际有用的东西(方法/清单/思维框架/判断标准)
|
||||||
- 避免空洞章节,尽量包含可操作内容(步骤/清单/方法)
|
- 避免「信息增量为零」的空洞章节
|
||||||
|
- 思考:这篇文章对读者职业/生活/认知有什么用?
|
||||||
|
|
||||||
### 平台预适配提示
|
### 平台推荐优化
|
||||||
- 知乎方向偏数据分析和深度逻辑
|
- 知乎:偏硬核数据分析和深度逻辑,结构要有论证链
|
||||||
- 小红书方向偏实操步骤和清单
|
- 小红书:偏实操步骤/清单/对比,结构要一目了然
|
||||||
- 公众号方向偏故事和情感共鸣
|
- 公众号:偏故事化开头+情感共鸣+金句结尾,段落节奏快
|
||||||
- 同一大纲应能适应不同平台侧重点
|
- 一个框架适应三平台,各平台可裁剪侧重点
|
||||||
|
|
||||||
直接输出大纲,不要输出思考过程。"""
|
直接输出大纲,不要输出思考过程。"""
|
||||||
try:
|
try:
|
||||||
|
|||||||
+8
-6
@@ -75,7 +75,7 @@ class Researcher:
|
|||||||
# 获取实时搜索数据作为 LLM 参考
|
# 获取实时搜索数据作为 LLM 参考
|
||||||
search_data = enrich_topic_research(self.topic)
|
search_data = enrich_topic_research(self.topic)
|
||||||
search_section = f"\n## 实时搜索结果\n{search_data}\n" if search_data else ""
|
search_section = f"\n## 实时搜索结果\n{search_data}\n" if search_data else ""
|
||||||
prompt = f"""你是一个行业研究员+内容策略师,擅长从案例中发现真洞察,并能判断什么内容对真实读者最有价值。
|
prompt = f"""你是一个行业研究员+内容策略师,擅长从案例中发现真洞察+抢占热点的敏锐嗅觉,能判断什么内容对真实读者最有价值且正被市场热议。
|
||||||
|
|
||||||
基于以下选题和相关案例,写出能支撑文章核心观点、对读者真正有用的研究发现。
|
基于以下选题和相关案例,写出能支撑文章核心观点、对读者真正有用的研究发现。
|
||||||
|
|
||||||
@@ -90,12 +90,14 @@ class Researcher:
|
|||||||
{cases_text}
|
{cases_text}
|
||||||
|
|
||||||
## 输出要求(按顺序):
|
## 输出要求(按顺序):
|
||||||
1. 核心发现:2-3个真正有价值的洞察。每条需包含这个发现对读者意味着什么,以及支撑数据。**所有数据必须是2025-2026年最新数据,禁用过时数据**
|
1. **国内外最新热点关联**:当前该领域正在讨论什么、国内外哪些事件/政策/数据在发酵、为什么现在这个话题值得关注。**所有数据必须是2025-2026年最新数据**
|
||||||
2. SEO关键词建议:重点布局哪些搜索词(3-5个,含1-2个长尾词)
|
2. **核心发现**:2-3个真正有价值的洞察。每条需包含这个发现对读者意味着什么,以及支撑数据(附数据来源)
|
||||||
3. 讨论点:哪个观点最有争议或最可能引发讨论?
|
3. **独特观点储备**:哪些角度别人没写过、可以讲出差异化?提供至少一个反向/冷门视角
|
||||||
4. 待验证:指出1-2个不确定方向
|
4. **SEO关键词建议**:重点布局哪些搜索词(3-5个,含1-2个长尾词),以及各平台近期搜索上升趋势
|
||||||
|
5. **讨论点**:哪个观点最有争议或最可能引发讨论/转发/评论?
|
||||||
|
6. **市场价值判断**:读完这篇文章,读者能得到什么实际改变/方案/能力?
|
||||||
|
|
||||||
风格:说人话,直击要点。避免「首先其次最后」「综上所述」。直接输出内容,不要输出思考过程。"""
|
风格:说人话,直击要点,像资深编辑在给作者做 briefing。避免「首先其次最后」「综上所述」。直接输出内容,不要输出思考过程。"""
|
||||||
try:
|
try:
|
||||||
return call_llm(prompt, temperature=0.5, max_tokens=1200, system_prompt="你是一个行业研究员,擅长从案例中发现真洞察。")
|
return call_llm(prompt, temperature=0.5, max_tokens=1200, system_prompt="你是一个行业研究员,擅长从案例中发现真洞察。")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+36
-15
@@ -105,17 +105,26 @@ class Writer:
|
|||||||
return content
|
return content
|
||||||
if HAVE_LLM and len(content) < 150:
|
if HAVE_LLM and len(content) < 150:
|
||||||
logger.info(f"使用 LLM 扩写章节: {section['title']}")
|
logger.info(f"使用 LLM 扩写章节: {section['title']}")
|
||||||
prompt = f"""你是一个真人写作者,正在写一篇关于「{self.topic['title']}」的文章。现在写「{section['title']}」这一节。
|
prompt = f"""你是一个真人写作者+行业观察者,正在写一篇关于「{self.topic['title']}」的文章。现在写「{section['title']}」这一节。
|
||||||
|
|
||||||
笔记要点:
|
笔记要点:
|
||||||
{content}
|
{content}
|
||||||
|
|
||||||
要求(逐条对照):
|
要求(逐条对照,每一条都不能跳过):
|
||||||
|
### 热点与时效
|
||||||
|
- **引用2025-2026年最新数据/事件/政策/行业报告**,禁用过时数据
|
||||||
|
- 体现当前国内外在讨论什么、最新的趋势变化
|
||||||
|
- 每个论点尽量配一个真实发生的最新案例
|
||||||
|
|
||||||
|
### 独特观点
|
||||||
|
- 有自己的判断和立场,拒绝"车轱辘话"和"正确废话"
|
||||||
|
- 至少提供一个"大多数人没想到"的角度
|
||||||
|
- 宁可尖锐,也不要平庸
|
||||||
|
|
||||||
### 价值
|
### 价值
|
||||||
- 回答读者一个具体问题或解决一个困惑
|
- 回答读者一个具体问题或解决一个困惑
|
||||||
- 每个论点配真实案例或数据,不写空话
|
- 每段回答一个"所以呢?"——读者看完能带走什么
|
||||||
- **所有数据必须使用2025-2026年最新数据**,禁用过时数据
|
- 结束时读者要有「学到了+想转发」的感觉
|
||||||
- 结束时读者要有「学到了」的感觉
|
|
||||||
|
|
||||||
### 真人感
|
### 真人感
|
||||||
- 用「你」或「我们」视角,不要用「我」
|
- 用「你」或「我们」视角,不要用「我」
|
||||||
@@ -128,9 +137,14 @@ class Writer:
|
|||||||
- 自然融入1-2个目标搜索词,不生硬堆砌
|
- 自然融入1-2个目标搜索词,不生硬堆砌
|
||||||
- 第一句包含核心关键词
|
- 第一句包含核心关键词
|
||||||
|
|
||||||
### 长度
|
### 专业与简洁
|
||||||
|
- 语言精准,不注水,不为了字数凑内容
|
||||||
- 200-400字,写到点子上就停
|
- 200-400字,写到点子上就停
|
||||||
|
|
||||||
|
### 平台推荐友好
|
||||||
|
- 节奏不能平,要有起承转合
|
||||||
|
- 结尾要让人有点赞/收藏/转发的冲动
|
||||||
|
|
||||||
直接输出段落正文。"""
|
直接输出段落正文。"""
|
||||||
try:
|
try:
|
||||||
expanded = call_llm(prompt, temperature=0.6, max_tokens=1500)
|
expanded = call_llm(prompt, temperature=0.6, max_tokens=1500)
|
||||||
@@ -279,11 +293,13 @@ class Writer:
|
|||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 有信息量:一看就知道能解决什么问题
|
- 有信息量:一看就知道能解决什么问题
|
||||||
- 含知乎搜索关键词(SEO)
|
- 含知乎搜索关键词(SEO),利用知乎搜索联想热词
|
||||||
- 带数字或对比最好(「3个方法」「从…到…」)
|
- 带数字或对比最好(「3个方法」「从…到…」)
|
||||||
- 20字以内
|
- 20字以内
|
||||||
- 参考知乎真实高赞标题,不要套路句式
|
- 参考知乎真实高赞标题风格,不要套路句式
|
||||||
- 避免「如何…」废句式、「XXX指南/手册/全攻略」
|
- 避免「如何…」废句式、「XXX指南/手册/全攻略」
|
||||||
|
- 有观点、有态度,不是中性描述
|
||||||
|
- 直击目标读者痛点或好奇心
|
||||||
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
||||||
|
|
||||||
生成 3 个选项,每行一个。""",
|
生成 3 个选项,每行一个。""",
|
||||||
@@ -295,10 +311,12 @@ class Writer:
|
|||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 制造好奇心和点击欲,让人觉得不点开会错过
|
- 制造好奇心和点击欲,让人觉得不点开会错过
|
||||||
- 包含微信搜索关键词(微信SEO)
|
- 包含微信搜索关键词(微信SEO),利用搜一搜热门词
|
||||||
- 口语化,不要书面腔
|
- 口语化,不要书面腔
|
||||||
- 不要感叹号堆砌,不要「重磅/震惊/紧急」
|
- 不要感叹号堆砌,不要「重磅/震惊/紧急」
|
||||||
- 字数15-25字最佳
|
- 字数15-25字最佳
|
||||||
|
- 有情绪感召力:共鸣/好奇/焦虑/期待
|
||||||
|
- 参考近期10万+标题的语气节奏
|
||||||
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
||||||
|
|
||||||
生成 3 个选项,每行一个。""",
|
生成 3 个选项,每行一个。""",
|
||||||
@@ -310,12 +328,13 @@ class Writer:
|
|||||||
|
|
||||||
要求:
|
要求:
|
||||||
- 20字以内
|
- 20字以内
|
||||||
- 采用爆款模式:数字+结果/痛点+方案/反常识观点
|
- 采用爆款模式:数字+结果/痛点+方案/反常识观点/对比式
|
||||||
- 包含小红书搜索关键词(SEO)
|
- 包含小红书搜索关键词(SEO),利用搜索下拉热词
|
||||||
- 带1个emoji点缀
|
- 带1个精准emoji点缀,不要三个起堆
|
||||||
- 有场景感/结果感
|
- 有场景感/结果感/获得感
|
||||||
- 不要「必看/收藏/码住」
|
- 不要「必看/收藏/码住」
|
||||||
- 像真实用户写的,不是运营写的
|
- 像真实用户写的,不是运营写的
|
||||||
|
- 参考小红书搜索热榜标题风格
|
||||||
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
- 直接输出3个标题选项,每行一个,不要输出思考过程
|
||||||
|
|
||||||
生成 3 个选项,每行一个。""",
|
生成 3 个选项,每行一个。""",
|
||||||
@@ -354,8 +373,10 @@ class Writer:
|
|||||||
|
|
||||||
# WeChat: insert image placeholder at start of body
|
# WeChat: insert image placeholder at start of body
|
||||||
if platform == "wechat":
|
if platform == "wechat":
|
||||||
img_tag = '<p><img src="placeholder.jpg" alt="配图" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
|
img_svg = '<svg xmlns="http://www.w3.org/2000/svg" width="1080" height="600" viewBox="0 0 1080 600" style="width:100%;max-width:1080px;border-radius:8px;background:#f0f2f5"><rect width="1080" height="600" fill="#f0f2f5"/><g transform="translate(540,260)" text-anchor="middle" font-family="-apple-system,BlinkMacSystemFont,sans-serif"><circle cx="0" cy="-20" r="40" fill="#d0d5dd"/><path d="M-25,10 L-10,10 L-5,0 L5,0 L10,10 L25,10 L25,60 L-25,60 Z" fill="#d0d5dd"/><circle cx="0" cy="10" r="15" fill="#fff"/><text y="80" font-size="18" fill="#98a2b3">点击替换配图(建议 1080×600)</text></g></svg>'
|
||||||
# Insert after <h1> if present, else prepend
|
import base64
|
||||||
|
img_b64 = 'data:image/svg+xml;base64,' + base64.b64encode(img_svg.encode('utf-8')).decode('ascii')
|
||||||
|
img_tag = f'<p><img src="{img_b64}" alt="配图" style="width:100%;max-width:1080px;border-radius:8px;"></p>\n'
|
||||||
h1_end = html_content.find('</h1>')
|
h1_end = html_content.find('</h1>')
|
||||||
if h1_end != -1:
|
if h1_end != -1:
|
||||||
html_content = html_content[:h1_end + 5] + '\n' + img_tag + html_content[h1_end + 5:]
|
html_content = html_content[:h1_end + 5] + '\n' + img_tag + html_content[h1_end + 5:]
|
||||||
|
|||||||
Reference in New Issue
Block a user