271 lines
10 KiB
Markdown
271 lines
10 KiB
Markdown
# 特性开关系统
|
||
|
||
> 所属项目: 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
|
||
/// <summary>
|
||
/// 特性开关常量 — 编译时 #if + 运行时查询
|
||
/// 对应原始 88 个 feature flags(54 可编译,34 损坏)
|
||
/// </summary>
|
||
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
|
||
/// <summary>
|
||
/// 运行时特性开关查询接口
|
||
/// </summary>
|
||
public interface IFeatureFlagService
|
||
{
|
||
/// <summary>查询指定标志是否启用</summary>
|
||
bool IsEnabled(string flag);
|
||
|
||
/// <summary>动态设置标志状态(用于测试或运行时覆盖)</summary>
|
||
void SetFlag(string flag, bool enabled);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 18.3 FeatureFlagService 实现
|
||
|
||
```csharp
|
||
/// <summary>
|
||
/// 运行时特性开关实现
|
||
/// 编译时使用 #if FLAG_NAME,运行时使用 IsEnabled()
|
||
/// 对应原始 GrowthBook SDK 的本地替代
|
||
/// </summary>
|
||
public sealed class FeatureFlagService : IFeatureFlagService
|
||
{
|
||
private readonly ConcurrentDictionary<string, bool> _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<IUltraplanService, UltraplanService>();
|
||
#endif
|
||
|
||
#if VOICE_MODE
|
||
services.AddSingleton<IVoiceService, VoiceService>();
|
||
#endif
|
||
```
|
||
|
||
编译时标志通过 MSBuild 属性注入,对应原始 `build.ts` 的 `feature()` 函数:
|
||
|
||
```xml
|
||
<!-- Directory.Build.props -->
|
||
<PropertyGroup Condition="'$(FEATURE_ULTRAPLAN)' == 'true'">
|
||
<DefineConstants>$(DefineConstants);ULTRAPLAN</DefineConstants>
|
||
</PropertyGroup>
|
||
```
|
||
|
||
构建命令示例:
|
||
|
||
```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<IFeatureFlagService, FeatureFlagService>();
|
||
|
||
// 使用示例(构造函数注入)
|
||
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 个标志审计)
|