Nanobot(OpenClaw 轻量实现)的底层原理解析

作者:vivo AI技术开发团队- Lin Weiwei
本文以精简版 OpenClaw——Nanobot 为切入点,拆解其核心原理。 其本质是基于循环执行的“提示词构建 + 调用大模型 + 工具操作”的本地 Agent 架构。通过分析消息处理、上下文构建、循环决策(AgentLoop)与工具调用(Tools)等流程,揭示其运行机制。

1分钟看图掌握核心观点👇

动图
 
动图封面
 

图 1 VS 图 2,您更倾向于哪张图来辅助理解全文呢?欢迎在评论区留言。

一、轻量版的OpenClaw:Nanobot

OpenClaw 为何能引爆科技圈,底层是否出现了颠覆性的新技术?这是近期每个 AI 从业者都在好奇的问题。虽然网上已有海量的解析文章,但堆砌的各种新概念往往让人迷失其中。面对技术狂欢,回归代码本源才是看清事物本质的最佳捷径。

1.1 核心结论先行

OpenClaw 本质上是一个运行在用户终端的Agent。它的核心运转逻辑比较常规:循环环调用大模型 API -> 解析输出 -> 本地执行系统命令 -> 结果回传。它并没有引入颠覆性的 AI 新技术,在架构上,与过去运行在服务端的 Agent(智能体)没有本质区别。

既然底层逻辑如此简单,它为何能引发如此大的反响?我认为核心在于以下两点:

(1)开放性强(真正接管用户电脑)

过去的服务端 Agent 被限制在云端。而 OpenClaw 极其大胆地开放了本地权限,允许大模型动态生成并执行 Python、Shell 等脚本。这种“放权”让大模型从“只能聊天的智囊”变成了“能敲键盘的双手”,直接操作本地文件和应用,能力边界得到了实质性的突破。

(2)可玩性高(从“云端公共智能体”到“专属机器人”)

过去所有人都能访问,缺乏“拥有感”和“实体感”。

现在,当你把它部署在自己的电脑上,个性化配置(可以逛clawhub市场配置Skills、Tools)、强烈的个人专属感,极大地满足了大众的参与感与“炫耀欲”。

1.2 五脏俱全的Nanobot

由于 OpenClaw 源码庞杂,40万行要看完需要1-2周时间,但抢占商机却需要争分夺秒,等不了太久。因此我们需要快速取其精华。为了降低阅读门槛,本文选择从它的“微缩版”——开源项目 Nanobot 入手。Nanobot 麻雀虽小,五脏俱全。通过拆解这段精简的源码,我们能以极低的认知成本,快速看透 OpenClaw 的底层运行逻辑。

我通过启动Nanobot控制电脑自行安装并部署一个博客网站,一次性通过。核心功能已经与OpenClaw相当。

以下是我实际运行的情况:

二、Nanobot技术架构

2.1 技术流程图

2.2 技术架构图

为了更容易理解,后续内容按一次消息的生命周期顺序来讲解。

2.3 一条消息的完整生命周期

以下是OpenClaw的完整生命周期的6个阶段,Nanobot同样也是以下6个阶段。各环节有一定的简化。

图片来源:链接

三、阶段一:消息接收

channel模块负责建立起与外部渠道的连接、消息收发,是Nanobot重要的模块。

3.1 初始化阶段

(ChannelManager,在gateway命令中执行)

初始化:在 _init_channels 方法中,根据配置动态加载并实例化具体的频道类(如 Telegram-

Channel、DiscordChannel 等)。

Config (YAML/JSON)
        │
        ▼
┌─────────────────┐
│ ChannelManager  │
│   .__init__()   │
└────────┬────────┘
         │ _init_channels()
         ▼
┌─────────────────────────────────────────────────────────────┐
│ 遍历配置中 enabled 的 channel,按需动态 import:             │
│   • TelegramChannel    • DiscordChannel    • FeishuChannel │
│   • WhatsAppChannel    • SlackChannel      • DingTalkChannel│
│   • EmailChannel       • QQChannel         • MatrixChannel  │
│   • MochatChannel                                           │
└─────────────────────────────────────────────────────────────┘
         │
         ▼
self.channels: dict[str, BaseChannel]

