第 2 章 · 02 config 四级覆盖与 reasonix.example.toml 精读


文档摘要

第 2 章 · 02 config 四级覆盖与 reasonix.example.toml 精读 本节摘要:本节深潜 Reasonix 的配置体系。先讲 config 四级覆盖(flag 命令行 > project > user > built-in defaults),理解"哪一级生效、为什么 user/global 字段不被 project 覆盖"。然后逐段精读 260 行——DeepSeek 预设( 的 endpoint 复用)、双模型 分离缓存会话、 定价、 工具启用、 MCP 服务器、 权限策略等核心配置块。最后回到 SPEC §1.

第 2 章 · 02 config 四级覆盖与 reasonix.example.toml 精读

本节摘要:本节深潜 Reasonix 的配置体系。先讲 config 四级覆盖(flag 命令行 > project ./reasonix.toml > user ~/.reasonix/config.toml > built-in defaults),理解"哪一级生效、为什么 user/global 字段不被 project 覆盖"。然后逐段精读 reasonix.example.toml 260 行——DeepSeek 预设([[providers]] kind = "openai" 的 endpoint 复用)、双模型 [agent] executor+planner 分离缓存会话、[[provider.pricing]] 定价、[tools] enabled 工具启用、[[plugins]] MCP 服务器、[permission] 权限策略等核心配置块。最后回到 SPEC §1.3——为什么 TOML 是唯一被接受的第三方依赖(BurntSushi/toml),以及"加新模型是 config 编辑不是代码改动"如何成为"薄 harness"的最佳注脚。

内容来源:原项目源码 reasonix.example.toml(260 行)、docs/SPEC.md §5 Configuration、internal/config/(约 31.8K 行)。

⚠️ 注意:reasonix.example.toml 只是"示例文件",真正生效的是 ./reasonix.toml(项目级)与 ~/.reasonix/config.toml(用户级)。example 文件用于 reasonix setup 写出默认配置与文档展示,不会被自动加载。

学习目标

阅读完本节,你应当能够:

  1. 默写 config 四级覆盖的优先级顺序。
  2. 解释 user/global 字段为何不被 project reasonix.toml 覆盖。
  3. 读懂 reasonix.example.toml 中 DeepSeek 预设的每个字段。
  4. 说清双模型 executor + planner 如何"各自独立缓存稳定会话"。
  5. 区分 [[plugins]](MCP stdio/http/sse)与 [[extension.sidecars]](Extension Protocol)。
  6. 解释 TOML 为何是 SPEC §1.3 唯一接受的第三方依赖。

一、config 四级覆盖

SPEC §5 开篇钉死了解析顺序(docs/SPEC.md:660-662):

Resolution order: flag > project ./reasonix.toml > the user config file > built-in defaults.

从高到低四级:

级别 来源 作用域 示例
1 最高 flag 命令行 单次运行 reasonix --model deepseek-pro
2 project ./reasonix.toml 当前项目 项目根的 reasonix.toml
3 user 用户级 当前用户所有项目 macOS/Linux ~/.reasonix/config.toml,Windows %AppData%\reasonix\config.toml
4 最低 built-in defaults 全局兜底 代码内置默认值

注意自 v1.8.1 起,用户级配置文件位置从 ~/.reasonix.toml 改为 ~/.reasonix/config.toml(Windows 为 %AppData%\reasonix\config.toml),reasonix setup 会写出默认配置到这个路径。

reasonix.example.toml 顶部也标注了这条顺序(reasonix.example.toml:1-3):

# Reasonix configuration. # Resolution order: flag > ./reasonix.toml > <Reasonix home>/config.toml > built-in defaults.

例外:user/global only 字段。SPEC §5 说明(docs/SPEC.md:665-666):

Fields marked user/global only are not overridden by project reasonix.toml.

也就是说,有些字段(如桌面偏好、全局通知开关)标记为"仅用户级",项目 reasonix.toml 写了也不生效——这是为了防止恶意项目通过 reasonix.toml 篡改用户的全局偏好。Provider 的密钥通过 api_key_env 命名环境变量、实际值存在 <Reasonix home>/.env,绝不在配置文件里写明文密钥(reasonix.example.toml:4-6):

# Provider entries name secrets via api_key_env; saved key values live in # Reasonix's global <Reasonix home>/.env. Never put API key values here.

二、default_model 与 [ui]

example.toml 第 7-15 行:

