# 特性开关系统 > 所属项目: free-code .NET 10 重写 > 所属模块: [UI 与扩展设计](UI与扩展设计.md) > 原始代码: `scripts/build.ts` `feature()` 编译时开关 + 运行时 GrowthBook --- ## 概述 特性开关系统控制 free-code 的 88 个功能标志,其中 54 个可编译、34 个损坏。原始实现有两个层次:编译时用 `scripts/build.ts` 的 `feature()` 函数注入 `#define`,运行时用 GrowthBook SDK 从云端拉取实验配置。 .NET 重写将这两个层次统一到同一套接口下。`FeatureFlags` 静态类持有全部 54 个常量,供编译时 `#if` 和运行时查询共同使用;`IFeatureFlagService` 抽象运行时查询;`FeatureFlagService` 从 `appsettings.json` 的 `FeatureFlags` 配置节读取初始值,取代原始 GrowthBook 的云端下发机制,确保零网络依赖。 --- ## 18.1 FeatureFlags 常量清单 ```csharp /// /// 特性开关常量 — 编译时 #if + 运行时查询 /// 对应原始 88 个 feature flags(54 可编译,34 损坏) /// public static class FeatureFlags { // ===== 交互与 UI ===== public const string Ultraplan = "ULTRAPLAN"; public const string Ultrathink = "ULTRATHINK"; public const string VoiceMode = "VOICE_MODE"; public const string TokenBudget = "TOKEN_BUDGET"; public const string HistoryPicker = "HISTORY_PICKER"; public const string MessageActions = "MESSAGE_ACTIONS"; public const string QuickSearch = "QUICK_SEARCH"; public const string ShotStats = "SHOT_STATS"; // ===== Agent、记忆与规划 ===== public const string BuiltinExplorePlanAgents = "BUILTIN_EXPLORE_PLAN_AGENTS"; public const string VerificationAgent = "VERIFICATION_AGENT"; public const string AgentTriggers = "AGENT_TRIGGERS"; public const string AgentTriggersRemote = "AGENT_TRIGGERS_REMOTE"; public const string ExtractMemories = "EXTRACT_MEMORIES"; public const string CompactionReminders = "COMPACTION_REMINDERS"; public const string CachedMicrocompact = "CACHED_MICROCOMPACT"; public const string TeamMem = "TEAMMEM"; // ===== 工具与基础设施 ===== public const string BridgeMode = "BRIDGE_MODE"; public const string BashClassifier = "BASH_CLASSIFIER"; public const string PromptCacheBreakDetection = "PROMPT_CACHE_BREAK_DETECTION"; // ===== 其余 35 个可编译标志 ===== public const string Buddy = "BUDDY"; public const string V2Todo = "V2_TODO"; public const string EnableLspTool = "ENABLE_LSP_TOOL"; public const string ContextCollapse = "CONTEXT_COLLAPSE"; public const string TokenBudgetWarning = "TOKEN_BUDGET_WARNING"; public const string MemoryAutoExtract = "MEMORY_AUTO_EXTRACT"; public const string CompactHistory = "COMPACT_HISTORY"; public const string PermissionClassifier = "PERMISSION_CLASSIFIER"; public const string AsyncToolExecution = "ASYNC_TOOL_EXECUTION"; public const string Speculation = "SPECULATION"; public const string PromptSuggestion = "PROMPT_SUGGESTION"; public const string SkillImprovement = "SKILL_IMPROVEMENT"; public const string AgentSwarm = "ENABLE_AGENT_SWARMS"; public const string WorkflowTool = "WORKFLOW_TOOL"; public const string MonitorTool = "MONITOR_TOOL"; public const string McpSkills = "MCP_SKILLS"; public const string ChicagoMcp = "CHICAGO_MCP"; public const string EnableDeskApi = "ENABLE_DESK_API"; public const string Heapdump = "HEAPDUMP"; public const string EffortFlag = "EFFORT"; public const string Advisor = "ADVISOR"; public const string ThinkingBudget = "THINKING_BUDGET"; public const string RemoteEnv = "REMOTE_ENV"; public const string NotifyTool = "NOTIFY_TOOL"; public const string CaptureTool = "CAPTURE_TOOL"; public const string ReplayUserMessages = "REPLAY_USER_MESSAGES"; public const string TelemetryFlush = "TELEMETRY_FLUSH"; public const string StripUnusedImports = "STRIP_UNUSED_IMPORTS"; public const string ToolResultStorage = "TOOL_RESULT_STORAGE"; public const string McpStreaming = "MCP_STREAMING"; public const string PlanMode = "PLAN_MODE"; public const string PlanModeV2 = "PLAN_MODE_V2"; public const string RemoteControl = "REMOTE_CONTROL"; public const string ConsentedRemoteControl = "CONSENTED_REMOTE_CONTROL"; public const string CcrV2 = "CCR_V2"; public const string EnableSandbox = "ENABLE_SANDBOX"; public const string DiffBasedEdit = "DIFF_BASED_EDIT"; public const string OutputTruncation = "OUTPUT_TRUNCATION"; public const string WebSearchTool = "WEB_SEARCH_TOOL"; public const string PythonTool = "PYTHON_TOOL"; public const string NotebookEditTool = "NOTEBOOK_EDIT_TOOL"; public const string DedupeToolCalls = "DEDUPE_TOOL_CALLS"; public const string IdeIntegration = "IDE_INTEGRATION"; public const string RateLimitUi = "RATE_LIMIT_UI"; public const string TokenCounting = "TOKEN_COUNTING"; } ``` --- ## 标志分类一览 | 类别 | 标志数量 | 说明 | |---|---|---| | 交互与 UI | 8 | 提示词增强、历史记录、语音模式等 | | Agent、记忆与规划 | 8 | 多 Agent、记忆提取、规划模式等 | | 工具与基础设施 | 3 | IDE 桥接、分类器、缓存检测 | | 其余可编译标志 | 35 | 各类实验功能 | | **合计可编译** | **54** | | | 损坏标志(不含)| 34 | 需重建,详见 FEATURES.md | --- ## 18.2 IFeatureFlagService 接口 ```csharp /// /// 运行时特性开关查询接口 /// public interface IFeatureFlagService { /// 查询指定标志是否启用 bool IsEnabled(string flag); /// 动态设置标志状态(用于测试或运行时覆盖) void SetFlag(string flag, bool enabled); } ``` --- ## 18.3 FeatureFlagService 实现 ```csharp /// /// 运行时特性开关实现 /// 编译时使用 #if FLAG_NAME,运行时使用 IsEnabled() /// 对应原始 GrowthBook SDK 的本地替代 /// public sealed class FeatureFlagService : IFeatureFlagService { private readonly ConcurrentDictionary _flags = new(); public FeatureFlagService(IConfiguration config) { // 从 appsettings.json 的 FeatureFlags 节加载 var section = config.GetSection("FeatureFlags"); foreach (var child in section.GetChildren()) _flags[child.Key] = bool.Parse(child.Value ?? "false"); } public bool IsEnabled(string flag) => _flags.GetValueOrDefault(flag, false); public void SetFlag(string flag, bool enabled) => _flags[flag] = enabled; } ``` --- ## 双模式设计 特性开关系统同时支持两种工作模式,对应原始实现的两个不同层次。 ### 编译时模式(`#if`) ```csharp // 编译时完全消除死代码,减小二进制体积 #if ULTRAPLAN services.AddSingleton(); #endif #if VOICE_MODE services.AddSingleton(); #endif ``` 编译时标志通过 MSBuild 属性注入,对应原始 `build.ts` 的 `feature()` 函数: ```xml $(DefineConstants);ULTRAPLAN ``` 构建命令示例: ```bash # 全功能构建(对应原始 build:dev:full) dotnet build -p:FEATURE_ULTRAPLAN=true -p:FEATURE_VOICE_MODE=true ... # 仅启用语音模式(对应原始默认构建) dotnet build -p:FEATURE_VOICE_MODE=true ``` ### 运行时模式(`IsEnabled()`) ```csharp // 不重新编译就能切换,由配置文件驱动 if (_features.IsEnabled(FeatureFlags.TokenBudget)) { // 显示 token 预算组件 } if (_features.IsEnabled(FeatureFlags.ExtractMemories)) { await _memoryService.TryExtractAsync(messages); } ``` `appsettings.json` 配置示例: ```json { "FeatureFlags": { "TOKEN_BUDGET": true, "HISTORY_PICKER": true, "EXTRACT_MEMORIES": false, "ULTRAPLAN": false } } ``` --- ## 原始实现对比 | 原始(TypeScript)| .NET 重写 | 说明 | |---|---|---| | `scripts/build.ts` `feature("FLAG")` | MSBuild `-p:FEATURE_FLAG=true` | 编译时开关 | | `#if defined(FLAG)` in bundled output | `#if FLAG_NAME` in C# | 死代码消除 | | GrowthBook SDK(云端下发)| `FeatureFlagService`(配置文件)| 运行时开关 | | `growthbook.isOn("flag-name")` | `_features.IsEnabled(FeatureFlags.Xxx)` | 查询 API | | 34 损坏标志(缺少资源/类型)| 保留常量,实现体标记 TODO | 向后兼容占位 | **关键差异**:.NET 重写移除了 GrowthBook 的网络依赖。原始实现在每次启动时联系 GrowthBook 服务器更新标志状态,这是一个隐式的遥测上报渠道。`FeatureFlagService` 完全本地化,配置通过 `appsettings.json` 或环境变量覆盖,无任何出站请求。 --- ## DI 注册 ```csharp // Program.cs 或服务注册扩展方法 services.AddSingleton(); // 使用示例(构造函数注入) public sealed class SomeService(IFeatureFlagService features) { public void DoWork() { if (features.IsEnabled(FeatureFlags.AsyncToolExecution)) { // 异步工具执行路径 } } } ``` --- ## 参考资料 - [UI 与扩展设计 — 总览](UI与扩展设计.md) - [UI 与扩展设计 — Terminal.Gui 终端 UI](UI与扩展设计-Terminal-Gui终端UI.md) - [核心模块设计 — 查询引擎 (QueryEngine)](../核心模块设计/核心模块设计-查询引擎-QueryEngine.md) - [测试与构建](../测试与构建/) - [FEATURES.md](../../../FEATURES.md)(完整 88 个标志审计)