3.2 通信层Channel

Channel 是通信模块,底层使用 WebSocket、Http 长轮询(Long Polling)、Webhook等多种技术。

3.3 消息流程: Inbound (接收用户消息)

在实例化具体频道时,ChannelManager 会将 MessageBus(self.bus)作为参数传递给它们。

具体的频道实例从外部平台接收到消息后,会直接使用这个 MessageBus 将消息投递到内部系统的处理队列中。

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│  聊天平台    │     │  Channel    │     │  MessageBus │
│ (Telegram/  │────▶│ (实现类)    │────▶│  (队列)     │
│  Discord/   │     │ .start()    │     │             │
│  Feishu...) │     │ 监听消息    │     │ publish_    │
└─────────────┘     └──────┬──────┘     │  inbound()  │
                           │            └──────┬──────┘
                           │                     │
                           │              ┌──────▼──────┐
                           │              │  Agent       │
                           │              │ (处理消息)   │
                           │              └─────────────┘
                           │
            ┌──────────────┴──────────────┐
            │   _handle_message()          │
            │   1. is_allowed() 权限检查   │
            │   2. 构建 InboundMessage     │
            │   3. bus.publish_inbound()  │
            └─────────────────────────────┘

四、阶段二:访问控制与路由

此阶段Nanobot实现非常简单,相比之下OpenClaw会复杂一些,做了不同群聊、私聊等不同级别的会话权限控制。

五、阶段三:提示词构建ContextBuilder

  • 我们知道大龙虾是用户私人机器人,那么她怎么做到感知用户,有记忆的?
  • 我们说了很多的session、memory、Skills等在哪个环节被使用的?
  • Skills与Tools的区别是什么?

Nanobot将prompt做了拆分,将各种来源的信息(系统身份、历史对话、记忆、技能等)组装成 LLM 能理解的格式。

Agents.md 举例

# 代理指令

你是一个有帮助的 AI 助手。保持简洁、准确和友好。

## 定时提醒

当用户要求在特定时间提醒时,使用 `exec` 运行:

>Nanobot cron add --name "reminder" --message "您的信息" --at "YYYY-MM-DDTHH:MM:SS" --deliver --to "USER_ID" --channel "CHANNEL"

从当前会话中获取 USER_ID 和 CHANNEL(例如,从 `telegram:8281248569` 中获取 `8281248569` 和 `telegram`)。

**不要只是将提醒写入 MEMORY.md** — 那不会触发实际通知。

## 心跳任务

每 30 分钟检查 `HEARTBEAT.md`。使用文件工具管理周期性任务:

- **添加**:使用 `edit_file` 追加新任务
- **移除**:使用 `edit_file` 删除已完成的任务
- **重写**:使用 `write_file` 替换所有任务

当用户要求创建重复/周期性任务时,更新 `HEARTBEAT.md` 而不是创建一次性 cron 提醒。

为什么不直接写成prompt,因为分开写更容易阅读和维护。

5.1 在整个流程中的位置

消息流程中的 ContextBuilder

────────────────────────────────────────────────────────────────────────

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Channel   │────▶│  MessageBus │────▶│  AgentLoop  │
└─────────────┘     └──────┬──────┘     └──────┬──────┘
                           │                    │
                           │                    ▼
                           │            ┌───────────────┐
                           │            │ _process_message()
                           │            └───────┬───────┘
                           │                    │
                           │                    ▼
                           │            ┌───────────────┐
                           │            │ ContextBuilder│ ← 本文件
                           │            │ .build_messages│
                           │            └───────┬───────┘
                           │                    │
                           ▼                    ▼
                    ┌─────────────┐     ┌─────────────┐
                    │   Session   │     │      LLM    │
                    │   (历史)    │     │   Provider  │
                    └─────────────┘     └─────────────┘

5.2 构建流程

build_system_prompt()

