process_title.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. import time
  2. import traceback
  3. from tqdm import tqdm
  4. from applications.api import fetch_deepseek_completion
  5. class Const:
  6. # title rewrite status
  7. TITLE_REWRITE_INIT_STATUS = 0
  8. TITLE_REWRITE_SUCCESS_STATUS = 1
  9. TITLE_REWRITE_FAIL_STATUS = 99
  10. TITLE_REWRITE_LOCK_STATUS = 101
  11. # article status
  12. ARTICLE_AUDIT_PASSED_STATUS = 1
  13. ARTICLE_POSITIVE_STATUS = 0
  14. # title useful status
  15. TITLE_USEFUL_STATUS = 1
  16. # prompt version
  17. PROMPT_VERSION = "xx_250228" # 信欣2025-02-28提供
  18. # block expire time 1h
  19. TITLE_REWRITE_LOCK_TIME = 60 * 60
  20. class TitleProcess(Const):
  21. def __init__(self, pool, aliyun_log):
  22. self.pool = pool
  23. self.aliyun_log = aliyun_log
  24. @classmethod
  25. def generate_title_rewrite_prompt(cls, ori_title):
  26. """
  27. 生成prompt
  28. """
  29. prompt = f"""
  30. 请将以下标题改写成适合公众号中小程序点击和传播的文章标题,文章标题的写作规范如下,请学习后进行文章标题的编写。直接输出最终的文章标题,文章标题撰写规范如下:
  31. 1. 标题结构:要点前置,信息明确
  32. 核心信息前置:标题开头直接点出文章的核心内容或亮点,吸引读者注意。例如:
  33. “我国存款最安全的五大银行,永远都不会倒闭,你知道是哪五家吗?”
  34. “亩产7000斤,被误认成萝卜却曾是‘救命粮’,如今成我国出口名蔬”。
  35. 简洁明了:标题通常在20字以内,信息集中且易于理解。
  36. 悬念前置结构:前半句设置反常/冲突场景(如"刑满释放蹬三轮")+后半句用结果反转制造悬念("政府领导登门分配工作")
  37. 多要素拼接:通过冒号/逗号分隔不同叙事主体(地域+人物冲突+权威评价),如"辽宁女子住高档小区被敲门,法院判决意外"
  38. 2. 情绪表达:激发共鸣,引发好奇
  39. 情感共鸣:通过情感化的语言触动读者,泪崩/守护/抱头痛哭等情感冲击词,配合家庭伦理场景
  40. 例如:
  41. “老母亲分家产,给亲闺女30万,给养女一筐青菜,养女意外摔倒,看到筐子里的东西,瞬间愣住了”。
  42. “儿子卖车卖房给母亲治病,母亲去世后儿媳收拾房间,打开床底柜,儿子突然痛哭”。
  43. 悬念与好奇心:通过提问或制造悬念,激发读者点击欲望。例如:
  44. “你知道是哪五家吗?”
  45. “打开床底柜,儿子突然痛哭”。
  46. 冲突性情绪词:拍桌大骂/气愤不已/眼红不已/算计等强对抗性词汇
  47. 结果反差刺激:用"风光善终/价值过亿/判决意外"等违反预期的结果
  48. 3. 语言风格:口语化、接地气
  49. 口语化表达:使用通俗易懂的语言,贴近读者生活。
  50. 刻意使用"赶都赶不走/各吃各的/我就知道你在家"等市井化用语。
  51. 例如:
  52. “狗屎运?江西男子钓鱼时发现青鱼尸骸,扒开后捡到鸡蛋大小的青鱼石”。
  53. “聪明的女人,不会帮婆家3种忙,而蠢女人才一再插手”。
  54. 接地气的词汇:使用“狗屎运”“蠢女人”等口语化词汇,增强亲切感。
  55. 身份反差构建:突出人物命运转折(老农→亿万富翁/囚犯→政府帮扶对象)
  56. 权威背书暗示:"专家气愤/法院判决/网友评价"等第三方视角增强可信度
  57. 4. 标点运用:增强语气,突出重点
  58. 问号与感叹号:通过问号制造悬念,感叹号强化情感。
  59. 在关键转折点使用("太气人了!/赔不了!")
  60. 问号制造互动:如"容嬷嬷是校花?"激发读者验证心理
  61. 例如:
  62. “你知道是哪五家吗?”
  63. “太无耻了!湖南,一名厨师被公司派到云南‘出差’被拒……”
  64. 引号与冒号:用于突出关键词或转折点。
  65. 破折号递进:用"——"引导关键信息("吃不完最好扔掉——")
  66. 例如:
  67. “被误认成萝卜却曾是‘救命粮’”。
  68. “女子归还后,失主拒绝支付报酬,还说:要有格局”。
  69. 5. 热点与话题性:结合社会热点或争议
  70. 社会热点:结合当前热点事件或争议话题,吸引关注。例如:
  71. “上海:男子超市连续购买46枚过期咸鸭蛋,2天分46次交易,向厂家索赔金14万,法院判了!”
  72. 争议性话题:通过争议性内容引发讨论。例如:
  73. “李玉成终于说出实话,公开吐槽马玉琴年纪太大,结婚28年疑似后悔”。
  74. 6. 数字与具体细节:增强可信度与吸引力
  75. 数字的运用:通过具体数字增强标题的可信度和吸引力。例如:
  76. “亩产7000斤”。
  77. “22年河南男子跳河救人,体力耗尽留遗言”。
  78. 细节描述:通过细节让标题更具画面感。例如:
  79. “打开床底柜,儿子突然痛哭”。
  80. “扒开后捡到鸡蛋大小的青鱼石”。
  81. 7. 价值诉求:传递实用信息或情感价值
  82. 实用信息:提供对读者有价值的信息。例如:
  83. “我国存款最安全的五大银行,永远都不会倒闭”。
  84. “72岁老人每天一个蒸苹果,半年后体检,看到指标变化让他乐开了花”。
  85. 情感价值:通过情感故事或人生哲理打动读者。例如:
  86. “父母越老越能暴露家庭最真实的一面:当父母70岁,子女不该抱有这三种期待”。
  87. 8. 名人效应与历史情怀:增强吸引力
  88. 名人效应:提及名人或历史事件,吸引关注。例如:
  89. “难怪王扶林说陈晓旭不够漂亮,看看他选的原黛玉候选人,那才叫美”。
  90. “1975年‘下馆子’的老照片,2元能吃些什么,勾起那段最难忘的时光”。
  91. 9.隐藏传播逻辑:通过标题中暗含的、能触发人性弱点(如猎奇、贪婪、同情)或社会痛点的心理机制,通过潜意识刺激读者点击欲望
  92. 人性弱点触发:贪婪(200万保单)、猎奇(林彪密件)、窥私(家庭算计)
  93. 生存焦虑关联:医疗(脑瘫儿)、养老(子女不孝)、食品安全(二次加热)
  94. 身份代入设计:选择"老太太/外甥女/退休母亲"等易引发群体共鸣的角色
  95. 输入的标题是: '{ori_title}'
  96. """
  97. return prompt
  98. class TitleRewrite(TitleProcess):
  99. async def roll_back_blocked_tasks(self):
  100. """
  101. rollback blocked tasks
  102. """
  103. query = f"""
  104. select id, title_rewrite_status_update_timestamp
  105. from publish_single_video_source
  106. where title_rewrite_status = {self.TITLE_REWRITE_LOCK_STATUS};
  107. """
  108. article_list = await self.pool.async_fetch(query=query, db_name="long_articles", )
  109. if article_list:
  110. blocked_id_list = [
  111. i["id"]
  112. for i in article_list
  113. if (
  114. int(time.time())
  115. - i["title_rewrite_status_update_timestamp"]
  116. )
  117. > self.TITLE_REWRITE_LOCK_TIME
  118. ]
  119. if blocked_id_list:
  120. update_query = f"""
  121. update publish_single_video_source
  122. set title_rewrite_status = %s
  123. where id in %s and title_rewrite_status = %s;
  124. """
  125. await self.pool.async_save(
  126. query=update_query,
  127. params=(
  128. self.TITLE_REWRITE_INIT_STATUS,
  129. tuple(blocked_id_list),
  130. self.TITLE_REWRITE_LOCK_STATUS,
  131. )
  132. )
  133. async def get_articles_batch(self, batch_size=1000):
  134. query = f"""
  135. select content_trace_id, article_title
  136. from publish_single_video_source
  137. where bad_status = {self.ARTICLE_POSITIVE_STATUS}
  138. and audit_status = {self.ARTICLE_AUDIT_PASSED_STATUS}
  139. and title_rewrite_status = {self.TITLE_REWRITE_INIT_STATUS}
  140. and platform in ('hksp', 'sph')
  141. limit {batch_size};
  142. """
  143. return await self.pool.async_fetch(query=query, db_name="long_articles")
  144. async def update_title_rewrite_status(self, content_trace_id, ori_status, new_status):
  145. query = f"""
  146. update publish_single_video_source
  147. set title_rewrite_status = %s, title_rewrite_status_update_timestamp = %s
  148. where content_trace_id = %s and title_rewrite_status= %s;
  149. """
  150. affected_rows = await self.pool.async_save(
  151. query=query, params=(new_status, int(time.time()), content_trace_id, ori_status)
  152. )
  153. return affected_rows
  154. async def insert_into_rewrite_table(self, content_trace_id, new_title):
  155. """
  156. insert into rewrite_table
  157. """
  158. insert_sql = f"""
  159. insert into video_title_rewrite
  160. (content_trace_id, new_title, status, prompt_version)
  161. values (%s, %s, %s, %s);
  162. """
  163. await self.pool.async_save(
  164. query=insert_sql,
  165. params=(
  166. content_trace_id,
  167. new_title,
  168. self.TITLE_USEFUL_STATUS,
  169. self.PROMPT_VERSION
  170. ),
  171. )
  172. async def rewrite_each_article(self, article):
  173. """
  174. rewrite each article
  175. """
  176. content_trace_id = article["content_trace_id"]
  177. article_title = article["article_title"]
  178. # lock each task
  179. affected_rows = await self.update_title_rewrite_status(
  180. content_trace_id=content_trace_id,
  181. ori_status=self.TITLE_REWRITE_INIT_STATUS,
  182. new_status=self.TITLE_REWRITE_LOCK_STATUS,
  183. )
  184. if not affected_rows:
  185. return
  186. try:
  187. prompt = self.generate_title_rewrite_prompt(article_title)
  188. new_title = fetch_deepseek_completion(model="default", prompt=prompt)
  189. # insert into rewrite table
  190. await self.insert_into_rewrite_table(
  191. content_trace_id=content_trace_id, new_title=new_title
  192. )
  193. # unlock
  194. await self.update_title_rewrite_status(
  195. content_trace_id=content_trace_id,
  196. ori_status=self.TITLE_REWRITE_LOCK_STATUS,
  197. new_status=self.TITLE_REWRITE_SUCCESS_STATUS,
  198. )
  199. except Exception as e:
  200. await self.aliyun_log.log(
  201. contents={
  202. "task": "title rewrite task",
  203. "function": "rewrite_each_article",
  204. "message": content_trace_id,
  205. "status": "fail",
  206. "data": {
  207. "error_message": str(e),
  208. "error_type": type(e).__name__,
  209. "traceback": traceback.format_exc(),
  210. }
  211. }
  212. )
  213. await self.update_title_rewrite_status(
  214. content_trace_id=content_trace_id,
  215. ori_status=self.TITLE_REWRITE_LOCK_STATUS,
  216. new_status=self.TITLE_REWRITE_FAIL_STATUS,
  217. )
  218. async def deal(self):
  219. """title rewrite task deal"""
  220. await self.roll_back_blocked_tasks()
  221. task_list = await self.get_articles_batch()
  222. bar = tqdm(task_list, desc="title rewrite task")
  223. for article in bar:
  224. await self.rewrite_each_article(article)
  225. bar.set_description("title rewrite task")
  226. class VideoPoolCategoryGeneration:
  227. pass
  228. class ArticlePoolCategoryGeneration:
  229. pass