OpenRouter Decisions 契约
Frevana 包装 M1 客户端契约:O/F/L 与失败语义
2026-11-07 · 建议入库路径:docs/openrouter-decisions.md。范围仅官方 OpenRouterTeam/skills 的 openrouter-decisions。本稿可冻结请求构造、响应校验及失败语义;Frevana 服务端透传兼容、鉴权获取路径和真实 Host 行为未验,不宣称已完成线上契约验收。
整体边界见 Decisions 架构。
1. 来源与契约级别
| 标识 | 含义 |
|---|---|
| F:Frevana 声明 | 本地完整 OpenAPI 和 slice 可直接证实的 endpoint、security、headers、200 |
| O:官方形状 | Owner 提供的官方 SKILL 与 references 快照;用于定义兼容目标,Frevana OpenAPI 没有声明其 body/response 字段 |
| L:本地策略 | 本仓客户端主动收窄、校验、门控与诊断规则,不是服务端承诺 |
| UNKNOWN | 需要实现核查或授权实验,当前不可作为可用能力依赖 |
Frevana operation:POST /openrouter/v1/decisions,operationId=OpenRouterCompatibleController_decisions,tag=OpenRouter Compatible,security=bearer(HTTP Bearer/JWT)。requestBody 缺省,不是“声明了空 JSON body”;responses 仅列 200、没有 schema。 不能从这里推导所有官方字段均已透传。
官方接口为 POST https://openrouter.ai/api/alpha/decisions;本项目只用 POST https://api.frevana.com/openrouter/v1/decisions 做推理。不把 messages/chat/completions、Responses 或 System One 路由拿来替代。本轮不实现其它 17 个官方 skills。
2. 官方与包装差异
| 项目 | 官方 skill / OpenRouter | Frevana 本项目 |
|---|---|---|
| 推理目标 | /api/alpha/decisions | F:/openrouter/v1/decisions |
| Authorization | OpenRouter API key | F:Bearer JWT;按本任务约定为 Frevana 用户/会话凭据 |
| JSON body | O:model + state + questions | 采用 O 的兼容目标,F 无 schema,需验证 |
| answers / usage | O:typed answers 与使用信息 | 按 O 校验;无 F schema,不预设外层 envelope |
| 归因 | 官方脚本不提供这组 Frevana 头 | F:5 个可选归因头,去重发送 |
| 选模 | 公开 models catalog | 开发阶段仍读公开 catalog;F 无 decisions models endpoint |
| 传输脚本 | 官方 HTTP/SDK 直连官方推理 | 改编为仅 Frevana;不保留官方默认推理 URL 和 key 环境变量 |
| 错误 | 官方可有错误规范 | F 本地规格未列,客户端以 HTTP 状态和 unknown 错误体处理 |
3. 请求字段表
以下请求字段均为 O(Frevana OpenAPI 未声明);“必需”表示本稿兼容契约要求,不是 F schema 的 required。
| 字段 | 类型 / 约束 | 本轮处理 |
|---|---|---|
model | 非空 string,实际在线请求必需 | L:只收公开 catalog 确认的 canonical_slug 或版本化 id;禁止 alias;CLI 可补齐配置,但发出 body 必须有 model |
state | string / JSON object / JSON array,必需 | 优先具名 object,只发问题需要的字段;null、number、boolean 作为顶层 state 不合法 |
questions | 非空 object,key 为应用自定 question ID | 一题一个 primitive;key 用于结果匹配,不靠 key 向模型传达语义 |
questions.<id>.type | noul / choice / score / box,必需 | 严格判别联合,不接受 chat 类型 |
questions.<id>.instructions | string 或 JSON object/array,必需 | L:默认写清楚的字符串;问事实,不要求生成解释或操作指令 |
questions.<id>.criteria | 见下表 | 与 instructions 同向;不包含 API 参数或业务阈值 |
session_id | 可选 string | 本地子集接受;F 透传及分组效果未验,不是登录会话或归因头替代品 |
user | 可选 string | 本地子集接受;不得冒充鉴权主体;默认省略,F 透传未验 |
trace、provider | O 参考提到的观测/路由扩展,具体结构本稿不冻结 | 官方捆绑脚本不接收;L:首轮拒绝,未来明确 schema 且核验 Frevana 后另评审 |
assets | O 图像扩展 map | 首轮本地子集拒绝,不因官方可用就宣称 F 支持;后续按图像能力单独评审 |
images、messages、temperature 等 | 不在本轮 Decisions 请求子集 | L:拒绝,不能原样套用 chat body |
首轮顶层 allowlist 为 model/state/questions/session_id/user;question 只接受 type/instructions/criteria。未知请求字段报本地 ValidationError,避免被静默丢弃。JSON 结构必须可序列化;函数、循环引用和非有限数不作为有效输入。
| primitive | criteria 请求形状 | 语义与适用条件 |
|---|---|---|
noul | 可省略;提供时恰好 {"true": criterion, "false": criterion} | 独立二元事实 P(yes);多标签可各问一题,不把中间概率读作中等强度 |
choice | option-name → criterion 的 object,至少两项 | 互斥选一;criterion 通常为字符串或 JSON 结构;官方脚本还允许 null,L 首轮要求明确非 null 描述 |
score | 有序 criterion array,至少两级,低到高 | 一个维度的有序程度;不是金额、数量或算术计算器 |
box | 不允许 criteria | 指令指明目标与图像;所给官方快照限定 Jev 1.14,当前可用模型/版本须另查能力与探针 |
criterion/instructions 的 JSON 结构指 object/array 或 string,不是任意 boolean/number/null。choice 的 null 例外仅为上游脚本能力差异,首轮不使用。
图像与 box 的覆盖边界
四 primitive 均纳入契约设计,首轮 App 只演示文本 noul(choice 可选)。将来图像输入优先用顶层 state array:字符串说明旁放 {"type":"image_url","image_url":{"url":"data:image/png;base64,..."}},参考说明远程 http(s) 图像 URL 不会被拉取。不同模型的格式、尺寸与 token 限制不可互相套用,F 对图像的兼容和大小限制 UNKNOWN。
box 即使目标不存在也可能返回四条边;必须同请求配一个存在性 noul,仅在 yes 分支读取坐标。no 分支为无匹配,中间区 fallback。不要把非空 box 当存在证据。本轮不发送图像或 box 探针。
4. 响应字段表与运行时校验
以下也全部为 O 兼容目标(Frevana OpenAPI 未声明),数值示例不是 Frevana 实测。接收 HTTP 200 后仍将 body 视作 unknown,从顶层开始校验;不调用 .choices[0].message。
| 字段 | 形状与要求 | 本地处理 |
|---|---|---|
id | 可选 string | 有则可用于关联;不可作为幂等承诺 |
model | 必需 string,响应的实际 build | 记录并核对请求 pin;不匹配则阻断门控 |
provider | 可选 string | 缺省不捏造 |
answers | 必需 object,question ID → answer | L:键集合与请求完全一致;缺失/多出/错误 type 都是 ProtocolError |
usage | 必需 object(本地兼容目标) | 缺失时报告协议不兼容,不填零 |
usage.input_tokens / output_tokens | number | L:有限非负数,记录计量;不凭响应反推出原始 state |
usage.cost | 可选 number,O 说明单位 USD | 有限非负;缺省为未知,不能当 0;不等于已确认的 Frevana 结算金额 |
每个 answer 必须有与 question 相等的 type,主结果字段不可省略:
| answer.type | 必需主结果 | 可选字段 | 本地校验 |
|---|---|---|---|
noul | noul: number | probabilities、confidence(如存在) | noul 为有限 [0,1];不以 confidence 覆盖 noul |
choice | choice: string | probabilities: Record<string, number>、confidence | choice 必须在请求 criteria;有 probabilities 时 key 必须与选项对应,值有限 [0,1] |
score | score: number | probabilities、legend: Record<string, criterion>、confidence | n 级时 L 限于 [0,n−1];map key 对应 "0"…"n−1";legend 保持原 criteria 的结构 |
box | box: {top,bottom,left,right},各 number | confidence | L:坐标有限 [0,1],top≤bottom、left≤right;原点为左上角 |
probabilities、confidence、legend 是可选,读取前判存在;不存在不等于零。confidence 若有则要求有限 [0,1],它描述分布集中程度,不是正确率或行动授权。score 是层级位置的概率加权值,不据此反推精确数值。概率分布如需和为 1 的检查,实施时写明浮点容差,不能要求每个浮点结果精确相等;不得默默归一化后继续决策。
响应未知的附加字段允许保留为脱敏诊断,不让其参与业务;已知字段形状错误不能忽略。不凭空兼容 {data: ...}、SSE 或其它 envelope,观察到差异后先修订契约再适配。请求与响应都从 unknown 校验;缺答案、类型错、非 JSON、错误模型或越界值走错误/fallback,绝不默认批准。
5. 头字段、归因与鉴权
| Header | 必需性 / 来源 | 含义与约束 |
|---|---|---|
Authorization: Bearer <FREVANA_SESSION_JWT> | F security 必需;凭据语义按 Owner 上下文 | Frevana 用户/会话 JWT;不能用 OpenRouter key、catalog key、Docs Basic 账号替换 |
Content-Type: application/json | L 发送要求 | JSON body |
x-frevana-agent-app-instance-id | F 可选 string | 当前用户拥有的 App instance/team ID;vibe- 前缀可表示 vibe session。缺省不记录 Agent App 用量归因 |
x-frevana-agent-run-id | F 可选 string | run ID,文档说明写入 usage.run_id |
x-frevana-agent-channel-id | F 可选 string | 执行通道/会话标识,文档说明写入 channel_id |
x-frevana-agent-job-id | F 可选 string | Job ID,文档说明写入 job_id;hello 无 Job,不伪造 |
x-frevana-agent-trigger-type | F 可选 string | manual/scheduled/workflow/audit 是示例,不是 OpenAPI enum;hello 可传 manual |
OpenAPI 对五个归因头各定义两次,共十条 parameter。客户端按不区分大小写的 header name 去重,每名只发送一个值,合并描述而不发逗号拼接双份。其它归因值不从用户自由文本/state 提取,也不能靠 getRootPath 推导。API 允许可选不等于本项目完成用量追踪:正式归因验收必须有合法实例 ID 及服务端记录证据;缺 ID 时明确标“无归因”,不填假值。trace/session_id/user 不替代这组归因头。
Bearer 来源与传输路径:UNKNOWN
Host 文档只证实存在 auth shim 注入步骤与 frevana-auth-request 分发,没有在已读资料中给出可采用的认证方法签名。既不能断言 JWT 已注入页面,也不能断言必然要走 Host 代理。window.frevana.app.getRootPath、agent/files 等存在不证明认证链路可用。
设计首选 Host 受信侧代理加入 Bearer 和归因,App 注入一个本地 DecisionTransport。若之后确认有适合页面的受限凭据接口,再评估浏览器直发;必须取得实际 iframe 的 CSP/CORS/preflight 与刷新/退出证据。未取得合法机制时返回 AuthUnavailable,禁止在线提交。不得发明 window.frevana.auth.getToken() 或 frevana.fetch(),不得读 userData、Application Support、auth-preload、SQL 或账号文件“补齐认证”,不得将 token 打入 VITE_*、持久化或日志。
探测顺序:只读核对 shim/handler 签名与校验 → 记录 Host 版本和受信上下文 → 隔离 App 验方法与资源/网络能力 → 另经授权做一次最小 Frevana 付费请求,验证实际 response/model/归因。OPTIONS 不是推理验收;空 POST 也可能执行或收费。本轮全部停留在文档核查,无真实推理请求。
6. 合法样例:官方形状下的客户端输入
下面可通过拟议本地校验;不代表已在 Frevana 调通。模型字符串引用所给官方响应快照中的版本格式,不是本轮选定或确认仍在售的模型;正式运行须用实时公开 catalog 确认的 pin 替换。本例不提供可直接误运行的 curl。
目标为 POST https://api.frevana.com/openrouter/v1/decisions。头:Authorization 的值由受信传输层临时提供,Content-Type 为 application/json,可选 trigger-type=manual;其它归因头仅在有真实上下文时填写。
{
"model": "typesafe/jev-1.13-20260917",
"state": {
"message": "我被重复扣款了,请退还多扣的费用。"
},
"questions": {
"requests_refund": {
"type": "noul",
"instructions": "`message` 的发送者是否在请求退还已支付的费用?",
"criteria": {
"true": "发送者明确希望退还已经支付或被扣除的费用。",
"false": "发送者只咨询退款政策、查询到账状态,或明确拒绝退款。"
}
}
}
}仅供离线协议测试的对应 response fixture(合成数值,未实测):
{
"model": "typesafe/jev-1.13-20260917",
"answers": {
"requests_refund": { "type": "noul", "noul": 0.82 }
},
"usage": { "input_tokens": 100, "output_tokens": 8 }
}可选字段被省略是合法情况,不能因为缺 confidence/cost 就补零。该 0.82 只能用于演示解析与分支,不是定阈值证据。
7. 非法样例:应在本地拒绝,不发网络
{
"model": "typesafe/jev-1.13-20260917",
"state": { "message": "我想了解退款政策。" },
"questions": {
"requests_refund": {
"type": "noul",
"instructions": "`message` 的发送者是否请求退还已支付费用?",
"criteria": { "yes": "请求退款", "no": "仅咨询政策" }
}
}
}非法点唯一且明确:noul.criteria 提供时必须是字符串键 true 和 false,不能是 yes/no。预期 ValidationError 指向 questions.requests_refund.criteria,不出站、不产生默认回答。不能断言 Frevana 对此一定返回 400,因为 F 没有错误 schema。
8. 官方七步 → 契约与验收
| 步骤 | 本项目要求 | 验收证据 |
|---|---|---|
| 1 判断 vs 计算 | 退款意图为判断;金额运算、日期比较、计数、ID/路径抽取、确定政策留代码;规则已定结果则跳过模型 | policy 的规则表与空输入分支 |
| 2 primitive | noul 独立事实,choice 互斥,score 有序,box 定位;可共存标签不强塞 choice | 每题选型理由与四形状离线 fixture |
| 3 最小 state | 只发问题读取的 message/上下文;不发根目录、凭据、UI 状态或只供代码规则使用的字段 | state builder 字段审查 |
| 4 questions | instructions 和 criteria 含义完整,问事实、避免双重否定;question key 不承担说明;运行时选项用名字不靠数组位置 | 人工仅看 state+question 能答;无其它题依赖 |
| 5 选模 + API | 开发时公开 catalog 筛选能力/上下文/价格/provider,pin canonical_slug 或版本 id;推理只到 Frevana | catalog 时间戳/摘要、pin、实际响应 model;本轮尚缺在线证据 |
| 6 门控 | 阈值为命名常量,保留 raw probabilities;所有分支含 fallback;confidence 不等于正确/授权 | 可复核真值表和边界测试 |
| 7 探针后信阈值 | 代表性样本、含否定和攻击、留出集;改模型/问题/输入构造即重跑 | 原始 request/answer、预期路由、模型、延迟、成本、阈值来源;本轮不运行 |
退款示例可暂用 REFUND_YES_MIN=0.7、REFUND_NO_MAX=0.3 作为未校准演示常量,不是生产结论:
| 前提 / 输出 | 代码结果 |
|---|---|
| 空输入或代码规则直接确定 | 本地提示/已定结果,跳过请求 |
| 合法回答 p≥YES_MIN | 展示“退款请求”建议;不实际退款 |
| 合法回答 p≤NO_MAX | 展示“其它请求” |
| NO_MAX < p < YES_MIN | 人工判断 fallback |
| 鉴权/HTTP/超时/取消/协议或模型不匹配 | 展示明确错误/取消;不能当作 no 或 yes |
没有 fallback 的真正二分逻辑可从官方建议的 0.5 起步,但也须探针。choice 或 box 与 presence noul 配对时仅在 yes 分支消费;独立问题的概率不要求互补,不跨 primitive/模型复用阈值。noul 接近 0.5 是不确定,不是中度退款意图。
9. M1 冻结清单与后续验证
本稿冻结:官方形状下的本地请求 allowlist、四 primitive 的区别、逐问题响应匹配、Frevana 单推理目标、去重归因头、无合法认证即禁提交、原始概率与门控分离、合法/非法例的本地预期。
M1 评审可通过文档冻结;以下仍 OPEN,不因评审通过自动关闭:Frevana 对 body/可选字段/四 primitive/response 的实际兼容;JWT 获取与刷新;Host 代理或浏览器直发选择;实例归因链;模型可用性与版本保持;CORS/CSP;错误体/计费/重试语义。首轮仅 noul 在线验证也不能宣称其它 primitive 已实测。没有付费探针时阈值仍未校准。
10. 参考
本地依据(Owner 打包快照,2026-11-07):openrouter-decisions-SKILL.md(sha256 32e7f139e70b…0b65fc)、decisions-api.md(b9dba95c2a2a…9d1071d)、question-design.md(046243042511…2d17c2)、models.md(64f6083b79cb…8e24b39)、decision-model-limits.md(7981f8a7cb63…7495804);Frevana decisions OpenAPI slice(6a3a1e355221…fe8b219)与完整 OpenAPI 本地副本 .tmp-verify/frevana-openapi-full.json(sha256 前缀 454d5f6fe702bff8248f)。快照目录:/tmp/codex-arch-decisions/context/。外置资源与构建交付遵循 前端栈,其 UNKNOWN 不在本稿降级。整体边界见 Decisions 架构。
官方来源:openrouter-decisions、Decisions API。脚本改编仅限 lib.ts 的校验骨架与 decide.ts、models.ts 的职责参考;2026-11-07 只读核对相对浮动 main,上游 revision 未 pin,实施时锁 commit/license;不复制默认直连推理行为。Frevana Docs UI 与 OpenAPI 本轮使用本地副本,未进行 Basic 登录。