build_system_prompt()
     │
     ├─▶ _get_identity()          # 核心身份
     │    # Nanobot 🐈
     │    You are Nanobot, a helpful AI assistant.
     │    Runtime: macOS x86_64, Python 3.x.x
     │    Workspace: /path/to/workspace
     │
     ├─▶ _load_bootstrap_files()  # 引导文件
     │    加载: AGENTS.md, SOUL.md, USER.md, TOOLS.md, IDENTITY.md
     │
     ├─▶ memory.get_memory_context()  # 长期记忆
     │    ## Long-term Memory
     │    (从 memory/MEMORY.md 读取)
     │
     ├─▶ Skills.load_Skills_for_context()  # 常用技能
     │    # Active Skills
     │
     └─▶ Skills.build_Skills_summary()  # 技能摘要
          # Skills (可用技能列表,供 LLM 按需加载)

构建系统提示词 (build_system_prompt)

构建prompt通过以下5个步骤分别获取5个部分的prompt内容并拼接。

将多个来源的信息拼接成完整的 system prompt:

① _get_identity()构建本地设备信息、划定存储目录、行为准确prompt

这是源码:

def _get_identity(self) -> str:
    """Get the core identity section."""
    workspace_path = str(self.workspace.expanduser().resolve())
    system = platform.system()
    runtime = f"{'macOS' if system == 'Darwin' else system} {platform.machine()}, Python {platform.python_version()}"
  
    return f"""# Nanobot 🐈

You are Nanobot, a helpful AI assistant.

## Runtime
{runtime}

## Workspace
Your workspace is at: {workspace_path}
- Long-term memory: {workspace_path}/memory/MEMORY.md (write important facts here)
- History log: {workspace_path}/memory/HISTORY.md (grep-searchable). Each entry starts with [YYYY-MM-DD HH:MM].
- Custom Skills: {workspace_path}/Skills/{{Skill-name}}/Skill.md

## Nanobot Guidelines
- State intent before tool calls, but NEVER predict or claim results before receiving them.
- Before modifying a file, read it first. Do not assume files or directories exist.
- After writing or editing a file, re-read it if accuracy matters.
- If a tool call fails, analyze the error before retrying with a different approach.
- Ask for clarification when the request is ambiguous.

Reply directly with text for conversations. Only use the 'message' tool to send to a specific chat channel."""

② ._load_bootstrap_files() 添加预设的个性、行为准则等prompt

Agents.mdSoul.mdUser.md是预设的prompt部分内容,可以设定用户个性、设定智能体的行为准则、哪些事情能做,哪些事情不能做,遇到特定事情要怎么做,设定有哪些工具可以用。

③ memory.get_memory_context() 长期记忆的prompt构建

长期记忆的文件存储在memory/MEMORY.md文件中,程序在运行中会不断维护这个文件。memory.get_memory_context() 为读取该文件内容。

④ Skills.load_Skills_for_context() 拼接常用技巧的prompt

注意Skill与tools的区别:

在 AI 智能体的开发语境中,Skill(技能)和 Tool(工具)在很多框架(如 OpenAI API、LangChain)中经常被混用,但从概念和设计逻辑上,它们有明显的层级和本质区别:

  • Skill:Agent 解决特定问题的一套工作流(Workflow)或经验(Prompt + Logic)。它通常包含“什么时候用”、“怎么用”以及“如何处理结果”的认知。
  • Skill = prompt + Tools + Workflow。它是prompt中的一个组成部分,可以在开始时传递,也可以在后续过程中传递。
  • Tool: 纯粹的代码逻辑、API 或物理/软件接口。它不具备智能,只负责“输入 A,输出 B”。特点:通用性强,与具体的 Agent 无关。谁都可以调用它。它通过Function Call参数传递,不在prompt中。

⑤ build_Skills_summary() 构建技能摘要列表

用于生成所有可用技能的摘要列表,以 XML 格式返回,供 LLM 了解有哪些技能可以调用。

采用渐进式加载策略:只给 LLM 看技能名称和描述,完整内容需要时再通过 read_file 读取

以天气技能为例,获得的摘要内容如下:

# Skills

The following Skills extend your capabilities. To use a Skill, read its Skill.md file using the read_file tool. Skills with available="false" need dependencies installed first - you can try installing them with apt/brew.

<Skills>
  <Skill available="true">
    <name>weather</name>
    <description>Get current weather and forecasts (no API key required).</description>
    <location>/path/to/Nanobot/Skills/weather/Skill.md</location>
  </Skill>
</Skills>

构建消息列表 (build_messages)

生成发送给 LLM 的完整消息数组:

