free-code-dotnet/docs/UI与扩展设计/UI与扩展设计-特性开关系统.md
应文浩wenhao.ying@xiaobao100.com e25ac591a7 init easy-code
2026-04-06 07:24:24 +08:00

10 KiB
Raw Blame History

特性开关系统

所属项目: free-code .NET 10 重写 所属模块: UI 与扩展设计 原始代码: scripts/build.ts feature() 编译时开关 + 运行时 GrowthBook


概述

特性开关系统控制 free-code 的 88 个功能标志,其中 54 个可编译、34 个损坏。原始实现有两个层次:编译时用 scripts/build.tsfeature() 函数注入 #define,运行时用 GrowthBook SDK 从云端拉取实验配置。

.NET 重写将这两个层次统一到同一套接口下。FeatureFlags 静态类持有全部 54 个常量,供编译时 #if 和运行时查询共同使用;IFeatureFlagService 抽象运行时查询;FeatureFlagServiceappsettings.jsonFeatureFlags 配置节读取初始值,取代原始 GrowthBook 的云端下发机制,确保零网络依赖。


18.1 FeatureFlags 常量清单

/// <summary>
/// 特性开关常量 — 编译时 #if + 运行时查询
/// 对应原始 88 个 feature flags54 可编译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 接口

/// <summary>
/// 运行时特性开关查询接口
/// </summary>
public interface IFeatureFlagService
{
    /// <summary>查询指定标志是否启用</summary>
    bool IsEnabled(string flag);

    /// <summary>动态设置标志状态(用于测试或运行时覆盖)</summary>
    void SetFlag(string flag, bool enabled);
}

18.3 FeatureFlagService 实现

/// <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

// 编译时完全消除死代码,减小二进制体积
#if ULTRAPLAN
services.AddSingleton<IUltraplanService, UltraplanService>();
#endif

#if VOICE_MODE
services.AddSingleton<IVoiceService, VoiceService>();
#endif

编译时标志通过 MSBuild 属性注入,对应原始 build.tsfeature() 函数:

<!-- Directory.Build.props -->
<PropertyGroup Condition="'$(FEATURE_ULTRAPLAN)' == 'true'">
  <DefineConstants>$(DefineConstants);ULTRAPLAN</DefineConstants>
</PropertyGroup>

构建命令示例:

# 全功能构建(对应原始 build:dev:full
dotnet build -p:FEATURE_ULTRAPLAN=true -p:FEATURE_VOICE_MODE=true ...

# 仅启用语音模式(对应原始默认构建)
dotnet build -p:FEATURE_VOICE_MODE=true

运行时模式(IsEnabled()

// 不重新编译就能切换,由配置文件驱动
if (_features.IsEnabled(FeatureFlags.TokenBudget))
{
    // 显示 token 预算组件
}

if (_features.IsEnabled(FeatureFlags.ExtractMemories))
{
    await _memoryService.TryExtractAsync(messages);
}

appsettings.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 注册

// Program.cs 或服务注册扩展方法
services.AddSingleton<IFeatureFlagService, FeatureFlagService>();

// 使用示例(构造函数注入)
public sealed class SomeService(IFeatureFlagService features)
{
    public void DoWork()
    {
        if (features.IsEnabled(FeatureFlags.AsyncToolExecution))
        {
            // 异步工具执行路径
        }
    }
}

参考资料