OpenRouter presets 怎么用:把一整套参数收成一个名字

2026-08-18

一、它解决的是”改一个参数要发一次版”

如果你的服务里散着好几处调用大模型的代码,大概率也散着好几份几乎一样的配置:模型名写死在常量里,系统提示塞在某个字符串模板里,provider 的路由偏好在另一个封装函数里被硬编码。想把某处的模型换一版,或者把系统提示改两句,流程就变成了改代码、过评审、发版本。更麻烦的是同一份配置在 Python 后端和 TypeScript 前端各抄了一遍,改的时候还得记得两边都改。

OpenRouter 的 preset 就是冲着这件事去的。官方文档《Presets》页把它的定位写得很直白:preset 让你把 LLM 配置从代码里分离出来,在 OpenRouter 的网页应用里创建和管理,然后在 API 请求里引用。文档举的例子是给不同用途各建一个:email-copywriterinbound-classifiercode-reviewer

按该页列举,一个 preset 可以管理六类设置:provider 路由偏好(按价格、延迟等排序)、模型选择(单个模型或带回退的模型数组)、系统提示、生成参数(temperaturetop_p 等)、provider 的包含/排除规则,以及 tools——包括 OpenRouter 的服务端工具,文档点名了 web search、advisors 和 subagents。

这篇只讲一件具体的事:preset 里到底存了什么结构,以及当请求里也带了同名字段时,谁盖过谁。

二、前置条件

账号与密钥。 你需要一个 OpenRouter 账号和一个 API key。文中所有示例里的密钥都写成环境变量 $OPENROUTER_API_KEY,不要把 key 直接写进代码或命令历史。

创建入口在网页端。 官方文档写明 preset 通过 OpenRouter 的网页应用创建和管理,给出的地址是 openrouter.ai/settings/presets,新建页是 openrouter.ai/settings/presets/new。我们没有登录过这个页面,因此这里只转述文档给出的路径,不描述它长什么样。

组织账号的可见范围。 文档在”Other Notes”里写明:如果用的是组织账号,所有成员都能访问组织的 presets。也就是说 preset 是工作区级别的共享资源,不是个人私有配置——改动会影响到引用同一个 slug 的所有调用方,这一点在多人协作时要先说清楚。

slug 是标识。 从 API 建 preset 时,路径参数 {slug} 是”URL-safe identifier”。文档写明:如果工作区里已存在同 slug 的 preset,会创建一个新版本并把它指定为 active version;不存在则新建一个 preset。

三、按文档写明的步骤走一遍

步骤 1:把一个跑通的请求体存成 preset

除了在网页端建,官方文档还提供了从推理请求体直接建 preset 的做法——文档自述这适合”把一个已知可用的请求捕获成可复用配置,而不必在 UI 里重新敲一遍”。每种推理 skin 各有一个端点:

端点对应的推理 skin
POST /api/v1/presets/{slug}/chat/completionsChat Completions
POST /api/v1/presets/{slug}/messagesAnthropic Messages
POST /api/v1/presets/{slug}/responsesResponses

用法是把你本来要发给对应推理路由的同一份 JSON body,改发到这个端点。文档给出的 Chat Completions 例子原样如下:

curl https://openrouter.ai/api/v1/presets/email-copywriter/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o",
    "temperature": 0.7,
    "provider": { "sort": "price" },
    "messages": [
      { "role": "system", "content": "You are a helpful assistant." },
      { "role": "user", "content": "Write a marketing email." }
    ]
  }'

这里的 openai/gpt-4o 只是官方文档写这页时用的示例值。平台上有哪些模型、哪些端点随时在变,别把它当成推荐清单或可用清单来用。

这一步实际改了什么:文档写明 OpenRouter 只持久化与 preset config 重叠的字段,并以「e.g.」的口吻举了 modeltemperatureprovidertop_psystemtools——这是举例不是封闭清单,别把它当成”只有这六个字段会被存”的保证。反方向的那句写得更死一些:messagesinputpromptstream 这类瞬时字段会被静默忽略。所以上面那段 body 里的 messages 数组不会被存进去,只有配置字段和从中抽取的系统提示会留下。

