Skip to content

Configuration zh

Dev edited this page Sep 7, 2026 · 3 revisions

English · 中文

配置

对应 v0.12.0(3a762b1)。实现在 src/config.rs(load_config / merge)。 完整字段列表见仓库根的 config.toml.example。


两级配置

nanopi 从 v0.6(fd7cdd0)起就是两级配置,两个文件都是可选的。

层级 路径
全局 ~/.nanopi/config.toml
项目 <cwd>/.nanopi/config.toml

NANOPI_HOME 环境变量可以改变全局那一层的根目录(主要用于测试隔离)。

完整优先级,从高到低:

命令行 --flag  >  OPENAI_* 环境变量  >  项目配置  >  全局配置  >  默认值

合并语义:不是简单的"项目覆盖全局"

这是最容易踩的地方。merge() 里有三类行为:

覆盖(项目赢)

model, base_url, api_key, api_key_file, api_kind, provider, trust.default

Option 语义:None 表示"未设置",不会覆盖下层的值。

拼接(两侧叠加)

[[extensions]]
[hooks] 的所有事件
skills.disabled / skills.extra_dirs

源码注释说得很直白:"Extensions concatenate (both additive, never override)"、"hooks are policies, not overrides"。

实际影响:全局装的插件和项目装的插件会同时加载。 你没办法在项目配置里"关掉"某个全局插件,只能再加。两个插件若导出同名工具,后者会因工具名冲突被跳过并告警。

hooks 同理——项目里加的 hook 不会替换全局 hook,两边都会跑。

特殊

tool_exec_mode:显式设置的(非默认值)赢;默认是 Parallel,用户 opt-in 到 Sequential。


一个常见的困惑

如果你在 nanopi 仓库自己的目录下测试,注意仓库里有一个 .nanopi/config.toml。它是项目级配置,会覆盖你的全局设置——包括 base_url 和 model。

症状:你在 ~/.nanopi/config.toml 里改了 endpoint,启动却发现连的是别的地址。

要么改项目那份,要么换个工作目录测试。


插件路径的 ~ 展开

[[extensions]]
path = "~/.nanopi/extensions/my-tool.wasm"

~ 展开的是 $HOME,不认 NANOPI_HOME。

也就是说,即使你设了 NANOPI_HOME=/tmp/test(config.toml 确实会从那里读),上面这个 path 仍然解析到 $HOME/.nanopi/extensions/,于是:

nanopi: skipping extension (not found): /root/.nanopi/extensions/my-tool.wasm

插件路径建议一律写绝对路径。


API key 的三种给法

# A. 文件(推荐)—— 内容就是 token,建议 chmod 600
api_key_file = "~/.nanopi/api_key"

# B. 内联(本地开发方便)—— 启动会打印 stderr 警告,防止误提交
api_key = "sk-..."
# C. 环境变量,优先级高于配置文件
export OPENAI_API_KEY=sk-...

内联那种会在每次启动时告警:

⚠ api_key is inline in config.toml — consider api_key_file or OPENAI_API_KEY env var

插件配置字段

[[extensions]]
path = "/abs/path/plugin.component.wasm"   # 或目录,向下扫一层
max_files = 64             # path 是目录时的上限,默认 64
allow_fs = false           # host-fs-read
allow_network = false      # host-http-get
url_allowlist = []         # host-http-get 能到的 host;空 = 拒绝一切
allow_store = false        # host-store-get / host-store-set        (v0.12)
allow_context = false      # host-set-context                       (v0.12)
allow_tools = []           # host-call-tool,**按工具名逐个给**      (v0.12)
allow_send_message = false # host-send-user-message                 (v0.12)
events = []                # 这个插件可以**观察**的生命周期事件      (v0.12)

上面每一个默认值都是关闭的那一侧。四个布尔量默认 false,三个列表默认 []——而 [] 的含义是拒绝一切,不是"允许一切"。

字段 默认 授予什么
allow_fs false 读工作目录以内的文件
allow_network + url_allowlist false、[] 只对 allowlist 里的 host 发 HTTP(S) GET
allow_store false 一份跨重启存活的键值 JSON store
allow_context false 每个 turn 折进模型 system prompt 的一段文字
allow_tools [] 调用列出来的内置工具(bash、edit、find、grep、ls、read、write)
allow_send_message false 发起或者带偏一个 turn——这一个是花钱的
events [] 按 PI 的事件名观察生命周期事件

allow_tools 是列表而不是布尔量,这是刻意的:allow_tools = ["bash"] 就是任意命令执行,它会直接绕过 allow_fs 的工作目录限制和 url_allowlist 的逐 host 审批,让这个插件身上其他所有授权都变成装饰;而 allow_tools = ["find", "read"] 表达的是"可以走、可以读,不可以写、不可以执行"——这个区别没有任何一个"每插件一个开关"能表达。列表里写了不是内置工具的名字,对这个插件是加载错误,报错会列出合法的那几个。

能力门的完整语义见 能力门;事件订阅见 插件事件。

path 指向目录时会非递归扫描一层,把其中的 .wasm 都装上。

拼错的授权键是加载错误,不是静默的空操作(v0.12)

[[extensions]] 带着 #[serde(deny_unknown_fields)]。所以下面这份配置起不来:

[[extensions]]
path = "/abs/path/plugin.wasm"
allow_stroe = true          # ← 配置加载失败,并点出这个字段

它挡住的是那种你看不见的故障:v0.12 之前这个拼写错误能正常解析,什么都没授予,而用户以为自己的插件有 store 了。一个"要了但没拿到"的授权必须响亮,否则症状会以插件内部莫名其妙的 error: store access denied 出现——看起来像是插件自己的 bug。

注意这里的不对称:未知的键是硬性的配置错误,而 events 列表里面的未知事件名只是插件加载时的一条告警,并会列出合法名字。这是刻意的:[[extensions]] 整体在标准(不带 wasm feature 的)二进制里必须保持"忽略并告警",所以 events 里的任何东西都不能被允许中断配置加载。

受限网络下的注意事项

allow_network 打开后,插件的 host-http-get 走的是宿主进程的网络栈,也就跟着 nanopi 进程的 http_proxy / https_proxy 走。所以在受限网络后面:

  • 插件不需要自己实现代理,配好环境变量就够了;
  • 但 host-http-get 10 秒超时且不跟随重定向,走代理的一次请求很容易踩到这两条中的第一条,而失败是以 in-band 的 error: 字符串回给插件的,不是 trap——插件要自己处理,不要假设一定成功;
  • url_allowlist 匹配的是 URL 解析出来的 host,代理地址不参与匹配,所以配置里写的仍然是目标站点的域名。

相关

Clone this wiki locally