default_model = "deepseek" # a provider name (→ its default model) or "provider/model" # language = "zh" # ui language; empty = auto-detect from $LANG / $REASONIX_LANG [ui] theme = "auto" # auto|dark|light; controls CLI colors only; REASONIX_THEME can override per run # theme_style = "graphite" # graphite|aurora|slate|carbon|nocturne|amber and legacy aliases show_turn_usage = true # CLI/TUI: show per-request token and cost receipts in transcript scrollback
  • default_model 是最关键的全局字段——它可以是 provider 名(如 "deepseek",自动取该 provider 的 default model),也可以是裸模型名,或显式 "provider/model"Config.ResolveModel 负责解析这三种形态。
  • language 空 = 自动从 $LANG / $REASONIX_LANG 探测,显式设 "zh" 强制中文。
  • [ui] 控制终端 UI 外观(主题/样式/是否显示每轮 token 与成本回执),不影响模型行为。

三、DeepSeek 预设:[[providers]] kind = "openai"

这是 example.toml 最核心的一段(reasonix.example.toml:59-74):

[[providers]] name = "deepseek" kind = "openai" base_url = "https://api.deepseek.com" models = ["deepseek-v4-flash", "deepseek-v4-pro"] default = "deepseek-v4-flash" # optional; defaults to the first of `models` api_key_env = "DEEPSEEK_API_KEY" context_window = 1000000 effort = "high"

逐字段解读:

  • name = "deepseek":provider 实例名,default_model--model 用它引用。
  • kind = "openai":provider 的"种类"——注册表里的 key。DeepSeek 走的是 openai kind,因为 DeepSeek API 是 OpenAI /chat/completions 兼容的。
  • base_url:端点 URL,OpenAI 兼容实现会 POST 到 base_url + "/chat/completions"
  • models:一个 vendor endpoint 可暴露多个模型,列表形式免去重复声明 base_url/api_key。
  • default:列表形式时的默认模型(缺省取 models[0])。
  • api_key_env:密钥的环境变量名,实际密钥从 <Reasonix home>/.env 读。
  • context_window:provider 级上下文窗口(token),接近这个值时触发压缩(第 6 章);0 禁用压缩。
  • effort:DeepSeek thinking 模式的推理强度(high | max),DeepSeek thinking 始终开启。

💡 契约要点:这就是"薄 harness"的最佳注脚。SPEC §3.1 原文(docs/SPEC.md:91-93):

Adding another OpenAI-compatible model is a config edit, not a code change.

加一个新的 OpenAI 兼容模型,是改 config,不是改代码。DeepSeek、Kimi、GLM、MiniMax、Qwen……它们在 Reasonix 里都是 kind = "openai" 的配置实例,只是 base_url/models/api_key_env 不同。

四、Anthropic 原生 kind

example.toml 也展示了 Anthropic 原生 provider(reasonix.example.toml:104-114):

[[providers]] name = "claude" kind = "anthropic" model = "claude-opus-4-8" api_key_env = "ANTHROPIC_API_KEY" context_window = 1000000 price = { cache_hit = 0.5, input = 5, output = 25, currency = "$" } thinking = "adaptive" effort = "high"

kind = "anthropic" 走的是 internal/provider/anthropic(原生 Messages API,不经 OpenAI shim)。它有自己的特性:thinking = "adaptive" 开启扩展思考并跨工具调用回放签名 reasoning block;Anthropic 当前模型不接受 temperature(代码里 internal/provider/anthropic 不发采样参数)。

注意 kind = "responses" 也存在(走 internal/provider/responses,注册了 "responses""dashscope-responses" 两个 kind),用于 OpenAI Responses API 与 DashScope。三种 kind 覆盖了主流端点。

五、双模型 executor + planner

[agent] 段(reasonix.example.toml:31-48):

[agent] temperature = 0.0 soft_compact_ratio = 0.5 # notice only; keeps the cache-first prefix intact tool_result_snip_ratio = 0.6 # snip stale tool results before summary compaction compact_ratio = 0.8 # try compacting when prompt reaches this fraction compact_force_ratio = 0.9 # force compacting at this high-water mark # planner_model = "deepseek-pro" # optional: enable two-model collaboration

注释掉的 planner_model 是双模型协作的开关。SPEC §3.5 讲清了为什么双模型必须分离会话(docs/SPEC.md:201-204):

When agent.planner_model names a provider different from the executor, a Coordinator runs two models in separate sessions to keep each one's prompt prefix cache-stable.

  • executor(执行者):全功能 tool-using Agent,跑在自己的会话里,执行 planner 给出的计划。
  • planner(规划者):低频,跑在另一个会话里,只带只读研究工具,产出简洁计划。
  • 两会话永不混合:这样任何一个模型的 prefix 都不会被对方的轮次干扰,各自 prepend-only 增长,各自 prefix cache 稳定。

SPEC §3.5 最后一句点睛(docs/SPEC.md:237-240):

