Skip to main content
作者:@benbrandt

电梯演讲

您提议进行什么更改?
允许代理为给定会话提供任意配置选择器列表。我们不仅可以支持模式或模型,还可以让每个代理更灵活地指定允许客户端向用户提供的配置。

现状

今天的事情是如何运作的,这导致了什么问题?我们为什么要改变?
目前,我们允许代理指定它们可以运行的模式列表。所选项目的状态可以被客户端代理修改。 明显的下一个选择器是模型选择器。然而,在实现这一点时,很明显,即使对于我们当前的代理,也不只是”你想要哪个模型?“这么简单,还有关于在推理参数方面给定模型的变体,可能更好地表达为另一个选择器。 添加更多硬编码的选择器选项可能会导致协议需要支持许多可选选项,或者实现者需要尝试找到最佳现有选择器来强行输入选项(如果没有明显的适合)。而且,如果几个月后,没有代理支持模式或推理选择器之类的东西,协议就会剩下没人真正使用的剩余方法,使界面变得混乱。 由于这个领域发展很快,我们理想地会找到一个足够约束的更灵活的选项,以允许客户端和代理都能对提供的选项进行推理。

我们提议对此做什么

您提议如何改善情况?
相反,我们可以允许代理在 session/new 响应中提供配置选项,这些选项不仅提供选项列表,还提供一个某种类型的 key,作为该选择器的唯一标识符。 此外,我们可以选择允许代理用语义类别标记每个选项,以便客户端可以可靠地广泛区分常见选项类型(例如模型选择器与会话模式选择器与思维/推理级别),而无需从选项 idname 推断含义。这仅用于 UX(例如键盘快捷键、图标、首选位置),并且不能要求正确性。 当客户端收到或发送对此选择器的更新时,它需要选择器 key 和新值的 key。 一开始,我们可以继续提供单值选择器(下拉菜单),但允许代理决定这些是什么。

美好的未来

一旦这个功能存在,事情将如何发展?
代理提供可用配置选项的列表。代理不能依赖客户端设置甚至显示这些选项,因为它可能不支持。因此,代理必须始终为它提供的每个选项提供一个默认配置值,并且必须能够在不设置这些配置选项的情况下运行一轮。 客户端可以渲染提供的选项,向代理发送更新值,并显示代理在执行过程中所做的任何更改(例如,如果它因为回退或策略变化而改变模式或模型,使用户始终可以看到当前状态)。 由于我们正在进入一个存在多个配置选项的世界,其中一些可能相互依赖,代理必须在更改时提供完整的配置选项及其当前值的集合。我们将用一些额外发送给客户端的数据进行权衡,以帮助最小化客户端需要管理的状态量。客户端将提交一个新值,并接收完整的配置选项状态,然后它可以替换其当前状态并渲染。所以如果更改模型意味着没有思维选项,或者新选项变得可用,或者另一个值需要更改(因为选项的值不同),代理将通过提供整个新状态来反映这一点(或者如果 somehow 是无效的选择则返回错误)。

实施细节和计划

告诉我更多关于您的实施。您的详细实施计划是什么?
一开始,我们可以基于 Session Modes api 来实现。 类似于这样的 InitializeResponse
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "sessionId": "sess_abc123def456",
    "configOptions": [
      {
        "id": "mode", // 这是用于传达正在使用哪个选项的唯一 `key`
        "name": "Session Mode", // 人类可读的选项标签
        "description": "客户端向用户显示的可选描述。"
        "category": "mode",
        "type": "select",
        "currentValue": "ask",
        "options": [
          {
            "value": "ask",
            "name": "Ask",
            "description": "在进行任何更改之前请求许可"
          },
          {
            "value": "code",
            "name": "Code",
            "description": "使用完整工具访问编写和修改代码"
          }
        ]
      },
      {
        "id": "models",
        "name": "Model",
        "category": "model",
        "type": "select",
        "currentValue": "ask",
        "options": [
          {
            "value": "model-1",
            "name": "Model 1",
            "description": "最快的模型"
          },
          {
            "value": "model-2",
            "name": "Model 2",
            "description": "最强大的模型"
          }
        ]
      }
    ]
  }
}

选项类别(可选)

