现在大火的ClawdBot(MoltBot)成为一个现象级产品,网上很多关于使用和安装的视频和文章。其实我最感兴趣的是它内部的实现机制。因为我也在做微信客服机器人,先从几个基本的我感兴趣的点做了点研究。
作为AI智能体,其中最重要的两个功能是:

  1. 如何控制桌面,像人一样操作电脑
  2. 如何让智能体拥有记忆,这样不用每次都重复之前教它的

一、项目如何实现让大模型长时间操控本地电脑?它可能定义了很多本地的工具,或者是skills,它如何调用本地的工具,或者是skills,然后把结果返回给大模型?这是遵循了一种什么标准协议吗?

1. 长时间操控本地电脑是怎么实现的?

本质是一个 Agent 循环:用户发一条消息后,本地跑一轮「LLM → 工具调用 → 执行 → 结果回传 → 再调 LLM」,可以多轮重复,直到模型不再发起工具调用或达到停止条件。

  • 入口runEmbeddedPiAgentsrc/agents/pi-embedded-runner/run.ts)→ runEmbeddedAttemptsrc/agents/pi-embedded-runner/run/attempt.ts)。
  • 会话与驱动:用 pi-coding-agentcreateAgentSession() 创建/恢复会话,把「内置工具 + 自定义工具」传进去;用户输入通过 activeSession.steer(prompt) 驱动这一轮。
  • 工具执行与循环
    • LLM 返回的 assistant 消息里带有 tool_calls(工具名 + 参数)。
    • Session 内部根据这些 tool_calls 调用对应工具的 execute(...),得到结果。
    • 把结果作为 tool result 消息追加到对话历史,再请求 LLM。
    • 如此重复,形成多步「思考 → 调工具 → 执行 → 再思考」,从而实现对本地电脑的长时间、多步操控(执行命令、读改文件、发消息等)。

也就是说:长时间操控 = 多轮「LLM 决策 + 本地工具执行 + 结果回传」的循环,由 pi-coding-agent 的 session 和 Moltbot 的工具实现一起完成。


2. 本地「工具」和「Skills」分别是什么?如何被调用?

Tools(可被模型直接调用的函数)

  • 定义位置
    • 核心工具在 src/agents/tools/(如 bash-toolsbrowser-toolchannel-toolsmoltbot-tools 等)。
    • 汇总与策略在 src/agents/pi-tools.tscreateMoltbotCodingTools)。
  • 形态:每个工具是一个 AgentTool(来自 @mariozechner/pi-agent-core):
    • name, description
    • parameters:JSON Schema,描述参数
    • execute(toolCallId, params, signal, onUpdate):真正在本地执行,返回 AgentToolResult
  • 如何交给模型
    • 在创建 session 时,Moltbot 把这一批工具通过 pi-tool-definition-adapter 转成 pi-coding-agent 的 ToolDefinition,传给 createAgentSession({ tools: builtInTools, customTools: allCustomTools, ... })
    • pi-coding-agent / pi-ai 再把这些工具转成各厂商 API 的 function calling 格式(OpenAI / Anthropic / Google 等),随请求发给 LLM。
  • 调用与回传
    • 模型在回复里返回 tool_calls(id、name、arguments)。
    • Session 根据 name 找到对应 ToolDefinition.execute,在本地执行,得到结果。
    • 结果被格式化成 tool result 消息,追加到对话历史,再继续请求 LLM。

插件也可以通过 api.registerTool({ name, description, parameters, execute }) 注册额外工具;是否对某 agent 开放由 tools.allow / tools.denytools.profile 等配置控制。

Skills(教模型「怎么用」的说明,不是可执行函数)

  • 本质AgentSkills 兼容 的说明文档(例如 SKILL.md),放在 skills/~/.clawdbot/skills、插件等目录(见 docs/tools/skills.md)。
  • 用途:通过 resolveSkillsPromptForRun 等,把「技能列表 + 按需加载说明」拼进 system prompt,告诉模型有哪些技能、怎么用工具,不直接执行代码
  • 和 Tools 的关系:Skills 是「说明书」;真正被模型调用并执行的是 Toolsexecute。Skills 帮助模型更正确地选择和使用这些工具。

