tt-help-cli-ycl 1.4.17 → 1.4.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -150,6 +150,7 @@ export function buildDiscoverPrompt(
150
150
  hardRules = [],
151
151
  existingCount = 0,
152
152
  forceNicheSwitch = null,
153
+ lastRoundFeedback = null,
153
154
  ) {
154
155
  const lang = getLang(country);
155
156
  const langNames = {
@@ -167,8 +168,285 @@ export function buildDiscoverPrompt(
167
168
  };
168
169
  const langName = langNames[lang] || lang;
169
170
 
170
- // 正样本:该国高分 tag(带分数,让 LLM 知道哪些方向最有效)
171
- const productive = history.productive || [];
171
+ // ====== 上轮反馈:告知 LLM 上一轮生成了什么、哪些被去重 ======
172
+ let lastRoundRejectionHint = "";
173
+ if (lastRoundFeedback) {
174
+ if (lastRoundFeedback.allRejected) {
175
+ lastRoundRejectionHint = `\n=== ⛔ CRITICAL: Last round ALL tags were DUPLICATES ===\nYou generated: ${lastRoundFeedback.raw.join(", ")}\nALL of these already exist in our database — NONE were added.\nYou MUST generate COMPLETELY DIFFERENT tags this round. Do NOT repeat any of the above.\nTry a totally different product category, use-case, or selling context.`;
176
+ } else if (lastRoundFeedback.rejected.length > 0) {
177
+ lastRoundRejectionHint = `\n=== ⚠️ Last round feedback ===\nYou generated: ${lastRoundFeedback.raw.join(", ")}\nRejected (already exist): ${lastRoundFeedback.rejected.join(", ")}\nAdded: ${lastRoundFeedback.survived.join(", ") || "(none)"}\nAvoid the rejected tags above — they already exist in our database.`;
178
+ }
179
+ }
180
+
181
+ // 正样本:该国高分 tag。当 forceNicheSwitch 激活时,仅展示目标 niche 的 productive tag
182
+ let productive = history.productive || [];
183
+ if (forceNicheSwitch && productive.length > 0) {
184
+ const targetNiche = forceNicheSwitch.to;
185
+ const NICHE_KEYWORDS = {
186
+ fashion: [
187
+ "clothes",
188
+ "kleidung",
189
+ "kleding",
190
+ "vestiti",
191
+ "ropa",
192
+ "outfit",
193
+ "mode",
194
+ "shoes",
195
+ "sneakers",
196
+ "zapatos",
197
+ "chaussures",
198
+ "vintage",
199
+ "secondhand",
200
+ "thrift",
201
+ "moda",
202
+ "scarpe",
203
+ "schuhe",
204
+ "schoenen",
205
+ "cipő",
206
+ "boty",
207
+ "tenisky",
208
+ "sapatos",
209
+ "calzado",
210
+ ],
211
+ food: [
212
+ "food",
213
+ "bakery",
214
+ "brot",
215
+ "brood",
216
+ "pane",
217
+ "comida",
218
+ "restaurant",
219
+ "cafe",
220
+ "kitchen",
221
+ "konyha",
222
+ "patisserie",
223
+ "boulangerie",
224
+ "wypieki",
225
+ "etterm",
226
+ "essen",
227
+ "eten",
228
+ "jedzenie",
229
+ "étel",
230
+ "cibo",
231
+ "chleb",
232
+ "bread",
233
+ "pekar",
234
+ "bageri",
235
+ "forno",
236
+ "padaria",
237
+ ],
238
+ handmade_crafts: [
239
+ "handmade",
240
+ "handwerk",
241
+ "handgemaakt",
242
+ "artigianato",
243
+ "hechoamano",
244
+ "kézműves",
245
+ "kreativ",
246
+ "craft",
247
+ "diy",
248
+ "knutselen",
249
+ "ceramic",
250
+ "keramiek",
251
+ "kerzen",
252
+ "gyertya",
253
+ "resin",
254
+ "macrame",
255
+ "pottery",
256
+ "weaving",
257
+ "fattoamano",
258
+ "handgemacht",
259
+ "rękodzieło",
260
+ "ručně",
261
+ ],
262
+ home_living: [
263
+ "home",
264
+ "wohnung",
265
+ "huis",
266
+ "casa",
267
+ "lakberendezes",
268
+ "interior",
269
+ "deco",
270
+ "decoration",
271
+ "einrichten",
272
+ "furniture",
273
+ "möbel",
274
+ "meubilair",
275
+ "epipla",
276
+ "butor",
277
+ "nabytek",
278
+ "tapic",
279
+ "reinigung",
280
+ "cleaning",
281
+ "muebles",
282
+ "mobili",
283
+ "meble",
284
+ "bútor",
285
+ "dom",
286
+ "otthon",
287
+ "thuis",
288
+ "zuhause",
289
+ ],
290
+ electronics_tech: [
291
+ "tech",
292
+ "elektronik",
293
+ "electronica",
294
+ "gadget",
295
+ "phone",
296
+ "smartphone",
297
+ "laptop",
298
+ "computer",
299
+ "audio",
300
+ "headphones",
301
+ "charger",
302
+ "usb",
303
+ "playstation",
304
+ "gaming",
305
+ "telefon",
306
+ "handy",
307
+ "tablet",
308
+ "pc",
309
+ "console",
310
+ "smartwatch",
311
+ "cuffie",
312
+ "auricolari",
313
+ "caricatore",
314
+ ],
315
+ pets_animals: [
316
+ "pet",
317
+ "hund",
318
+ "dog",
319
+ "cat",
320
+ "kat",
321
+ "kuty",
322
+ "konia",
323
+ "pets",
324
+ "tier",
325
+ "dieren",
326
+ "animale",
327
+ "animal",
328
+ "mascota",
329
+ "animais",
330
+ "zwierzę",
331
+ "állat",
332
+ "cane",
333
+ "gatto",
334
+ "perro",
335
+ "chien",
336
+ "chat",
337
+ ],
338
+ beauty_health: [
339
+ "beauty",
340
+ "kosmetik",
341
+ "cosmetica",
342
+ "beaute",
343
+ "belleza",
344
+ "szepseg",
345
+ "hair",
346
+ "haj",
347
+ "skincare",
348
+ "makeup",
349
+ "parfum",
350
+ "perfume",
351
+ "wellness",
352
+ "gesundheit",
353
+ "salud",
354
+ "sante",
355
+ "szépség",
356
+ "krása",
357
+ ],
358
+ books_collectibles: [
359
+ "book",
360
+ "buch",
361
+ "boek",
362
+ "libro",
363
+ "knihy",
364
+ "konyv",
365
+ "collectible",
366
+ "sammlung",
367
+ "verzameling",
368
+ "collezionismo",
369
+ "coleccion",
370
+ "antique",
371
+ "antiquariat",
372
+ "vintage",
373
+ "vinyl",
374
+ "schallplatten",
375
+ "pokemon",
376
+ "kaarten",
377
+ "moedas",
378
+ "mince",
379
+ "livre",
380
+ "książka",
381
+ "carte",
382
+ ],
383
+ services_professional: [
384
+ "service",
385
+ "dienst",
386
+ "servizio",
387
+ "servicio",
388
+ "szolgaltatas",
389
+ "handwerker",
390
+ "craftsman",
391
+ "repair",
392
+ "reparatur",
393
+ "reparatie",
394
+ "riparazione",
395
+ "reparacion",
396
+ "cleaning",
397
+ "reinigung",
398
+ "moving",
399
+ "umzug",
400
+ "verhuur",
401
+ "mudanca",
402
+ "florist",
403
+ "photography",
404
+ "fotograf",
405
+ "usługa",
406
+ "szolgáltatás",
407
+ "serviço",
408
+ ],
409
+ marketplace_local: [
410
+ "market",
411
+ "markt",
412
+ "mercato",
413
+ "mercado",
414
+ "piac",
415
+ "trh",
416
+ "flohmarkt",
417
+ "rommelmarkt",
418
+ "brocante",
419
+ "bazar",
420
+ "mercadillo",
421
+ "feira",
422
+ "local",
423
+ "lokal",
424
+ "locale",
425
+ "winkel",
426
+ "boutique",
427
+ "shop",
428
+ "tienda",
429
+ "negozio",
430
+ "bol",
431
+ "webshop",
432
+ "eshop",
433
+ "sklep",
434
+ "obchod",
435
+ "bolt",
436
+ "butik",
437
+ ],
438
+ };
439
+ const keywords = NICHE_KEYWORDS[targetNiche] || [];
440
+ const nicheFiltered = productive.filter((t) =>
441
+ keywords.some((kw) => t.tag.toLowerCase().includes(kw.toLowerCase())),
442
+ );
443
+ if (nicheFiltered.length > 0) {
444
+ console.error(
445
+ `[tag-service] 🎯 niche-filtered productive: ${productive.length} → ${nicheFiltered.length} (target: ${targetNiche})`,
446
+ );
447
+ productive = nicheFiltered;
448
+ }
449
+ }
172
450
  const productiveHint =
173
451
  productive.length > 0
174
452
  ? `\nTags that already worked well for ${country} (with scores): ${productive
@@ -335,7 +613,7 @@ ${hardRules.map((r) => ` ${r.ruleType === "hard" ? "⛔" : "💡"} ${r.ruleText
335
613
  : ""
336
614
  }
337
615
 
338
- ${productiveHint}${deadHint}${repeatedDeadHint}${langPatternHint}${errorHint}${userHint}${strategyReview}${healthHint}${nicheHint}${forceNicheSwitchHint}
616
+ ${lastRoundRejectionHint}${productiveHint}${deadHint}${repeatedDeadHint}${langPatternHint}${errorHint}${userHint}${strategyReview}${healthHint}${nicheHint}${forceNicheSwitchHint}
339
617
 
340
618
  ## Quality check before responding
341
619
 
@@ -517,17 +795,44 @@ export async function discoverTagsForCountry(
517
795
  // 读取历史打分记录
518
796
  const productive = store.getTagsByCountry(country, 50);
519
797
  const dead = store.getDeadTags(country);
520
- // 全局去重:获取所有已存在的 tag 名(不限国家、不限状态),避免跨国家重复入库
521
- const { getAllExistingTagNames } = await import("./db-tags.js");
522
- const allExisting = getAllExistingTagNames();
798
+
799
+ // ====== P0修复: 清理 truly dead + 分类未分类 dead,释放去重空间 ======
800
+ const { classifyDeadTags, cleanupTrulyDeadTags: cleanupTrulyDead } =
801
+ await import("./tag-selflearn.js");
802
+ const classifyResult = classifyDeadTags();
803
+ if (classifyResult.classified > 0) {
804
+ console.error(
805
+ `[tag-service] 🧹 ${country} 分类了 ${classifyResult.classified} 个 dead tag (not_exist=${classifyResult.not_exist}, no_match=${classifyResult.no_match}, wrong_country=${classifyResult.wrong_country})`,
806
+ );
807
+ }
808
+ const cleanupResult = cleanupTrulyDead(2);
809
+ if (cleanupResult.deleted > 0) {
810
+ console.error(
811
+ `[tag-service] 🗑️ ${country} 清理了 ${cleanupResult.deleted} 个确认不存在的 dead tag,释放去重空间`,
812
+ );
813
+ }
814
+
815
+ // 全局去重:仅用 productive + scored 的活跃 tag 名做去重(排除 dead)
816
+ // dead tag 同名重新生成时 insertTag 会将其重置为 new
817
+ const { getActiveTagNames } = await import("./db-tags.js");
818
+ const allExisting = getActiveTagNames();
523
819
  console.error(
524
- `[tag-service] ${country} 全局去重集合: ${allExisting.size} 个已存在 tag`,
820
+ `[tag-service] ${country} 活跃去重集合: ${allExisting.size} 个 (productive+scored, 排除dead)`,
525
821
  );
526
822
 
527
823
  // 获取最近生成的标签(用于检测重复生成)
528
- const { getRecentGeneratedTags } = await import("./db-tags.js");
824
+ const { getRecentGeneratedTags, getLastRoundAllGeneratedTags } =
825
+ await import("./db-tags.js");
529
826
  const recentGenerated = getRecentGeneratedTags(country, 10);
530
827
 
828
+ // ====== 上轮反馈:告知 LLM 上一轮全部被去重的情况 ======
829
+ const lastRoundFeedback = getLastRoundAllGeneratedTags(country);
830
+ if (lastRoundFeedback?.allRejected) {
831
+ console.error(
832
+ `[tag-service] 🔁 ${country} 上轮全部被去重: ${lastRoundFeedback.raw.join(", ")}`,
833
+ );
834
+ }
835
+
531
836
  const history = { productive, dead, recentGenerated };
532
837
 
533
838
  // 读取历史策略效果(自学习)
@@ -542,7 +847,7 @@ export async function discoverTagsForCountry(
542
847
  );
543
848
  }
544
849
 
545
- // 组装 prompt(加入健康指标、niche 推荐、硬规则)
850
+ // 组装 prompt(加入健康指标、niche 推荐、硬规则、上轮反馈)
546
851
  const prompt = buildDiscoverPrompt(
547
852
  country,
548
853
  count,
@@ -554,6 +859,7 @@ export async function discoverTagsForCountry(
554
859
  hardRules,
555
860
  allExisting.size,
556
861
  forceNicheSwitch,
862
+ lastRoundFeedback,
557
863
  );
558
864
  console.error(
559
865
  `[tag-service] LLM discover ${country} round=${round} (lang=${getLang(country)}, count=${count}, temp=${temperature}, health=${health?.healthStatus || "unknown"})`,
@@ -591,12 +897,13 @@ export async function discoverTagsForCountry(
591
897
  if (result.inserted) inserted.push(result.tag);
592
898
  }
593
899
 
594
- // 记录 discover log(策略 + 结果 + 健康状态 + niche)
900
+ // 记录 discover log(策略 + 结果 + 健康状态 + niche + 原始 tag)
595
901
  const { insertDiscoverLog } = await import("./db-tags.js");
596
902
  insertDiscoverLog({
597
903
  country,
598
904
  round,
599
905
  strategy,
906
+ tagsRaw: deduped,
600
907
  tagsGenerated: unique,
601
908
  tagsAdded: inserted.length,
602
909
  productiveSample: productive.slice(0, 20).map((t) => t.tag),