处理图片 (_build_user_content)

如果用户发送了图片,将图片转为 base64 编码后放入消息:

# 输入: 用户消息 + 图片路径列表
# 输出: 
[
    {"type": "image_url", "image_url": {"url": "data:image/png;base64,..."}},
    {"type": "text", "text": "用户消息内容"}
]

5.3 数据流示例

用户发送: "帮我看看这个图片里有什么" + 图片文件

                     │
                              ▼
                    ContextBuilder.build_messages()
                              │
          ┌───────────────────┼───────────────────┐
          │                   │                   │
          ▼                   ▼                   ▼
    ┌──────────┐       ┌──────────┐       ┌──────────┐
    │ System   │       │ History  │       │ Current  │
    │ Prompt   │       │ (Session)│       │ Message  │
    │          │       │          │       │ + Image  │
    └──────────┘       └──────────┘       └──────────┘
          │                   │                   │
          └───────────────────┼───────────────────┘
                              │
                              ▼
                    [
                      {"role": "system", "content": "你是 Nanobot..."},
                      {"role": "user", "content": "之前对话..."},
                      {"role": "user", "content": "Current Time..."},
                      {"role": "user", "content": [
                        {"type": "image_url", "image_url": {"url": "data:image/..."}},
                        {"type": "text", "text": "帮我看看这个图片里有什么"}
                      ]}
                    ]
                              │
                              ▼
                         LLM Provider

5.4 内置Skill清单

当前可用 Skills 列表

六、阶段四、阶段五:AgentLoop

Nanobot的奥秘,即

  • 如何循环迭代直至完成任务?循环是否有上限?
  • 是怎么调用Tools来完成的,要调哪些Tools?
  • Token消耗巨大的原因是什么?

答案都在AgentLoop中。

Agent 模块是 Nanobot 的核心处理引擎,负责在接收消息后调用 LLM、执行工具、生成回复,并管理会话状态和长期记忆。

6.1 核心组件

agent/
├── loop.py       # AgentLoop - 核心处理代码
├── context.py    # ContextBuilder - 构建系统提示词
├── memory.py     # MemoryStore - 两层记忆系统
├── Skills.py     # SkillsLoader - 技能加载器
├── subagent.py   # SubagentManager - 子任务管理器
└── tools/        # 工具集
    ├── base.py        # Tool 抽象基类
    ├── registry.py    # ToolRegistry 工具注册表
    ├── filesystem.py  # 文件操作工具
    ├── shell.py       # ExecTool 命令执行
    ├── message.py     # MessageTool 消息发送
    ├── web.py         # WebSearchTool, WebFetchTool
    ├── spawn.py       # SpawnTool 子任务触发
    ├── cron.py        # CronTool 定时任务
    └── mcp.py         # MCP 工具连接

6.2 AgentLoop 核心处理流程

核心循环_run_agent_loop的位置

_process_message() 是处理单条消息的核心方法:

_process_message(msg: InboundMessage)
     │
     │ 1. 获取或创建 Session
     ▼
session = sessions.get_or_create(session_key)
     │
     │ 2. 检查是否需要历史记录
     │    (当 messages 超过 memory_window)
     ▼
触发 _consolidate_memory() 异步任务
     │
     │ 3. 构建 Context (系统提示词 + 历史)
     ▼
ContextBuilder.build_messages()
     │
     │ 4. 运行 Agent Loop
     ▼
_run_agent_loop(messages, on_progress)
     │
     │ 5. 保存会话 & 内存合并
     ▼
_save_turn() + sessions.save() + _consolidate_memory()
     │
     │ 6. 返回响应
     ▼
OutboundMessage → bus.publish_outbound()

源代码如下:

核心循环_run_agent_loop内部实现

run_agent_loop实现 ReAct 模式(推理 + 行动),用于执行 AI 代理(Agent)的核心迭代循环。它的主要作用是不断与大语言模型(LLM)交互,直到模型给出最终回答或达到最大迭代次数。

① 参考源码可以看到循环是通过while实现

循环的调用大模型以及执行大模型返回需要运行的工具,持续将大模型结果以及工具执行结果添加到上下文中。

② 循环的极限是多少?设置最大迭代次数,避免无限循环:

