package sysadmin import "os" // Feature 功能开关视图。value / effectiveValue 是进程实际读到的配置, // 不等于能力已经接入;是否可用看 implementationState 与 adapterReadiness。 type Feature struct { Key string `json:"key"` Label string `json:"label"` Group string `json:"group"` Value string `json:"value"` ConfiguredValue string `json:"configuredValue"` EffectiveValue string `json:"effectiveValue"` ImplementationState string `json:"implementationState"` AdapterReadiness string `json:"adapterReadiness"` AdapterReason string `json:"adapterReason,omitempty"` RolloutScope string `json:"rolloutScope"` Source string `json:"source"` RequiresRestart bool `json:"requiresRestart"` Description string `json:"description"` } const ( implPlaceholder = "placeholder" implDryRun = "dry_run" implShadow = "shadow" implLive = "live" adapterReady = "ready" adapterUnavailable = "unavailable" adapterDegraded = "degraded" rolloutEnvironment = "environment" ) type featureMaturity struct { state string adapter string reason string } // featureMaturityCatalog 是源码接线事实,不随环境变量翻转。 // 未列出的开关视为已接入的环境级能力;列出的项即使配置为 true 也保持此处状态。 var featureMaturityCatalog = map[string]featureMaturity{ "FAV_PUBLISHER_ENABLED": { state: implDryRun, adapter: adapterUnavailable, reason: "观察窗未授权,禁止 live", }, "FAV_DEGRADED_READ": { state: implDryRun, adapter: adapterUnavailable, reason: "降级读保持关闭", }, "MULTI_INTENT_PLAN_TIER": { state: implLive, adapter: adapterDegraded, reason: "I2 接线就绪:shadow 持久不可执行计划 + 项目 allowlist + 指标;默认仍 legacy,升档需 allowlist 与阈值验收", }, "INTENT_PLAN_PROJECT_ALLOWLIST": { state: implLive, adapter: adapterReady, reason: "空值拒绝所有 elevated tier;* 放开全部;逗号分隔项目 ID", }, "MULTI_INTENT_U9_SEGMENT": { state: implDryRun, adapter: adapterUnavailable, reason: "仅有 PI proposer,尚未进入真实聊天分段;提议请求已使用 reasoning profile", }, "MULTI_INTENT_U10_MUTATE": { state: implDryRun, adapter: adapterUnavailable, reason: "适配器仍是 dry-run/validated-only", }, "MULTI_INTENT_U11_DOCGOV": { state: implDryRun, adapter: adapterUnavailable, reason: "适配器仍是 dry-run/validated-only", }, "MULTI_INTENT_U12_RUN_CONTROL": { state: implDryRun, adapter: adapterUnavailable, reason: "适配器仍是 dry-run/validated-only", }, "SEMANTIC_SUMMARY_SHADOW": { state: implShadow, adapter: adapterDegraded, reason: "影子组件默认关闭;dev/test schema 61 已含 chat_summaries。摘要请求已用 reasoning profile,pi-agent 经 bind-mount 的 pi-args.mjs 带 --no-tools。授权可见集 digest 仍空", }, "SEMANTIC_SUMMARY_REPLACE": { state: implPlaceholder, adapter: adapterUnavailable, reason: "替换路径明确未接线,打开开关不会替换叙事", }, "PI_CHANGE_REPORT_NARRATIVE": { state: implShadow, adapter: adapterDegraded, reason: "只读事实已含截断后的差异片段和影响说明,不含整篇文档。开关默认关闭。叙事请求已用 reasoning profile;pi-agent 经 bind-mount 已带 --no-tools。未授权打开前保持 shadow", }, } type featureSpec struct { key string // 环境变量名 label string group string fallback string // 组合根未提供生效值时的兜底 description string } // featureCatalog 覆盖 config.Config 主要开关与各业务包灰度 flag(宁全勿缺)。 // 生效值由组合根(app)解析后注入,避免本包反向依赖业务模块。 var featureCatalog = []featureSpec{ {key: "AGENT_BACKEND", label: "Agent 执行后端", group: "agent", fallback: "pi-agent", description: "对话与任务执行的 Agent 后端(stub / cursor / pi-agent)"}, {key: "MAX_CONCURRENT_AGENTS", label: "Agent 全局并发上限", group: "agent", fallback: "8", description: "保护 pi-agent / GPU / API 的并发闸"}, {key: "PI_AGENT_RUNNER_URL", label: "PI Runner 地址", group: "agent", fallback: "", description: "产品内 PI Agent Runner 服务地址;空表示未接入"}, {key: "ALLOW_IN_MEMORY", label: "允许内存降级模式", group: "system", fallback: "false", description: "数据库不可用时降级内存模式(仅限本地 Demo)"}, {key: "AUTH_REGISTRATION_ENABLED", label: "开放注册", group: "auth", fallback: "true", description: "是否允许用户自主注册"}, {key: "AUTH_REQUIRE_APPROVAL", label: "注册需审核", group: "auth", fallback: "false", description: "新注册用户是否需要管理员审核后激活"}, {key: "FAV_PUBLISHER_ENABLED", label: "FAV 发布器", group: "artifact-vault", fallback: "false", description: "Final Artifact Vault 后台发布器;默认关闭"}, {key: "FAV_DEGRADED_READ", label: "FAV 降级读", group: "artifact-vault", fallback: "false", description: "FAV 缺失时允许降级读取;默认关闭"}, {key: "INTENT_V2_SHADOW", label: "意图 V2 影子模式", group: "intent", fallback: "false", description: "V2 意图识别影子运行,仅观测不接管"}, {key: "INTENT_V2_DECISION", label: "意图 V2 决策", group: "intent", fallback: "false", description: "V2 意图识别接管路由决策"}, {key: "INTENT_V2_DECISION_TIER", label: "意图 V2 决策档位", group: "intent", fallback: "none", description: "V2 灰度风险档位:none/query/generate/write_low/write_high/all"}, {key: "WORKFLOW_INTENTS_ENABLED", label: "工作流意图", group: "intent", fallback: "true", description: "识别工作流类意图"}, {key: "MULTI_INTENT_ENABLED", label: "多意图识别", group: "intent", fallback: "true", description: "单条消息多意图拆解"}, {key: "MULTI_INTENT_PLAN_TIER", label: "多意图计划档位", group: "intentflow", fallback: "legacy", description: "意图计划灰度:legacy/shadow/auto_read/confirm/full"}, {key: "INTENT_PLAN_PROJECT_ALLOWLIST", label: "多意图计划项目白名单", group: "intentflow", fallback: "", description: "elevated tier 项目 allowlist:空拒绝;* 全部;逗号分隔项目 ID"}, {key: "MULTI_INTENT_U9_SEGMENT", label: "U9 LLM 分段", group: "intentflow", fallback: "false", description: "LLM 意图分段提议"}, {key: "MULTI_INTENT_U10_MUTATE", label: "U10 变更适配器", group: "intentflow", fallback: "false", description: "低/中风险 mutate 动作适配器"}, {key: "MULTI_INTENT_U11_DOCGOV", label: "U11 文档治理适配器", group: "intentflow", fallback: "false", description: "docgov 动作适配器"}, {key: "MULTI_INTENT_U12_RUN_CONTROL", label: "U12 运行控制适配器", group: "intentflow", fallback: "false", description: "workflow/run-control 动作适配器"}, {key: "WORKFLOW_SCOPE_MODE", label: "工作流作用域裁决", group: "intent", fallback: "strict", description: "文档命中裁决:strict 命中他流即拦截;weighted 按工作流关系权重打分"}, {key: "SEMANTIC_SUMMARY_SHADOW", label: "语义摘要影子", group: "ctxmgr", fallback: "false", description: "上下文语义摘要影子生成"}, {key: "SEMANTIC_SUMMARY_REPLACE", label: "语义摘要替换", group: "ctxmgr", fallback: "false", description: "语义摘要替换确定性叙事(保留位,未接线)"}, {key: "PI_CHANGE_REPORT_NARRATIVE", label: "PI 变更报告叙事", group: "docrevision", fallback: "false", description: "变更报告 PI 叙事增强;默认仅确定性回退"}, } // Features 返回功能开关快照;source 恒为 env,均需重启生效。 func (s *Service) Features() []Feature { values := map[string]string{} if s.featureValues != nil { values = s.featureValues() } out := make([]Feature, 0, len(featureCatalog)) for _, spec := range featureCatalog { value, ok := values[spec.key] if !ok { if env, set := os.LookupEnv(spec.key); set { value = env } else { value = spec.fallback } } maturity := featureMaturity{state: implLive, adapter: adapterReady} if known, ok := featureMaturityCatalog[spec.key]; ok { maturity = known } out = append(out, Feature{ Key: spec.key, Label: spec.label, Group: spec.group, Value: value, ConfiguredValue: value, EffectiveValue: value, ImplementationState: maturity.state, AdapterReadiness: maturity.adapter, AdapterReason: maturity.reason, RolloutScope: rolloutEnvironment, Source: "env", RequiresRestart: true, Description: spec.description, }) } return out }