我用 Rust 写了一个 AI Agent:从 nanobot 到 ferrum-bot 的架构演进

  • King
  • 发布于 11小时前
  • 阅读 50

之前写了好几篇用Rust构建AIAgent的文章,读者朋友们一直在问:"到底完整的项目长什么样?"今天,它来了。受nanobot启发,我用Rust重新设计了一个生产级的Agent运行时——ferrum-bot。

之前写了好几篇用 Rust 构建 AI Agent 的文章,读者朋友们一直在问:"到底完整的项目长什么样?"今天,它来了。 受 nanobot 启发,我用 Rust 重新设计了一个生产级的 Agent 运行时 —— ferrum-bot。

为什么是 Rust?

在 AI Agent 的赛道上,Python 凭借生态优势一家独大。但当我们想要构建一个每天稳定运行、资源可控、易于部署的个人助手时,Rust 的优势就显现出来了:

  • 确定性行为:没有 GC 停顿,运行时表现可预测
  • 类型安全:复杂的 Agent 逻辑在编译期就能发现大部分问题
  • 单二进制部署cargo build 之后就是一个可执行文件
  • 资源效率:同样的功能,内存占用大幅降低

正如 ferrum-bot 的设计哲学:"不是功能堆砌,而是一个你可以信任并扩展的健壮核心。"


项目全景:9 个 Crate 的 Workspace 架构

ferrum-bot 采用 Rust Workspace 组织代码,共包含 9 个 crate,每个都有明确的职责边界:

ferrum-bot/
├── ferrumbot-cli        # CLI 入口和命令解析
├── ferrumbot-config     # 配置管理和持久化
├── ferrumbot-core       # 消息总线、会话管理
├── ferrumbot-agent      # Agent 核心循环
├── ferrumbot-tools      # 工具框架和内置工具
├── ferrumbot-providers  # LLM Provider 抽象
├── ferrumbot-channels   # 消息通道适配器
├── ferrumbot-cron       # 定时任务系统
└── ferrumbot-runtime    # 运行时和网关

这种分层设计的好处是每一层都可以独立测试、替换或复用。比如你想对接 Discord,只需要在 ferrumbot-channels 里新增一个实现即可。


核心架构解析

3.1 AgentLoop:消息驱动的核心循环

Agent 的核心是一个异步消息循环,基于 Tokio 的 mpsc 通道实现:

pub struct AgentLoop {
    pub bus: MessageBus,                    // 消息总线
    pub provider: Arc<dyn LlmProvider>,     // LLM Provider
    pub workspace: PathBuf,                 // 工作目录
    pub tools: ToolRegistry,                // 工具注册表
    pub sessions: Mutex<SessionManager>,    // 会话管理
    pub cron: Option<CronService>,          // 定时服务
    // ...
}

设计亮点

  • 消息总线解耦:通过 MessageBus 分离入站和出站消息,方便接入不同通道
  • 会话隔离:每个对话有独立的 session key,持久化到 ~/.ferrum-bot/sessions/
  • 最大迭代限制max_tool_iterations 防止工具调用死循环

3.2 工具系统:trait-based 的可扩展设计

工具系统是 Agent 的"手脚",ferrum-bot 采用 async-trait 定义统一的 Tool 接口:

#[async_trait]
pub trait Tool: Send + Sync {
    fn name(&self) -> &'static str;
    fn description(&self) &'static str;
    fn parameters(&self) -> Value;        // JSON Schema
    async fn execute(&self, args: Value, ctx: ToolContext) -> Result<String>;
}

内置工具一览:

工具类别 工具名 功能说明
文件操作 file_read / file_write / file_edit / file_list 带工作区限制的 FS 操作
命令执行 exec 带危险命令守卫的 shell 执行
网络工具 web_search / web_fetch 搜索和网页抓取
消息 message_send 跨通道消息发送
定时任务 cron_add / cron_remove 动态任务调度
进程 spawn 后台进程管理