The sessions never mix, so neither model's prefix is disturbed by the other's turns; both grow prepend-only and stay cache-friendly. This reconciles "cache-first" with "two-model collaboration": switching models inside one shared conversation would break the prefix and tank cache hits, so we don't.

"在同一个共享会话里切换模型"会破坏 prefix、击穿缓存命中,所以绝不那么做——而是开两个会话。这是 Reasonix 把"缓存优先"与"双模型协作"调和出来的精巧设计,第 3 章第 2 节会展开。

compact_ratio 等四个比例是上下文维护的阈值(第 6 章详讲):soft_compact_ratio 只是提醒,tool_result_snip_ratio 裁剪旧工具输出,compact_ratio 触发摘要压缩,compact_force_ratio 强制压缩。

六、[tools]、[[plugins]]、[permissions]

[tools] 工具启用(reasonix.example.toml:121-125):

[tools] enabled = [] # empty = all built-in tools bash_timeout_seconds = 120 mcp_startup_timeout_seconds = 30 mcp_call_timeout_seconds = 300

enabled = [] 表示启用全部内置工具;列出名字则只启用子集。三个超时分管 bash、MCP 启动、MCP 调用。

[[plugins]] MCP 服务器(reasonix.example.toml:170-181):

# [[plugins]] # name = "example" # command = "reasonix-plugin-example" # # startup_timeout_seconds = 60 # # call_timeout_seconds = 600

每个 [[plugins]] 是一个 MCP 兼容的外部可执行文件,type 默认 stdio(本地子进程),也支持 http(Streamable HTTP)与 sse(legacy)。${VAR} 在 command/args/env/url/headers 里展开,密钥来自环境而非配置文件。第 7 章详讲。

[permissions] 权限策略(SPEC §5 例子,docs/SPEC.md:733-736):

[permissions] mode = "ask" deny = ["Bash(rm -rf*)", "Bash(git push*)"] allow = ["Bash(go test:*)", "Bash(git status:*)"] ask = []

mode 是 writer 工具的兜底姿态(ask|allow|deny);deny/allow/ask 是规则列表,语法是 Claude Code 风格的 Tool(specifier)。优先级 deny > ask > allow > fallback。第 5 章详讲。

七、TOML 为何是唯一接受的依赖

SPEC §1.3 原文(docs/SPEC.md:14-16):

A third-party dependency must be pure-Go, lightweight, and must not compromise the single-binary / cross-platform / distribution story. TOML parsing is the one accepted dependency.

为什么是 TOML 而不是 YAML/JSON?

  • YAML:规范复杂,Go 实现多带 cgo 或体量大,容易破坏单二进制;缩进敏感易出错。
  • JSON:无注释,配置文件无法写说明,用户体验差。
  • TOML:语义清晰、有注释、纯 Go 实现轻量(BurntSushi/toml),正好满足 SPEC §1.3 三条要求(纯 Go、轻量、不破坏单二进制)。

internal/config 包约 31.8K 行,负责四级覆盖合并、api_key_env 解析、模型引用解析(Config.ResolveModel)、.mcp.json 合并等。它是 Reasonix 把"配置驱动一切"落到实处的核心,第 2 章只讲用法,内部实现细节会在用到时穿插。

💡 契约要点:配置驱动不是"用 TOML 写配置"那么简单,而是把模型、工具、插件、权限、沙箱、压缩阈值全部声明式化。Reasonix 的几乎所有运行时行为都能通过 config 调整,这是"薄 harness"的工程兑现。

本节要点回顾

  1. 四级覆盖:flag > ./reasonix.toml > ~/.reasonix/config.toml > built-in defaults;user/global only 字段不被 project 覆盖;密钥走 api_key_env + .env,绝不在配置写明文。
  2. DeepSeek 预设:[[providers]] kind = "openai" 复用 OpenAI 兼容实现,base_url/models/api_key_env 不同即新模型。
  3. 双模型 executor+planner:planner_model 开启,两会话分离、各自 prefix cache 稳定;在共享会话切模型会击穿缓存,所以绝不那么做。
  4. 配置块:[tools] 启用与超时、[[plugins]] MCP 服务器(stdio/http/sse)、[permissions] 权限(deny>ask>allow>fallback)。
  5. TOML 是唯一接受的依赖:纯 Go、轻量、有注释,正好满足 SPEC §1.3;internal/config 约 31.8K 行落地"配置驱动一切"。
  6. 薄 harness 兑现:加新 OpenAI 兼容模型 = 改 config,不是改代码。

下一章,我们正式进入 Provider——SPEC §3.1 的 Provider interface 与 Factory 注册表,看 internal/provider 如何用几十行 Go 把"核心只懂接口"变成可运行的代码。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U