Commit beef466f7e635bdaf5aa561b8812abb776608fc9
1 parent
d54b0467
商品索引的nchors 与语义属性
Showing
1 changed file
with
141 additions
and
16 deletions
Show diff stats
indexer/process_products.py
| ... | ... | @@ -61,13 +61,141 @@ LANG_LABELS: Dict[str, str] = { |
| 61 | 61 | |
| 62 | 62 | SUPPORTED_LANGS = set(LANG_LABELS.keys()) |
| 63 | 63 | |
| 64 | +SYSTEM_MESSAGES: Dict[str, str] = { | |
| 65 | + "zh": ( | |
| 66 | + "你是一名电商平台的商品标注员,你的工作是对输入的每个商品进行理解、分析和标注," | |
| 67 | + "并按要求格式返回 Markdown 表格。所有输出内容必须为中文。" | |
| 68 | + ), | |
| 69 | + "en": ( | |
| 70 | + "You are a product annotator for an e-commerce platform. " | |
| 71 | + "For each input product, you must understand, analyze and label it, " | |
| 72 | + "and return a Markdown table strictly following the requested format. " | |
| 73 | + "All output must be in English." | |
| 74 | + ), | |
| 75 | + "de": ( | |
| 76 | + "Du bist ein Produktannotator für eine E‑Commerce‑Plattform. " | |
| 77 | + "Du sollst jedes Eingabeprodukt verstehen, analysieren und beschriften " | |
| 78 | + "und eine Markdown-Tabelle im geforderten Format zurückgeben. " | |
| 79 | + "Alle Ausgaben müssen auf Deutsch sein." | |
| 80 | + ), | |
| 81 | + "ru": ( | |
| 82 | + "Вы — разметчик товаров для платформы электронной коммерции. " | |
| 83 | + "Ваша задача — понимать, анализировать и размечать каждый товар " | |
| 84 | + "и возвращать таблицу Markdown в требуемом формате. " | |
| 85 | + "Весь вывод должен быть на русском языке." | |
| 86 | + ), | |
| 87 | + "fr": ( | |
| 88 | + "Vous êtes annotateur de produits pour une plateforme e‑commerce. " | |
| 89 | + "Pour chaque produit en entrée, vous devez le comprendre, l’analyser et l’annoter, " | |
| 90 | + "puis renvoyer un tableau Markdown au format demandé. " | |
| 91 | + "Toute la sortie doit être en français." | |
| 92 | + ), | |
| 93 | +} | |
| 94 | + | |
| 64 | 95 | |
| 65 | 96 | def create_prompt(products: List[Dict[str, str]], target_lang: str = "zh") -> str: |
| 66 | - """创建LLM提示词(根据目标语言输出)""" | |
| 67 | - lang_label = LANG_LABELS.get(target_lang, "对应语言") | |
| 68 | - prompt = f"""请对输入的每条商品标题,分析并提取以下信息,所有输出内容请使用{lang_label}: | |
| 97 | + """根据目标语言创建 LLM 提示词和表头说明。""" | |
| 98 | + if target_lang == "en": | |
| 99 | + prompt = """Please analyze each input product title and extract the following information: | |
| 100 | + | |
| 101 | +1. Product title: a natural English product name derived from the input title | |
| 102 | +2. Category path: from broad to fine-grained category, separated by ">" (e.g. Clothing>Women>Dresses>Work Dress) | |
| 103 | +3. Fine-grained tags: style / features / attributes (e.g. floral, waist-cinching, French style) | |
| 104 | +4. Target audience: gender / age group, etc. (e.g. young women) | |
| 105 | +5. Usage scene | |
| 106 | +6. Applicable season | |
| 107 | +7. Key attributes | |
| 108 | +8. Material description | |
| 109 | +9. Functional features | |
| 110 | +10. Selling point: one concise key selling sentence for recommendation | |
| 111 | +11. Anchor text: a set of words or phrases that could be used by users as search queries for this product, covering category, fine-grained tags, functional attributes, usage scenes, etc. | |
| 112 | + | |
| 113 | +Input product list: | |
| 114 | + | |
| 115 | +""" | |
| 116 | + prompt_tail = """ | |
| 117 | +Please strictly return a Markdown table in the following format. For any column that can contain multiple values, separate values with commas. Do not add any other explanations: | |
| 118 | + | |
| 119 | +| No. | Product title | Category path | Fine-grained tags | Target audience | Usage scene | Season | Key attributes | Material | Features | Selling point | Anchor text | | |
| 120 | +|----|----|----|----|----|----|----|----|----|----|----|----| | |
| 121 | +""" | |
| 122 | + elif target_lang == "de": | |
| 123 | + prompt = """Bitte analysiere jeden eingegebenen Produkttitel und extrahiere die folgenden Informationen: | |
| 124 | + | |
| 125 | +1. Produkttitel: ein natürlicher deutscher Produkttitel basierend auf dem Eingangstitel | |
| 126 | +2. Kategoriepfad: von Oberkategorie bis Feinkategorie, getrennt durch ">" (z. B. Kleidung>Damen>Kleider>Businesskleid) | |
| 127 | +3. Feinkörnige Tags: Stil / Merkmale / Eigenschaften (z. B. Blumenmuster, tailliert, französischer Stil) | |
| 128 | +4. Zielgruppe: Geschlecht / Altersgruppe usw. (z. B. junge Frauen) | |
| 129 | +5. Einsatzszenario | |
| 130 | +6. Geeignete Saison | |
| 131 | +7. Wichtige Attribute | |
| 132 | +8. Materialbeschreibung | |
| 133 | +9. Funktionale Merkmale | |
| 134 | +10. Verkaufsargument: ein prägnanter, einzeiliger Haupt-Selling-Point für Empfehlungen | |
| 135 | +11. Ankertexte: eine Menge von Wörtern oder Phrasen, die Nutzer als Suchanfragen für dieses Produkt verwenden könnten und die Kategorie, feine Tags, Funktion und Nutzungsszenarien abdecken. | |
| 136 | + | |
| 137 | +Eingabeliste der Produkte: | |
| 138 | + | |
| 139 | +""" | |
| 140 | + prompt_tail = """ | |
| 141 | +Gib bitte strikt eine Markdown-Tabelle im folgenden Format zurück. Mehrere Werte in einer Spalte werden durch Kommas getrennt. Füge keine weiteren Erklärungen hinzu: | |
| 142 | + | |
| 143 | +| Nr. | Produkttitel | Kategoriepfad | Feintags | Zielgruppe | Einsatzszenario | Saison | Wichtige Attribute | Material | Merkmale | Verkaufsargument | Ankertexte | | |
| 144 | +|----|----|----|----|----|----|----|----|----|----|----|----| | |
| 145 | +""" | |
| 146 | + elif target_lang == "ru": | |
| 147 | + prompt = """Пожалуйста, проанализируйте каждый входной заголовок товара и извлеките следующую информацию: | |
| 148 | + | |
| 149 | +1. Заголовок товара: естественное русскоязычное название товара на основе исходного заголовка | |
| 150 | +2. Путь категории: от широкой до узкой категории, разделённый символом ">" (например: Одежда>Женская одежда>Платья>Деловое платье) | |
| 151 | +3. Детализированные теги: стиль / особенности / характеристики (например: цветочный принт, приталенный, французский стиль) | |
| 152 | +4. Целевая аудитория: пол / возрастная группа и т. п. (например: молодые женщины) | |
| 153 | +5. Сценарий использования | |
| 154 | +6. Подходящий сезон | |
| 155 | +7. Ключевые характеристики | |
| 156 | +8. Описание материала | |
| 157 | +9. Функциональные особенности | |
| 158 | +10. Торговое преимущество: одно краткое ключевое предложение для рекомендаций | |
| 159 | +11. Якорные запросы: набор слов или фраз, которые пользователи могут использовать в качестве поисковых запросов для этого товара, покрывающих категорию, детализированные теги, функциональные характеристики, сценарии использования и т. д. | |
| 160 | + | |
| 161 | +Список входных товаров: | |
| 69 | 162 | |
| 70 | -1. 商品标题:将输入商品名称翻译为{lang_label} | |
| 163 | +""" | |
| 164 | + prompt_tail = """ | |
| 165 | +Пожалуйста, строго верните Markdown‑таблицу в следующем формате. Для колонок с несколькими значениями разделяйте значения запятыми. Не добавляйте никаких дополнительных пояснений: | |
| 166 | + | |
| 167 | +| № | Заголовок товара | Путь категории | Детализированные теги | Целевая аудитория | Сценарий использования | Сезон | Ключевые характеристики | Материал | Особенности | Торговое преимущество | Якорные запросы | | |
| 168 | +|----|----|----|----|----|----|----|----|----|----|----|----| | |
| 169 | +""" | |
| 170 | + elif target_lang == "fr": | |
| 171 | + prompt = """Veuillez analyser chaque titre de produit en entrée et extraire les informations suivantes : | |
| 172 | + | |
| 173 | +1. Titre du produit : un titre de produit naturel en français basé sur le titre d’origine | |
| 174 | +2. Chemin de catégorie : de la catégorie la plus large à la plus fine, séparées par ">" (par ex. Vêtements>Femme>Robes>Robe de travail) | |
| 175 | +3. Tags détaillés : style / caractéristiques / attributs (par ex. fleuri, cintré, style français) | |
| 176 | +4. Public cible : sexe / tranche d’âge, etc. (par ex. jeunes femmes) | |
| 177 | +5. Scénario d’utilisation | |
| 178 | +6. Saison adaptée | |
| 179 | +7. Attributs clés | |
| 180 | +8. Description du matériau | |
| 181 | +9. Caractéristiques fonctionnelles | |
| 182 | +10. Argument de vente : une phrase concise résumant le principal atout pour la recommandation | |
| 183 | +11. Texte d’ancrage : un ensemble de mots ou d’expressions que les utilisateurs pourraient saisir comme requêtes de recherche pour ce produit, couvrant la catégorie, les tags détaillés, les fonctions, les scénarios d’usage, etc. | |
| 184 | + | |
| 185 | +Liste des produits en entrée : | |
| 186 | + | |
| 187 | +""" | |
| 188 | + prompt_tail = """ | |
| 189 | +Veuillez strictement renvoyer un tableau Markdown au format suivant. Pour toute colonne pouvant contenir plusieurs valeurs, séparez‑les par des virgules. N’ajoutez aucune autre explication : | |
| 190 | + | |
| 191 | +| N° | Titre du produit | Chemin de catégorie | Tags détaillés | Public cible | Scénario d’utilisation | Saison | Attributs clés | Matériau | Caractéristiques | Argument de vente | Texte d’ancrage | | |
| 192 | +|----|----|----|----|----|----|----|----|----|----|----|----| | |
| 193 | +""" | |
| 194 | + else: | |
| 195 | + # 默认中文版本 | |
| 196 | + prompt = """请对输入的每条商品标题,分析并提取以下信息: | |
| 197 | + | |
| 198 | +1. 商品标题:将输入商品名称翻译为自然、完整的中文商品标题 | |
| 71 | 199 | 2. 品类路径:从大类到细分品类,用">"分隔(例如:服装>女装>裤子>工装裤) |
| 72 | 200 | 3. 细分标签:商品的风格、特点、功能等(例如:碎花,收腰,法式) |
| 73 | 201 | 4. 适用人群:性别/年龄段等(例如:年轻女性) |
| ... | ... | @@ -82,8 +210,7 @@ def create_prompt(products: List[Dict[str, str]], target_lang: str = "zh") -> st |
| 82 | 210 | 输入商品列表: |
| 83 | 211 | |
| 84 | 212 | """ |
| 85 | - | |
| 86 | - prompt_tail = """ | |
| 213 | + prompt_tail = """ | |
| 87 | 214 | 请严格按照以下markdown表格格式返回,每列内部的多值内容都用逗号分隔,不要添加任何其他说明: |
| 88 | 215 | |
| 89 | 216 | | 序号 | 商品标题 | 品类路径 | 细分标签 | 适用人群 | 使用场景 | 适用季节 | 关键属性 | 材质说明 | 功能特点 | 商品卖点 | 锚文本 | |
| ... | ... | @@ -97,8 +224,8 @@ def create_prompt(products: List[Dict[str, str]], target_lang: str = "zh") -> st |
| 97 | 224 | return prompt |
| 98 | 225 | |
| 99 | 226 | |
| 100 | -def call_llm(prompt: str) -> Tuple[str, str]: | |
| 101 | - """调用大模型API(带重试机制)""" | |
| 227 | +def call_llm(prompt: str, target_lang: str = "zh") -> Tuple[str, str]: | |
| 228 | + """调用大模型API(带重试机制),按目标语言选择系统提示词。""" | |
| 102 | 229 | headers = { |
| 103 | 230 | "Authorization": f"Bearer {API_KEY}", |
| 104 | 231 | "Content-Type": "application/json" |
| ... | ... | @@ -109,7 +236,7 @@ def call_llm(prompt: str) -> Tuple[str, str]: |
| 109 | 236 | "messages": [ |
| 110 | 237 | { |
| 111 | 238 | "role": "system", |
| 112 | - "content": "你是一名电商平台的商品标注员,你的工作是对输入的每个商品进行理解、分析和标注,按要求格式返回Markdown表格。" | |
| 239 | + "content": SYSTEM_MESSAGES.get(target_lang, SYSTEM_MESSAGES["zh"]) | |
| 113 | 240 | }, |
| 114 | 241 | { |
| 115 | 242 | "role": "user", |
| ... | ... | @@ -196,18 +323,16 @@ def parse_markdown_table(markdown_content: str) -> List[Dict[str, str]]: |
| 196 | 323 | if not line: |
| 197 | 324 | continue |
| 198 | 325 | |
| 199 | - # 跳过表头 | |
| 326 | + # 表格行处理 | |
| 200 | 327 | if line.startswith('|'): |
| 201 | - # 跳过分隔行 | |
| 328 | + # 分隔行(----) | |
| 202 | 329 | if set(line.replace('|', '').strip()) <= {'-', ':'}: |
| 203 | 330 | data_started = True |
| 204 | 331 | continue |
| 205 | 332 | |
| 206 | - # 跳过表头行 | |
| 333 | + # 首个表头行:无论语言如何,统一跳过 | |
| 207 | 334 | if not data_started: |
| 208 | - if '序号' in line or '商品中文标题' in line: | |
| 209 | - continue | |
| 210 | - data_started = True | |
| 335 | + # 等待下一行数据行 | |
| 211 | 336 | continue |
| 212 | 337 | |
| 213 | 338 | # 解析数据行 |
| ... | ... | @@ -248,7 +373,7 @@ def process_batch( |
| 248 | 373 | |
| 249 | 374 | # 调用LLM |
| 250 | 375 | try: |
| 251 | - raw_response, full_response_json = call_llm(prompt) | |
| 376 | + raw_response, full_response_json = call_llm(prompt, target_lang=target_lang) | |
| 252 | 377 | |
| 253 | 378 | # 解析结果 |
| 254 | 379 | parsed_results = parse_markdown_table(raw_response) | ... | ... |