安全设计

  • 工作区限制:restrict_to_workspace = true 时,文件操作不能越界
  • 命令守卫:exec 工具内置危险命令检测(如 rm -rf /
  • 网络限制:web_fetch 阻止访问内网 IP 和本地主机

3.3 Provider 抽象:一行代码切换模型

通过 LlmProvider trait,ferrum-bot 支持所有 OpenAI-compatible 的 API:

#[async_trait]
pub trait LlmProvider: Send + Sync {
    async fn chat(
        &self,
        messages: Vec<Value>,
        tools: Option<Vec<Value>>,
        model: Option<&str>,
        max_tokens: Option<usize>,
        temperature: Option<f32>,
    ) -> Result<LlmResponse>;
}

配置示例(~/.ferrum-bot/config.json):

{
  "providers": {
    "openai": {
      "api_key": "sk-...",
      "base_url": "https://api.openai.com/v1"
    }
  },
  "agents": {
    "defaults": {
      "model": "anthropic/claude-opus-4-5"
    }
  }
}

3.4 消息总线:通道无关的架构

MessageBus 是系统的神经系统,使用 Tokio mpsc 实现:

#[derive(Clone)]
pub struct MessageBus {
    inbound_tx: mpsc::Sender<InboundMessage>,
    outbound_tx: mpsc::Sender<OutboundMessage>,
    // ...
}

这种设计让 ferrum-bot 可以无缝对接多种通道:

  • CLI 模式:终端交互
  • Gateway 模式:HTTP 服务
  • WhatsApp:Cloud API 适配器已就绪
  • Telegram/Discord/飞书:脚手架已搭好

核心代码走读:一次完整的对话流程

让我们跟踪一次用户消息的完整处理流程:

// 1. 接收消息
impl AgentLoop {
    pub async fn run(&self) {
        while *self.running.lock().await {
            let Some(msg) = self.bus.consume_inbound().await else { continue };

            // 2. 处理消息
            match self.process_message(msg).await {
                Ok(Some(outbound)) => {
                    // 3. 发送回复
                    self.bus.publish_outbound(outbound).await?;
                }
                // ...
            }
        }
    }
}

工具调用循环(ReAct 模式):

async fn process_message_with_session(...) -> Result<Option<OutboundMessage>> {
    // 加载历史对话
    let history = sessions.get_or_create(session_key)?.get_history(50);
    let mut messages = self.context.build_messages(history, &msg.content, ...);

    // 最多 max_iterations 轮工具调用
    for _ in 0..self.max_iterations {
        let resp = self.provider.chat(messages.clone(), tool_defs, ...).await?;

        if resp.has_tool_calls() {
            // 执行工具
            for call in resp.tool_calls {
                let result = self.tools.execute(&call.name, call.arguments, ctx).await;
                // 将结果加入上下文
                self.context.add_tool_result(&mut messages, ...);
            }
        } else {
            // 得到最终回复
            final_content = resp.content;
            break;
        }
    }

    // 持久化会话
    session.add_message("user", &msg.content);
    session.add_message("assistant", &final_content);
    sessions.save(session_key)?;
}

这就是经典的 ReAct(Reasoning + Acting) 模式:LLM 思考 → 选择工具 → 观察结果 → 继续思考,直到得到最终答案。


使用体验:CLI 设计哲学

ferrum-bot 的 CLI 设计遵循 "日常使用,而非演示" 的理念:

# 初始化配置
cargo run -- onboard

# 查看状态
cargo run -- status

# 进入交互式对话
cargo run -- agent

# 单次提问
cargo run -- agent -m "帮我写一个快速排序"

# 启动网关服务
cargo run -- gateway -p 8080

# 管理定时任务
cargo run -- cron add -n "早报" -m "生成今日简报" --cron "0 9 * * *" --deliver

REPL 内置命令:

  • /session <id> - 切换会话
  • /new [name] - 创建新会话
  • /multi - 多行输入模式
  • /last - 查看上一次回复
  • /retry - 重新生成

与 nanobot 的对比

特性 nanobot ferrum-bot
语言 Python Rust
部署 需要 Python 环境 单二进制文件
运行时稳定性 GC 可能导致停顿 无 GC,确定性延迟
类型安全 运行时检查 编译期保证
工具扩展 动态加载 编译期注册(可热插拔)
默认安全边界 基础 工作区限制 + 命令守卫

ferrum-bot 不是要取代 nanobot,而是为那些追求部署简洁、运行时稳定、资源可控的场景提供另一种选择。


项目现状与路线图

已实现

  • ✅ Core Agent 循环和工具系统
  • ✅ OpenAI-compatible Provider
  • ✅ 文件、执行、网络、消息、定时任务工具
  • ✅ 会话持久化
  • ✅ WhatsApp Cloud API 通道
  • ✅ Cron 定时任务系统

计划中

  • 🚧 Telegram / Discord / 飞书适配器
  • 🚧 MCP (Model Context Protocol) 支持
  • 🚧 插件系统(动态加载 .so/.dll)
  • 🚧 Web UI 管理面板

写在最后

从写第一篇 Rust Agent 文章到开源 ferrum-bot,历时数周。这个项目验证了 Rust 在 AI 应用开发中的可行性 —— 我们完全可以既享受 Rust 的性能和可靠性,又不失 Agent 的灵活和强大

如果你也在探索 Rust + AI 的可能性,欢迎来 GitHub 看看:

🔗 https://github.com/lispking/ferrum-bot

如果觉得有帮助,给个 ⭐ 支持下吧!有问题也可以在 Issues 讨论,或者关注我的公众号获取更多 Rust + AI 的实践分享。

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论