- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
导读
本文讲解 Operit 中“思考质量(Thinking Quality)”档位从 provider 内部私有映射走向统一公开契约的改造方案。核心是ThinkingQualityMapping数据模型与ThinkingQualityMappingRegistry注册表:它们同时服务于请求构建与 UI 渲染,让 Android 与 Web 两端能够读取同一份“控制类型、参数名、每档显示值与 wire value”的描述。读完本文,你将掌握该映射契约的完整数据结构、规则解析与模型匹配逻辑、JSON 配置编写方式,以及displayLabel与类型化wireValue分离设计的工程动机。
背景:旧实现的三个痛点
在引入统一映射契约之前,OpenAI、Gemini、DeepSeek、NVIDIA、SiliconFlow 和 OpenRouter 等 provider 的“思考程度”映射是分散且私有的:
- 每个 provider 在自己的请求构建代码内部保存“全局档位 → 请求参数”的映射表;
- UI 层无法读取同一份描述,只能显示笼统的全局数字档位;
- 由于映射定义不在公共位置,请求构建与界面展示之间存在重复描述、容易漂移,也无法为不同模型提供差异化的真实档位文本。
这意味着用户看到的“档位数字”与请求里真正下发的参数值之间隔着一段不可见、不可校验的逻辑。新的映射契约正是为了解决这三个问题而设计:一份定义,两端(请求构建 + UI)共用。
核心契约:ThinkingQualityMapping数据结构
映射契约的核心实现在 ThinkingQualityMapping.kt。整个契约由以下类型协同组成:
控制类型:ThinkingQualityControl
internal enum class ThinkingQualityControl { LEVELS, TOGGLE_ONLY, UNSUPPORTED }三种取值语义如下:
| 取值 | 含义 |
|---|---|
LEVELS | provider 支持多档程度参数,UI 渲染离散滑块 |
TOGGLE_ONLY | provider 没有程度参数、只有开关,UI 只显示开关 |
UNSUPPORTED | provider/模型不支持思考控制,UI 不显示相关控件 |
契约中特别强调:没有程度参数的 provider 必须显式声明TOGGLE_ONLY,绝不交由 UI 去猜测档位含义。这是“显式优于隐式”的契约约束,避免 UI 对未知 provider 臆造档位。
wire value 类型化:ThinkingQualityWireValue
internal sealed interface ThinkingQualityWireValue { data class Text(val value: String) : ThinkingQualityWireValue data class Number(val value: Int) : ThinkingQualityWireValue data object Omitted : ThinkingQualityWireValue }不同 provider 的思考参数值形态差异很大:OpenAI 系是字符串(low/high),Gemini 的 thinkingBudget 是整数(如1024/8192),某些场景还会省略值。因此 wire value 采用密封类型,区分Text、Number与Omitted,保证内部请求构建时能拿到类型正确的参数值,而不是统一字符串化后再由各 provider 各自转换。
选项与动作:ThinkingQualityOption/ThinkingQualityJsonAction
internal data class ThinkingQualityOption( val id: String, val displayLabel: String, val wireValue: ThinkingQualityWireValue, val actions: List<ThinkingQualityJsonAction> = emptyList(), ) internal data class ThinkingQualityJsonAction( val path: String, val value: Any?, val overwrite: Boolean = false, )id:内部档位标识(如low、high、8192),也是 UI 与请求构建之间传递的“选中项”契约值;displayLabel:展示给用户看的文本;wireValue:真正写入请求的类型化值;actions:选中该档位时需要额外写入的 JSON 路径动作(支持嵌套路径与overwrite语义)。
映射主体:ThinkingQualityMapping
internal data class ThinkingQualityMapping( val control: ThinkingQualityControl, val parameterLabel: String, val options: List<ThinkingQualityOption>, val reasoningRequired: Boolean = false, val disabledValue: String? = null, val enabledActions: List<ThinkingQualityJsonAction> = emptyList(), val disabledActions: List<ThinkingQualityJsonAction> = emptyList(), )关键字段语义:
parameterLabel:思考参数名(如reasoning_effort、thinkingBudget、thinkingLevel),供请求构建使用,不出现在 UI 文案中;reasoningRequired:该模型是否必须开启思考(如部分模型不支持关闭思考);enabledActions/disabledActions:开启 / 关闭思考开关时对请求 JSON 执行的动作序列(如写reasoning.effort = "none");disabledValue:关闭思考时使用的参数值。
伴侣对象提供两个便捷工厂:toggleOnly(...)构造显式开关型映射,unsupported()构造不支持映射。
单一事实来源:ThinkingQualityMappingRegistry
ThinkingQualityMappingRegistry是契约中的“注册表”,承担从 provider/模型/端点解析出映射的唯一入口,也是“请求构建和 UI 都通过它获取定义”这一原则的实现:
fun resolve( providerTypeId: String, modelName: String, apiEndpoint: String, thinkingConfigurations: String ): ThinkingQualityMapping解析过程采用规则优先匹配:将thinkingConfigurationsJSON 解析为规则列表,按 JSON 数组顺序取第一条同时命中 provider、模型与端点的启用规则,后续规则不再评估;没有规则命中时返回unsupported()。源码注释明确指出:“The JSON array order is the user-visible priority order”。
规则数据结构:ThinkingConfigurationRule
规则包含:
id、enabled:规则标识与开关(enabled=false的规则在解析时被跳过);providerIds:命中哪些 provider(同时兼容providers与providerTypeIds两个 JSON 键);matcher:模型匹配器;endpointSuffixes:端点后缀匹配(用于区分同一 provider 的 chat 与 responses 协议);control/parameterLabel/reasoningRequired:映射核心字段;enabledActions/disabledActions/disabledValue:开关动作与关闭值;options:档位列表。
provider 匹配不区分大小写(统一转大写);端点匹配会先剥离查询串(?之后)与锚点(#之后)、去掉尾部/并转小写,再判断是否以给定后缀结尾。
模型匹配器:ThinkingModelMatcher
模型匹配支持多种模式,命中任意一种即匹配成功(ThinkingConfigurationRule.fromJson同时读取match子对象与规则根级同名键):
| 匹配键 | 语义 |
|---|---|
modelPrefix | 模型名以指定前缀开头 |
modelContains | 模型名包含指定子串 |
modelSuffix | 模型名以指定后缀结尾 |
modelRegex | 正则匹配(忽略大小写) |
firstSegment | 模型名/分割后的首段相等 |
lastSegmentPrefix/lastSegmentContains/lastSegmentRegex | 针对末段(如gpt-5.6-luna这类带组织前缀的模型)的前缀 / 包含 / 正则匹配 |
匹配器为空时视为通配(命中所有模型)。这套设计让一条规则既能精确锁定某类模型(如 Gemini 3.x),也能用正则覆盖模型族。
JSON 配置格式与真实示例
映射规则以 JSON 形式存放在 ModelThinkingConfigDefaultsCollect.kt 的ModelThinkingConfigDefaults.DEFAULT_JSON中(共 434 行)。规则数组既可以直接以[...]顶层数组书写,也可以包在{"rules": [...]}对象中(rulesArray负责兼容两种形态),空串按[]处理。
示例一:OpenAI chat 系(字符串档位)
{ "id": "openai-chat-reasoning-effort", "providers": ["OPENAI", "OPENAI_GENERIC"], "match": {"modelRegex": ["(?:^|/)(?:o[1-9]|gpt-[5-9]|gpt-oss|codex)"]}, "control": "levels", "parameterLabel": "reasoning_effort", "options": [ {"id": "low", "label": "low", "path": "reasoning_effort", "value": "low"}, {"id": "medium", "label": "medium", "path": "reasoning_effort", "value": "medium"}, {"id": "high", "label": "high", "path": "reasoning_effort", "value": "high"}, {"id": "xhigh", "label": "xhigh", "path": "reasoning_effort", "value": "xhigh"}, {"id": "max", "label": "max", "path": "reasoning_effort", "value": "max"} ] }这里的label会被解析为displayLabel,path指明写入请求 JSON 的路径,value生成类型化wireValue。
示例二:OpenAI Responses 协议(带启用/关闭动作与嵌套路径)
{ "id": "openai-responses-reasoning-effort", "providers": ["OPENAI_RESPONSES", "OPENAI_RESPONSES_GENERIC", "OPENAI_CODEX"], "control": "levels", "parameterLabel": "reasoning.effort", "enable": [ {"path": "reasoning.summary", "value": "auto"}, {"path": "include", "value": ["reasoning.encrypted_content"]} ], "disable": [ {"path": "reasoning.effort", "value": "none"} ], "options": [ {"id": "low", "label": "low", "path": "reasoning.effort", "value": "low"} ] }enable/disable数组对应enabledActions/disabledActions,支持向include写入数组值,展示出动作系统的表达能力。
示例三:Gemini 数字档位与必开思考
{ "id": "gemini-25-thinking-budget", "providers": ["GOOGLE", "GEMINI_GENERIC"], "match": {"modelPrefix": ["gemini-2.5"]}, "control": "levels", "parameterLabel": "thinkingBudget", "enable": [ {"path": "generationConfig.thinkingConfig.includeThoughts", "value": true} ], "disable": [ {"path": "generationConfig.thinkingConfig.includeThoughts", "value": false}, {"path": "generationConfig.thinkingConfig.thinkingBudget", "value": 0} ], "options": [ {"id": "1024", "label": "1024", "path": "generationConfig.thinkingConfig.thinkingBudget", "value": 1024}, {"id": "4096", "label": "4096", "path": "generationConfig.thinkingConfig.thinkingBudget", "value": 4096} ] }Gemini 的档位是整数 token 预算,path深入generationConfig.thinkingConfig嵌套结构;disable同时写两个路径实现关闭。而 Gemini 3.x 系列则使用thinkingLevel(MINIMAL/LOW/MEDIUM/HIGH字符串枚举)并标记"required": true,表示思考不可关闭。
此外,默认配置还覆盖 DeepSeek(区分/responses端点与 chat 端点、reasoning.effort三档)、SiliconFlow(数字档位如128/8192)等 provider;thinkingConfigurations也支持通过validateConfigurations校验、formatConfigurations美化格式化,便于在设置界面维护自定义规则。
档位独立与 displayLabel / wireValue 分离
契约的关键设计原则是:每个 level 保留独立位置,即使多个 level 使用相同的 wire value。UI 只展示displayLabel,内部请求则继续使用类型化的wireValue。这一点在ThinkingQualityMapping的辅助方法中得到体现:
fun optionFor(id: String): ThinkingQualityOption? = options.firstOrNull { it.id == id } fun textValueFor(id: String): String? = (optionFor(id)?.wireValue as? ThinkingQualityWireValue.Text)?.value fun numberValueFor(id: String): Int? = (optionFor(id)?.wireValue as? ThinkingQualityWireValue.Number)?.valueUI 通过optionFor按档位 id 定位选项并读取displayLabel;请求构建通过textValueFor/numberValueFor取出类型化的真实值。由于二者都从同一个options列表取数,UI 文本与请求参数永远不会脱节,也天然支持“多个显示档位映射到同一个 wire value”的场景(例如某 provider 的 low 与 medium 都下发"low",但界面仍展示两个独立档位,保留用户的选择状态与未来协议升级空间)。
从契约到请求:ThinkingConfigurationApplier
映射契约不只是“描述”,还负责把档位真正写进请求。ThinkingConfigurationApplier.apply(...)是请求构建侧的执行入口,流程如下:
- 通过
ThinkingQualityMappingRegistry.resolve(...)解析出当前 provider/模型/端点的映射; UNSUPPORTED直接返回,不修改请求;- 计算
thinkingEnabled = enableThinking || mapping.reasoningRequired(模型必须思考时自动开启); - 按开关状态应用
enabledActions或disabledActions; - 若开启且为
LEVELS,则按选中的optionId应用该档位的actions;选项不属于当前映射时抛出IllegalArgumentException,防止脏档位写入请求。
动作执行支持path的点号嵌套路径写入(putJsonPath),未开启overwrite时若目标路径已存在则跳过写入(hasJsonPath先探测),避免覆盖请求中的既有字段。modelParameters(...)变体还会把最终请求 JSON 转成ModelParameter列表(字符串 / 整数 / 浮点 / 布尔 / 对象,Gemini 协议的thinkingConfig归入GENERATION分类),供设置界面预览当前模型的实际请求参数。
源码注释强调:选中的档位属于模型配置,绝不在此处读取全局偏好,从机制上保证了“每模型独立档位”的契约。
UI 消费:显示标签而非参数名
在 Android 端,ThinkingQualitySlider.kt 直接以ThinkingQualityMapping为输入:根据mapping.control判断是否渲染滑块(LEVELS且选项非空、当前选中项存在才渲染),通过options.indexOfFirst { it.id == value }定位选中索引,标题右侧展示selectedOption.displayLabel作为当前值,track 下方按档位渲染映射文本标签。契约约束“不在用户界面显示 provider 参数名”——parameterLabel仅用于请求侧,UI 只消费displayLabel。
同一份映射还被 Classic 与 Agent 两套输入样式共享(ClassicChatSettingsBar.kt、AgentChatInputSection.kt),并在 ModelConfigScreen.kt 与 ModelConfigManager.kt 中参与模型配置的读写,内部统一使用thinking_option_id字符串契约传递选中档位,不再保留全局固定档位数字。
Web 同步:映射随模型选择下发
Web 端沿用同一契约:服务端通过 WebChatModels.kt 中的WebModelSelectorState携带thinking_quality_mapping(@SerialName("thinking_quality_mapping"))字段,随当前 provider/model 一并返回;ThinkingQualitySlider.tsx 与 chatTypes.ts 按 mapping 渲染标签,输入设置仍只保存当前内部 level。这样 Android 与 Web 两端读到的档位文本来自同一个解析结果,保证多端一致。
契约验证:测试用例与工程记录
映射契约的测试集中在 ThinkingQualityMappingTest.kt,覆盖了契约的核心保证:
- 模型级差异化:
grok-4.6命中reasoning_effort四档(low/medium/high/xhigh),gpt-5.6-luna命中五档(low/medium/high/xhigh/max); - 类型化 wire value:SiliconFlow
Qwen3的显示标签为128等字符串,但numberValueFor("8192")返回整数8192,验证displayLabel与wireValue类型分离; - 显式 TOGGLE_ONLY:Zhipu
glm-4.7-thinking断言为TOGGLE_ONLY,参数为thinking.type且reasoningRequired=false;旧模型glm-3-turbo断言为UNSUPPORTED; - 必开思考:Zhipu
glm-5.3断言LEVELS+reasoningRequired=true; - 端点区分:DeepSeek
deepseek-chat与deepseek-reasoner分属不同映射族。
相关测试还包括 OpenAiChatReasoningEffortTest.kt、GeminiThinkingConfigTest.kt 与 OpenCodeThinkingConfigurationTest.kt。整个改造的进度与视觉验收记录见 docs/TODO/thinking_quality_slider_ui/index.md 及同目录下的 02_native_slider.md、03_web_parity.md、04_verification.md。
小结
ThinkingQualityMapping映射契约把“思考程度”从 provider 各自的私有实现中抽离为一份可解析、可校验、请求与 UI 共用的声明式定义:ThinkingQualityControl明确控制形态,ThinkingQualityOption以独立档位承载displayLabel与类型化wireValue的分离,ThinkingQualityMappingRegistry按规则(provider + 模型匹配 + 端点后缀)解析出唯一映射,ThinkingConfigurationApplier将选中档位安全写入请求。Android 与 Web 两端因此能对同一模型展示同一套真实档位文本,同时保留扩展新 provider 时只需新增 JSON 规则的低成本路径。
- AI Agent
- 人工智能
- 大模型
- AI 应用
- 工具调用
- 本地部署
- MCP Clients
- Agent 记忆
【免费下载链接】Operit
The most powerful AI agent and AI chat software on Android/Operit是一款Android上能力最为强大、发展最久的AI Agent
相关推荐
Operit 全局思考强度到 Gemini thinkingConfig 的映射:参数契约、源码实现与 JVM 测试
Operit 全局思考强度到 Gemini thinkingConfig 的映射:参数契约、源码实现与 JVM 测试 本篇技术指南围绕 Operit(Andro
AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit 动态思考选项:基于 Provider 真实能力的思考强度映射、配置持久化与请求序列化实战
Operit 动态思考选项:基于 Provider 真实能力的思考强度映射、配置持久化与请求序列化实战 导读 本篇技术指南围绕 Operit(Android 平
AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化Operit Gemini 全局思考程度映射的测试与交付验证指南
Operit Gemini 全局思考程度映射的测试与交付验证指南 本篇技术指南围绕 Operit(Android AI Agent 应用)中 Gemini 全局
AI Agent人工智能大模型AI 应用工具调用本地部署MCP ClientsAgent 记忆GUI 自动化
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考