所以:

  • 调用本地能力 = 调用 Toolsexecute,由 session 根据 LLM 的 tool_calls 自动完成。
  • 把结果返回给大模型 = 把 AgentToolResult 转成对话里的 tool result 消息,再发给 LLM,形成下一轮输入。

3. 遵循了什么标准/协议?

  • 工具调用层(和 LLM 的约定)

    • OpenAI / Anthropic / Google 等厂商的 Function Calling(Tool Use) 一致:
      • 请求里带 tools(name、description、parameters 的 schema);
      • 模型回复里带 tool_calls(id、name、arguments);
      • 应用执行后把 tool results 再塞回 messages,继续请求。
    • pi-ai@mariozechner/pi-ai)负责把这一套抽象成统一接口,并转换成各厂商的 API 格式;pi-agent-core 提供 AgentTool / AgentToolResult 等类型;pi-coding-agent 负责 session、历史与工具执行循环。
    • 因此:「大模型 ↔ 本地工具」的协议 = 各厂商通用的 function calling / tool use 协议,由 pi-ai 适配到具体 API。
  • 会话与消息结构

    • AgentMessageAgentToolResult 等来自 pi-agent-core,是 Moltbot 与 pi-coding-agent/pi-ai 之间的内部约定,用于在本地表示「带工具调用的多轮对话」。
  • 与 MCP、ACP 的关系

    • MCP(Model Context Protocol):在仓库里主要出现在 mcporter、Claude Code CLI、或禁用 MCP 配置等场景;主流程里「大模型调本地工具」并不是用 MCP 驱动,而是用上面的 function calling + pi-ai。
    • ACP(Agent Client Protocol,@agentclientprotocol/sdk:在 src/acp 里使用,是 IDE/客户端与 gateway 之间的通信协议(例如把 sessionUpdate: "tool_call" 推给前端);和「模型 ↔ 工具」的协议不是同一层。

4. 简要对照表

问题 答案
如何长时间操控本地电脑? Agent 循环:用户消息 → session.steer → LLM 可能返回 tool_calls → 本地执行对应 Tool 的 execute → 结果写回对话 → 再调 LLM,循环多轮。
本地工具如何定义? src/agents/tools/pi-tools.ts 中定义为 AgentTool(name/description/parameters/execute);插件用 api.registerTool
Skills 是什么? AgentSkills 兼容的 SKILL.md 文档,被注入 system prompt,教模型如何使用工具;不直接执行,真正执行的是 Tools。
如何调用并回传结果? Session 根据 LLM 的 tool_calls 调用 ToolDefinition.execute,得到 AgentToolResult,转成 tool result 消息追加到历史再请求 LLM。
遵循的协议? 工具调用:与 OpenAI/Anthropic/Google 等一致的 Function Calling / Tool Use,由 pi-ai 做厂商适配;客户端通信:ACP;主流程不用 MCP 驱动工具调用。

二、项目中如何实现让大模型即有长期记忆,又有短期记忆

1. 概念区分(文档里的定义)

  • Context(文档 Context):每次请求时真正发给模型的那一段内容,受模型 context window 限制。
    文档原话:“Context is not the same thing as ‘memory’: memory can be stored on disk and reloaded later; context is what’s inside the model’s current window.”

  • Memory(文档 Memory):以 Markdown 文件 形式持久化在 agent workspace 里,可跨会话、跨请求存在;模型通过 工具 按需读取,而不是整份塞进 context。

因此可以对应理解为:

  • 短期记忆 ≈ 当前 context 里的内容(本会话的近期对话 + 摘要),受 context window 限制。
  • 长期记忆 ≈ 存在磁盘上的 memory 文件,通过 memory_search / memory_get 按需取回。

2. 短期记忆:怎么实现、怎么不爆窗

载体

  • 会话历史写在 session transcript
    ~/.clawdbot/agents/<agentId>/sessions/<sessionId>.jsonl
    里面是整段对话(用户/助手消息、工具调用与结果、以及 compaction 产生的摘要条目)。

如何变成「短期」

  • 每次请求不会把整份 transcript 都发给模型,而是:
    • context window 内放:system prompt + 一段近期历史 + 必要时的一个 compaction 摘要
  • 当历史太长、接近或超过 context 限制时:
    • CompactionCompactionSession management & compaction):把更早的对话压缩成一条摘要写入 transcript,之后请求里只带「摘要 + 摘要之后的近期消息」,这样模型看到的仍是连贯但有限的「短期」窗口。
    • PruningSession pruning):在单次请求前,从当前请求的 in-memory 上下文里删掉一部分旧的 tool results,减轻体积,不改写 transcript。

