# 特性开关系统
> 所属项目: 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 个标志审计)