单模型Agent配置的工程化笔记 —— 四档思考、权限顺序与静态校验

单模型、四档思考:从默认档到 #max

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 本文讨论的配置只保留 deepseek/deepseek-flash 一个模型,agent 之间的差异全部由 thinking tier 表达。11 月的改动集中在三件事:把思考档从三档扩到四档、重排 agent 档位;把"agent 级权限不得放宽全局 deny"从约定变成校验器;跟上 CLI 2.2.5/2.2.6 与 superpowers v7.0.2 的基线。 |

只保留一个模型,把"换模型"变成"换思考档位",成本模型会简单很多:没有第二份单价、没有第二套缓存前缀。但配置层的语义约束反而更多------思考参数放在哪一层、agent 之间如何升级、权限数组按什么顺序匹配、以及"文件里写对了"和"运行时真的这么执行"之间的差距。

下面按三个边界展开:档位、权限、校验。每一条都对应一次具体的改动,而不是抽象的原则。

一、第四档:#max 不是"更强的模型"

这套配置只使用 deepseek/deepseek-flash 一个模型,agent 之间的差异全部由 thinking tier 表达。11 月之前有三档:默认(thinking 关)、#low、#high。新增的 #max 是第四档:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| "variants": { "id": "low", "settings": { "reasoningEffort": "low" }, "body": { "thinking": { "type": "enabled" } } }, { "id": "high", "settings": { "reasoningEffort": "high" }, "body": { "thinking": { "type": "enabled" } } }, { "id": "max", "settings": { "reasoningEffort": "max" }, "body": { "thinking": { "type": "enabled" } } } |

有两个容易写错的点。

第一,effort 的实际等级不是 low/medium/high 三段。上游 API 的映射是 minimal→low,medium/xhigh→high,max/ultra→max。也就是说 #high 对应的是默认等级,真正高于它的只有 #max。新增这一档不是"换模型",只是把同一个模型推到更高的推理预算。

第二,thinking 的开关和强度是两个不同层的参数:toggle 是请求体字段 body.thinking,effort 是 provider setting settings.reasoningEffort(配置里 camelCase,上线变成 reasoning_effort)。Agent 不能携带 provider setting------它的 request overlay 只有 headers/body------所以档位只能做成 model variant,agent 只写 model: deepseek/deepseek-flash#max 这样的引用。变体还必须重新声明 thinking: enabled:模型层默认是 disabled,overlay 是深度合并,变体省略 toggle 就会继承 disabled。同理,不要用 null 去"清掉"继承来的 temperature------mergeOverlay 只把 undefined 当作未设置,null 是合法值,会原样发到请求体里。

图 1 同一模型上的四档推理预算

上游默认 thinking 是开的、effort 是 high。这意味着不写 disabled 的默认档其实一直在付 reasoning token;#high 只是把默认值写明,#low 是往下切一档,#max 才是真的加预算。

为什么 #max 只给 oracle 和 reviewer?成本结构决定的。四个档共用同一份单价,档位差异只体现在 reasoning token 数量,按输出价计费;而 effort 是按步支付的。写型 agent 往往要跑几十步,用 #max 等于把高档价格乘上步数。只读的根因分析和代码审查链路短、步数上限 40,才适合把预算花在单步推理上。

对应的架构约束是:升级永远意味着换 agent,而不是把同一个 agent 的档位调高。Agent 不能在会话中途切换 variant,所以 fallback 链里写的是 oracle#max → reviewer#max → deep-worker#high 这样的 agent 跳转,而不是"把 deep-worker 提到 max"。写型 agent 不碰 #max。

二、重排:谁该在哪一档

四档落下来之后,agent 的分工重新排了一次:

表 1 思考档位与 Agent 映射

档位 Agent 定位
默认(thinking 关) orchestrator / explore / librarian / vision 路由、检索、纯读取
#low light-orchestrator / consultant / ui-builder 单文件、有限判断、UI
#high planner / deep-worker / solo 规划、深度推理、批量实现
#max oracle / reviewer 只读升级:根因、关键/安全审查

图 2 Agent 档位分布:写型停在 #high

几个变化值得说明:

  • planner 从 #low 提到 #high。规划阶段的错误会放大到后续所有实现步骤,这里省 reasoning token 不划算。
  • consultant 和 ui-builder 从默认档提到 #low。它们不是纯检索,需要有限的方案判断和布局推理,但还不到多步规划的程度。
  • oracle 和 reviewer 从 #high 提到 #max。两者都是只读升级链路,且正是"需要更深推理、但步数不多"的典型场景。
  • solo 保持 #high。它是单模型内联执行器,属于写型 agent,不是升级终点。

