# 模型角色系统指南 Flyto Agent Engine 的模型角色抽象和管理系统. ## 目录 - [设计目标](#设计目标) - [四种角色](#四种角色) - [默认映射](#默认映射) - [ModelRegistry API](#modelregistry-api) - [自定义角色映射](#自定义角色映射) - [非预置模型支持](#非预置模型支持) - [定价与成本计算](#定价与成本计算) - [Prompt Caching](#prompt-caching) - [Extended Thinking](#extended-thinking) ## 设计目标 1. **不硬编码模型 ID** -- 业务逻辑不直接引用 `claude-sonnet-4-6` 等具体 ID,而是引用角色 `RoleMain` 2. **角色抽象** -- 不同用途使用不同模型,通过角色映射统一管理 3. **运行时可修改** -- 线程安全的 ModelRegistry,支持运行时切换模型 4. **第三方模型** -- 支持注册任意模型配置,不限于预置模型 ## 四种角色 | 角色 | 常量 | 用途 | 典型场景 | |------|------|------|---------| | **Main** | `config.RoleMain` | 主对话模型 | 用户直接交互,工具调用决策,代码生成 | | **Fast** | `config.RoleFast` | 快速/廉价模型 | 上下文压缩摘要,工具结果摘要,命令分类 | | **Thinking** | `config.RoleThinking` | 深度思考模型 | 复杂推理,安全分析,架构决策 | | **Embed** | `config.RoleEmbed` | 嵌入模型 | 语义搜索(预留,当前未使用) | ### 角色使用场景 ``` 用户输入 "重构这个模块" │ ├── RoleMain ──► 理解任务、规划步骤、调用工具、生成代码 │ ├── RoleFast ──► 压缩旧对话历史为摘要(节省成本) │ 工具输出过长时生成简短摘要 │ └── RoleThinking ──► 分析复杂的重构方案(如果启用 extended thinking) ``` ### 成本对比 以 100K input tokens + 10K output tokens 为例: | 角色 | 默认模型 | 输入成本 | 输出成本 | 总计 | |------|---------|---------|---------|------| | Main | claude-sonnet-4-6 | $0.30 | $0.15 | $0.45 | | Fast | claude-haiku-4-5 | $0.08 | $0.04 | $0.12 | | Thinking | claude-sonnet-4-6 | $0.30 | $0.15 | $0.45 | Fast 模型用于摘要等内部任务,节省约 73% 的成本. ### SubAgent Fork 模式的成本优势 子 Agent 从独立 Engine 实例改为共享 prompt cache 的 fork 模式(`pkg/engine/subagent.go`),显著降低子 Agent 调用成本. **核心机制**:API 层传完整工具列表(和父 agent 一样),确保 cache key 一致.实际执行时用 `canUseTool` 函数限制可用工具集. **经济账**(以 Sonnet 定价计算): | 项目 | 独立 Engine(旧) | Fork 模式(新) | |------|-------------------|-----------------| | 系统提示 10K tokens | $0.03(全量付费) | $0.003(缓存读取 10%) | | 多余工具描述 3K tokens | 不需要 | $0.009(多传但缓存命中) | | **每次子 agent 净成本差** | | **省 ~$0.018** | 一个复杂任务调用 10 个子 agent = 省 $0.18.长时间多轮对话中,子 agent 调用越多,fork 模式优势越明显. **安全性不降低**:`canUseTool` 在运行时拦截,子 agent 请求被禁工具会收到错误消息,模型会自己换工具. ## 默认映射 ```go var DefaultRoles = map[ModelRole]string{ RoleMain: "claude-sonnet-4-6", RoleFast: "claude-haiku-4-5", RoleThinking: "claude-sonnet-4-6", } ``` 默认不设置 `RoleEmbed`,因为当前记忆系统使用文本相似度搜索而非向量嵌入. ## ModelRegistry API ### 创建 ```go // 使用默认配置 registry := config.NewModelRegistry() // 查看当前映射 fmt.Println(registry) // ModelRegistry{main=claude-sonnet-4-6, fast=claude-haiku-4-5, thinking=claude-sonnet-4-6} ``` ### 查询 ```go // 按角色获取模型 ID modelID := registry.GetRole(config.RoleMain) // "claude-sonnet-4-6" // 获取模型配置 cfg := registry.GetConfig("claude-sonnet-4-6") fmt.Println(cfg.ContextWindow) // 200000 fmt.Println(cfg.MaxOutputTokens) // 16384 fmt.Println(cfg.SupportsCaching) // true fmt.Println(cfg.SupportsThinking) // true // 便捷方法:按角色获取配置 cfg = registry.GetConfigForRole(config.RoleFast) // claude-haiku-4-5 的配置 // 获取上下文窗口(未知模型返回默认值 200000) window := registry.ContextWindow("unknown-model") // 200000 ``` ### 修改 ```go // 切换 Main 角色到 opus registry.SetRole(config.RoleMain, "claude-opus-4-6") // 切换 Fast 角色 registry.SetRole(config.RoleFast, "claude-3-5-haiku") ``` ### 在 Engine 中使用 ```go agent, err := engine.New(&engine.Config{ Model: "claude-sonnet-4-6", // 向后兼容:设置 RoleMain Models: registry, // 完整的角色系统 APIKey: os.Getenv("ANTHROPIC_API_KEY"), Cwd: ".", }) ``` ## 自定义角色映射 ### 通过配置文件 在 `settings.json` 中配置(计划中的功能): ```json { "models": { "main": "claude-opus-4-6", "fast": "claude-haiku-4-5", "thinking": "claude-opus-4-6" } } ``` ### 通过代码 ```go registry := config.NewModelRegistry() // 高端配置:Main 用 Opus,Fast 用 Haiku registry.SetRole(config.RoleMain, "claude-opus-4-6") registry.SetRole(config.RoleFast, "claude-haiku-4-5") registry.SetRole(config.RoleThinking, "claude-opus-4-6") // 经济配置:全部用 Haiku registry.SetRole(config.RoleMain, "claude-haiku-4-5") registry.SetRole(config.RoleFast, "claude-haiku-4-5") registry.SetRole(config.RoleThinking, "claude-haiku-4-5") ``` ## 非预置模型支持 ModelRegistry 允许注册任意第三方模型.`SetRole` 不要求模型已注册(允许使用未预置的模型 ID),但如果需要精确的成本计算和上下文窗口管理,应先注册模型配置. ### 注册第三方模型 ```go registry := config.NewModelRegistry() // 注册一个兼容 API 的第三方模型 registry.Register("my-custom-model", &config.ModelConfig{ ID: "my-custom-model", ContextWindow: 128000, MaxOutputTokens: 8192, InputPrice: 1.0, // 每百万 token(美元) OutputPrice: 5.0, CacheReadPrice: 0.1, CacheWritePrice: 1.25, SupportsCaching: false, SupportsThinking: false, SupportsImages: false, }) // 设置为主模型 registry.SetRole(config.RoleMain, "my-custom-model") ``` ### 使用非预置模型的注意事项 1. **上下文窗口** -- 未注册模型的 `ContextWindow()` 返回默认值 200000.如果实际窗口更小,可能触发过早的压缩或超限错误 2. **成本估算** -- 未注册模型的 `EstimateCost()` 使用 Sonnet 默认定价,成本报告可能不准确 3. **Prompt Caching** -- `SupportsCaching` 为 false 时,引擎不会添加 cache_control 标记 4. **Extended Thinking** -- `SupportsThinking` 为 false 时,引擎不会发送 thinking 配置 ## 定价与成本计算 ### 预置模型定价 | 模型 | 输入 | 输出 | 缓存读 | 缓存写 | |------|------|------|--------|--------| | claude-opus-4-6 | $15.00/M | $75.00/M | $1.50/M | $18.75/M | | claude-sonnet-4-6 | $3.00/M | $15.00/M | $0.30/M | $3.75/M | | claude-haiku-4-5 | $0.80/M | $4.00/M | $0.08/M | $1.00/M | 价格单位:每百万 token(美元). ### 成本估算 API ```go // 完整成本估算(含缓存) cost := registry.EstimateCost( "claude-sonnet-4-6", 100000, // inputTokens 10000, // outputTokens 50000, // cacheReadTokens 20000, // cacheWriteTokens ) // cost = 100000*3/1M + 10000*15/1M + 50000*0.3/1M + 20000*3.75/1M // = 0.30 + 0.15 + 0.015 + 0.075 = $0.54 // 简化版(不含缓存,向后兼容) cost := registry.EstimateSimpleCost("claude-sonnet-4-6", 100000, 10000) // = $0.45 ``` ### 通过 Prompt Caching 节省成本 启用 Prompt Caching 后,重复的系统提示和工具描述可从缓存读取(10% 原价),首次写入缓存多付 25%. 以 Sonnet 4.6 为例,系统提示 10K tokens 在 10 轮对话中的成本: | | 无缓存 | 有缓存 | |--|-------|--------| | 第 1 轮 | $0.03 | $0.0375(写入缓存) | | 第 2-10 轮 | $0.27 | $0.027(缓存读取) | | **总计** | **$0.30** | **$0.0645** | 节省 78%. ## Prompt Caching ### 机制 通过在 API 请求的 system content block 上添加 `cache_control` 标记,标记静态内容为可缓存. ```go // API 请求中的系统提示块 blocks := []api.SystemContentBlock{ { Type: "text", Text: "...静态的系统提示词...", CacheControl: &api.CacheControl{Type: "ephemeral"}, // 标记为可缓存 }, { Type: "text", Text: "...动态的环境信息...", // 不缓存 }, } ``` ### 缓存边界 引擎自动识别系统提示中的静态和动态部分: ``` ┌─────────────────────────────────┐ │ 角色定义 + 行为准则 (静态) │ ← cache_control: ephemeral │ 工具使用指南 (静态) │ │ Git 安全协议 (静态) │ │ 代码质量准则 (静态) │ ├─────────────────────────────────┤ │ 环境信息 (动态: cwd, git) │ ← 不缓存 │ 技能列表 (会话级动态) │ │ 用户追加提示 (动态) │ └─────────────────────────────────┘ ``` ### Beta Headers Prompt Caching 需要对应的 beta header: ```go req := &api.MessageRequest{ Beta: &api.BetaFeatures{ PromptCaching: true, // prompt-caching-2024-07-31 PromptCachingScope: "global", // prompt-caching-scope-2025-02-19 }, } ``` ## Extended Thinking ### 配置 ```go req := &api.MessageRequest{ Thinking: &api.ThinkingConfig{ Type: "enabled", BudgetTokens: 10000, // thinking token 预算 }, Beta: &api.BetaFeatures{ ExtendedThinking: true, // extended-thinking-2025-01-24 }, } ``` ### Effort / Fast Mode 通过 BetaFeatures 控制模型的推理深度: ```go // 低 effort(快速响应) Beta: &api.BetaFeatures{ Effort: "low", // effort-2025-04-01 } // Fast Mode(更激进的加速) Beta: &api.BetaFeatures{ FastMode: true, // fast-mode-2025-04-01 } ``` | 模式 | Effort | 说明 | |------|--------|------| | 默认 | (无) | 标准推理深度 | | 低 | `low` | 减少推理深度,更快响应 | | 中 | `medium` | 适中推理深度 | | 高 | `high` | 最深推理,最慢但最准确 | | 快速 | FastMode | 极速模式,跳过部分推理 | ### 模型能力矩阵 | 模型 | Caching | Thinking | Images | |------|---------|----------|--------| | claude-opus-4-6 | Yes | Yes | Yes | | claude-sonnet-4-6 | Yes | Yes | Yes | | claude-haiku-4-5 | Yes | No | Yes | | claude-haiku-3-5 | Yes | No | Yes | | claude-3-5-sonnet | Yes | No | Yes | | claude-3-opus | Yes | No | Yes | 代码位置:`pkg/config/models.go`