相关代码/配置

  • Compaction 配置:agents.defaults.compaction(如 reserveTokensFloor、触发阈值等)。
  • 摘要写入 transcript、以及之后从 transcript 重建「发给模型的 messages」的逻辑在 pi-coding-agent 的 session 与 Moltbot 的 compaction 流程里。

所以:短期记忆 = 当前 context 窗口内的会话内容,由 transcript 持久化 + compaction(摘要旧对话)+ pruning(裁掉部分旧 tool 结果) 共同保证「既保留历史,又不超窗」。


3. 长期记忆:怎么实现、怎么被模型用到

载体

  • Markdown 文件,在 agent workspace(默认 ~/clawd)下:
    • MEMORY.md(或 memory.md):持久事实、偏好、决策(长期)。
    • memory/YYYY-MM-DD.md:按天的日志/近期记录(偏中期,但也是持久存储)。

模型如何「记得」

  • 模型不会在每次请求时把整份 MEMORY 塞进 context。
  • 需要时由模型主动调用工具
    • memory_searchmemory-tool.ts):对 MEMORY.md + memory/*.md(及可选 session 来源)做语义检索,返回相关片段(path + 行范围 + 片段内容)。
    • memory_get:按 path(及可选的 from/lines)读取某段内容。
  • System prompt 里有一节 「Memory Recall」system-prompt.ts):要求模型在回答与「过往工作、决策、日期、人、偏好、待办」相关的问题前,先对 MEMORY.md + memory/*.md 做 memory_search,再用 memory_get 只拉需要的那几行,以控制 context 占用。

长期记忆的写入

  • 用户说「记住这个」时,模型用 write 等工具把内容写到 MEMORY.mdmemory/YYYY-MM-DD.md
  • Pre-compaction memory flushMemory):在快要触发 compaction 前,会做一次静默的 agent 轮次,用固定 prompt 提醒模型「把该留的笔记写到 memory/YYYY-MM-DD.md」,避免重要信息只在对话里、被 compaction 压成摘要后细节丢失;写完后通常用 NO_REPLY,用户看不到这轮。

检索实现(记忆索引)

  • memory plugin(默认 memory-core)提供:
    • 对上述 Markdown 做分块、embedding,写入 SQLite 索引(如 ~/.clawdbot/memory/<agentId>.sqlite)。
    • 支持本地 / OpenAI / Gemini 等 embedding;可选 BM25 + 向量 的混合检索。
  • 配置在 agents.defaults.memorySearch(enabled、provider、extraPaths、session 等)。

因此:长期记忆 = 磁盘上的 memory Markdown + 按需通过 memory_search / memory_get 取回,既不被 context 长度限制,又能被模型在需要时「想起来」。


4. 二者如何一起工作(小结)

维度 短期记忆 长期记忆
存什么 当前会话的对话 + 工具结果 + 摘要 MEMORY.md、memory/YYYY-MM-DD.md 等 Markdown
存在哪 Session transcript(jsonl) Workspace 下的 memory 文件
模型如何用 直接作为本次请求的 context 发过去 通过 memory_search + memory_get 按需拉取
长度限制 受 context window 限制 不受 context 限制,只受磁盘与检索条数限制
典型机制 Compaction(摘要旧对话)、Pruning 向量/混合检索、pre-compaction memory flush

整体设计可以概括成:

  • 短期 = 当前对话窗口内的内容(transcript + compaction/pruning 控制「窗口内」是什么)。
  • 长期 = 持久化的 memory 文件 + 通过 memory 工具在需要时检索并注入到当前对话中,从而让大模型同时拥有「当前会话的短期记忆」和「跨会话的长期记忆」。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