前 3 篇把 nanobot 的核心架构讲透了:消息总线、Tool + Provider、状态机。一路下来,理论上已经能拼出一个能跑通 LLM 对话的"迷你 nanobot"。
但有个问题——这个迷你版只能在自己电脑上 python xxx.py 跑。
没有 CLI 命令、没有配置文件、不能装到别人电脑上用。它是个 demo,不是产品。
这一篇讲怎么把 demo 变成产品。s06 入口与配置 解决"怎么让用户装上就能用",s07 Provider 实战 解决"怎么让用户不写代码就能切 LLM 厂商"。
1. demo 和产品的差距在哪
前 3 篇拼出来的"迷你 nanobot"只能在自己电脑上 python xxx.py 跑。
几个具体的的缺口:
没有统一入口——你写个 agent.py,客户问"装到哪、怎么启动"没有配置文件——API key 写死在代码里,换个 key 要重发版 不支持多厂商切换——用户想从 Anthropic 换到 DeepSeek,得改业务代码
s06 章节处理前 2 个,s07 章节处理第 3 个。
2. nanobot核心设计:三个入口汇入同一个 Config 校验
nanobot 提供了三种"启动方式":
| 入口 | 怎么用 | 适用场景 |
|---|---|---|
| CLI | nanobot agent -m "你好" |
命令行交互、CI/CD |
| SDK | from nanobot import Nanobot |
Python 项目里嵌入 |
| HTTP API | POST /v1/chat/completions |
Web 前端、其他服务对接 |
这三种入口都要先过同一个 Config 校验,再启动消息泵。
为什么这样设计?因为配置错的 Agent 跑起来会出各种诡异问题。在启动前把配置校验好,比跑起来再 debug 高效 100 倍。
nanobot 用 Pydantic v2 写这个校验:
python
from pydantic import BaseModel, Field
class ProviderConfig(BaseModel):
name: str # 厂商名
base_url: str | None = None # 走代理时填
api_key: str = "" # 留空时从 env 读
class Config(BaseModel):
model_config = {"populate_by_name": True}
provider: ProviderConfig = Field(default_factory=ProviderConfig)
api_key: str = Field(default="", alias="ANTHROPIC_API_KEY")
# ... 几十个字段
两个关键点:
1. camelCase 别名让 JSON 和 Python 类属性无缝互转
JSON 配置习惯 apiKey,Python 类属性习惯 api_key。用 alias 让两种写法都能用:
json
{ "apiKey": "sk-xxx" } // JSON 用户习惯
{ "api_key": "sk-xxx" } // Python 用户习惯
Pydantic v2 都接受。
2. 配置加载优先级:环境变量 > 配置文件 > 默认值
这是 Pydantic 的 BaseSettings 行为。默认用环境变量兜底意味着:
CI/CD:把 API key 放在 secret env var 里,不用管配置文件 本地开发:写在 ~/.nanobot/config.json测试:用默认值(mock)
3. 一个常见疑问:为什么改了配置不生效
假设你改了 ~/.nanobot/config.json 里的 apiKey,会发现程序读到的还是旧的 key。
这不是 bug,是 Pydantic v2 的 BaseSettings 默认行为:loader.py 用 os.environ 优先于文件。
三种解法:
把 JSON 里的 apiKey改成api_key(snake_case 让 Pydantic 别用 alias 匹配)改成用 NANOBOT_API_KEY环境变量重启 gateway(配置不热加载)
关键经验:schema 里那个字段的 alias 决定了 JSON 里能用哪个 key——想加新字段时必须同时改 schema 和 alias。
4. OpenAI 兼容协议 = 一个类覆盖几十家
第 2 篇讲过 Provider 抽象的设计思想(s03)。现在s07 翻开 nanobot 真实代码,看这个抽象设计怎么落地。
最让人眼前一亮的是 OpenAICompatProvider 这个类——一个类,适配几十家厂商。
为什么?因为 2024 年 OpenAI 把 Chat Completions API 开放出来后,几乎所有"二线"厂商都按这个规范实现了一遍自己:
OpenRouter(聚合多家) vLLM(自部署) Ollama(本地) DeepSeek 智谱 BigModel Moonshot 月之暗面 Kimi 阿里 DashScope(部分模型) ……
大家 API 长一个样。 nanobot 写一个 OpenAICompatProvider 类就吃下了:
python
class OpenAICompatProvider(LLMProvider):
def __init__(self, spec: ProviderSpec):
self.client = OpenAI(
base_url=spec.base_url,
api_key=os.getenv(spec.env_key),
)
async def chat(self, messages, tools):
return self.client.chat.completions.create(
model=spec.model,
messages=messages,
tools=tools,
)
PROVIDERS 表里几十家厂商都是 backend 指向 openai_compat,只是 base_url 和 env_key 不同:
python
ProviderSpec(name="deepseek", base_url="https://api.deepseek.com/v1", env_key="DEEPSEEK_API_KEY")
ProviderSpec(name="zhipu", base_url="https://open.bigmodel.cn/api/paas/v4", env_key="ZHIPU_API_KEY")
ProviderSpec(name="ollama", base_url="http://localhost:11434/v1", env_key="OLLAMA_API_KEY")
ProviderSpec(name="vllm", base_url="http://localhost:8000/v1", env_key="VLLM_API_KEY")
新增一家厂商只要加一行 ProviderSpec,核心 OpenAICompatProvider 一行不动。
5. 一个对比:传统接入 vs OpenAI 兼容协议
国产 LLM 厂商这几年大量采用 OpenAI 兼容协议——月之暗面 Kimi、智谱 BigModel、Moonshot 都是。
传统接入:新写一个 kimi_provider.py,继承 LLMProvider,自己处理 HTTP 请求、调 API、解析响应。200 行起步,调试一周不稀奇。
OpenAI 兼容协议接入:加一行 ProviderSpec:
python
ProviderSpec(name="kimi", base_url="https://api.moonshot.cn/v1", env_key="KIMI_API_KEY")
业务代码一行不动,Kimi 接入完成。OpenAICompatProvider 已经被写好,新厂商只是配置里的一行。
这就是 OpenAI 兼容协议的威力——它让"换 LLM 厂商"从"写适配代码"变成"填配置"。
6. 三种接入新厂商的方式
新加厂商时一般走方案 1:
方案 1:最简,不写新代码
把新厂商的 base URL 配在 config.json 的 providers[].baseUrl,backend 选 openai_compat。90% 的场景走这条。
json
{
"providers": [
{
"name": "kimi",
"baseUrl": "https://api.moonshot.cn/v1",
"envKey": "KIMI_API_KEY"
}
]
}
方案 2:品牌化(贡献 PR)
在 registry.py 的 PROVIDERS 列表加一项,让 nanobot 官方支持你的厂商。给开源项目做贡献。
方案 3:完全新 backend
写 nanobot/providers/zhipu_provider.py 继承 LLMProvider,并在 factory.py 注册。只有厂商 API 不是 OpenAI 兼容时才需要。
7. 一个常见的误区:配置写死在 Python 文件里
很多 Agent 项目的第一版长这样:
python
# config.py
PROVIDER = "anthropic"
API_KEY = "sk-xxx"
代码能跑,但有几个问题:
换 key 要重发版 没法支持"用户在网页里改配置"——改 Python 文件得重启整个服务 多环境(开发/测试/生产)得手动改文件
正确做法:用 Pydantic v2 把配置抽成 schema,配置文件 + 环境变量两条路都能用:
python
class Config(BaseModel):
provider: str = "anthropic"
api_key: str = Field(default="", alias="ANTHROPIC_API_KEY")
再配一个 file watcher 热加载,用户在网页改了配置 5 秒内生效。
一句话建议:别把配置写死在 Python 文件里。从一开始就建 config/schema.py + config/loader.py,让配置文件和环境变量两条路都能用。前期投入小,后面扩展省事。
8. 下一篇
下一篇进 s08 + s09 章节,讲多渠道架构——17 个聊天平台(Telegram / Discord / 飞书 / 微信 / 邮件 / Slack……)是怎么用同一个 BaseChannel 抽象统一接入的,以及 session_key 这个设计怎么决定"哪些算一段对话"。
9. 参考资料
nanobot 源码:https://github.com/HKUDS/nanobot nanobot-tutorial(14 章配套教程):https://github.com/yaoweizhang/nanobot_tutorial nanobot 官方 Roadmap:https://github.com/HKUDS/nanobot/discussions/431
跟读建议:跑 python s06_entry_config/code.py 看 Pydantic 配置校验流程,再翻 nanobot/config/schema.py 看真实 schema 定义。跑 python s07_providers/code.py 看 base_url 切换效果。
夜雨聆风