package intent import ( "os" "strings" "sync" ) // V2DecisionTier 灰度切换的风险档位(由低到高逐步启用 V2 路由) type V2DecisionTier string const ( V2TierNone V2DecisionTier = "none" V2TierQuery V2DecisionTier = "query" V2TierGenerate V2DecisionTier = "generate" V2TierWriteLow V2DecisionTier = "write_low" V2TierWriteHigh V2DecisionTier = "write_high" V2TierAll V2DecisionTier = "all" ) // FeatureFlags Phase 5 意图识别灰度开关(环境变量覆盖,默认保守) type FeatureFlags struct { IntentV2Shadow bool IntentV2Decision bool V2DecisionTier V2DecisionTier WorkflowIntentsEnabled bool MultiIntentEnabled bool SemanticFallbackEnabled bool } var ( flagsMu sync.RWMutex activeFlags = defaultFeatureFlags() ) func defaultFeatureFlags() FeatureFlags { return FeatureFlags{ IntentV2Shadow: envBool("INTENT_V2_SHADOW", false), IntentV2Decision: envBool("INTENT_V2_DECISION", false), V2DecisionTier: parseV2DecisionTier(os.Getenv("INTENT_V2_DECISION_TIER")), WorkflowIntentsEnabled: envBool("WORKFLOW_INTENTS_ENABLED", true), MultiIntentEnabled: envBool("MULTI_INTENT_ENABLED", true), SemanticFallbackEnabled: envBool("SEMANTIC_FALLBACK_ENABLED", false), } } func parseV2DecisionTier(raw string) V2DecisionTier { switch strings.ToLower(strings.TrimSpace(raw)) { case "", "none": return V2TierNone case "query": return V2TierQuery case "generate": return V2TierGenerate case "write_low", "write-low", "low_write": return V2TierWriteLow case "write_high", "write-high", "high_write": return V2TierWriteHigh case "all": return V2TierAll default: return V2TierQuery } } func envBool(key string, def bool) bool { v := strings.TrimSpace(os.Getenv(key)) if v == "" { return def } return v == "1" || strings.EqualFold(v, "true") || strings.EqualFold(v, "yes") } // CurrentFeatureFlags 返回当前生效的功能开关 func CurrentFeatureFlags() FeatureFlags { flagsMu.RLock() defer flagsMu.RUnlock() return activeFlags } // SetFeatureFlagsForTest 测试专用:临时覆盖开关 func SetFeatureFlagsForTest(f FeatureFlags) { flagsMu.Lock() activeFlags = f flagsMu.Unlock() } // ResetFeatureFlagsForTest 测试专用:恢复默认 func ResetFeatureFlagsForTest() { flagsMu.Lock() activeFlags = defaultFeatureFlags() flagsMu.Unlock() } // tierAllowsRisk 判断当前灰度档位是否允许该风险等级的 V2 路由 func tierAllowsRisk(tier V2DecisionTier, risk RiskLevel) bool { switch tier { case V2TierNone: return false case V2TierQuery: return risk == RiskLow case V2TierGenerate: return risk == RiskLow || risk == RiskMedium case V2TierWriteLow: return risk != RiskHigh case V2TierWriteHigh, V2TierAll: return true default: return false } }