This commit is contained in:
2026-08-23 20:49:40 +02:00
parent 3b678ca857
commit f3d4fb56ed
31 changed files with 3608 additions and 111 deletions
@@ -0,0 +1,58 @@
package company
import "testing"
// Regression: the generic invent shapes (" is a … product from …" and
// " je … znamk|kategor|produkt …") matched any length of copy. In Slovenian " je "
// appears in almost every sentence and A1 spec lists start with "Znamka: <brand>",
// so real formula output was classified as invent fallback, discarded, and replaced
// with the supplier feed text — Review then showed Original ≈ Enriched.
func TestLooksLikeHeuristicSynthesize_keepsRealFormulaCopy(t *testing.T) {
t.Parallel()
slovenianFormulaCopy := "<h2>BRUNNER zložljiv stol ONE SHOT za udobje na poti</h2>" +
"<p>Zložljiv stol iz aluminija je zasnovan za dolge ure udobnega sedenja na terenu, na snemanju ali ob " +
"kampiranju. Okvir se razpre v enem gibu in ostane stabilen tudi na neravnih tleh, medtem ko tkanina " +
"sedišča prijetno diha in se hitro suši. Naslonjala za roke razbremenijo ramena, hrbtni del pa podpira " +
"naravno držo. Zaradi majhne teže ga brez napora prenesete od avtomobila do prizorišča.</p>" +
"<h2>Stabilnost in dolga življenjska doba</h2>" +
"<p>Konstrukcija združuje trpežne materiale in premišljene detajle. Aluminijasti profili so odporni proti " +
"koroziji, spoji pa so ojačani na mestih največjih obremenitev. Sivo črna kombinacija ostaja videti " +
"urejena tudi po sezoni uporabe na prostem, saj se madeži manj poznajo.</p>" +
"<ul><li>Znamka: BRUNNER</li><li>Model: ONE SHOT</li><li>Barva: sivo črna</li></ul>"
if LooksLikeHeuristicSynthesize(slovenianFormulaCopy) {
t.Fatal("real Slovenian formula copy must not read as invent fallback")
}
englishFormulaCopy := "<h2>Acme Monitor Pro</h2><p>This monitor is a strong choice for colour-critical work, " +
"with a factory-calibrated panel and a stand that adjusts through a wide range. Cable routing keeps the " +
"desk tidy, and the built-in hub means one cable to the laptop. Panel uniformity holds up across the " +
"whole surface, so gradients stay smooth from edge to edge in long editing sessions.</p>" +
"<ul><li>Brand: Acme</li><li>Key features: USB-C hub, height adjustment</li></ul>"
// "Key features" is ordinary section wording: suggestive enough to force a
// re-enhance, never enough to discard the copy.
if IsInventFallbackDescription(englishFormulaCopy) {
t.Fatal("real English formula copy must not read as invent fallback")
}
if IsInventFallbackDescription(slovenianFormulaCopy) {
t.Fatal("real Slovenian formula copy must not read as invent fallback")
}
}
func TestLooksLikeHeuristicSynthesize_stillCatchesInvent(t *testing.T) {
t.Parallel()
cases := []string{
"Vogel's WALL 3245 is a TV Mounts product from Vogel's. Key specs: width 45 cm, max_load 40 kg.",
"Gorenje EHT6020 je izdelek znamke Gorenje.",
"Samsung QLED je izdelek v kategoriji Televizorji.",
"Acme Widget is a catalog product with the known attributes.",
"Acme Widget — catalog product.",
"BRUNNER ONE SHOT — Stolčki, znamka BRUNNER. Širina: 44 cm.",
"<h2>Gorenje EHT6020</h2><p>Gorenje EHT6020 — Štedilniki, znamka Gorenje.</p>" +
"<h3>Ključne lastnosti</h3><ul><li>Širina: 60 cm</li></ul>",
}
for _, c := range cases {
if !LooksLikeHeuristicSynthesize(c) {
t.Fatalf("invent fallback must still be detected: %q", c)
}
}
}
+54 -7
View File
@@ -31,10 +31,12 @@ func IsWeakPriorEnhanceDescription(priorDesc string, titles ...string) bool {
return reason == "weak" || reason == "title-echo"
}
// heuristicSynthesizePhrases are distinctive invent / formula-skeleton snippets from
// heuristicSynthesizePhrases are distinctive invent snippets from
// synthesizeDescriptionFromTitle / synthesizeDescriptionFromFormula / factualDescriptionIntro.
// These may look "strong" enough to pass IsWeakPriorEnhanceDescription but must never
// hash-skip enhance (would leave fallback copy forever on reprocess).
// Only whole sentences that no copywriter would produce belong here — anything a
// real category formula could legitimately emit goes in synthSkeletonPhrases.
var heuristicSynthesizePhrases = []string{
"is a catalog product with the known attributes",
"is listed in the ",
@@ -43,19 +45,44 @@ var heuristicSynthesizePhrases = []string{
"je izdelek v kategoriji",
"je izdelek znamke",
". ključne specifikacije:",
" — katalogski izdelek",
" — catalog product",
}
// synthSkeletonPhrases also appear in synthesize output but are ordinary words a
// real description may use ("Ključne lastnosti" as a heading, "znamka X" in a spec
// line, "Key features" as a section title). They are suggestive, not proof, so only
// the hash-skip gate consults them — see IsInventFallbackDescription.
var synthSkeletonPhrases = []string{
// factualDescriptionIntro (post-phrase-avoidance invent)
" — znamka ",
", znamka ",
" — katalogski izdelek",
" — from ",
" — catalog product",
// synthesizeDescriptionFromFormula heading fallbacks
"ključne lastnosti",
"key features",
}
// LooksLikeHeuristicSynthesize reports invent / formula-skeleton fallback copy.
func LooksLikeHeuristicSynthesize(desc string) bool {
// maxSynthSkeletonRunes bounds the suggestive signals above. Every synthesize helper
// emits at most a title heading, one intro sentence, and a short "Label: value"
// list, so it stays well under this; real category-formula prose (multiple 100-word
// paragraphs plus a spec list) runs several times longer.
const maxSynthSkeletonRunes = 600
// maxSynthInventRunes bounds the generic "<title> is a … product from …" /
// "<title> je … znamke …" shapes. Those describe a single invent sentence, so they
// may only match copy that is itself about one sentence long. Unbounded, " je "
// plus "znamk" matched almost any Slovenian description.
const maxSynthInventRunes = 2 * shortBoilerplateDescRunes
// IsInventFallbackDescription reports copy that is unmistakably machine invent —
// whole sentences emitted by the synthesize helpers, or the one-sentence
// "<title> is a <category> product from <brand>" shape.
//
// This is the strict test, safe for discarding a description outright. The looser
// LooksLikeHeuristicSynthesize adds ambiguous wording and is only used where a
// false positive costs one extra enhance call rather than the copy itself.
func IsInventFallbackDescription(desc string) bool {
plain := strings.ToLower(plainTextForWeakCheck(desc))
if plain == "" {
return false
@@ -65,19 +92,39 @@ func LooksLikeHeuristicSynthesize(desc string) bool {
return true
}
}
if len([]rune(plain)) > maxSynthInventRunes {
return false
}
// EN invent: "<title> is a <category> product from <brand>"
if strings.Contains(plain, " is a ") && strings.Contains(plain, " product from ") {
return true
}
// SL/CS short invent: "<title> je … znamk|kategor|produkt …"
if strings.Contains(plain, " je ") &&
return strings.Contains(plain, " je ") &&
(strings.Contains(plain, "znamk") ||
strings.Contains(plain, "kategor") ||
strings.Contains(plain, "produkt") ||
strings.Contains(plain, "televiz") ||
strings.Contains(plain, "monitor")) {
strings.Contains(plain, "monitor"))
}
// LooksLikeHeuristicSynthesize reports invent / formula-skeleton fallback copy for
// the enhance hash-skip gate: IsInventFallbackDescription plus wording that merely
// suggests a skeleton, inside a body short enough to be one. A false positive here
// forces one re-enhance, which is cheap; never use it to drop a description.
func LooksLikeHeuristicSynthesize(desc string) bool {
if IsInventFallbackDescription(desc) {
return true
}
plain := strings.ToLower(plainTextForWeakCheck(desc))
if plain == "" || len([]rune(plain)) > maxSynthSkeletonRunes {
return false
}
for _, p := range synthSkeletonPhrases {
if p != "" && strings.Contains(plain, p) {
return true
}
}
return false
}