RustyClaw:我要用 Rust 重写一个AI代理(因为梗在召唤我)

“你知道 Rust 最棒的一点是什么吗?它不会允许你编译粗制滥造的代码。你知道最糟糕的一点是什么吗?起初你写的所有代码都是粗制滥造的。” —— 蟹老板,大概是这样说的 比一个 AI 代理更好的是什么?是一个用 Rust 重写 的 AI 代理。 如果你上网超过五分钟,就会知道这个梗。不管是什么项目:文本编辑器、DNS 服务器、BMI 计算器,总会有人跳出来评论“你应该用 Rust 重写它”。这就是 Rewrite It In Rust —— 简称 RIIR,和地心引力一样不可避免的存在。 好吧,那我就来做一次真的。我将把一个有 8,300 行代码的 Python AI 代理移植到 Rust。但不是因为这个梗在召唤我(嗯…有一点是因为它)。我这么做,是因为我需要一个实验对象。 论点 最近几周,我一直在写关于静默失败、五种防止幻觉的方法、以及"一个 LLM 如何生成看似正确但实际上错误的代码"的文章。我甚至还给它起了个名字:对抗性开发。永远不要相信,总要验证。 很多理论,是时候实践了。 于是,我需要一个项目,满足三个特点:范围适中(而不是一个需求会不断变化的新应用)、明确的真相来源(现有可用的 Python 代码)、以及足够的复杂度,让 LLM 的幻觉能“藏起来”。一个纯粹的移植可以完全满足这三点。输入和期望输出已然存在。如果 Rust 版本的行为和 Python 的不完全一样,那肯定有问题。就是这么简单。 既然要做移植,那为什么不顺便真正学学 Rust 呢?借用检查器 (borrow checker)、所有权 (ownership)、生命周期 (lifetimes)… 我读了好几年资料,却几乎没有亲自实践过。如果是写一个真实项目而不是第 N 次看教程,一切或许会大不相同。 目标对象 它的名字叫 nanobot。这是一个基于 OpenClaw 开发的个人 AI 代理。它能将各种 LLM(如 Claude、GPT、DeepSeek)接入聊天渠道——Telegram、Discord、Slack、电子邮件——并赋予它们更多功能。比如读取和编辑文件、执行命令、网络搜索、通过 cron 编排任务,甚至在对话之间保存记忆。 它可以正常工作。而且已经运行了几个月。在 Python 上。 问题呢?它是单线程的。一次只能处理一条消息。如果你连续发送三条信息,它会像周六中午的超市购物队伍一样排队等待。它的内存消耗约为 50MB,而它实际上只是在不同的 API 之间传递 JSON。此外,它的错误处理方式令人羞愧:到处都是return f"Error: {str(e)}"。 ...

2026年2月24日 · Fernando

为什么 git status 这么慢啊?

缓慢的觉醒 你已经在数据科学项目上工作了一段时间。有二十个 notebook,一些图片,还有那种三个月前看起来不错的典型文件夹结构。 你执行 git status 想看看改了什么,然后… 等待。继续等待。在等待的时候你开始怀疑是不是电脑卡死了还是在沉思什么。 剧透:它不是在沉思。它在痛苦。 问题有名有姓 Git 不慢。你的仓库慢。 当你执行 git status 时,git 需要做两件看起来简单但实际很复杂的事情: 扫描整个文件树 看看有什么变化 比较每个文件 与保存的版本 在普通仓库里这是瞬间完成的。但 Jupyter notebook 是伪装成文档的 JSON。而且不是普通的 JSON:是包含代码、输出、base64 编码图片、内核元数据,以及设计这个格式的人能想到的所有东西的 JSON。 一个带几张图表的"小" notebook 可能有几兆大小。乘以二十个 notebook,你就有了一个每次看它都在呻吟的仓库。 如果你还重命名了文件夹… git 会理解为"你删除了 50 个文件然后创建了 50 个新文件"。保证有趣。 解决方案 1:给你的仓库配个门卫 第一个解决方案优雅得让人恨不得早点知道。 叫做 FSMonitor,工作原理是:不让 git 每次都扫描整个仓库,而是操作系统告诉它哪些文件变了。 用人话说:就像在门口有个门卫告诉你"只有小王进来了",而不用每次都检查整个宾客名单。 激活方法: 1 2 git config core.fsmonitor true git config core.untrackedcache true 搞定。就这样。 激活后第一次执行 git status 还是会花同样(或更多)时间,因为要初始化缓存。但从第二次开始… 魔法。 ...

2026年1月19日 · Fernando

ChromaDB:如何使用向量数据库避免教学踩坑

问题:教授还没有教过的内容 我有一个包含47个课时的编程课程。每个课时都有notes(我解释概念的地方)和labs(学生练习的地方)。我有个问题:有时我在labs中使用了还没在notes中解释过的概念。 “好的,在这个练习中用map来转换列表。” 问题在哪?我要到三个课时之后才解释map是什么鬼东西。 这种情况比你想象的更常见。你脑子里装着所有材料,在不同地方跳来跳去,不知不觉就假设学生知道你还没告诉他们的东西。结果:挫败感、困惑,还有学生认为自己很笨,其实笨的是你。 手动解决方案是检查每个lab,记下使用了哪些概念,然后验证这些概念之前是否已经解释过。但我有47个课时,每个都有好几个notebook。算了吧。 解决方案:使用ChromaDB进行语义搜索 思路很简单: 从每个notebook中提取概念(教了什么,用了什么) 保存在一个理解语义而不仅仅是文本的数据库中 对于lab中使用的每个概念,验证它是否存在于之前的notes中 这里"理解语义"是关键。如果在notes中我说"高阶函数",在lab中使用"higher-order function",grep什么也找不到。但语义上它们是一样的。 这就是ChromaDB的用武之地:一个向量数据库,它将文本转换为嵌入向量并允许按相似性搜索。用人话说:你保存文本,然后可以问"有没有类似这个的东西?“它会返回最相似的结果。 5分钟了解ChromaDB ChromaDB就像SQLite但用于嵌入向量。一个文件(或文件夹),无需服务器,无需配置。安装,使用,开跑。 1 2 3 pip install chromadb # 或者如果你使用uv: uv add chromadb 基本概念 在普通数据库中你保存带列的行。在ChromaDB中你保存带嵌入向量的文档: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import chromadb # 创建客户端(持久化到磁盘) client = chromadb.PersistentClient(path="./mi_db") # 创建一个"集合"(类似表) collection = client.get_or_create_collection( name="conceptos", metadata={"hnsw:space": "cosine"} # 余弦距离 ) # 保存文档 collection.add( ids=["c1", "c2", "c3"], documents=["纯函数", "for循环", "递归"], metadatas=[ {"clase": "class_010", "tipo": "notes"}, {"clase": "class_015", "tipo": "notes"}, {"clase": "class_020", "tipo": "notes"}, ] ) 就这样。ChromaDB自动: ...

2026年1月18日 · Fernando