每个顶级配置选项可以包含一个可选的 category 字段。这旨在帮助客户端广泛区分常见选择器并提供一致的 UX(例如,将键盘快捷键附加到给定类别的第一个选项)。 除了 category 之外,客户端应该使用代理提供的 configOptions 数组的顺序作为建立优先级和解决平局的主要方式。例如,如果多个选项共享相同的 category,客户端可以在分配键盘快捷键或决定哪些选项最突出地显示时偏好列表中第一个匹配的选项。 category 是语义元数据,不能要求正确性。客户端必须优雅地处理缺失或未知的类别。 _ 开头的类别名称可自由用于自定义使用。以 _ 开头的类别名称保留给 ACP 规范。 建议的枚举:
  • mode - 会话模式选择器
  • model - 模型选择器
  • thought_level - 思维/推理级别选择器
  • 任何以 _ 开头的字符串 - 自定义类别(例如 _my_custom_category
当我们引入这个时,我们还可以允许分组选项,以防单个选择器的选项有逻辑子标题和分组。
{
  "id": "models",
  "name": "Model",
  "currentValue": "ask",
  "type": "select",
  "options": [
    {
      "group": "Provider A",
      "options": [
        {
          "value": "model-1",
          "name": "Model 1",
          "description": "最快的模型"
        }
      ]
    },
    {
      "group": "Provider B",
      "options": [
        {
          "value": "model-2",
          "name": "Model 2",
          "description": "最强大的模型"
        }
      ]
    }
  ]
}
我们使用所有这些的对象列表,以确保配置选项和可能值在不同语言中的一致排序(无论是否保留排序)。 对于分组选项,需要探索分组和未分组选项是否可以交错,或者是否需要限制为一种模式(可能是后者)。 对于从客户端和代理更新值,它将遵循与 session modes 相同的模式,但有一个额外的 key。
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "session/set_config_option",
  "params": {
    "sessionId": "sess_abc123def456",
    "configId": "mode",
    "value": "code"
  }
}
而对此请求的响应将返回带有当前值的完整配置选项列表。
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "configOptions": [
      {
        "id": "mode",
        "name": "Session Mode",
        "type": "select",
        "currentValue": "ask",
        "options": [..]
      },
      {
        "id": "models",
        "name": "Model",
        "type": "select",
        "currentValue": "ask",
        "options": [..]
      }
    ]
  }
}
通知也将返回带有当前值的完整配置选项列表。
{
  "jsonrpc": "2.0",
  "method": "session/update",
  "params": {
    "sessionId": "sess_abc123def456",
    "update": {
      "sessionUpdate": "config_option_update",
      "configOptions": [
        {
          "id": "mode",
          "name": "Session Mode",
          "type": "select",
          "currentValue": "ask",
          "options": [..]
        },
        {
          "id": "models",
          "name": "Model",
          "type": "select",
          "currentValue": "ask",
          "options": [..]
        }
      ]
    }
  }
}
我们可能还会将会话模式移动为在 @deprecated 中,以支持这种方法。在移除之前,我们可能希望代理支持两个字段,然后如果客户端使用新的配置选项,应该只使用提供的配置选项而不使用 modes 字段以避免重复。 配置选项还将采用一个 type 字段来指定客户端显示的不同输入形式。如果客户端收到它不识别的选项,它应该忽略它。由于代理需要有一个默认值,它可以优雅地忽略该选项,代理应该无论如何处理它。客户端还应该将选项列表视为由代理优先排序。因此,如果由于某种原因代理提供的选项多于客户端合理可以显示的选项,客户端应该显示尽可能多的选项,从列表开头开始。 我们将开始只支持 select,并根据需要扩展到其他类型。

常见问题

在编写本文档的过程中或随后的讨论中出现了哪些问题?

您考虑了哪些替代方法,为什么选择这一个?

如前所述,Zed 团队已经研究并实现了模型选择器的实验性支持。 然而,这已经与我们上周对 Codex CLI 建模其模型选择器的方式不同,因此似乎合理的是,根据协议的核心设计原则,仅在客户端需要呈现忠实 UX 时限制代理实现。最大化代理在迭代新范式的最佳方式时的灵活性似乎是关键,目前尚不清楚客户端是否从知道选择的是什么类型中受益。 我们在内部最初讨论过一个更接近这个提案的设计,但后来又放弃了,认为这会对客户端有所帮助。然而,正如我们现在与多个代理实现打交道的那样,目前尚不清楚这是否真的对客户端有所帮助,而允许更多灵活性似乎是可取的。

连接级配置选项呢?

这个 RFD 只关心会话级配置,对于这些配置,似乎合理地要求代理可以始终选择一个默认值,并且在继续之前不需要客户端的输入。 在设置代理时似乎还需要另一种类型的配置选项(即提供程序选项、插件等),这些选项更持久,可能需要代理在能够创建会话之前就需要。这些需要在初始化阶段或附近的地方解决,超出这个 RFD 的范围。

多值选择器呢?或复选框?或者 在此处插入您喜欢的输入选项

这是我们应该讨论的问题,即对于第一个版本我们要引入多少复杂性,以及我们希望通过客户端功能如何以允许更多选项类型在未来表达这个。

修订历史

  • 2025-10-29:初始草稿
  • 2026-01-09:添加选项类别
  • 2026-01-15:允许类别扩展