欢迎光临
我们一直在努力

5 万行 Rust 代码重塑 AI Agent:SkillLite 从 Python 到 Rust 的架构演进与选型复盘

本文记录了一个 AI Agent 项目从 Python + Rust 混合架构逐步演进为纯 Rust 核心的技术实践,探讨在 AI 时代选择 Rust 作为核心开发语言的真实考量与取舍。纯属个人经验分享,如有疏漏,欢迎指正。

一、背景:一个自主进化的 AI Agent

2025 年底,我启动了开源项目 SkillLite——一款与传统 AI Agent 不同的产品,主打单 Agent 自主进化、安全沙箱与 P2P 组网。

愿景很清晰,但落地过程很快给了我一记闷棍。

二、最初的技术选型:Python + Rust

2.1 为什么最初选择了 Python?

坦率地说,当时选择Python几乎没有悬念:

  • 生态碾压:LangChain、LlamaIndex、AutoGPT、Claude Agent SDK……AI 领域几乎所有基础设施都是 Python。

  • 开发效率:动态语言的快速迭代在 AI 这种强探索性领域非常重要。

  • 个人熟悉度:此前主要用Python较多,对生态更熟。

  • 于是架构自然演变成典型的「Python 调 Rust」模式:

    ┌─────────────────────────────────────────────────────┐
    │ Python SDK │
    │ (Agent业务逻辑、CLI、链式调用) │
    └─────────────────────────────────────────────────────┘


    ┌─────────────────────────────────────────────────────┐
    │ PyO3 绑定层 │
    │ (Python ↔ Rust 互操作) │
    └─────────────────────────────────────────────────────┘


    ┌─────────────────────────────────────────────────────┐
    │ Rust Core │
    │ (底层执行能力、文件/网络/沙箱) │
    └─────────────────────────────────────────────────────┘

    2.2 这个架构的致命问题

    随着代码量从几千行涨到上万行,问题逐渐暴露:

    问题一:多语言sdk的重复核心逻辑维护

    一开始rust主要做的sandbox部分,agent模块放到Python 层,如果只是针对python还好,但是如果未来要兼容其他的语言比如typescript,go等则需要维护多份核心逻辑:Agent 循环、任务规划、记忆管理。为保持多端一致,大量时间耗在「对齐」上,既累又容易出错。

    问题二:版本地狱

    Python 有 Poetry / Rye / uv 之争,Rust 有 Cargo,两套依赖体系完全不同,协调成本呈指数增长。

    问题三:瓶颈出现在不该出现的地方

    Rust 核心已经足够快,真正的瓶颈反而在 Python 层:GIL、序列化与跨语言调用开销。

    # 真实场景下的耗时分布
    result = agent.run(task) # 表面看约 0.1 秒
    # 实际拆解:
    # PyO3 序列化参数: 50ms
    # GIL 争用等待: 30ms
    # 结果反序列化: 20ms
    # Rust 核心执行: 0.001ms

    讽刺之处在于:在 Rust 里优化半天的部分,被 Python 一侧轻松拖后腿。

    问题四:安全难以保障

    Python 的可观测性与安全审计能力与 Rust 不在同一量级。当 Agent 开始执行敏感操作时,说不担心是假的。

    三、全面转向 Rust

    3.1 重构不是请客吃饭

    我做出了一个艰难的决定:以 Rust 为核心,Python 仅保留一层薄集成。

    这意味着:

    • 推翻约 80% 的 Python 业务代码;

    • 重新划分模块边界;

    • 从同步 Rust 跨到 async Rust,重新学习与踩坑。

    但继续同时维护两套核心逻辑,已经超出我能承受的范围。

    3.2 新的架构设计

    当前架构演变为:

    ┌──────────────────────────────────────────────────────────────┐
    │ Python SDK (≈600行) │
    │ 仅做参数透传和结果展示 │
    └──────────────────────────────────────────────────────────────┘


    ┌──────────────────────────────────────────────────────────────┐
    │ Rust Core (≈53,000行) │
    ├──────────────────────────────────────────────────────────────┤
    │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
    │ │ skilllite- │ │ skilllite- │ │ skilllite- │ │
    │ │ agent │ │ evolution │ │ sandbox │ │
    │ │ (核心循环) │ │ (进化引擎) │ │ (安全沙箱) │ │
    │ └─────────────┘ └─────────────┘ └─────────────┘ │
    │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
    │ │ skilllite- │ │ skilllite- │ │ skilllite- │ │
    │ │ swarm │ │ executor │ │ commands │ │
    │ │ (P2P组网) │ │ (会话管理) │ │ (CLI命令) │ │
    │ └─────────────┘ └─────────────┘ └─────────────┘ │
    └──────────────────────────────────────────────────────────────┘

    核心原则:一个功能只在一个地方实现。

    四、Rust vs Python:AI 时代的真实对比

    4.1 我的真实体验

    维度

    Python

    Rust

    生态丰富度

    ★★★★★

    ★★★☆☆

    开发速度

    ★★★★★

    ★★★☆☆

    运行性能

    ★★★☆☆

    ★★★★★

    类型安全

    ★★☆☆☆

    ★★★★★

    内存安全

    ★★☆☆☆

    ★★★★★

    编译期检查

    ★☆☆☆☆

    ★★★★★

    学习曲线

    ☆☆☆☆☆

    ★★★★★

    AI 库支持

    ★★★★★

    ★★☆☆☆

    4.2 那些劝退的瞬间

    坦白说,在 AI 领域选 Rust 是「逆流而上」:

    • 没有 LangChain:Agent 循环、任务规划、工具调用都要自己实现。

    • 没有 LlamaIndex:向量检索、chunk 策略都得自己写、自己调。

    • Async 生态更重:tokio 比 asyncio 复杂不少,调试成本高。

    • crates 依赖地狱:传递依赖的版本冲突,有时能刷满一屏错误。

    4.3 但我收获了什么

    1. 内存安全

    当 Agent 在用户机器上执行文件操作、网络请求时,内存安全不是「锦上添花」,而是「生死攸关」。Rust 的 ownership 在编译期就杜绝了 use-after-free、data race:

    // Rust 的 ownership 让这类 bug 在编译期就无处遁形
    let agent = Agent::new();
    let agent_clone = agent; // agent 被 move
    // agent.run(); // 编译错误:agent 已不可用
    agent_clone.run(); // 只能使用 agent_clone

    2. 可预测的性能

    Python 的性能抖动常常是「玄学」。Rust 给出的是可预期的执行时间,在实时 Agent 场景里这是刚需。

    3. 自进化成为可能

    这是我最看重的一点。Agent 需要能自我修改 prompt、优化行为。若核心逻辑在 Python 里,如何保证自进化过程不破坏运行时?Rust 的静态链接与单一二进制分发,让这件事可控。

    4. 核心逻辑只维护一份

    不再维护两套实现,也不再有「两边版本对不齐」的焦虑,Cargo.lock 保证了可重现构建。

    五、Rust 做 AI Agent 的坑与取

    5.1 生态不成熟是事实

    我踩过的坑包括:

    • HTTP 客户端:reqwest 好用,但比 requests/httpx 复杂不少;

    • Async 生态:tokio 强大,但 async 代码的调试体验仍然痛苦;

    • 序列化:serde 很强,可每次加字段仍要小心依赖与版本;

    • LLM SDK:anthropic-rs、openai-rs 都各有各的坑。

    5.2 换个角度看问题

    正是这种「不成熟」,倒逼我做了更清晰的取舍:

    1. 不再盲从框架

    Python Agent 领域框架林立:LangChain、LlamaIndex、AutoGen……个个自称「业界标准」。用 Rust 则没得抄,只能自己回答:Agent 的核心是什么?最简循环是什么?

    答案出乎意料地简单:感知 → 思考 → 行动 → 反馈。

    于是我没有照搬任何框架,而是基于这一循环设计了 skilllite-agent:

    // 极简的Agent循环
    loop {
    let context = self.perceive().await?; // 感知
    let thought = self.think(context).await?; // 思考
    let action = self.act(thought).await?; // 行动
    self.reflect(&action).await?; // 反馈
    }

    2. 每个模块必须足够简单

    Rust 很难让你写出「能用就行」的代码。每写一个模块都要自问:边界在哪?错误是否处理完整?性能是否真的需要这样?结果是每个 crate 都更克制,遵循「做一件事并做好」的原则。

    3. 深入理解而非浅层调用

    用 Python 调 LangChain,不必关心内部 chunk 策略;用 Rust 则必须自己实现。这反而让我对 AI Agent 的理解更深:

    • 任务规划不是「让 LLM 生成计划」,而是「目标 → 分解 → 执行 → 验证」的迭代循环;

    • 记忆管理不是「存进去就能检索」,而要考虑向量质量、相关性、时效性;

    • 工具调用不是简单的 RPC,而是一套明确的函数签名与执行契约。

    5.3 最终的取舍原则

    我遵循的原则是:最小可用,渐进增强。

    • Agent 循环:先做单一模式(Simple),再逐步支持复杂规划(Planning);

    • 工具生态:只做高频刚需(文件、命令、网络),其余暂不铺开;

    • 进化机制:从规则驱动起步,再逐步引入学习能力。

    不追求一步到位,追求可持续演进。

    六、技术细节:Rust 架构一览

    6.1 模块划分

    skilllite-agent/ # Agent 核心(约 19 个子模块)
    ├── llm/ # 多模型支持(Claude / OpenAI)
    ├── agent_loop/ # 执行循环(规划 / 执行 / 反思)
    ├── skills/ # 技能加载
    ├── extensions/ # 扩展机制(Memory / Registry)
    └── types/ # 核心类型定义

    skilllite-evolution/ # 进化引擎
    ├── rules/ # 进化规则
    ├── triggers/ # 触发条件
    └── knowledge/ # 知识回流

    skilllite-sandbox/ # 安全沙箱
    ├── levels/ # L1–L4 安全级别
    ├── isolation/ # 进程隔离
    └── policy/ # 策略引擎

    skilllite-swarm/ # P2P 分布式
    ├── discovery/ # 节点发现
    ├── gossip/ # Gossip 协议
    └── sync/ # 状态同步

    skilllite-executor/ # 会话管理
    skilllite-core/ # 协议与配置
    skilllite-commands/ # CLI 命令
    skilllite-fs/ # 文件系统工具

    6.2 关键设计决策

    1. 静态分发优于动态分发

    尽量少用 dyn Trait,多用泛型与 const generic,便于编译器优化;代价是编译时间变长,但可接受。

    2. 错误处理统一

    各模块用 thiserror 定义错误类型,用 anyhow 做顶层聚合,错误传播清晰、可追溯:

    // 统一的错误定义
    #[derive(thiserror::Error, Debug)]
    pub enum AgentError {
    #[error("LLM调用失败: {0}")]
    LlmError(#[from] LlmError),

    #[error("工具执行失败: {0}")]
    ToolError(#[from] ToolError),

    #[error("记忆系统错误: {0}")]
    MemoryError(#[from] MemoryError),
    }

    3. 异步 Runtime 选择 tokio

    async-std 更轻,但 tokio 生态更完整。在 AI 这种 IO 密集场景下,tokio 的生态优势更明显。

    七、写在最后

    五万行 Rust 写下来,收获的不只是一个 AI Agent 框架,更是一种思维方式:用编译期的严格,换运行期的安心。

    在 AI 时代,Python 仍是绝对主角。但若你做的不是「AI 玩具」而是「AI 产品」,若你在意安全、自进化与可控性,Rust 值得认真考虑。

    Rust 不是银弹:学习曲线陡、异步生态有时不够优雅、AI 库稀缺。但正是这些「不完美」,逼人走出舒适区,把「在做什么」想清楚。

    水平有限,若有不当之处,欢迎指正。


    相关资源

    • GitHub: https://github.com/EXboys/skilllite

    赞(0)
    未经允许不得转载:171主机测评 » 5 万行 Rust 代码重塑 AI Agent:SkillLite 从 Python 到 Rust 的架构演进与选型复盘
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址