- 桌面应用
- 开发者工具
- 人工智能
- AI 应用
- AI Agent
- 代码智能体
【免费下载链接】warp
Warp is an agentic development environment, born out of the terminal.
导读
在 Warp 的编排(Orchestration)配置界面中,plan card 与 run_agents 确认卡允许用户为子 Agent 选择执行引擎(Harness)与模型。本指南以 QUALITY-643 技术规格为核心,深入剖析该功能的两大痛点——harness 选择器硬编码、模型选择器始终展示 Warp 内部 LLM 目录——以及对应的六项改造方案,覆盖从HarnessAvailabilityModel数据源、共享 picker 逻辑,到 Claude Code 的ANTHROPIC_MODEL环境变量与 Codex 的~/.codex/config.toml模型写入的完整调用链。读完本文,你将掌握 Warp 如何在桌面端与 Web 端保持一致、如何让第三方 harness CLI 真正收到所选模型,以及本地/云执行模式下模型传递的差异与安全约束。
一、背景:编排配置 UI 的 Harness 与模型选择现状
1.1 两张共享逻辑的卡片
编排配置 UI 由两个界面构成:
- plan card(
app/src/ai/document/orchestration_config_block.rs):用于规划阶段配置子 Agent 的执行参数; - run_agents 确认卡(
app/src/ai/blocklist/inline_action/run_agents_card_view.rs):在真正启动子 Agent 前进行最终确认。
两张卡片共用app/src/ai/blocklist/inline_action/orchestration_controls.rs中的选择器逻辑,包括populate_harness_picker()(约 L380)、populate_model_picker_for_harness()(约 L314)、is_model_in_filtered_choices()(约 L353)、first_filtered_model_id()(约 L368)、sync_picker_selections()(约 L508)与matches_harness_filter()(约 L299)。
1.2 两个核心问题
规格文档明确指出现状的两个缺陷:
- Harness 选择器硬编码为
[Oz, Claude, Codex]——既没有读取服务端下发的availableHarnesses列表,也没有包含 Gemini,更不尊重管理员在组织设置中的启用/禁用状态; - 模型选择器始终展示 Warp 内部 LLM 目录,并按 Provider 过滤(Anthropic 对应 Claude、OpenAI 对应 Codex)。这些内部 ID(如
claude-4-6-opus-high)无法被第三方 harness CLI 识别。服务端其实维护着各 harness 各自的模型目录,桌面客户端也已经在HarnessAvailabilityModel中拉取并缓存了这些数据,但编排 UI 一直没有使用。
1.3 模型 ID 无法送达子进程
除了 UI 层的问题,模型 ID 还存在两条“断链”:
- Claude Code收不到
ANTHROPIC_MODEL环境变量; - Codex通过
~/.codex/config.toml传递模型,但这只适合云端/远程环境(文件系统隔离),本地子 Agent 绝不能触碰它——详见app/src/pane_group/pane/local_harness_launch.rsL143 附近的注释,本地 Codex 子进程必须依赖用户已有的本地配置,否则会覆盖用户全机器的~/.codex/config.toml并与并行 Agent 产生竞态。
二、数据源:HarnessAvailabilityModel与展示元数据
2.1 服务端目录的客户端缓存
app/src/ai/harness_availability.rs中的HarnessAvailabilityModel是单例(SingletonEntity),负责向服务端拉取并缓存可用 harness 列表:
pub struct HarnessAvailability { pub harness: Harness, pub display_name: String, pub enabled: bool, pub available_models: Vec<HarnessModelInfo>, } pub struct HarnessModelInfo { pub id: String, pub display_name: String, pub reasoning_level: Option<String>, }其核心 API 包括:
available_harnesses()→&[HarnessAvailability]:完整 harness 列表(harness、display_name、enabled、available_models);models_for(harness)→Option<&[HarnessModelInfo]>:某 harness 的模型列表(id、display_name),当列表为空时返回None;is_harness_enabled(harness)→bool:判断某 harness 是否被管理员启用;- 变更时发出
HarnessAvailabilityEvent::Changed事件。
值得注意的缓存策略(源码依据):
- 缓存键为
"AvailableHarnesses",序列化写入 private user preferences(fn cache/fn get_cached); - 服务端尚未响应时使用
default_harnesses()兜底:仅含Harness::Oz(enabled=true),保证 UI 在预取阶段即可用; - 网络从离线恢复(
NetworkStatusEvent::NetworkStatusChanged)、登录完成(AuthManagerEvent::AuthComplete)、团队变更(UserWorkspacesEvent::TeamsChanged)都会触发refresh()重新拉取; refresh()成功后若列表有变化会使缓存失效并发出HarnessAvailabilityEvent::Changed,供 UI 重新填充选择器;normalize_harness_display_names()会强制将 Oz 的 display_name 固定为客户端侧的harness_display::display_name()返回值。
此外,should_show_harness_selector()依据FeatureFlag::AgentHarness且已知 harness 数量 > 1 来决定是否展示 harness 选择器;has_any_enabled_harness()则用于判断是否至少存在一个可用的 harness(影响云 Agent 能否启动,配合cloud_agent_start_blocker的NoEnabledHarnesses判定)。
2.2 展示元数据:图标、品牌色与显示名
app/src/ai/harness_display.rs为每个Harness变体提供统一的展示元数据,确保多个 UI 面(下拉框、会话侧栏)不会漂移:
pub fn display_name(harness: Harness) -> &'static str { match harness { Harness::Oz => "Warp Agent", Harness::Claude => "Claude Code", Harness::OpenCode => "OpenCode", Harness::Gemini => "Gemini CLI", Harness::Codex => "Codex", Harness::Unknown => "Unknown", } }icon_for()返回对应的品牌图标(Oz→Agent、Claude→ClaudeLogo、Codex→OpenAILogo、Gemini→GeminiLogo),brand_color()返回品牌色调(Claude 橙色、Gemini 蓝色、OpenAI 色),并提供了圆形象底(circle_background)与圆上图标填充色(icon_fill_on_circle)等更精细的渲染辅助。
三、改造方案一:Harness 选择器改为服务端数据驱动
3.1 替换硬编码列表
populate_harness_picker()(orchestration_controls.rs)中原本硬编码的[Harness::Oz, Harness::Claude, Harness::Codex]迭代(L392 附近)将被替换为读取HarnessAvailabilityModel::as_ref(ctx).available_harnesses()。
对每个HarnessAvailability条目:
- 图标使用
harness_display::icon_for()与harness_display::brand_color()(已覆盖包括 Gemini 在内的所有变体); - 标签使用
harness_display::display_name()(服务端的display_name字段也可用,但客户端名称与服务端返回前保证非空,且二者一致); - 若
!entry.enabled:渲染为禁用项(不可选中、文字置灰)。MenuItemFieldsAPI 支持.with_disabled(true),并可为禁用原因附加 tooltip; - 已启用条目排在禁用条目之前。
从源码看,该改造在populate_harness_picker的 row→menu item 映射中已有雏形:row.disabled_reason为Some(reason)时调用with_disabled(true).with_tooltip(reason),否则注册harness_changed(row.id)选中动作——改造的关键是把 row 的来源从硬编码清单切换为harness_snapshot背后的服务端数据。
3.2 订阅变更事件
两张卡片视图需要同时订阅HarnessAvailabilityEvent::Changed,在服务端列表更新时重新填充 harness 选择器。这一订阅与两张卡片已有的LLMPreferencesEvent::UpdatedAvailableLLMs订阅并存,相互独立。
对应行为清单:PRODUCT.md behaviors 1–5(harness 列表来源、品牌图标、禁用态、排序、选中项被禁用时的保留与提示)。
四、改造方案二:模型选择器切换为 Harness 专属模型 + "Default model" 入口
4.1 按执行模式与 Harness 分支
populate_model_picker_for_harness()需要新增is_local: bool参数(或直接传入当前RunAgentsExecutionMode),使选择器具备执行模式感知能力,替代当前按 Provider 过滤LLMPreferences的逻辑:
let harness = Harness::parse_orchestration_harness(harness_type); match harness { Some(Harness::Oz) | None => { // 维持现状:LLMPreferences 按 Provider 过滤 } Some(Harness::Codex) if is_local => { // 本地 Codex:只有 "Default model" 入口(无法传递模型) } Some(harness) => { // 1. 始终在最前加 "Default model" 入口(值为空字符串) // 2. 读取 HarnessAvailabilityModel::as_ref(ctx).models_for(harness) // 3. Some(models):每个 HarnessModelInfo 追加为菜单项, // display_name 作标签,id 作为 model_changed 动作值 // 4. None:仅展示 "Default model"(加载中/空状态) } }"Default model" 入口使用标签"Default model"并发出A::model_changed(String::new())(空字符串),与 Web UI 的行为一致(Web 端同样以值""添加该入口)。
4.2 各类 Harness 的模型目录内容
结合 PRODUCT.md 的行为定义:
- Oz(或未设置):展示 Warp LLM 目录(单 Agent 模式下同一套模型),此行为不得回归;
- Claude Code:顶部
"Default model",其后为服务端提供的 Claude Code 模型目录(如best、opus、sonnet、haiku、opus (1M context)、sonnet (1M context),以及opus 4.7、sonnet 4.6等 pinned 版本); - Codex(Cloud 模式):顶部
"Default model",其后为服务端提供的 Codex 模型目录(如default、GPT-5.5、GPT-5.4、GPT-5.4 mini)。注意其中default条目与服务端约定为“不写 model 键”,与 "Default model" 的实际效果相同但为 Codex 专属语义; - Codex(Local 模式):只展示
"Default model"。理由已在 1.3 节说明:Codex CLI 从全局共享的~/.codex/config.toml读取模型,子 Agent 写入会破坏用户已有配置并与并行 Agent 竞争; - Gemini(当前对编排禁用):遵循同一模式——顶部
"Default model",其后是服务端提供的 Gemini 模型(如有)。
每个模型条目展示服务端目录中的display_name,原始id(如"opus"、"gpt-5.4")则作为选中后的 model_id 存下来并最终传给 harness 进程。harness 模型条目不带 Provider 图标或模型规格侧边信息(这些元数据不存在)。
4.3 同步改造三处辅助函数
is_model_in_filtered_choices():非 Oz harness 时,将 model_id 对照HarnessAvailabilityModel::models_for()校验,或接受空字符串(对应 "Default model" 入口);本地 Codex 时仅空字符串有效;first_filtered_model_id():非 Oz harness 时返回Some(String::new())(即 "Default model" 入口)作为默认值;sync_picker_selections():非 Oz harness 时从HarnessAvailabilityModel::models_for()查找 display_name(而非LLMPreferences),并将空 model_id 映射为 "Default model" 标签。
4.4 执行模式切换与 Harness 变更时的重置
当执行模式在 Local 与 Cloud 之间切换时,HarnessChanged/ExecutionModeToggled处理器必须重新填充模型选择器——因为 Codex 的可用模型取决于执行模式。用户切换 harness 时 model_id 会重置(各 harness 模型目录互不相交):非 Oz harness 重置为 "Default model"(空字符串),Oz 重置为第一个可用的 Warp LLM;唯一的例外是空字符串本身("Default model")可以在非 Oz harness 之间保留。
对应行为清单:behaviors 6–10(各 harness 模型目录内容)、11–14(默认值、重置、加载/空状态)、15–16(跨 UI 一致性、sync 逻辑)。
五、改造方案三:双卡片订阅 Harness 模型到达事件
两张卡片视图(orchestration_config_block.rs、run_agents_card_view.rs)已有LLMPreferencesEvent::UpdatedAvailableLLMs订阅,需要新增对HarnessAvailabilityModel的订阅:
- harness 列表变化时 → 重新填充harness 选择器(behaviors 1–5);
- harness 模型目录到达时 → 重新填充模型选择器,但仅当当前 harness 为非 Oz 时执行(behavior 15)。
这一机制同时覆盖两种边界情况(PRODUCT.md behaviors 13–14):
- 加载态:用户切换到非 Oz harness 时模型目录尚未拉取完,选择器只显示 "Default model";目录到达后自动补全完整列表,并保持 "Default model" 处于选中状态;
- 空态:服务端返回空模型列表时,选择器只显示 "Default model",model_id 保持为空。
六、改造方案四:向本地 Claude Code 子进程传递ANTHROPIC_MODEL
6.1 环境变量注入点
app/src/pane_group/pane/local_harness_launch.rs的prepare_local_harness_child_launch()(L159)新增model_id: Option<String>参数,在基于task_env_vars()构建出env_vars后:
- Claude:合并
harness_model_env_vars(harness, model_id.as_deref())的返回值,即当 model_id 非空时设置ANTHROPIC_MODEL; - Codex:本地子进程不注入任何模型覆盖——UI 层保证本地 Codex 的 model_id 为空(behavior 8),因此此处无需动作;现有代码也刻意跳过
prepare_codex_environment_config()(L143 附近的注释说明:本地 Codex 必须依赖用户已有的本地认证与会话状态,不能运行共享的 Codex 环境准备流程,否则会向~/.codex/auth.json播种OPENAI_API_KEY并重写全机器的~/.codex/config.toml)。
6.2harness_model_env_vars的底层实现
app/src/ai/agent_sdk/driver/harness/mod.rs的harness_model_env_vars()(L473)实现如下关键语义:
// 文档注释:我们使用 ANTHROPIC_MODEL 环境变量而非 --model CLI 标志, // 因为环境变量是最可靠的机制,且能避免与 Claude Code settings.json 的优先级冲突。 let Some(model_id) = third_party_harness_model_config .map(|config| config.model_id.as_str()) .filter(|id| !id.is_empty()) else { return env_vars; }; match selected_harness { Harness::Claude => { env_vars.insert(OsString::from("ANTHROPIC_MODEL"), OsString::from(model_id)); } Harness::Oz | Harness::OpenCode | Harness::Gemini | Harness::Codex | Harness::Unknown => {} }即在本地 Claude 子进程启动时(prepare_local_harness_child_launch中env_vars.extend(harness_model_env_vars(harness, harness_model_config.as_ref())),L272–275),模型 ID 会进入隐藏子 pane 的环境;而对 Codex 等其它 harness 返回空 map。
6.3 调用点更新
terminal_pane.rs的launch_local_harness_child()(L1302 附近)中调用prepare_local_harness_child_launch()(L1325 附近)时需传入model_id值。注意terminal_pane.rs中apply_child_model_id_override目前只设置 Oz 的 LLM 偏好——这正是需要被新链路补充/修正的部分。
对应行为清单:behaviors 17(Claude Code 模型送达)、20("Default model" 时不注入任何覆盖)。
七、改造方案五:Cloud/Remote 路径下写入 Codex 的config.toml
7.1 写入规则
app/src/ai/agent_sdk/driver/harness/codex.rs的prepare_codex_config_toml()(L815)新增模型处理:在set_codex_openai_base_url()之后——
- 若
model_id为Some(id)且 id 非空、且不等于"default":写入doc["model"] = toml_edit::value(id); - 否则(
None、空串或字面量"default"):执行doc.remove("model"),清除任何既有键。
对应常量CODEX_MODEL_KEY: &str = "model"(L580)。set_codex_model()的过滤条件!id.is_empty() && *id != "default"与set_codex_model_reasoning_effort()的reasoning_level处理一同构成完整的模型写入逻辑。
7.2 附带逻辑:模型迁移条目与启动提示抑制
set_codex_model()在写入model键后还会调用set_codex_model_migration()(L924)——Codex 的 TUI 会在会话启动时提示用户升级旧模型,即使model键已被固定。通过在[notice.model_migrations]表中按所选模型 ID 盖章迁移条目(将迁移目标映射到自身,如gpt-5.4 = "gpt-5.4")可抑制该提示。规格注释特别说明:客户端不维护模型版本清单,因此无需随 Anthropic/OpenAI 淘汰模型而发布客户端更新。
7.3 测试证据
codex_tests.rs中已有与新行为完全对应的测试用例:
prepare_codex_config_toml_writes_model_when_specified:非 default 模型 ID 写入顶层model键,并断言notice.model_migrations中的自映射条目;prepare_codex_config_toml_writes_model_migration_for_older_model:旧模型同样写入并盖章迁移条目;prepare_codex_config_toml_skips_model_for_default_sentinel:字面量"default"哨兵表示“让 Codex 自选默认模型”,不得写入model键或任何迁移条目;prepare_codex_config_toml_skips_model_when_none:未提供模型 ID 时不写入model键或[notice.model_migrations]条目;prepare_codex_config_toml_writes_model_reasoning_effort_when_specified与..._removes_stale_model_reasoning_effort_when_none:model_reasoning_effort的写入与清理。
规格还要求更新codex_tests.rs:201附近的既有测试——该测试原用于验证预先存在的model键会被保留,需改为验证新的写/删行为;同时所有用例都必须保证既有的非模型键(openai_base_url、projects、mcp_servers)在任何情况下都被保留。
7.4 调用链
codex.rs中build_runner()调用prepare_codex_environment_config()时需透传model_id;local_harness_launch.rs无需改动——本地 Codex 子进程不调用该函数、也不接收模型覆盖。
对应行为清单:behavior 18(Cloud 模式写入 /default不写 / Local 模式不写)。
八、改造方案六:远程启动路径无需改动
远程启动路径已通过StartAgentExecutionMode::Remote { model_id }(run_agents_to_start_agent_mode())将model_id传给服务端。当 UI 开始存储 harness 原生 ID(或 "Default model" 对应的空字符串)后,服务端无需任何翻译即可收到正确值。Claude Code 远程 Agent 的模型由服务端注入(对应ANTHROPIC_MODEL),Codex 远程 Agent 的模型在云端环境内的~/.codex/config.toml写入(云端文件系统隔离,因此写入安全)。
对应行为清单:behaviors 17(remote)、18(remote)。
九、测试与验证策略
9.1 单元测试计划
orchestration_controls 新增测试:
- harness 选择器从
HarnessAvailabilityModel填充;禁用 harness 可见但不可选(behaviors 1–4); populate_model_picker_for_harness(harness="claude"):顶部 "Default model",其后为HarnessAvailabilityModel中的模型(behavior 7);- (harness="codex",cloud 模式):顶部 "Default model",其后为 Codex 模型(behavior 8);
- (harness="codex",local 模式):仅 "Default model"(behavior 8);
- (harness="oz"):维持 Warp LLM 目录(behavior 6);
is_model_in_filtered_choices:非 Oz harness 时 Warp ID 返回 false、空字符串返回 true(behavior 12);first_filtered_model_id:非 Oz harness 返回空字符串(behavior 11);- harness 从 Claude(model="opus")切到 Oz:模型重置为第一个 Warp LLM(behavior 12)。
local_harness_launch 新增测试:
- harness 为 Claude 且提供 model_id 时,
ANTHROPIC_MODEL合并进 env_vars(behavior 17); - model_id 为 None 或空时,不设置
ANTHROPIC_MODEL(behavior 20)。
codex_tests.rs 更新测试(如 7.3 节所列)覆盖写键、"default"删除、None删除与既有键保留(behaviors 18、20)。
orchestration_config_tests:既有测试必须继续通过(behavior 21——matches_active_config对 harness 专属 model_id 与 Warp model_id 一视同仁,按请求 model_id 与配置 model_id 的精确字符串匹配决定是否自动启动)。
9.2 预提交检查
规格要求 PR 前运行cargo fmt、cargo clippy与./script/presubmit。
9.3 手工验证清单
- 打开 plan card 上的编排配置 → harness 选择器展示 Oz、Claude Code、Codex(服务端若返回 Gemini 也会出现,可能处于禁用态);
- 选中 Claude Code → 模型选择器顶部为 "Default model",其后为
best、opus、sonnet等; - 选中 Codex(Cloud 模式)→ 顶部 "Default model",其后为
default、GPT-5.5、GPT-5.4等; - 选中 Codex(Local 模式)→ 仅 "Default model";
- 选中 Codex 后将执行模式从 Local 切到 Cloud → 模型选择器补全为完整 Codex 目录;
- 选中 Oz → 模型选择器回到 Warp LLM 目录;
- 将 harness 从 Claude(选中 "opus")切到 Oz → 模型重置;
- 本地启动 Claude Code + "opus" → 子进程环境中存在
ANTHROPIC_MODEL=opus; - 本地启动 Claude Code + "Default model" → 子进程环境中无
ANTHROPIC_MODEL; - 云端启动 Codex + "gpt-5.4" → 云环境内
~/.codex/config.toml中存在model = "gpt-5.4"; - 云端启动 Codex + "default" →
~/.codex/config.toml中无model键。
十、并行化建议:不推荐并行
规格文档明确建议不进行并行化:各改动间耦合紧密——harness 选择器(改造一)与模型选择器(改造二)在orchestration_controls.rs中共享状态,卡片视图的订阅(改造三)同时依赖两个选择器,启动侧改动(四、五)又依赖从选择器流出的 model_id 格式正确。总范围约 7 个文件、每处中等规模改动,非常适合由单个 Agent 顺序执行。
附:相关源码路径速查
| 关注点 | 文件 |
|---|---|
| 共享选择器逻辑 | orchestration_controls.rs |
| plan card 视图 | orchestration_config_block.rs |
| run_agents 确认卡 | run_agents_card_view.rs |
| Harness 可用性与模型数据 | harness_availability.rs |
| Harness 展示元数据 | harness_display.rs |
| 模型环境变量注入 | harness/mod.rs |
| 本地 harness 子进程启动 | local_harness_launch.rs |
| 本地子进程调用点 | terminal_pane.rs |
| Codex config.toml 写入 | codex.rs |
| Codex 配置测试 | codex_tests.rs |
| 产品行为规格 | PRODUCT.md |
- 桌面应用
- 开发者工具
- 人工智能
- AI 应用
- AI Agent
- 代码智能体
【免费下载链接】warp
Warp is an agentic development environment, born out of the terminal.
相关推荐
HCCL 环境变量 HCCL_INTRA_PCIE_ENABLE 详解:Server 内 PCIe 通信链路的选择与配置
HCCL 环境变量 HCCL_INTRA_PCIE_ENABLE 详解:Server 内 PCIe 通信链路的选择与配置 本篇技术指南聚焦 CANN/HCCL(
通信高性能计算人工智能AscendCANNCAI 环境变量完全参考:从模型选择到 CTF、记忆与安全护栏的配置实战
CAI 环境变量完全参考:从模型选择到 CTF、记忆与安全护栏的配置实战 CAI(Cybersecurity AI)以环境变量作为核心配置入口,本指南基于 do
人工智能AI Agent网络安全渗透测试工具调用AI 评测Wolverine环境变量配置详解:OPENAI_API_KEY与模型选择终极指南
Wolverine环境变量配置详解:OPENAI_API_KEY与模型选择终极指南 想要让你的Python脚本拥有自我修复能力吗?Wolverine正是这样一个
AI 应用开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考