找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2824

积分

0

好友

384

主题
发表于 5 天前 | 查看: 23| 回复: 0

当社区里很多人还在用 Python 或 Node.js 构建 AI Agent 时,我选择了一条不同的路:用 Rust 来重写。这并非为了炫技,而是我深信,AI Agent 要迈向规模化与高可靠性的未来,必须有更坚实、更高效的基础设施作为支撑。

为何选择 Rust 重写 Hermes Agent?

hermes-agent 是由 Nous Research 开源的一款自进化 AI 代理框架,在 GitHub 上已经积累了近 20.7k 的星标。它有几个特性让我印象深刻:

HERMES AGENT 项目介绍:一个与你共同成长的代理

Hermes Agent 功能特性表格

这些功能听起来很强大,但它的实现有一个根本性问题——整个项目基于 Python 构建,总计约 5 万行代码。仅 run_agent.py 这个文件就快有 8500 行,维护和扩展的复杂度可想而知。

Python 版本 run_agent.py 代码截图

使用 Python 版本时,我遇到了几个明显的痛点:

  • 启动缓慢:冷启动需要 3-5 秒。
  • 内存占用高:即使空载,内存占用也超过 200MB。
  • 异步/同步桥接地狱:处理 async/sync 转换的 _run_async() 函数长达 50 行,只为应对主线程、工作线程和嵌套事件循环的各种边界情况。
  • 部署复杂:依赖 Python 版本、pip 包、Node.js 运行环境(用于 MCP)等,环境配置繁琐。
  • 类型安全缺失:运行时错误难以在早期发现。

既然这个框架的理念和功能如此吸引人,我下定决心:用 Rust 对其进行一次彻底的重写。

架构设计:模块化与清晰依赖

一个优秀的 Rust 项目,其架构设计是成功的基石。我将整个系统拆分为 13 个独立的 crate(库),并严格遵循了无环依赖图(DAG)的原则:

hermes-rs/
  crates/
    hermes-core/        # 共享类型:Message、ToolCall、Platform、Error
    hermes-config/      # YAML 配置 + .env + SOUL.md 人格加载
    hermes-security/    # 注入扫描、环境变量过滤、路径防护
    hermes-state/       # SQLite + FTS5 全文搜索
    hermes-llm/         # LLM 客户端:OpenAI 兼容 + SSE 流式
    hermes-terminal/    # 终端执行后端:Local + Docker
    hermes-skills/      # 技能系统:SKILL.md 解析、CRUD
    hermes-tools/       # 工具注册表 + 9 个内置工具
    hermes-mcp/         # MCP 协议客户端(stdio + JSON-RPC)
    hermes-agent/       # 核心 Agent 循环:对话编排、上下文压缩
    hermes-gateway/     # 网关 + 5 个平台适配器
    hermes-cron/        # 定时任务调度器
    hermes-cli/         # 交互式终端 UI

它们的依赖关系清晰明了:

hermes-cli
  └─ hermes-agent
       ├─ hermes-llm ──── hermes-config ──── hermes-core
       ├─ hermes-tools ── hermes-terminal ── hermes-security
       ├─ hermes-mcp
       └─ hermes-skills
  └─ hermes-gateway
  └─ hermes-cron

为什么要拆分得如此细致? 因为 Rust 的编译单元是 crate。这样做带来了几个显著优势:

  1. 增量编译极快:修改一个工具的实现,不需要重新编译整个 Agent。
  2. 依赖隔离清晰:例如,hermes-gateway 可以完全不依赖 hermes-terminal
  3. 未来可定制编译:如果某个项目不需要 Telegram 支持,可以通过 cargo build --no-default-features 来剔除相关模块。

核心设计:四个关键 Trait 构建系统骨架

Rust 的 trait 系统是整个项目的灵魂。整个系统围绕四个核心 trait 构建,它们定义了各模块间的交互契约。

1. LlmClient —— LLM 调用抽象

