跳到主要内容

nanobot 是什么 · 全景与阅读地图

30 秒导读: nanobot 是一个独立、开源的 MCP Host——你给它一份配置(声明用哪个模型、连哪些 MCP server、agent 的人设),它就把这些东西装配成一个能对话、能调工具的 agent,并通过 HTTP 暴露出来(自带聊天 UI)。它最独特的地方在于:几乎所有东西内部都长成一个 MCP server,连它自己对外也是一个 MCP server。


1. 这是什么(零基础也能懂)

一句话定义: nanobot 是把「大语言模型 + 一组 MCP server + 一份配置」组装成 agent 体验的宿主程序(host)。

先说清一个词:MCP(Model Context Protocol,模型上下文协议),是给 LLM 提供工具/资源/提示词的标准协议;提供这些能力的进程叫 MCP server。而 MCP Host 就是那个"把 LLM、上下文、若干 MCP server 拼到一起,对用户呈现成一个 agent"的服务(定义见 README.md:13-19)。

给谁用 / 解决什么问题。 VSCode、Claude、Cursor、ChatGPT、Goose 里都内置了 MCP host,但它们绑死在各自的应用里。nanobot 把这一层单独拆出来做成开源、可部署的组件(README.md:8-11):

  • 想自建一个"接了某几个 MCP server 的专属聊天机器人"的人;
  • 想把 agent 能力嵌进自己应用(网页、Slack、SMS、语音……)的开发者;
  • 想快速把一个 MCP server 包装成"能聊天的产品"的人(官方示例:Blackjack 游戏、HuggingFace、Shopify 购物)。