三种 skin 的系统提示来源不同,这点容易踩:Chat Completions 是从 messages 里抽 system 消息;Anthropic Messages skin 用的是顶层 system 字段;Responses skin 用的是 instructions 字段。三者最终都落到 preset 的同一个 system_prompt 上。

Windows 侧的差别。 上面这条命令是官方文档里的 bash 写法。在 Linux/macOS 的 shell 里可以直接粘。在 Windows 上,如果你用的是 PowerShell,curl 默认是 Invoke-WebRequest 的别名、且单引号内的 JSON 引号处理与 bash 不同,直接粘会报错;可行的做法是显式调用 curl.exe,或把 JSON 存成文件后用 -d "@body.json"。用 Git Bash / WSL 则与文档写法一致。这一段是通用的命令行常识,不是 OpenRouter 官方文档的内容,以你本机 curl --help 的实际输出为准。

步骤 2:在请求里引用它

文档写明有三种引用方式,都是官方文档里的原样示例:

把 preset 当模型名用(Direct Model Reference):

{
  "model": "@preset/email-copywriter",
  "messages": [
    { "role": "user", "content": "Write a marketing email about our new feature" }
  ]
}

用独立的 preset 字段:

{
  "model": "openai/gpt-4",
  "preset": "email-copywriter",
  "messages": [
    { "role": "user", "content": "Write a marketing email about our new feature" }
  ]
}

把两者写在一起:

{
  "model": "openai/gpt-4@preset/email-copywriter",
  "messages": [
    { "role": "user", "content": "Write a marketing email about our new feature" }
  ]
}

后两种写法里请求同时带了 model,谁生效由下一节的覆盖规则决定。

步骤 3:看清返回的字段结构

三个创建端点都返回带”designated version”的 preset 对象。文档给出的响应形状是这样嵌套的:

  • data.id / data.name / data.slug / data.status / data.created_at / data.updated_at
  • data.designated_version_id
  • data.designated_version:内含 idversionsystem_promptconfigcreated_atupdated_at

关键在于 configsystem_prompt 是分开的两层:系统提示单独占一个字段,其余配置(示例里是 modeltemperatureprovider)收在 config 对象里。你在做配置审计时,要比对的是 designated_version.config 这一层,而不是 preset 顶层。

四、覆盖优先级:普通字段是覆盖,tools 是并集

这是 preset 最容易想当然的地方,官方文档分两处写,规则并不一样。

普通字段——浅合并。 文档写明:如果你在请求里提供了参数,它们优先于 preset 的值;两者是 shallow-merged(浅合并),意思是请求级字段覆盖 preset 中同名字段,而请求里没出现的 preset 字段被保留。所以 preset 更像是一层默认值,请求是补丁。

浅合并这个词值得多留意一句:文档只说明了顶层字段级别的覆盖,对 provider 这类嵌套对象内部的键是否逐键合并,官方文档没有说明这一点。稳妥的做法是——凡是你要在请求里覆写的嵌套对象,就把它整体写完整,别指望只写其中一个键、其余从 preset 继承。

tools 数组——并集加按 identity 覆盖。 这条是例外,文档单独用一节写了它:当引用 preset 的请求自己也发了 tools 时,两个数组取并集,并且请求里的工具会覆盖 preset 中同一 identity 的工具。identity 按工具种类分三类判定:

工具种类identity 的判定依据
openrouter:advisoropenrouter:subagent类型 + parameters.name
Function tools函数名
其它 OpenRouter 服务端工具(如 openrouter:web_search单例,按类型判定

顺序上,文档写明 preset 的 tools 保持它们原有的相对顺序,请求里独有的工具追加在后面。

advisor 和 subagent 按”类型 + 名字”判定这一条很实用:文档写明这两类工具可以在一个 preset 里出现多次、一个条目对应一个具名实例,所以一个 preset 可以定义一整班具名的 worker,而请求可以只覆盖其中某一个具名实例、保留 preset 里其余的。文档给的例子里,一个 subagent 条目长这样(节选自官方示例):

{
  "type": "openrouter:subagent",
  "parameters": {
    "name": "ideator",
    "model": "anthropic/claude-fable-5",
    "instructions": "You brainstorm creative directions, naming, and copy. Return several distinct options."
  }
}

同样,里面的模型值只是官方文档的示例值,不构成模型清单。文档还写明:任何引用 @preset/{slug} 的客户端都会在服务端被应用上这些 tools,不需要 SDK 或编排代码,之后增删、调整工具也不用动客户端。

版本这一层。 文档在两处提到版本:一处说版本历史会保留,便于了解改动并回滚;同一处紧接着写”通过 API 寻址一个 preset 时,永远使用最新版本”。另一处是创建端点,新版本会被”designated as the active version”,响应里也带 designated_version_id。把这两处放在一起看:通过 API 引用 slug 拿到的是当前生效的那一版,回滚要在版本历史那一侧做。至于推理请求里能不能显式指定某个历史版本号,官方文档没有说明这一点——SDK 文档里的 getVersion 是按 slug 加版本号读取某一版的内容,不是让推理请求走某一版。

五、边界

  • preset 的可用范围:《Presets》这一页在落盘时没有出现 betapreviewexperimentaldeprecated 的标注。但页内引用到的 advisor、subagent 等服务端工具各有自己的文档页,它们的状态要以各自那一页为准,别因为能写进 preset 就当成稳定能力。
  • 数量与配额:一个工作区能建多少 preset、preset config 有多大限制,本文不涉及此类会变动的数值,也请以官方定价与用量说明页为准。
  • 组织内的权限粒度:文档只写了组织成员都能访问组织 presets,谁能改、谁只能读,官方文档没有说明这一点
  • 跨 skin 的字段兼容:三个创建端点接受三种不同 skin 的 body,但它们最终写进的是同一份 preset config。某个 skin 特有的字段是否也能落进 config,文档只举了几个重叠字段(modeltemperatureprovidertop_psystemtools)并且明确写成举例,举例之外的字段会怎样,官方文档没有说明这一点。想确认某个字段有没有被存,只能建完之后回读一遍。

以上组合出的用法,均为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。该平台迭代频繁,端点、字段与合并规则随版本变动,请以官方文档最新内容为准。

六、怎么验证配对了

按官方 SDK 文档,presets 一共有七个操作:listgetcreatePresetsChatCompletionscreatePresetsMessagescreatePresetsResponseslistVersionsgetVersion。做验证主要用后面几个读取类的:

  1. 确认 preset 存在且内容是你想要的get 按 slug 取回 preset,文档写明它会把当前 designated version 内联返回。重点看 designated_version.configsystem_prompt 两处,确认你以为存进去的字段真的存进去了——尤其是从请求体建 preset 时,被静默忽略的瞬时字段不会有任何报错提示。
  2. 确认新版本确实生成了listVersions 按 slug 列出所有版本,文档写明按版本号升序(最旧在前)。重复 POST 同一个 slug 之后,这里应当多出一版。
  3. 确认某一版的具体内容getVersion 接受 slugversion 两个参数,用来核对历史版本改了什么。
  4. 确认请求侧的覆盖真的生效:先只发 @preset/{slug} 不带任何参数发一次,再带上你要覆盖的字段发一次,比对返回中的生成结果元数据。这一步是按上面那条浅合并规则推演出的验证方法,不是官方文档给出的验证步骤。

list 会列出该账号下的所有 preset,文档写明按最近更新时间倒序。SDK 文档给每个操作都列了错误状态表,读起来有两点值得记:读取类操作统一列了 400、401、500,其中按 slug 取的三个(getlistVersionsgetVersion)多一个 404——slug 拼错时会落在这里,而不是静默回退到默认配置;三个创建端点的表更长,除上述之外还列了 403 与 404,以及一个 409。这几个状态码分别在什么条件下触发,SDK 页只给了错误类型名(如 errors.ConflictResponseError),官方文档没有说明这一点——所以写客户端时别按猜出来的语义分支处理,先把错误体原样打出来看。

真要说 preset 最值钱的地方,不是省了几行配置代码,而是把”改配置”这件事从发版流程里摘了出来,同时保留了版本历史。代价是配置不在代码仓库里,改动不走 code review——所以谁能改、改了通知谁,得靠你自己的流程补上。


本文依据 OpenRouter 官方文档(openrouter.ai/docs)于 2026-08-18 的公开内容整理。 该平台闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观与运行表现的任何描述。 该平台的供应商、模型与路由策略随时变动,文中不列具体供应商名单与模型清单; 价格、额度与限流的具体数值请以官方定价页与用量说明为准。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。