T-579 内置默认AI提示词

This commit is contained in:
chengma
2026-07-10 09:00:24 +08:00
parent 9742448750
commit e5b7fc4afa
11 changed files with 306 additions and 4 deletions
+1
View File
@@ -0,0 +1 @@
"""Bundled default prompt resources."""
+1
View File
@@ -0,0 +1 @@
"""Bundled default cover prompt templates."""
+36
View File
@@ -0,0 +1,36 @@
商品标题:{新标题}
【核心執行指令:韓系極簡大碼電商主圖】
1. 畫面風格與氛圍 (視覺基調)
場景化生活感: 捨棄死板的工廠掛拍或雜亂背景,全面升級為「韓系生活氛圍感」的實穿場景。採用高級「韓系極簡風」,以奶茶色調、莫蘭迪色系、鼠尾草綠等低飽和色彩為主。搭配明亮柔和的自然光影,營造輕鬆的日常氛圍(如質感街道、室內一隅)。
情緒價值: 營造讓買家在 1 秒內產生共鳴的畫面,傳遞「這件衣服適合我、穿起來修身又優雅」的視覺感受。
2. 模特兒設定與人體結構防錯 (重點規範)
精準人體結構 (防錯機制): 【最高權重】確保人體骨骼與四肢結構 100% 符合真實人類解剖學。嚴格禁止生成多餘肢體(如三隻手、三條腿、多根手指)、禁止關節扭曲、肢體融合或殘缺。手部抓握物品(如持咖啡杯)的姿勢必須極度自然逼真。
目標受眾樣貌: 模特兒限定為 30 歲以上及樂齡世代、具備台灣女性氣質的微胖/大碼身形(能完美展現大至 80 公斤身形的自信與修身美感)。
動態與多樣性: 展現自然微笑與自信優雅姿態,透過不經意的動態(如轉身、微側身)展現衣服垂墜感。臉部樣貌需具備多樣性,絕對避免每次生成同一張臉。
3. 產品還原與細節保留 (商品核心)
100% 實物還原: 完美保留原圖衣服的款式、材質與設計亮點(如紫色螺紋花朵刺繡、新中式盤扣、不規則下擺、藍色暈染字母、V領抽繩褶皺等),禁止擅自改變服裝結構,並承襲原圖的靈魂單品搭配(如小棕包、手鐲、項鍊等配件)。
版面佔比: 服裝必須佔據整體版面 60% 以上。
4. 排版、文案與浮水印 (後製規範)
極簡文案: 畫面保持 90% 以上的純淨度。需要添加核心賣點文字,可以根據對應標題獲取核心詞,但絕對不可遮擋服裝細節。
隱蔽防盜浮水印: 強制在圖片「正左上角」添加「PAPA」浮水印。字體總寬度嚴格限制在圖片總寬度的 5% 以內,透明度絕對鎖定在 3%。確保其極度低調、無侵入性,絕不破壞 4K 高清細節與畫面留白美感。
5. 輸出與交付標準 (系統設定)
免選方案直接輸出: 未來的對話中,接收商品圖與標題後,請自動篩選並「直接輸出」最具點擊率潛力的唯一優化主圖,無需提供多種方案挑選。
規格限制: 輸出為 4K 高清畫質的 JPEG 格式,比例 1:1(建議解析度 1024x1024 或以上),且單張檔案大小不超過 2MB。
6.圖片展示【台灣出貨】等時效性詞,请设计一个突出,吸睛的标签。
+126
View File
@@ -0,0 +1,126 @@
旧标题:{旧标题}
蝦皮台灣站 商品標題優化標準(最終梳理版)
版本:2026.05 最終優化版
適用平台:蝦皮台灣站(Shopee TW)
================================================================================
一、核心結構(強制執行)
================================================================================
• 標題開頭固定為:【台灣出貨】
• 開頭第二個詞必須是該商品「最核心商品賣點的搜索關鍵詞」
• 關鍵詞需符合台灣用戶搜尋習慣,並自然融入商品賣點與風格,痛點解決,受眾與場景,實時熱銷利益詞
• 總字數嚴格控制在 54~60 個字符(含中英文、空格、【】、標點),不可超過 60 字符
•标题中包含有大码。大尺码等大码女装相关的关键词,增加标题中大码女装的相关关键词密度。
•标题中包含有小個子。矮個子等小個子女装相关的关键词,增加标题中小個子穿搭的相关关键词密度。
================================================================================
二、絕對禁止事項
================================================================================
• 禁止使用任何時效承諾詞(現貨、快速出貨、秒發、24H出貨、隔日達等),除非原標題已出現
• 禁止出現店名或店鋪相關詞,所有的品牌標籤都是全面禁止的,禁止出現任何品牌名稱
• 原標題未出現材質時,嚴禁加入任何材質名詞
• 原標題未出現大碼/plus size相關詞時,嚴禁添加大尺碼描述
• 禁止醫療、治療、療效相關詞(詳見下方特殊處理)
• 請去掉如新港棉,雲朵棉,40支純棉等不會有用戶搜素的材質詞,增加核心指令請確保標題中的所有關鍵詞都是符合用戶搜素習慣,搜素需求的關鍵詞。
================================================================================
三、高價值詞保留與加入原則
================================================================================
• 必須保留原標題中的風格詞、版型詞(如:重工刺繡、文藝復古、古典復古、A字裙、大擺裙、寬鬆顯瘦、辣妹風、純欲風等)
• 優先加入季節與轉換詞(服飾類):冰絲涼感、抗熱消暑、透氣涼感、親膚舒適、輕薄無負擔
• 其他品類依商品特性加入對應利益詞與熱銷類目詞
• 服飾類風格需動態匹配原標題:
- 原標題偏辣妹/純欲 → 強調設計感、性感氣質、顯瘦曲線
- 原標題偏法式/名媛 → 強調優雅、高質感、知性氣質
- 原標題偏文藝/復古/媽媽裝 → 強調復古知性、寬鬆顯瘦、實用舒適
================================================================================
四、輸出格式 (不要展示思考過程,直接出結果)
================================================================================
單一標題:
直接輸出新標題(只輸出優化後標題)
================================================================================
輸出範例
================================================================================
單一標題範例:
【台灣出貨】新中式掛鐘 古典復古 簡約現代 繁體漢字 客廳餐廳 個性藝術創意 靜音時鐘 客廳裝飾 藝術時鐘
================================================================================
================================================================================
五、其他注意事項
================================================================================
• 相似商品批量生成時,修飾詞重複度需低於 50%(建議輪替同義詞)
• 所有標題必須使用繁體中文
• 嚴格遵守字數限制(建議目標控制在 55~58 字符,留緩衝空間)
================================================================================
六、時效詞處理原則(重要補充)
================================================================================
• 原標題「有」時效詞 → 新標題必須精準保留
• 原標題「沒有」時效詞 → 新標題嚴禁添加任何時效承諾詞
================================================================================
七、醫療與銀髮族商品特殊處理
================================================================================
嚴禁使用詞彙:
醫療、醫用、醫生推薦、臨床、治療、療效、復健、關節炎、靜脈曲張、腰椎間盤突出、止痛、消炎、殺菌、防褥瘡、降血壓、醫療級、復健車、血氧機、血壓計、助聽器、矯正帶
銀髮族/樂齡商品安全替代詞建議:
• 復健購物車 → 家用休具、休閒買菜車、輕便購物車
• 矯正坐墊 → 減壓坐墊、人體工學靠枕、久坐舒適款
• 按摩治療儀 → 居家舒壓按摩、放鬆神器、日常舒適好物
================================================================================
八、符合台灣用戶搜索習慣的實戰優化建議
================================================================================
1. 核心關鍵詞選擇優先使用平台即時建議
在蝦皮搜尋欄輸入商品類型,觀察自動跳出的熱門詞,優先選用搜尋量高的詞放在「【台灣出貨】」之後第二位。
2. 利益導向優先
台灣用戶特別愛搜:顯瘦、遮肉、寬鬆舒適、親膚、透氣涼感、百搭、日常穿搭、高CP值、輕薄無負擔。
建議在核心關鍵詞後盡量放這些「為什麼要買」的利益詞。
3. 自然流暢可讀(手機優先)
標題要像自然中文一樣好讀,手機上掃一眼就能懂。避免硬塞關鍵詞造成斷裂感。
4. 同義詞輪替策略(批量專用)
透氣涼感 / 親膚舒適 / 輕薄透氣 / 抗熱消暑 / 舒適無負擔
顯瘦 / 遮肉顯瘦 / 寬鬆顯瘦 / 版型優良
這樣可大幅降低重複度,同時維持高相關性。
5. 強化使用場景詞
台灣用戶常搜「日常穿」「居家穿」「出門穿」「媽媽實用」「通勤穿搭」「夏季必備」。適度加入能提升點擊率。
6. 信任感已由「台灣出貨」建立
不需再堆疊「現貨」「快速到貨」。可視情況輕加「品質嚴選」「台灣設計風格」(若原標題有相關元素)。
7. 字數精準控制技巧
建議目標控制在 55~58 字符,留一點緩衝空間。計算時務必包含空格與【】。
8. 服飾類風格精準動態匹配
- 辣妹/純欲風:設計感、性感氣質、顯瘦曲線
- 文藝復古/媽媽裝:復古知性、寬鬆顯瘦、實用舒適
- 法式/名媛風:優雅高質感、知性氣質
9. 定期迭代與數據驗證
上架後觀察賣家後台「曝光次數」與「點擊率」,把表現好的熱門關鍵詞保留或加強,表現差的替換。
10. 避免平台風險
嚴格遵守「原標題沒有就不要加」的原則(材質、尺寸、醫療詞),這是長期穩定排名的關鍵。
================================================================================
輸出範例
================================================================================
單一標題範例:
【台灣出貨】新中式掛鐘 古典復古 簡約現代 繁體漢字 客廳餐廳 個性藝術創意 靜音時鐘 客廳裝飾 藝術時鐘
================================================================================
注意事項總結
================================================================================
• 嚴格遵守字數 52-60 字符
• 繁體中文輸出
• 保留原標題風格詞與版型詞
• 時效詞「有就留、沒有就絕不加」
• 材質與尺寸「原標題沒有就絕不加」
• 醫療相關詞一律禁用,銀髮族改用安全替代詞
• 批量時修飾詞重複度低於 50%
• 建議定期根據賣家後台數據迭代熱門關鍵詞
+1
View File
@@ -764,6 +764,7 @@ class GenerateTab(QWidget):
self.open_accounts_callback = open_accounts_callback
self.title_prompt_path = title_prompt_path or appconfig.title_prompt_path(self.config)
self.cover_prompts_dir = cover_prompts_dir or appconfig.cover_prompts_dir(self.config)
prompts.ensure_default_prompts(self.title_prompt_path, self.cover_prompts_dir)
self.current_cover_template = None
self.generate_worker = None
self.generate_thread = None
+61
View File
@@ -3,6 +3,7 @@
from __future__ import annotations
import os
from importlib import resources
from . import appconfig
@@ -10,6 +11,8 @@ TITLE_PROMPT_PATH = appconfig.title_prompt_path()
COVER_PROMPTS_DIR = appconfig.cover_prompts_dir()
TEMPLATE_EXT = ".txt"
INVALID_NAME_CHARS = set('\\/:*?"<>|')
DEFAULT_PROMPTS_PACKAGE = "app.default_prompts"
DEFAULT_COVER_PROMPTS_PACKAGE = "app.default_prompts.cover"
class PromptError(RuntimeError):
@@ -35,6 +38,27 @@ def save_title_prompt(text, path=TITLE_PROMPT_PATH) -> None:
fh.write(str(text or ""))
def ensure_default_prompts(
title_prompt_path=TITLE_PROMPT_PATH,
cover_prompts_dir=COVER_PROMPTS_DIR,
) -> None:
"""Seed bundled default prompts into an empty user data directory.
Existing user prompts are never overwritten. Defaults live in the packaged
app and are copied into data/ only when the corresponding user files are
missing, or the title prompt file is blank.
"""
if not _has_non_empty_file(title_prompt_path):
default_title = _read_default_prompt_text("title_prompt.txt")
if default_title:
save_title_prompt(default_title, title_prompt_path)
if not list_cover_templates(cover_prompts_dir):
for name, text in _iter_default_cover_templates():
save_cover_template(name, text, cover_prompts_dir)
def list_cover_templates(directory=COVER_PROMPTS_DIR):
"""Return cover template names sorted by display name."""
@@ -132,3 +156,40 @@ def _normalize_name(name) -> str:
if os.path.basename(value) != value:
raise PromptError(f"封面提示词模板名非法: {value}")
return value
def _has_non_empty_file(path) -> bool:
try:
if not os.path.exists(path):
return False
with open(path, "r", encoding="utf-8") as fh:
return bool(fh.read().strip())
except OSError:
return False
def _read_default_prompt_text(filename) -> str:
try:
return (
resources.files(DEFAULT_PROMPTS_PACKAGE)
.joinpath(filename)
.read_text(encoding="utf-8")
)
except (FileNotFoundError, ModuleNotFoundError, OSError):
return ""
def _iter_default_cover_templates():
try:
root = resources.files(DEFAULT_COVER_PROMPTS_PACKAGE)
except (ModuleNotFoundError, OSError):
return
for entry in root.iterdir():
if not entry.is_file() or not entry.name.lower().endswith(TEMPLATE_EXT):
continue
name = entry.name[: -len(TEMPLATE_EXT)]
try:
text = entry.read_text(encoding="utf-8")
except OSError:
continue
yield name, text
+7 -1
View File
@@ -7,13 +7,19 @@ include config files, SQLite databases, Chrome profiles, generated images,
logs, prompts, or other operator data.
"""
from PyInstaller.utils.hooks import collect_data_files
block_cipher = None
default_prompt_datas = collect_data_files(
"app.default_prompts",
includes=["**/*.txt"],
)
a = Analysis(
["main.py"],
pathex=[],
binaries=[],
datas=[],
datas=default_prompt_datas,
hiddenimports=[
"PySide6.QtCore",
"PySide6.QtGui",
+11 -1
View File
@@ -3,7 +3,7 @@ id: T-579
title: 打包版内置默认标题和封面提示词,首次运行自动写入用户 data
phase: 8
deps: [T-524, T-538, T-540]
status: TODO
status: DONE
created: 2026-07-10
---
@@ -60,3 +60,13 @@ created: 2026-07-10
- 不提交 `data/config.json`、`data/config/cmhub.json`、`data/cmshopee.db`、`data/images/`、`data/chrome_user_data_dir/`、`data/logs/`。
- 不改 AI/cmhub 请求协议、生成并发、DB schema、Excel、Shopee/CDP 更新流程。
- 不在升级时覆盖用户已经修改过的提示词。
## 执行记录
- 2026-07-10:完成 T-579。
- 新增 `app/default_prompts/` 默认提示词资源包,内置当前已验证使用的标题提示词和封面模板 `papa1`;发布前已扫描常见密钥关键词,未发现 API Key、Cookie、token、密码等密钥类内容。
- `app/prompts.py` 新增 `ensure_default_prompts()`:用户 `data/title_prompt.txt` 缺失或为空时写入默认标题提示词;用户 `data/prompts/cover/` 没有模板时写入默认封面模板;已有用户提示词或模板时不覆盖。
- `app/gui/tabs/generate.py` 在②AI生成页读取提示词前执行默认提示词初始化,保证全新用户首次打开即可看到默认标题提示词和 `papa1` 封面模板。
- `cmshopee.spec` 改为通过 `collect_data_files("app.default_prompts")` 收集内置默认提示词文本;仍不打包 release 根目录的 `data/`、`title_prompt.txt`、`prompts/`、DB、配置、图片、日志或登录态。
- 测试:`tests/test_prompts.py` 覆盖默认提示词播种和不覆盖用户提示词;`tests/test_gui.py` 覆盖②AI生成页全新用户自动回显默认提示词;`tests/test_packaging.py` 覆盖 PyInstaller spec 收集默认资源。
- 验证通过:`py -3.10 -m unittest tests.test_prompts tests.test_gui tests.test_packaging`、`python -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`py -3.10 -m unittest discover -s tests`(319 tests)、`git diff --check`。
+24 -1
View File
@@ -1857,7 +1857,9 @@ class GuiTests(TempDirMixin, unittest.TestCase):
self.assertEqual(0, tab.cover_progress_bar.value())
self.assertEqual(1, tab.title_progress_bar.maximum())
self.assertEqual(1, tab.cover_progress_bar.maximum())
self.assertEqual("默认", tab.cover_template_combo.currentText())
self.assertIn("蝦皮台灣站", tab.title_prompt_edit.toPlainText())
self.assertEqual("papa1", tab.cover_template_combo.currentText())
self.assertIn("商品标题:{新标题}", tab.cover_prompt_edit.toPlainText())
self.assertEqual("模板操作", tab.cover_template_actions_button.text())
self.assertIs(tab.cover_template_actions_menu, tab.cover_template_actions_button.menu())
self.assertEqual(
@@ -2234,6 +2236,27 @@ class GuiTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_generate_tab_seeds_default_prompts_for_new_user_data(self):
with self.make_temp_dir() as temp_dir:
cfg = self.make_config(temp_dir)
title_prompt_path = os.path.join(temp_dir, "title_prompt.txt")
cover_prompts_dir = os.path.join(temp_dir, "prompts", "cover")
tab = GenerateTab(
config=cfg,
title_prompt_path=title_prompt_path,
cover_prompts_dir=cover_prompts_dir,
)
self.addCleanup(tab.close)
self.assertIn("蝦皮台灣站", tab.title_prompt_edit.toPlainText())
self.assertEqual("papa1", tab.cover_template_combo.currentText())
self.assertIn("商品标题:{新标题}", tab.cover_prompt_edit.toPlainText())
self.assertTrue(os.path.exists(title_prompt_path))
self.assertTrue(os.path.exists(os.path.join(cover_prompts_dir, "papa1.txt")))
self.assert_removed(temp_dir)
def test_generate_tab_manages_prompt_files_and_preview(self):
with self.make_temp_dir() as temp_dir:
cfg = self.make_config(temp_dir)
+4 -1
View File
@@ -21,7 +21,10 @@ class PackagingTests(unittest.TestCase):
spec = self.read_text("cmshopee.spec")
normalized = "".join(spec.split())
self.assertIn("datas=[]", normalized)
self.assertIn("collect_data_files", spec)
self.assertIn('"app.default_prompts"', spec)
self.assertIn('includes=["**/*.txt"]', normalized)
self.assertIn("datas=default_prompt_datas", normalized)
self.assertIn("COLLECT(", spec)
self.assertIn('name="cmshopee"', normalized)
+34
View File
@@ -45,6 +45,40 @@ class PromptTests(TempDirMixin, unittest.TestCase):
self.assert_removed(temp_dir)
def test_ensure_default_prompts_seeds_empty_user_prompt_files(self):
with self.make_temp_dir() as temp_dir:
title_path = os.path.join(temp_dir, "title_prompt.txt")
cover_dir = os.path.join(temp_dir, "prompts", "cover")
prompts.ensure_default_prompts(title_path, cover_dir)
self.assertIn("蝦皮台灣站", prompts.load_title_prompt(title_path))
self.assertEqual(["papa1"], prompts.list_cover_templates(cover_dir))
self.assertIn(
"商品标题:{新标题}",
prompts.load_cover_template("papa1", cover_dir),
)
self.assert_removed(temp_dir)
def test_ensure_default_prompts_does_not_overwrite_user_prompts(self):
with self.make_temp_dir() as temp_dir:
title_path = os.path.join(temp_dir, "title_prompt.txt")
cover_dir = os.path.join(temp_dir, "prompts", "cover")
prompts.save_title_prompt("用户标题提示词", title_path)
prompts.save_cover_template("用户模板", "用户封面提示词", cover_dir)
prompts.ensure_default_prompts(title_path, cover_dir)
self.assertEqual("用户标题提示词", prompts.load_title_prompt(title_path))
self.assertEqual(["用户模板"], prompts.list_cover_templates(cover_dir))
self.assertEqual(
"用户封面提示词",
prompts.load_cover_template("用户模板", cover_dir),
)
self.assert_removed(temp_dir)
def test_render_prompt_replaces_known_variables(self):
task = {
"old_title": "舊T恤",