#[async_trait]
pub trait LlmClient: Send + Sync {
    async fn complete(&self, req: &CompletionRequest)
        -> Result<CompletionResponse, LlmError>;
    async fn stream(&self, req: &CompletionRequest,
        tx: mpsc::Sender<StreamDelta>)
        -> Result<CompletionResponse, LlmError>;
}

Python 版本需要用 if/elif 分支来处理不同 API 提供商的差异。而在 Rust 版本中,每种 API 都是一个独立的 struct,但它们都实现同一个 LlmClient trait。编译器会确保你不会遗漏任何一种情况的处理。

2. ToolHandler —— 工具执行抽象

#[async_trait]
pub trait ToolHandler: Send + Sync {
    async fn execute(&self, args: serde_json::Value,
        ctx: &ToolContext) -> Result<String, ToolError>;
}

每个具体的工具(如文件操作、网络请求)都是一个实现了 ToolHandlerstruct。它们被注册到一个中央的 ToolRegistry(内部使用 RwLock<HashMap>)中。这种设计既支持运行时的动态工具发现(如通过 MCP 协议),也支持编译时的静态工具注册。

3. TerminalBackend —— 执行环境抽象

#[async_trait]
pub trait TerminalBackend: Send + Sync {
    async fn execute(&self, cmd: &str, cwd: Option<&str>,
        timeout: Option<Duration>) -> Result<ExecResult, TerminalError>;
    async fn cleanup(&self) -> Result<(), TerminalError>;
}

无论是本地 Shell 执行,还是在 Docker 容器中执行命令,都通过同一个接口完成。切换执行环境只需要修改一行配置,无需改动业务逻辑代码。

4. PlatformAdapter —— 消息平台抽象

#[async_trait]
pub trait PlatformAdapter: Send + Sync {
    fn platform(&self) -> Platform;
    async fn connect(&mut self) -> Result<(), GatewayError>;
    async fn send(&self, chat_id: &str, content: &str,
        reply_to: Option<&str>) -> Result<SendResult, GatewayError>;
    fn set_message_handler(&mut self, handler: MessageHandler);
}

Telegram、Discord、Slack……每个消息平台的适配器都是一个独立的文件,它们都实现这个 PlatformAdapter trait。未来要新增一个平台,只需要实现这四个方法即可,与核心逻辑完全解耦。这种基于 Trait 的设计,是构建可扩展、高内聚低耦合系统的关键,也是现代 后端 & 架构 中倡导的核心模式之一。

攻克难题:彻底告别 Async/Sync 地狱

Python 版本中最让我头疼的就是那个约 50 行的 _run_async() 函数。它需要小心翼翼地处理三种复杂的并发场景:主线程的事件循环、工作线程池中的独立事件循环、以及 Gateway 内部的嵌套异步上下文。

在 Rust 版本中,这个问题从根本上被解决了。

整个应用运行在统一的 tokio 运行时上。所有的工具处理器都是 async fn。并行执行多个工具使用 JoinSet,Gateway 与 Agent 核心间的消息传递使用 mpsc channel。没有 sync/async 的显式桥接,没有线程局部存储的繁琐管理,也没有事件循环生命周期的担忧。

// Python: 需要50行的 _run_async() 来桥接同步和异步世界
// Rust: 直接 .await,一切如此简单
let result = registry.dispatch(&tool_name, args, &ctx).await;

安全保障:编译时守卫

Python 版本包含了大量的运行时安全检查,如环境变量泄露防护、Prompt 注入扫描、路径遍历检测等。这些安全措施在 Rust 版本中都得到了保留,并且借助语言特性得到了增强:

// 编译器保证你必须处理 Result,无法忽略错误
let result = self.backend.execute(command, cwd, timeout, None).await?;

// 类型系统防止你混淆不同的字符串标识符
pub struct SessionSource {
    pub platform: Platform, // 强类型枚举,而非普通的字符串
    pub chat_id: String,
    pub user_id: Option<String>,
    // ...
}

平滑迁移:零成本配置兼容

