The static adapter catalog still read I2-era states (degraded/shadow) after the 2026-09-26 dev rollout, so the admin features page showed 降级 for flags already enabled. Update six entries to live+ready with evidence-dated reasons (I2/I3/P8 acceptance, P8 per-key gates kept visible, shadow visibility digest now persisted, REPLACE wildcard wiring, PI narrative dev rollout); keep FAV red lines untouched. Tests updated; FAV now carries the configured-value-must-not-imply-readiness assertion.
167 lines
9.4 KiB
Go
167 lines
9.4 KiB
Go
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: adapterReady,
|
||
reason: "I2/I3/P8 已合入并按 allowlist 验收(shadow→auto_read→confirm,项目 6579);dev 现行 confirm+*(2026-09-26 授权放量);U11/U12 逐键 live flag 默认关,禁止 full 一次放量",
|
||
},
|
||
"INTENT_PLAN_PROJECT_ALLOWLIST": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "空值拒绝所有 elevated tier;* 放开全部;逗号分隔项目 ID",
|
||
},
|
||
"MULTI_INTENT_U9_SEGMENT": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "HandleTurn 在规则无法分段且 elevated tier 时调用 PI proposer;失败 fail-closed;RulesCanDecide 跳过 LLM",
|
||
},
|
||
"MULTI_INTENT_U10_MUTATE": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "project.fact.supplement 经 MutateOwner 写项目黑板(ctxmgr 端口);需 ExpectedResourceVersion",
|
||
},
|
||
"MULTI_INTENT_U11_DOCGOV": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "P8 后全部 docgov 键已接真实引擎:claim/review.submit 随域名 flag,review/final/proposal 三组逐键 flag(默认关)另控;项目 143 只读",
|
||
},
|
||
"MULTI_INTENT_U12_RUN_CONTROL": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "P8 后全部 run-control 键已接真实引擎:workflow.run/run.resume 随域名 flag,switch/restart 逐键 flag(默认关)另控",
|
||
},
|
||
"SEMANTIC_SUMMARY_SHADOW": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "影子链路生产化(durable jobs/租约/watermark/provenance,dev schema 75 含 chat_summaries);可见集 digest 已随 provenance 落盘;dev 已开启(2026-09-26)",
|
||
},
|
||
"SEMANTIC_SUMMARY_REPLACE": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "fail-closed 双开关已全量接线(master+SESSIONS,支持 * 全部会话含后续新建;四重校验;Facts/digest 保持 deterministic);dev 已开 *(2026-09-26)",
|
||
},
|
||
"PI_CHANGE_REPORT_NARRATIVE": {
|
||
state: implLive, adapter: adapterReady,
|
||
reason: "2026-09-23 收口真实 MiniMax 复测 PASS(截断差异+reasoning 落盘);dev 已按授权常驻开启(2026-09-26);test/main 保持默认关",
|
||
},
|
||
}
|
||
|
||
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: "JEV_DECIDE_MODE", label: "jev 决策模式", group: "jevdecide", fallback: "off", description: "产品侧 jev-service:off / shadow / assist;默认 off,失败回退规则"},
|
||
{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: "验证 OK 后以语义叙事替换会话 served Narrative;fail-closed 灰度,需同时配置会话白名单"},
|
||
{key: "SEMANTIC_SUMMARY_REPLACE_SESSIONS", label: "语义摘要替换白名单", group: "ctxmgr", fallback: "", description: "逗号分隔会话 ID;* 全部会话(含后续新建);空则不替换任何会话(纵深防御)"},
|
||
{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
|
||
}
|