product_title_agent.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. import asyncio
  2. from datetime import datetime
  3. import re
  4. import aiofiles
  5. import os
  6. import sys
  7. from typing import List, Dict, Any
  8. from typing import Any, Dict, List, Optional, Union
  9. from pydantic import BaseModel
  10. from typing import List
  11. from llama_index.core.program import LLMTextCompletionProgram
  12. from llama_index.core.output_parsers import PydanticOutputParser
  13. from llama_index.program.lmformatenforcer import (
  14. LMFormatEnforcerPydanticProgram,
  15. )
  16. from llama_index.core.prompts import PromptTemplate
  17. from llama_index.llms.llama_cpp import LlamaCPP
  18. from llama_index.llms.openai import OpenAI
  19. from llama_index.llms.litellm import LiteLLM
  20. from llama_index.core.llms.llm import LLM
  21. from src.models.product_model import Product
  22. from src.manager.template_manager import TemplateManager, TemplateService, TemplateType
  23. from src.models.ai_execution_record import MarketingInfo, LLMConfig, SuperPromptMixin, AgentConfig, AgentContent, AICompetitorAnalyzeMainKeywords, AICompetitorAnalyzeMainKeywordsResult, MarketingContentGeneration
  24. from ai.base_agent import BaseAgent
  25. from llama_index.core.prompts import PromptTemplate
  26. from llama_index.llms.litellm import LiteLLM
  27. from config.settings import MONGO_URL, MONGO_DB_NAME,LITELLM_API_BASE, LITELLM_API_KEY,OPENAI_API_KEY,OPENAI_API_BASE
  28. from utils.file import save_to_file, read_file
  29. from utils.logu import get_logger
  30. logger = get_logger("ai")
  31. class ProductTitleAgent(BaseAgent):
  32. async def get_promp(self, product_name, prompt: str='', prompt_keywords:Dict={}) -> PromptTemplate:
  33. context_template = '''\
  34. 这是我的产品信息:
  35. ```\n{product_info}\n```\n
  36. 这是我根据自己产品信息,参考同行竞品的主关键词和长尾关键词:
  37. ```\n{llm_keyword_markdown}\n```\n
  38. ---
  39. '''
  40. prompt_product = prompt or '''
  41. ### 必须满足以下要求
  42. #### 平台要求:
  43. 亚马逊商品要点(Bullet Point)也就是卖家常说的五点描述,五点描述重点了解商品的五个主要特征和优势,帮助买家快速确认商品是否适合自己。
  44. 要点
  45. 这些内容应等同于商品的“功能与优点”。建议您为 5 个要点采用以下格式:
  46. 特性要点 1: 内容(例如材质)
  47. 特性要点 2: 功能与优点
  48. 特性要点 3: 功能与优点
  49. 特性要点 4: 功能与优点
  50. 特性要点 5: 护理或特殊护理或保修
  51. 示例:
  52. 由 (100%) 柔软磨毛棉制成
  53. 适合周末休闲穿着的舒适衬衫
  54. 抗皱性强,无需熨烫
  55. 做旧水洗工艺让这款衬衫呈现出一种“穿过的”复古风格
  56. 温水机洗。滚筒干燥
  57. 常见的五点描述的例子包括:产品总体概要、尺寸 、 年龄适宜性、原产国、主要功能、材料和结构、保修信息
  58. 写作要求:
  59. - 以 大 写 字 母 开 头 。 例 如 : Cotton Fabric: Made from (100 %) cotton for softness andbreathability
  60. - 采用句段形式,请勿使用末尾标点符号。例如:Long Sleeve: Long sleeves add coverageand style
  61. - 在一个要点中使用分号分隔各个短语。例如: Machine Washable: Durable construction;easier care
  62. - 使用10 个以上字符,但不超过255 个字符。
  63. 例如:Loose Fit: Relaxed fit allows for easy movement and comfort
  64. 解释:这里有59 个字符,包括50 个英文字母和每个词之间的空格,所以多过 10 个字符,且没有超过255 个字符
  65. - 写出数字1-9 的文字形式,但名称、型号和测量值除外。
  66. 例如:Long Battery Life: Total of twenty-four hours battery life
  67. - 为要点添加标题,并在标题后面使用“:”,然后再在后面提供完整信息。
  68. 例如:Machine Washable: Durable construction allows for easy care
  69. - 在数字和度量单位之间添加一个空格。
  70. 例如:...60 Milliliter…
  71. - 使用清晰、自然的语言编写要点,避免使用不必要的关键词或短语。
  72. 例如:可机洗:经久耐用,易于护理
  73. - 突出商品特征和优势,而不是品牌的营销活动。
  74. 反面示例:限时折扣:现在购买,享受 \(50 \%\) 折扣
  75. - 突出商品如何满足买家需求,而不只是列出事实。
  76. 例如:多用途款式:适合舞蹈练习、娱乐玩耍或户外活动
  77. - 在商品变体之间保持数据一致。
  78. 例如:
  79. 变体1 要点:棉织物:由 \(100 \%\) 纯棉制成,柔软、透气
  80. 变体2 要点:棉织物:由 \(100 \%\) 纯棉制成,柔软、透气
  81. - 请勿转述或引用此ASIN 不包含的其他商品。
  82. 反面示例:充电速度:本充电器属于快充,和苹果官方售卖的充电器的充电速度一样快\(\textcircled{13}\) 去除或尽可能减少商品名称、商品描述或商品概览等属性的重复。突出附加信息或支持信息,以帮助买家做出更明智的决策。
  83. 例如:
  84. 要点1:材质:这款水壶由优质不锈钢制成,持久耐用要点2:容量:水壶容量为1.5 升,适合家庭或户外使用要点3:保温:独特的双层保温设计,能够保持饮品温度长达12 小时解释:在正确的例子中,每个要点都提供了不同的产品信息(材料、容量和保温设计),这样客户可以更全面地了解产品的各个特点\(\textcircled{14}\) 避免使用任何主观声明、绩效声明或对比声明,除非它们可在商品包装上得到核实。
  85. 反面示例:…比X 品牌更耐用、更便宜,功能更多
  86. - 避免使用与荣誉和奖项有关的声明,除非商品详情页面提供有详细证明信息,例如日期和授予机构等。
  87. 反面示例:…ASIN 销量常年保持同类目第三
  88. - 避免使用与消费者调查结果有关的声明,即使相关调查收集了一些主观意见,除非提供有来源和日期。
  89. 反面示例:…根据调查, \(9 5 \%\) 的用户都推荐这款产品
  90. 写作禁止:
  91. 1.禁止使用特殊字符
  92. 例如™、®、€、…、†、‡、o、¢、£、¥、©、±、\~、â。
  93. 【错误示例】¥¥ Cotton Fabric ¥¥: Made from \(100 \%\) cotton for softness and breathability
  94. 2.禁止使用任何表情符号例如☺
  95. 【错误示例】 Cotton Fabric: Made from \(100 \%\) cotton for softness and breathability
  96. 3.禁止使用ASIN 编号或以下字样
  97. 例如“不适用” \(/ ^ { \prime \prime } \mathsf { N A ^ { \prime \prime } } / ^ { \prime \prime } \mathsf { n } / \mathsf { a ^ { \prime \prime } } / ^ { \prime \prime } \mathsf { N } / \mathsf { A ^ { \prime \prime } }\) 、“不符合要求”、“尚待决定”、“待决定”、“待定”、“待复制”。
  98. 【错误示例】Material:NA
  99. 4.禁止使用禁用短语例如生态友好、环境友好、抗微生物、抗菌、竹制、含竹类、大豆制成、含大豆。
  100. 5.禁止使用禁用短语例如生态友好、环境友好、抗微生物、抗菌、竹制、含竹类、大豆制成、含大豆。有关更多信息,您可复制以下链接至浏览器,前往参阅一般商品限制内容https://sellercentral.amazon.com/help/hub/reference/external/G201707070?locale \(\ c =\) en-US【错误示例】材质:本产品原材料是 \(100 \%\) 环保的,具有抗菌特性
  101. 6.禁用保证信息
  102. 例如“全额退款”、“如果不满意,请将其退回”或“无条件保证,无限制”。
  103. 【错误示例】…如果不满意,我们提供全额退款7.禁用公司信息、网站链接、外部超链接或任何联系信息【错误示例】限时优惠:欢迎访问我们的网站(www.example.com)获取更多额外优惠
  104. 8.禁用引导评论相关的信息
  105. 包括但不限于鼓励买家留下5 星好评,或者引导买家不留负面评论等。
  106. 【错误示例】…5 星好评免费赠送小礼物
  107. 9.禁用时效性信息例如宣传活动、研讨会或讲座的日期。
  108. 【错误示例】…欢迎参加9 月16 号xx 产品线下推广活动,地址位于…
  109. 10.禁止要点重复每个要点必须说明一条唯一的商品信息。
  110. 【错误示例】棉织物:由 \(100 \%\) 纯棉制成,柔软、透气;宽松合体:宽松合体便于运动且穿
  111. 着舒适;可机洗:经久耐用,易于护理
  112. 解释:示例里同时包含了3 条商品信息
  113. #### 用户要求
  114. 作为专业的日本站亚马逊运营,你需要根据产品信息和参考关键词,为我生成日语的产品标题、ST搜索词、卖点1~5。
  115. 亚马逊标题要求:
  116. - 保持简洁:200 个字符是允许的最大字符数,因为手机屏幕会缩短较长的商品名称避免冗余:请勿在商品名称中包含冗余信息、不必要的同义词或过多的关键词优化词序:仅包含有助于买家快速识别和了解商品的信息,将词语排序以优先展示最重要的商品信息。
  117. - 创建商品名称时,请遵循以下标准:
  118. - 商品名称不能超过 200 个字符(包括空格)。
  119. - 商品名称不可包含促销用语,如“ 無料配送”或“ 100% 品質保証、人気商品、ベストセラー 。”
  120. - 商品名称不能使用以下特殊字符:!、$、?、_、{、}、^、¬、¦。其他特殊字符,如 ~、#、<、> 和 *,只能在特定上下文中使用。
  121. - 例如,您可以使用这些符号作为商品编码(“款式 #4301”)或测量值(“<10 磅”)。
  122. - 不允许使用特殊字符作为装饰。例如,商品名称“Paradise Towel Wear Co. Beach Coverup << Size Kids XXS >>”不符合要求,因为尺码周围存在过多使用符号的情况。
  123. - 商品名称必须包含可用于清晰描述商品的最少信息,如“Amazon Essentials Dress”“Columbia Hiking Boots”或“Sony Headphones”。
  124. - 商品名称中不能有重复字词。例如,“Baby Boy Outfits Baby Boy fall Winter Clothes Baby Boy Long Sleeve Suspender Outfit Sets”是不符合要求的商品名称。
  125. - 标题中需要选择1-3 个关键词/产品定位词/消费者用来搜索产品的词语+ 材质/功能/场景等和其他同类产品形成差异化的词语。做到简洁清晰、格式分明、无词语堆砌。
  126. - 标题参考顺序:品牌名称+大流量的核心关键词商品名称+关键属性(即商品的唯一销售主张)+1-3 个特性词/功能词/属性词+口味/款式+颜色+尺寸+包装数量+型号。例子:
  127. - (Brand name) Brushed Grey Towel Bar, 22 Inch 304 Stainless Steel Towel Rack Bathroom, Towel Holder Brushed Grey Wall Mount, Total Length 24 Inch
  128. - 根据标题公式,建议颜色/规格信息置后,将核心关键词和特性词提前,尽量避免重复信息。
  129. - (Brand Name) Towel Racks for Bathroom,304 Stainless Steel Towel Bar,Wall Mount Towel Holder with Total Length 24 Inch(22 Inch, Brushed Grey)
  130. - 核心关键词往前放,3-5 个最佳消费者关注的核心卖点。例如“纯白高纤维棉花脚趾保护套”这个产品,必须把“脚趾保护套”关键词放在标题最前面(这是核心商品关键词),颜色、规格、材质等信息放在后面。
  131. - 一定要写遵守亚马逊标题格式要求,控制字符,尽量精简善用标点和括号无语法、拼写错误,无中式表达
  132. - 生成的内容“产品标题、ST搜索词”各个关键词之间只能空格隔开,卖点需要用 '【】' 将重要的卖点括起来。
  133. ②关键词权重分配
  134. - 前60字符:放置核心关键词,确保移动端优先展示
  135. - 中间部分:补充高转化词和场景词末尾:加入技术参数或独特卖点
  136. 观点补充:
  137. - 根据你生成的 日语的产品标题、ST搜索词、卖点1~5 进行观点补充,解释你为什么要这样选择
  138. ---
  139. 生成内容的格式 {output_type}
  140. '''
  141. prompt_tmpl = PromptTemplate(template=context_template + prompt_product, )
  142. return prompt_tmpl.partial_format(
  143. **prompt_keywords,
  144. )
  145. async def get_promp_template_result(self, product_name, prompt: str='',llm_model='', output_type='markdown', verbose=False):
  146. llm_name = llm_model or self.llm._get_model_name()
  147. models = await AgentContent.find_one(
  148. AgentContent.product_name == product_name,
  149. AgentContent.model_name==llm_name,
  150. )
  151. if not models:
  152. msg = f"no content for {product_name}"
  153. # Either raise a proper exception
  154. raise ValueError(msg)
  155. main_keys_info = models.competitor.get('markdown')
  156. if not main_keys_info:
  157. # logger.error(f"AgentContent.competitor.get('markdown') not exist: {product_name}")
  158. msg = f"AgentContent.competitor.get('markdown') not exist: {product_name}"
  159. raise msg
  160. variables = {'product_name': product_name}
  161. product_info = await self.template_manager.execute_template("product_info", variables)
  162. prompt_tmpl = await self.get_promp(
  163. product_name, prompt,
  164. prompt_keywords={
  165. 'llm_keyword_markdown':main_keys_info, 'output_type':output_type, 'product_info':product_info
  166. })
  167. result = prompt_tmpl.format()
  168. logger.info(f"{result}")
  169. return result
  170. async def acomplete(self, product_name, prompt: str='',llm_model='', output_type='markdown', verbose=False):
  171. promp_template_result = await self.get_promp_template_result(product_name, prompt,llm_model, output_type, verbose)
  172. response = await self.llm.acomplete(promp_template_result, verbose=verbose)
  173. res = self.filter_markdown_content(response.text)
  174. logger.info(f"{res}")
  175. llm_name = llm_model or self.llm._get_model_name()
  176. models = await AgentContent.find_one(
  177. AgentContent.product_name == product_name,
  178. AgentContent.model_name==llm_name,
  179. )
  180. if not models.marketing:
  181. models.marketing = {}
  182. models.marketing[output_type] = res
  183. return await models.save()
  184. async def gen_marketing_file(self, product_name, output_path: str, llm_model: str=''):
  185. models = await AgentContent.find(
  186. AgentContent.product_name == product_name,
  187. ).to_list()
  188. logger.info(f"models {models}")
  189. content = f"# {product_name}\n\n"
  190. for model in models:
  191. logger.info(f"{model.product_name} {model.model_name}")
  192. marketing = model.marketing.get('markdown')
  193. content += f"## {model.model_name}\n\n{marketing}\n\n"
  194. promp_template_result = await self.get_promp_template_result(product_name, llm_model=models[0].model_name)
  195. content += f"## 提示词\n\n{promp_template_result}\n\n"
  196. return save_to_file(content, output_path)
  197. async def llm_task(product_name='狗刷牙指套'):
  198. m = TemplateManager(MONGO_URL, MONGO_DB_NAME)
  199. await m.initialize()
  200. model = 'openai/groq/llama-3.1-8b-instant'
  201. # model = 'groq/groq/qwen-2.5-coder-32b'
  202. # model = 'openai/glm-4-flash'
  203. # model = 'openai/deepseek-v3'
  204. # model = 'openai/groq/qwen-2.5-32b'
  205. # model = 'openai/deepseek-chat'
  206. model = 'openai/deepseek-reasoner'
  207. # model = 'openai/doubao-pro-32k-241215'
  208. llm_list = [
  209. LiteLLM(model='openai/doubao-pro-32k-241215', api_key=OPENAI_API_KEY, api_base=OPENAI_API_BASE),
  210. LiteLLM(model='openai/deepseek-reasoner', api_key=OPENAI_API_KEY, api_base=OPENAI_API_BASE),
  211. LiteLLM(model='openai/Qwen/Qwen3-235B-A22B', api_key=OPENAI_API_KEY, api_base=OPENAI_API_BASE),
  212. # LiteLLM(model='openai/deepseek-v3', api_key=OPENAI_API_KEY, api_base=OPENAI_API_BASE),
  213. # 'openai/QwQ-32B',
  214. ]
  215. agent_task_list = []
  216. for llm in llm_list:
  217. model = await AgentContent.find_one(
  218. AgentContent.product_name == product_name,
  219. AgentContent.model_name==llm.model,
  220. )
  221. if model:
  222. agent = ProductTitleAgent(llm=llm, template_manager=m)
  223. agent_task_list.append(agent.acomplete(product_name=product_name))
  224. res = await asyncio.gather(*agent_task_list)
  225. return
  226. task_list = []
  227. for llm in llm_list:
  228. agent = ProductTitleAgent(llm=llm, template_manager=m)
  229. agent_model = agent.get_promp(product_name=product_name)
  230. task_list.append(agent_model)
  231. # logger.info(f"{agent_model.competitor.items()}")
  232. res = await asyncio.gather(*task_list)
  233. async def gen_marketing_file(product_name):
  234. m = TemplateManager(MONGO_URL, MONGO_DB_NAME)
  235. await m.initialize()
  236. llm = LiteLLM(model='openai/doubao-pro-32k-241215', api_key=OPENAI_API_KEY, api_base=OPENAI_API_BASE),
  237. agent = ProductTitleAgent(llm=llm, template_manager=m)
  238. output_path = r'G:\code\amazone\copywriting_production\output\temp' + f"\\{product_name}-商品标题.md"
  239. res = await agent.gen_marketing_file(product_name, output_path)
  240. logger.info(f"{res}")
  241. return
  242. async def multi_llm_task():
  243. list_product_name = [
  244. '死皮处理五件套',
  245. # '镊子铲两件套',
  246. # '折叠剪刀',
  247. # '脸用刮毛刀4件套',
  248. # '路亚钓鱼小剪刀',
  249. ]
  250. llm_task_list = []
  251. gen_marketing_file_task_list = []
  252. for product_name in list_product_name:
  253. llm_task_list.append(llm_task(product_name))
  254. gen_marketing_file_task_list.append(gen_marketing_file(product_name))
  255. res = await asyncio.gather(*llm_task_list)
  256. res = await asyncio.gather(*gen_marketing_file_task_list)
  257. return res
  258. def main():
  259. # asyncio.run(llm_task())
  260. asyncio.run(multi_llm_task())
  261. if __name__ == "__main__":
  262. main()