这张表本身不复杂,难的是让路由、fallback 链、命令模板、skill 文档和 README 全部跟着改。只要有一处还写着 reviewer#high,升级链就会在运行时指向错误的档位。这次改动里,/review、/deep-review 的模板、code-review skill、orchestrator 的路由表都一起对齐了。

三、权限:last-match-wins 下的"合法越权"

OpenCode 的 permissions 是一个有序数组,匹配规则是 last-match-wins。全局 permissions 在前,agent 级 rules 追加在后。这个顺序带来一个很隐蔽的问题:

|------------------------------------------------------------------------------------------------------------------------------------------------------|
| // 全局 { "action": "edit", "resource": ".env", "effect": "deny" } // 某个 agent 的 frontmatter { "action": "edit", "resource": "*", "effect": "allow" } |

单看每一条都符合直觉,合在一起却是 agent 的 allow 覆盖了全局 deny------.env 又能被写了。这不是权限系统有 bug,而是"后写的规则赢"这一语义的直接结果。

反向的坑同样存在。全局权限是追加在内置 plan 规则之后的,如果给全局加一条 edit * allow,就会覆盖 plan 的 edit: * deny,把只读沙箱打开。所以这套配置里全局 edit 规则只写 deny,从不写 allow;普通 agent 的写权限来自 agent baseline,不需要全局放行。

另一个需要精确理解的是通配符语义。resource 是 location-relative 的 POSIX 路径,* 会跨过 /(实现上是 * → .*),所以 *.env 同时覆盖根目录 .env 和 dir/.env;单独的 .env 规则只是显式的根级保护。shell 的 resource 是完整命令文本,按通配符匹配,规则的尾部 * 才是前缀授权的关键:git status* 能覆盖 git status --short;匹配不是按单词逐个比较的,审批提示里的 ARITY 表只影响展示。

图 3 权限数组的匹配顺序与两个方向的风险

这次还修了一个具体的过宽规则:explore 的 codemap 写入 allow 原本是 *.opencode/codemap.md,因为 * 跨 /,它能匹配到更深层的路径;收紧成 .opencode/codemap.md 之后,只允许根目录下的 codemap 文件。同时移除了 explore/librarian 的 hidden: true------隐藏标记让它们在 agent 名册里不可见,路由命中后难以确认是否真的被调起。

四、校验器:把不变量变成退出码

上面的规则靠人记不住,所以 11 月的重点改动之一是把它们写进 scripts/validate-jsonc.js。

权限放宽检查的做法是:先收集全局 permissions 里 action 为 edit/shell/read/external_directory 的 deny 规则;再对每个 agent 的 allow 规则生成一组具体探针字符串,用与运行时一致的通配符正则去测试是否命中 deny。命中即报错。skill/subagent 两类 action 不参与检查------它们的 allow 选择的是名册条目,不是路径或命令,不会重新打开一个被 deny 的资源。

通配符转换必须和运行时一致,否则检查结果没有意义:

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| function wildcardToRegExp(pattern) { let escaped = pattern .replace(/\\/g, '/') .replace(/.+\^${}()\|\[\\\\]/g, '\\\&') .replace(/\\\*/g, '.\*') .replace(/\\?/g, '.'); if (escaped.endsWith(' .\*')) { escaped = escaped.slice(0, -3) + '( .\*)?'; } return new RegExp( \`\^{escaped}$`, process.platform === 'win32' ? 'si' : 's' ); } |

探针生成本身是有边界的:allow 里最多三个 * 时,用空串、单字符、路径片段(a/b)做全组合;超过三个就退化为"字面骨架 + 全 a/全 b 填充"的近似。这意味着检查是单向的:命中一定能证明重叠存在,没命中不能证明一定不重叠。这个取舍是有意写进注释的------静态检查不应该假装自己能解决不可判定问题。

图 4 deny-overlap 静态检查流程

更关键的是 fail-closed。loadGlobalDenies 读不到或解析不了默认配置时返回 null,lint 直接失败,而不是用空 deny 集合让所有检查"通过"。同理,agent frontmatter 的权限解析器要求一行一条规则;遇到看起来像规则但解析失败的行、或者 flow-list 形式的 permissions: {...},直接报错。否则一条 allow 可以因为"解析器没看懂"而绕过重叠检查。