超过前迭代次数小于设定的最大迭代次数(self.max_iterations,设置的是40次),则结束执行并返回已有结果。

现在让我们回答开篇的问题2: 如何循环迭代直至完成任务?循环是否有上限?while实现、上限40次;

并且粗暴的while循环,与快速增加的上下文增长(prompt增长),使得token消耗非常大。

③ Tools是怎么调用的?

# 1.调用大模型API
  response = await self.provider.chat(
  messages=messages,  
      tools=self.tools.get_definitions(), # 可用的Tools清单、使用说明书
      model=self.model,   
      temperature=self.temperature,  
      max_tokens=self.max_tokens,   
  )
  • 在prompt中已经将tools清单和使用说明书添加,大模型返回信息中会告诉应该要调用哪些工具。(众所周知,这是大模型的基本特性)

一份说明书通常是一个 JSON Schema,包含:

a.工具名称(如 get_weather)

b. 功能描述(如“获取指定城市的天气情况”)

c. 参数要求(如需要 city 字段,类型为字符串)

  • 大模型自主决策并生成调用指令(触发调用)

大模型会按照约定的格式(通常是 JSON),输出一个包含工具名称和具体参数的特殊响应。

  • 宿主系统(这里为电脑)并反馈(实际执行)

程序提取参数并在本地实际执行对应的函数:await self.tools.execute(tool_call.name, tool_call.arguments),并获得结果。

④ 如何完成本地电脑操作的与任务执行的?

通过上述分析,我们可以知道是通过大模型返回参数(动态的程序)、并结合本地tool(shell脚本等)来完成的。

包含以下几个关键环节:

  • 大模型生成代码(参数):大模型根据用户的自然语言指令,推理出需要执行的步骤,并编写相应的代码(通常是 Python、Shell 脚本、AppleScript 或 PowerShell)。这段代码就是大模型调用 Tool 时传入的“参数”。
  • Tool 执行环境(执行器):你提到的“shell脚本运行程序”在实际框架中通常是一个代码执行沙箱。这个 Tool 接收到代码后,会在真实的操作系统或 Docker 容器中拉起对应的解释器(如 Python 解释器、Jupyter 内核或 Bash 终端)来运行这段代码。

备注:这部分我们在Tools章节单独讲解

到这里,我们其实已经知道了Nanobot核心所在了。其他的内容诸如提示词构建、记忆、Skills、tools等只是“噪音”。后面环节为我们继续揭秘。

七、阶段六:发送回复

消息流程: Outbound (发送回复)

ChannelManager 在启动时会创建一个后台协程任务 _dispatch_outbound。

该任务通过 self.bus.consume_outbound() 不断从消息总线中监听并拉取待发送的 OutboundMessage。

获取到消息后,通过 msg.channel 识别目标平台,找到对应的频道实例,并调用 channel.send(msg),由具体频道类完成向外部平台的最终网络发送请求。

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Agent     │     │  MessageBus │     │   Channel   │
│ (生成回复)  │────▶│  (队列)     │────▶│  Manager    │
│             │     │             │     │             │
│ publish_    │     │ consume_    │     │ _dispatch   │
│ outbound()  │     │ outbound()  │     │ _outbound() │
└─────────────┘     └──────┬──────┘     └──────┬──────┘
                           │                     │
                           │                     ▼
                           │            ┌─────────────────┐
                           │            │ channel.send()  │
                           │            │ (调用具体实现)   │
                           │            └────────┬────────┘
                           │                     │
                           │                     ▼
                           │            ┌─────────────────┐
                           │            │  对应平台 API   │
                           │            │ (Telegram Bot/  │
                           │            │  Discord REST/  │
                           │            │  Feishu Web...) │
                           │            └─────────────────┘
                           │                     │
                           │                     ▼
                           │            ┌─────────────────┐
                           └───────────▶│   用户收到消息   │
                                        └─────────────────┘

六个阶段回顾

图片来源:链接

八、Agent记忆存储-Memory

8.1 MemoryStore 两层记忆系统

架构设计

MemoryStore 采用两层记忆架构,在会话内存和长期存储之间进行平衡:

┌─────────────────────────────────────────────────────────────────┐
│                      两层记忆架构                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Session (内存)                                                  │
│  ┌─────────────────────────────────────────┐                   │
│  │ messages: [...]                              │               │
│  │ last_consolidated: int                       │               │
│  │ updated_at: datetime                         │               │
│  └─────────────────────────────────────────┘                   │
│        │                                                          │
│        │ _consolidate_memory() (当 messages 超过 memory_window) │
│        ▼                                                          │
│  ┌──────────────────────┐      ┌────────────────────────────┐  │
│  │  memory/MEMORY.md    │      │  memory/HISTORY.md          │  │
│  │  (长期记忆)           │      │  (可 grep 搜索的历史日志)    │  │
│  │                      │      │                             │  │
│  │ ## Long-term Memory  │      │ [2026-03-0510:30] USER:   │  │
│  │ - 重要事实           │      │ [2026-03-0510:32] ASSISTANT│  │
│  │ - 用户偏好           │      │ [2026-03-0510:35] USER:   │  │
│  │ - 项目知识           │      │                             │  │
│  └──────────────────────┘      └────────────────────────────┘  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

内存合并触发条件

  • 自动触发:

unconsolidated >= memory_window(默认 100 条消息)

  • 手动触发: 用户发送 /new 命令(立即归档所有记忆)

合并流程

_consolidate_memory(session, archive_all=False)
     │
     │ 1. 提取待合并的消息
     ▼
old_messages = session.messages[last_consolidated:-keep_count]
     │
     │ 2. 构建 prompt 给 LLM
     ▼
prompt = "Process this conversation and call save_memory tool..."
     │
     │ 3. 调用 LLM 进行记忆抽取
     ▼
response = await provider.chat(messages, tools=[save_memory])
     │
     │ 4. 解析 LLM 返回的工具调用
     ▼
args = response.tool_calls[0].arguments
     │
     ├─▶ history_entry → append_history()  # 写入 HISTORY.md
     └─▶ memory_update → write_long_term() # 更新 MEMORY.md

九、能力提升的奥秘-Tools工具

前面提到的热点15个能力,主要靠本章节提到的这些工具。如果缺乏这些工具,那么各类龙虾机器人跟服务端运行的智能体没有太大区别。

那么, 沙箱安全环境真的存在吗,是否影响宿主系统?

9.1 Tool 工具系统

工具基类

所有工具继承自 Tool 抽象基类:

deny_patterns = [
    r"\brm\s+-[rf]{1,2}\b",          # rm -r, rm -rf
    r"\bdel\s+/[fq]\b",              # Windows del /f
    r"\brmdir\s+/s\b",               # Windows rmdir /s
    r"(?:^|[;&|]\s*)format\b",       # format 磁盘
    r"\b(mkfs|diskpart)\b",          # 磁盘操作
    r"\bdd\s+if=",                   # dd 裸设备
    r">\s*/dev/sd",                  # 写磁盘设备
    r"\b(shutdown|reboot|poweroff)\b",  # 关机重启
    r":\(\)\s*\{.*\};\s*:",          # fork bomb
]

内置工具

AgentLoop._register_default_tools() 注册默认工具:

工具执行流程

LLM 返回 tool_calls
     │
     ▼
ToolRegistry.execute(name, arguments)
     │
     ├─▶ 参数校验 (validate_params)
     │    对照 JSON Schema 检查类型、必填、范围
     │
     └─▶ 执行工具 (execute)
          │
          ▼
        返回结果字符串
          │
          ▼
        添加到 messages:
        {"role": "tool", "tool_call_id": "...", "name": "...", "content": "..."}

9.2 重点工具Shell介绍

ExecTool 核心解析

重要节点的技术流程

┌─────────────────────────────────────────────────────────────────────────────┐
│                           ExecTool 执行流程                                  │
└─────────────────────────────────────────────────────────────────────────────┘

LLM 调用 exec(command="...", working_dir="...")
     │
     ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1. 参数处理                                                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│ cwd = working_dir or self.working_dir or os.getcwd()                       │
│ env = os.environ.copy() + path_append                                       │
└─────────────────────────────────────────────────────────────────────────────┘
     │
     ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 2. 安全检查 (_guard_command)                                                │