这一点让我颇为自豪——Rust 版本能够直接读取和使用 Python 版本完全相同的配置文件,实现了真正的零迁移成本。

# ~/.hermes/config.yaml 或项目本地 .hermes/config.yaml
model:
  default: "anthropic/claude-sonnet-4"
  base_url: "https://openrouter.ai/api/v1"

terminal:
  backend: "local"
  timeout: 180

mcp_servers:
  filesystem:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]

HermesConfig 结构体通过 #[serde(default)] 属性确保即使是不完整的配置子集也能被正确加载和填充默认值。

此外,我还实现了项目级本地配置覆盖功能。在任意项目目录下放置一个 .hermes/config.yaml 文件,它的设置会自动覆盖全局配置。这使得不同项目可以方便地使用各自专属的模型和工具配置。

性能对比:数字说明一切

让我们用最直观的数据来看看这次重写带来的变化:

指标 Python 版本 Rust 版本
代码行数 ~50,000 ~5,000
模块数 100+ 文件 13 crate / 66 文件
编译产物 需要 Python 运行时 单个 ~25MB 静态二进制文件
类型安全 运行时检查(mypy 可选) 编译时保证
Async 模型 asyncio + threading 混合 纯 tokio async
错误处理 try/except Result<T, E> 全链路

技术选型思考

在构建这个 Rust 项目时,每一个技术选型都经过了仔细考量:

功能 选型 理由
异步运行时 tokio 生态最完善,性能经过充分验证
HTTP 客户端 reqwest 对 SSE (Server-Sent Events) 流式传输支持良好
序列化 serde + serde_json + serde_yaml Rust 生态序列化的事实标准
数据库 rusqlite (bundled) 捆绑 SQLite,实现零外部依赖
错误处理 thiserror + anyhow 库定义错误用 thiserror,应用层用 anyhow
CLI 解析 clap 声明式参数解析,功能强大且易用
日志 tracing 结构化日志,性能优异,与 tokio 集成好
Docker 交互 bollard 直接调用 Docker Engine API,控制力强

总结与展望

用 Rust 重写一个 5 万行的 Python 项目,听起来像是一项艰巨的挑战。但在实际推进的过程中,Rust 强大的类型系统和所有权模型,帮助我提前发现了大量潜藏在 Python 版本中的隐患:

  1. 数据竞争:Python 版本使用 threading.Lock 来保护某些内存或文件操作,但仍有遗漏的路径。Rust 的 Mutex 则在编译阶段就强制你必须通过它来访问受保护的数据。
  2. 错误传播:Python 版本中存在一些 except: 语句,无意中吞没了错误细节。Rust 的 ? 操作符让错误的传播变得显式且可追溯。
  3. 空值处理:Python 代码中遍布 if x is not None:。Rust 的 Option<T> 迫使你在编译时就必须处理好每一个可能为“空”的情况。

我坚信,AI Agent 的未来必然需要更强大的基础设施。 当 Agent 需要 7x24 小时不间断运行、处理成千上万的并发会话、并无缝接入数十个消息平台时,Python 的全局解释器锁(GIL)、动态类型的内存开销以及相对松散的运行时检查,都可能成为瓶颈。

Rust 并非解决所有问题的“银弹”,但对于构建高性能、高可靠性的 人工智能 基础设施而言,它无疑是一个极具前景的正确选择。

Rust 重写版 Hermes 的 GitHub 仓库截图

项目地址https://github.com/coder-brzhang/hermes-rs

这个项目目前仍在早期阶段,但核心架构和基础功能已经完成。如果你也对使用 Rust 构建下一代 AI Agent 系统感兴趣,欢迎在 云栈社区 或 GitHub 上交流探讨。我们才刚刚启程。




上一篇:开源Open Agent SDK发布:100%兼容Claude Code,解决闭源黑盒与云端高并发瓶颈
下一篇:百度AI资本局:李彦宏携昆仑芯、百图生科、爱奇艺密集赴港IPO
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-4-7 19:07 , Processed in 1.036265 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表