同一次改造还给配置加了模型层 lint:settings 下出现 thinking/temperature/top_p 等 openai-chat 协议不实现的键要报错(它们会被静默丢弃);body/headers 里出现 null 要报错(会覆盖基值);默认档必须显式发送 thinking: disabled(上游默认是开,不写就在每一步付 reasoning token);variant 的 reasoningEffort 只能是 low/high/max。Skill frontmatter 也加了检查------name 必须和目录名一致,否则 skill 会以一个没有 allow 规则引用的名字注册,等于静默消失。

五、版本维护:不报错不等于按你写的运行

11 月还跟了两次 CLI 基线(2.2.5、2.2.6)和一次插件版本(superpowers v7.0.1 → v7.0.2)。

图 5 11 月的三条改动线

这里有两个反复踩到的认知陷阱。

第一,v1 键不再"整份文件走迁移路径"。2.2.5 之后的行为是逐键迁移后与原生键合并,logLevel/server/layout/顶层 subagent_depth 这类 unsupported 键则丢弃并记诊断。文件不会被拒绝,运行时也不报错,但实际生效的配置已经和文件内容不一致。所以验收不能只看"启动成功",要看 debug config 的解析结果和其中的 unsupported/conflict 诊断。

第二,/releases/latest 当时仍指向 v1 线。判断版本只能看 /releases 列表顶部加桌面版 CLI 的 --version,不能用 latest 重定向,也不能用 PATH 上的 WinGet 1.18.4。

插件升级的闸门也不是"字节一致",更不是只看版本号。superpowers 的整套 OpenCode 集成都在 .opencode/plugins/superpowers.js 一个文件里;v7.0.1 → v7.0.2 改了 +61/−39 行:新增 BOOTSTRAP_MARKER、用 startsWith 守卫匹配 bootstrap 首行、把 V2 hooks 从 context 扩到 context/compaction/generate、bootstrap 文本里的 skill id 改成裸名。bootstrap 会进入 prompt 前缀,注入请求类型一变,compaction 的 payload 和缓存前缀都会受影响。所以升级前要读 release notes 并 diff 这个集成文件。

还有一个很小但真实存在的坑:write 工具落盘带 UTF-8 BOM。把文件内容直接拿去做 commit message,subject 首字符会变成 U+FEFF;拿去做 JSON 解析,某些解析器会直接报错。这次相关脚本也补了剥 BOM 的处理。

六、几条可复用的结论

**1.**思考参数只放模型层:toggle 进 body,effort 进 settings.reasoningEffort;agent 只写 variant 引用。

**2.**升级换 agent,不换档。同一 agent 无法在会话中切 variant,fallback 链必须写成 agent 跳转。

**3.**成本按步放大。贵档只给步数少的只读链路,写型 agent 停在 #high。

**4.**权限是 last-match-wins,后追加的 agent allow 能重新打开全局 deny;把这条写进静态检查,不要靠 review 记忆。

**5.**校验器要 fail-closed,并且必须复刻运行时的通配符语义;近似检查要明确"命中证明存在,未命中不证明不存在"。

6."运行时不报错"不是正确性证据。看解析后的配置,看诊断,看 lint 退出码。

项目地址: https://github.com/znlgis/my-opencode-deepseek-config

相关推荐
小田学Python1 天前
DeepSeek Harness 完整上手指南:安装部署 + 必装插件 + 用一周就回不去的功能
大数据·大模型·agent·ai工具·deepseek
我才是银古1 天前
视频是算出来的:DeepSeek Harness零素材全代码生成视频插件
ai·视频·插件·deepseek·dsh
我才是银古3 天前
6 小时做一部 10 分钟纪录片:DeepSeek 4.1 Flash + DeepSeek Harness 的完整制片记录
gis·deepseek·dsh
大模型真好玩3 天前
豆包真人分身操作技巧:这次让我来做主演
人工智能·agent·deepseek
XLYcmy3 天前
信息检索报告数字经济赋能城市低碳治理的路径研究——基于“传统数据库 + 生成式人工智能”双轮驱动的检索实践 检索实施与结果分析 Web of Science
数据库·ai·llm·prompt·ssci·deepseek·wos
用户7865157817214 天前
老款小爱音箱直连 DeepSeek:一个跑在音箱上的 BusyBox 客户端,如何做到“小爱优先、大模型兜底”
deepseek
Noir热爱生活4 天前
deepseek harness,关于桌面版无法打开vscode修复方案
electron·deepseek
ServBay5 天前
DeepSeek 再次崩了,DeepSeek V5正在路上?
后端·deepseek
大模型真好玩5 天前
DeepSeek Super-Agent核心特性详解与实战指南:把操作系统思想搬进智能体
人工智能·agent·deepseek