├─────────────────────────────────────────────────────────────────────────────┤
│ • 正则匹配 deny_patterns (危险命令)                                         │
│ • 白名单检查 allow_patterns (可选)                                          │
│ • 路径遍历检测 (..)                                                         │
│ • 工作区外路径检测 (restrict_to_workspace)                                  │
│                                                                            │
│ 返回: None (通过) 或 错误信息 (拦截)                                         │
└─────────────────────────────────────────────────────────────────────────────┘
     │
     ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 3. 命令执行                                                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│ process = asyncio.create_subprocess_shell(                                 │
│     command,                                                               │
│     stdout=PIPE,                                                           │
│     stderr=PIPE,                                                           │
│     cwd=cwd,                                                               │
│     env=env,                                                               │
│ )                                                                          │
└─────────────────────────────────────────────────────────────────────────────┘
     │
     ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 4. 超时控制                                                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│ await asyncio.wait_for(process.communicate(), timeout=60)                 │
│                                                                            │
│ 超时处理:                                                                   │
│ • process.kill() 杀死进程                                                   │
│ • process.wait() 等待完全终止                                               │
│ • 返回超时错误                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
     │
     ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 5. 输出处理                                                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│ • stdout/stderr 解码 (UTF-8, errors=replace)                               │
│ • 拼接 stdout + stderr                                                     │
│ • 附加 exit code                                                           │
│ • 截断超长输出 (max 10000 chars)                                           │
└─────────────────────────────────────────────────────────────────────────────┘
     │
     ▼
返回结果字符串

支持的语言

核心原理:create_subprocess_shell() 调用系统默认 shell,可执行任何系统支持的脚本。

9.3 安全机制

危险命令拦截

deny_patterns = [
    r"\brm\s+-[rf]{1,2}\b",          # rm -r, rm -rf
    r"\bdel\s+/[fq]\b",              # Windows del /f
    r"\brmdir\s+/s\b",               # Windows rmdir /s
    r"(?:^|[;&|]\s*)format\b",       # format 磁盘
    r"\b(mkfs|diskpart)\b",          # 磁盘操作
    r"\bdd\s+if=",                   # dd 裸设备
    r">\s*/dev/sd",                  # 写磁盘设备
    r"\b(shutdown|reboot|poweroff)\b",  # 关机重启
    r":\(\)\s*\{.*\};\s*:",          # fork bomb
]

白名单模式(可选)

allow_patterns = [r"^git", r"^npm", r"^python"]
# 只有匹配这些模式的命令才能执行

路径限制(可选)

restrict_to_workspace = True  # 只能访问工作区
# • 禁止 .. 路径遍历
# • 禁止访问工作区外的绝对路径

运行时保护

9.4 目前的不足

无真正沙箱

正则绕过风险

# 可被绕过的模式
r"\brm\s+-[rf]{1,2}\b"

# 绕过方式
rm -rf /    # 空格可被特殊字符分割
$(rm -rf /) # 命令替换
`rm -rf /`  # 反引号

其他风险

9.5 总结

Nanobot只有简单的安全校验,没有沙箱环境。在 OpenClaw 的架构里,则有沙箱能力。

十、回顾与总结

通过对 Nanobot 源码的剖析,我们已经清晰地看到各类大龙虾华丽外衣下的底层逻辑。

总结来说:

  1. 技术上没有魔法,只有工程的巧妙组合:Nanobot并非依赖某种颠覆性的 AI 新算法,而是通过“大模型 API + 循环控制 + 本地脚本执行”的经典架构,完成了从“动嘴”到“动手”的跨越。
  2. 真正的突破在于“场景与体验”:它打破了云端沙盒的限制,将 AI 真正下放到了用户的个人电脑中。这种“看得见、摸得着”且能实际操控系统的专属感,才是它引爆科技圈的根本原因。
  3. OpenClaw 的爆火给我们最大的启示是:在底层大模型能力日益强大的今天,如何让 AI 打破边界,与用户的真实环境(系统、文件、应用)产生深度的物理或逻辑交互,提供更具实体感和专属感的应用体验,将是下一代智能体(Agent)落地的核心方向。

备注:Nanobot可能与OpenClaw还有一些细微差异,因此仅能作为参考,请读者注意。

posted @ 2026-04-02 11:20  vivo互联网技术  阅读(29)  评论(0)    收藏  举报