它能做什么。

  • 读一份配置,连上本地(stdio 子进程)或远程(HTTP)的 MCP server;
  • 用配置里指定的模型(OpenAI / Anthropic / 及各种兼容端点)驱动对话;
  • 跑一个多轮工具调用循环:模型说要调工具 → nanobot 调 → 把结果喂回模型 → 直到模型给出最终回答;
  • 通过 HTTP 对外服务,并自带一个聊天 UI(默认 http://localhost:8080);
  • 反过来,nanobot 自己也能作为一个 MCP server 被别的 host 接入。

用起来什么样(最小示例)。 nanobot 支持两种配置写法。

第一种,单文件 nanobot.yaml——声明一个 agent 和它要连的 MCP server(README.md:78-96):

agents:
dealer:
name: Blackjack Dealer
model: gpt-4.1
mcpServers: blackjackmcp

mcpServers:
blackjackmcp:
url: https://blackjack.nanobot.ai/mcp
export OPENAI_API_KEY=sk-...
nanobot run ./nanobot.yaml
# UI: http://localhost:8080

第二种,目录式配置——一个共享的 nanobot.yaml 放 mcpServers / llmProviders,每个 agent 单独写成一个带 YAML 前置元数据的 Markdown 文件,正文就是 agent 的指令(README.md:181-197、真实示例见 examples/directory-config/agents/main.md:1-19):

---
name: Shopping Assistant
model: claude-3-7-sonnet-latest
mcpServers:
- store
temperature: 0.7
---

You are a helpful shopping assistant for the shopify.supply store.
Help users find products, answer questions about items, and provide excellent customer service.
nanobot run ./my-config/

约定:agents/main.md 若存在会自动成为默认入口;agents/*.md 里的同名 agent 会覆盖 nanobot.yaml 里的定义(README.md:207-216)。配置合并与优先级的细节见 配置与 Agent 定义

一句话直觉 / 类比。 把 nanobot 想成一台**"agent 的组装机"**:模型是发动机,MCP server 是各种可插拔的零件(工具、数据源、提示词),配置是装配图;nanobot 负责把它们拧到一起、通上电,再从一个门(HTTP)开出去。而它最"反直觉"的一点是——连它内部的零件、连它自己,都用同一种插槽(MCP)


2. 顶层全景(它大概怎么转)

怎么读这张图: 从上到下是一次 nanobot run 的启动 + 一次对话请求的走向;左边是启动装配,右边是运行时请求。

nanobot run ./config ← main.go → pkg/cli/root.go


┌───────────────┐ 加载 + 合并 + 校验配置(YAML / Markdown / extends / profiles)
│ config.Load │ pkg/config/load.go
└──────┬────────┘
│ types.Config

┌───────────────┐ 装配:LLM 客户端 + 工具服务 + agents + sampler
│ NewRuntime │ pkg/runtime/runtime.go
└──────┬────────┘ 并注册一堆「内置 MCP server」(meta / agent / system / skills…)
│ *Runtime(内嵌 tools.Service)

┌───────────────┐ HTTP host:MCP over HTTP,建/管会话,路由 JSON-RPC
│ HTTP Host │ pkg/server/server.go + pkg/mcp/httpserver.go
└──────┬────────┘
│ 一次 tools/call(或 chat)请求进来

┌───────────────┐ Agent 主循环:装配请求 → 调 LLM → 执行工具 → 回灌 → 收敛
│ agents.Complete│ pkg/agents/run.go
└──────┬────────┘
│ ┌──────────────┐ 给模型调的工具,来自这些 MCP server
├───────▶│ MCP servers │ pkg/tools/service.go(连接 / 映射 / 调用)
│ │ 远程 HTTP │ pkg/servers/*(内置:agent/meta/system/…)
│ │ 本地 stdio │
│ └──────────────┘

┌───────────────┐ 按 model 名选方言(OpenAI/Anthropic/…),转发到真实 API
│ llm.Client │ pkg/llm/client.go
└───────────────┘

主线走一遍(高层,不进代码):

  1. CLI 解析参数,拿到配置路径,加载并合并出一份 types.Config;
  2. NewRuntime 把 LLM 客户端、工具服务、agents 引擎、sampler 拧到一起,并注册所有内置 MCP server;
  3. 起 HTTP host,对外说 MCP 协议;客户端(或自带 UI)initialize 建一个会话;
  4. 用户发一句话 → 走到 agents.Complete;它把 agent 的指令、历史、可用工具装配成一次 LLM 请求;
  5. LLM 若要调工具,循环执行工具、把结果回灌,直到模型给出最终答复;
  6. 工具的实际执行由工具服务分发到对应 MCP server(远程 HTTP 或本地 stdio 子进程,或内置 server)。

部件一句话职责表(每行:包 | 干什么 | 关键入口):

一句话职责关键符号 / 文件
pkg/cliCLI 定义(run / call / sessions 等),装配启动流程、起 HTTP 或 stdio serverNewrunMCPpkg/cli/root.go:36,279
pkg/config加载 / 合并 / 校验配置,支持多来源、extends 继承、profiles、内置 agentLoadManyMergepkg/config/load.go:28,213
pkg/runtime顶层装配层:创建并连线 LLM 客户端、工具服务、agents、sampler,注册内置 serverNewRuntimepkg/runtime/runtime.go:82
pkg/serverHTTP host 的 MCP 语义层:分发 initialize / tools/list / tools/call 等方法NewServeronMessagepkg/server/server.go:44,480
pkg/mcpMCP 协议实现:会话、client、wire(stdio/HTTP 传输)、hook、类型NewHTTPServerpkg/mcp/httpserver.go:151
pkg/agentsAgent 执行引擎:请求装配、多轮工具循环、hook、上下文压缩Completepkg/agents/run.go:384
pkg/tools工具与 agent 的中央注册表:构建工具映射、连接 MCP server、分发调用、跑 hookBuildToolMappingsGetClientCallRunHookpkg/tools/service.go:983,289,658,625
pkg/llm按 model 名解析 provider,按 dialect 把请求路由到对应 API 客户端NewClientCompletepkg/llm/client.go:37,72
pkg/sampling处理 MCP 的 sampling 请求(server 反过来请 host 帮它调一次 LLM)NewSamplerSamplepkg/sampling/sampler.go:26,118
pkg/servers内置 MCP server 集合(agent / meta / system / skills / tasks / …)NewServer 于各子包 — pkg/servers/*/
pkg/session用户会话与对话状态管理、数据库存储、父子会话关系pkg/session/(见 runMCPsession.NewManager)

各部件的深入拆解散落在后面各章,本章只给"大盘"。


3. 巧妙之处提要(读者要带走的精华)

下面五点是 nanobot 区别于普通 "LLM + 工具" 脚手架的地方,每点先白话点出"妙在哪",再给源码锚点。深入讲解见对应章节。

3.1 万物皆 MCP:反身式(reflexive)设计

NewRuntime 启动时,把一堆内置能力也注册成 MCP server:nanobot.meta(自省:列/改会话、列 agent)、nanobot.agent(把每个 agent 暴露成一个带 chat 工具的 server)、nanobot.systemnanobot.skillsnanobot.tasks 等(pkg/runtime/runtime.go:124-165)。

妙处在于统一接口:不管是外部远程 server、本地子进程、还是 nanobot 自己的内置能力,对上层都长成同一种"MCP server + tool"。连 nanobot 对外也是一个 MCP server。详见 万物皆 MCP

3.2 hook 即工具调用

agent 的生命周期 hook(config / request / response / session)不是特殊回调,而是被当成一次普通的 MCP 工具调用来跑。InvokeHooks 遍历 hook 映射(pkg/mcp/hooks.go:59),最终 RunHook 把 hook target server/tool 直接拆开、s.Call(server, tool, in) —— 和调任何工具走完全一样的路径(pkg/tools/service.go:625-627)。

于是"改配置""改请求""改回答"这些扩展点,复用了同一套工具调用基础设施,不需要另造一套机制。见 万物皆 MCP §hook

3.3 client-per-server 会话缓存 + env 指纹

每个 MCP server 的连接(mcp.Client)是按会话缓存的:GetClient 把 client 存进会话,后续同一会话复用同一个 client,不重连(pkg/tools/service.go:289-313)。

更细的一手:缓存键还带上环境变量的 sha256 指纹(envMapHash,service.go:315-321)——环境变了(比如换了 token),factory.get(envHash) 会重建 client,避免拿旧凭证连错。见 工具与 MCP 层。

3.4 上下文压缩(context compaction)

对话变长会撑爆上下文窗口。nanobot 在每次 run 前估算 token,一旦超过上下文窗口的 83.5%(compactionThreshold = 0.835,默认窗口 200k)就触发压缩:把历史消息总结成一条带特殊 meta 标记的摘要,替换掉原始长历史(pkg/agents/compact.go:15-37shouldCompact)。

巧在阈值可按 agent 配的 contextWindow 调整,且压缩后的摘要会在轮次间携带(pkg/agents/run.go:610-632)。见 Agent 主循环

3.5 LLM 方言路由(dialect routing)

配置里写 model: anthropic/claude-...openai/gpt-...,llm.Client.Complete 先按 {provider}/{model} 解析出 provider,再按该 provider 的 dialect switch 到对应的 API 客户端:Anthropic Messages、OpenAI ChatCompletions、OpenAI/Open Responses、Bifrost 等(pkg/llm/client.go:130-162)。

妙处:上层 agent 循环完全不感知底层是哪家模型;换模型只是改配置里一个字符串。Azure / Bedrock / Ollama 都靠"选一个 dialect + 指一个 baseURL"接入。见 LLM 方言路由与 Sampling


4. 阅读地图(建议顺序)

本子库按"由浅入深、顺着数据流"排。推荐这样读:

  1. 本章(index) — 先建立"这是什么、大盘怎么转"的认知(你在这)。
  2. 01-config-and-agents.md — 配置与 Agent 定义。理解一切的起点:YAML / Markdown 两种写法、前置元数据字段、多来源合并与 extends / profiles 优先级。入口 config.LoadMany
  3. 02-agent-loop.md — Agent 主循环。一次对话如何从请求装配、调 LLM、执行工具、回灌、直到收敛;顺带上下文压缩。入口 agents.Complete
  4. 03-tools-and-mcp.md — 工具与 MCP 层。工具怎么从各 server 汇总成映射、client 怎么按会话连接与缓存、tools/call 怎么分发。入口 tools.Service
  5. 04-llm-dialects-and-sampling.md — LLM 方言路由与 Sampling。多模型如何被统一到一个 Complete 接口;server 反向请求 host 采样是怎么回事。入口 llm.Client / sampling.Sampler
  6. 05-reflexive-mcp-design.md — 万物皆 MCP。把前面几章串起来,看清 nanobot 最核心的架构哲学:内置 server、hook 即工具调用、host 反身式暴露。收尾章,建议最后读。

如果你只想快速定位某个机制:配置合并看 01;多轮循环 / 压缩看 02;server 连接 / 工具映射看 03;换模型 / 多 provider看 04;为什么处处是 MCP看 05。


5. 边界与定位(诚实提醒)

  • 仍在重度开发中。 README 顶部明确警告本项目"正在偏离最初设计与意图,预期会有重大破坏性变更、架构调整、API 演进"(README.md:5-6)。本文引用锚定在 sourceCommit 所示提交。
  • 本章不深入代码细节。 顶层全景与主线为主;每个机制的真实走读留给对应章节,避免在导航页堆砌代码。
  • 前端 UI 不在本子库范围。 nanobot 自带一个 Svelte 聊天 UI(前端源码在 packages/ui),本子库聚焦 Go 后端的 host / agent / MCP 机制。

6. 代码地图(导航索引)

一张跳转表:想直接读源码时按符号名 grep 定位(比行号抗漂移)。

主题文件路径符号名
程序入口 / _exec 特殊模式main.gomain
CLI 定义与启动装配pkg/cli/root.goNewNanobot.runMCP
配置加载 / 合并 / 内置 agentpkg/config/load.goLoadManyMergeloadBuiltinAgents
顶层运行时装配 + 内置 server 注册pkg/runtime/runtime.goNewRuntime
HTTP host(MCP over HTTP)pkg/mcp/httpserver.goNewHTTPServerHTTPServer.serveHTTP
MCP 方法分发(tools/list、tools/call…)pkg/server/server.goServer.initServer.handleCallToolServer.handleListToolsServer.onMessage
Agent 主循环pkg/agents/run.goAgents.CompleteAgents.run
上下文压缩pkg/agents/compact.goshouldCompactAgents.compactcompactionThreshold
工具映射 / 连接 / 调用 / hookpkg/tools/service.goBuildToolMappingsGetClientCallRunHook
hook 编排(hook 即工具调用)pkg/mcp/hooks.goInvokeHooks
LLM 方言路由pkg/llm/client.goNewClientClient.Complete
MCP sampling(host 反向采样)pkg/sampling/sampler.goNewSamplerSampler.Sample
内置 agent-as-server(chat 工具)pkg/servers/agent/agent.goNewServerchatCall