Files
knowledge-governance/internal/qaopsfields/language.go
T
姚章浩 533db535f1
build / build (push) Successful in 1m12s
feat(qaops): 接入真实模型补缺任务与总表看板
按已确认需求提供独立安全模型配置、真实探测、正式字典字段评估与原问题译中,持久批次支持只补空白、暂停恢复、有限重试、幂等和逐字段依据。覆盖固定规则、本地语言、组织关联与主表有效看板共用治理侧读模型,保留源生产和冻结历史。

验证:完整 make verify 退出0,702项Go与329项前端通过;默认118项外部/隔离集成明确跳过,另跑完整镜像及组织隔离PG竞态。12并发、输入漂移、过期租约、暂停/恢复及有限重试通过。用户授权的百炼 deepseek-v4.1-flash 完成真实后台与浏览器评估/翻译;552行、16列、11块看板及完整PNG、独立配置页测试/保存、重开恢复和零JS异常通过。仅非业务样本外发,自动处理关闭,原payload不变。

上线前治理库已做导出快照原生备份并真实恢复,60表行数与17个QA表内容哈希一致;不修改DobotAI生产或隧道。本提交时尚未部署,线上验收须独立完成。
2026-10-19 14:29:49 +08:00

127 lines
4.2 KiB
Go

package qaopsfields
import (
"regexp"
"strings"
"unicode"
)
const LanguageRuleVersion = "original-question-local-v1"
var nonLanguage = []*regexp.Regexp{
regexp.MustCompile("(?s)```.*?```|`[^`]*`"),
regexp.MustCompile(`(?i)https?://[^\s]+|[\w.+-]+@[\w.-]+\.[a-z]{2,}`),
regexp.MustCompile(`(?i)\b(?:DobotStudio(?:\s+Pro)?|DobotSCStudio|DobotAI|DOBOT|MG400|M1\s*Pro|VX500|X-Trainer|FlexPendant|Magician(?:\s+(?:Lite|E6))?|Nova\d*|ATOM(?:-W)?|CRA-IP68|CR\d+A-Ex|CR(?:AF|A|V2?|H)?\d*[AHV]?|Panel|SDK|API|PLC|TCP/IP|Modbus(?:\s+TCP)?|ROS|DH|STP|LUA|CE(?:-MD|-EMC)?|RoHS|RDS)\b`),
regexp.MustCompile(`(?i)\b\d+(?:\.\d+)?\s*(?:kg|mm|cm|ms|hz|khz|mhz|gb|mb|rpm|v|a|w|kw)?\b`),
}
var words = map[string]string{
"en": "how what why when where which can could does do is are the a to of for with without connect connection use using reset read write support supported install set configure current please",
"fr": "comment pourquoi quel quelle quelles est sont avec pour les une connecter connexion faire puis peux",
"de": "wie warum welche welcher kann ich mit verbinden verbindung funktioniert einstellen werden wird das die der",
"es": "cómo cómo por qué cuáles cuál puedo conectar conexión para los las una hay tiene configurar",
"pt": "você vocês qual quais ligar conexão não robô configuração preciso posso configurar",
"it": "come perché quale quali posso collegare collegamento impostare robotica della degli",
"da": "hvordan hvad hvorfor hvilken hvilke tilslutter forbinde indstille jeg",
"pl": "jak dlaczego jaki jaka jakie połączyć połączenie ustawić można jest",
"vi": "tiếng việt làm thế nào kết nối được không cách tôi với của",
"ru": "как что почему какой какие можно подключить подключение настроить работает для",
}
var languageNames = map[string]string{"zh": "中文", "en": "英文", "ja": "日文", "ko": "韩文", "fr": "法文", "de": "德文", "es": "西班牙文", "pt": "葡萄牙文", "it": "意大利文", "da": "丹麦文", "pl": "波兰文", "vi": "越南文", "ru": "俄文", "th": "泰文", "ar": "阿拉伯文", "mul": "多语言", "und": "未识别"}
func LanguageName(code string) string {
if name, ok := languageNames[code]; ok {
return name
}
return "未识别"
}
// Product identifiers and technical tokens carry no reliable language signal.
// Keep uncertain short input und instead of silently defaulting to English.
func DetectLanguage(question string) string {
text := NaturalText(question)
han, kana, hangul, thai, arabic, latin := 0, 0, 0, 0, 0, 0
for _, r := range text {
switch {
case unicode.In(r, unicode.Hiragana, unicode.Katakana):
kana++
case unicode.In(r, unicode.Hangul):
hangul++
case unicode.In(r, unicode.Han):
han++
case unicode.In(r, unicode.Thai):
thai++
case unicode.In(r, unicode.Arabic):
arabic++
case unicode.In(r, unicode.Latin):
latin++
}
}
scripts := 0
for _, n := range []int{kana, hangul, thai, arabic} {
if n > 0 {
scripts++
}
}
if scripts > 1 {
return "mul"
}
if kana > 0 {
if latin > 2*(kana+han)+12 {
return "mul"
}
return "ja"
}
if hangul > 0 {
if latin > 2*hangul+12 {
return "mul"
}
return "ko"
}
if thai > 0 {
return "th"
}
if arabic > 0 {
return "ar"
}
tokens := strings.FieldsFunc(strings.ToLower(text), func(r rune) bool { return !unicode.IsLetter(r) })
if han > 0 {
if len(tokens) >= 5 && latin > 2*han+12 {
return "mul"
}
return "zh"
}
scores := map[string]int{}
for lang, vocabulary := range words {
set := map[string]bool{}
for _, w := range strings.Fields(vocabulary) {
set[w] = true
}
for _, token := range tokens {
if set[token] {
scores[lang]++
}
}
}
best, maxScore, tied := "und", 0, false
for lang, score := range scores {
if score > maxScore {
best, maxScore, tied = lang, score, false
} else if score == maxScore && score > 0 {
tied = true
}
}
if tied || maxScore == 0 {
return "und"
}
return best
}
// NaturalText is a detection-only view; callers retain the original question verbatim.
func NaturalText(question string) string {
text := question
for _, pattern := range nonLanguage {
text = pattern.ReplaceAllString(text, " ")
}
return text
}