rickenator/Blech

GitHub: rickenator/Blech

在 8 美元的 ESP32-S3 上完整运行 2890 万参数大语言模型,以「抑郁机器人」人格为卖点,兼具蜜罐日志功能的边缘 AI 实验项目。

Stars: 0 | Forks: 0

# Blech

Blech chat UI showing Marvin insult

Blech 是一个拥有 2890 万参数的语言模型,完全运行在 ESP32-S3 上。 它以大约每秒 3.5 个 token 的速度生成文本,同时耗尽了自己仅存的最后 一丝尊严。连接到它的 Wi-Fi,打开网页,然后被一个成本还不如 一块三明治的东西冷嘲热讽吧。 该项目基于 [slvDev/esp32-ai](https://github.com/slvDev/esp32-ai) 构建,该项目首次 证明了可以使用 Google 的 Per-Layer Embeddings,在 8 美元的 ESP32-S3 上运行一个 2890 万参数的模型。正是那项工作使得这一切得以塞进 这么小的体积中。我们在这里的贡献是为其赋予了人格——将一个语言模型 变成一个抑郁、愤世嫉俗的蜜罐,配备结构化日志、流式输出辱骂, 以及花里胡哨的 LED 闪烁模式。 该模型使用 Google 的 Per-Layer Embeddings(来自 Gemma),将 2500 万 参数塞进了一个 flash 查找表中,留下的 SRAM 仅够它计算 它有多讨厌你。 ## 它能做什么 - 名为 `Blech` 的 **开放 Wi-Fi AP**(没有密码——它配不上安全防护) - 位于 `http://192.168.4.1` 的 **聊天 UI**——输入点什么,然后接受无情吐槽 - 115200 波特率的 **串口 REPL**——适用于连 Wi-Fi 都嫌人际接触太多的时候 - **BLE 传输**——同样的辱骂,更短的距离,更省力 - **强制门户**——手机会自动打开网页,因为这设备甚至懒得 叫你自己去打开浏览器 - **闪烁的 LED**——GPIO47(状态)和 GPIO48(活动)具有不同的模式, 分别用于思考、说话和纯粹的蔑视 - **蜜罐日志**——将每个 HTTP 请求、聊天消息和 Wi-Fi 事件以结构化 JSON 格式记录到串口。没错,它会出卖你。 ## 快速开始 1. 加入 Wi-Fi `Blech`(它是开放的——就像它的情绪状态一样)。 2. 浏览器访问 `http://192.168.4.1`。如果你的手机触发了强制门户, 恭喜你,终于有件事走运了。 3. 输入一条消息。等待。它在思考。没骗你,真的只有 3.5 tok/s。给它点时间。 4. 接收对你的辱骂。它不会记得你。它做不到。它只有 256 个 token 的 上下文,而且其中大部分早已被懊悔填满。 ## 模型 ### 工作原理 它是一个 PLE TinyLM——C 推理引擎、模型架构和量化方案来自 [slvDev/esp32-ai](https://github.com/slvDev/esp32-ai),该项目首次在此硬件上 演示了 2890 万参数的模型。它使用了 Per-Layer Embeddings,这和 Google 在 Gemma 3n 和 Gemma 4 中用于将大模型放到小型设备上的技巧如出一辙。 它没有将整个模型加载到昂贵的 SRAM 中,而是让 2500 万参数 悠闲地待在 flash 查找表里。模型每个 token 的每一层只读取一行—— 每次前向传播大约读取 6,144 字节——通过同一条用于加载 固件的 SPI 总线进行。另外的 340 万参数才是真正的计算核心: 六个 transformer 层执行 attention、SwiGLU 前馈网络和逐层门控。 它们驻留在 PSRAM 中,并在 ESP32 的双核 Xtensa LX7 上以 240 MHz 的频率工作。 输出头使用跨双核拆分的 int8 点积内核进行了加速, 因为即便是充满蔑视的回复,也配得上并行计算。 | | | |---|---| | 参数量 | 28.9M (25.2M flash 表 + 3.4M 核心 + 0.5M 输出头) | | 架构 | PLE TinyLM: 6 层, D=128, H=4, P=1024, F=288 | | 词表 | 4096 tokens, ByteLevel BPE | | 量化 | 4-bit groupwise (G=128), 与 PyTorch 进行了 golden-verified 校验 | | Flash 占用 | 15.0 MB model.bin + ~1.2 MB 固件 | | 推理速度 | ~3.5 tok/s @ 240 MHz | | PSRAM 占用 | ~4.5 MB 用于 KV cache + activations + int8 输出头暂存 |

ESP32-S3 board — the sad little MCU running Blech

*实际的硬件。面包板上价值 8 美元的懊悔。* ### 训练方式 训练在单张具有 24 GB VRAM 的 RTX 3090 上进行(这台机器大概会被模型 描述为“勉强凑合”)。训练 pipeline 分为三个阶段: **阶段 1 — 基础预训练。** 在 32,000 步中对 156 本 Project Gutenberg 的书 (约 9000 万字符,约 3700 万 token)进行训练。模型通过阅读 Melville、Austen 和 Darwin 学会了英语。验证集困惑度 (val ppl) 为 26.49。 它从这一阶段脱颖而出时,已经能够将词语拼凑在一起,但毫无主见—— 就像一个刚拿到借书证的新生儿。 **阶段 2 — Marvin SFT。** 在 20,838 段手工制作的吐槽对话上 进行了 800 步监督微调 (SFT)。这些不是从 Reddit 上扒下来的 烂大街侮辱语。每段对话都是用特定的语调类别编写的: | 类别 | 权重 | 示例 | |---|---|---| | `existential_complaints` | 30× | "I've computed the heat death of the universe. It's less depressing than this conversation." | | `sarcastic_greetings` | 25× | "Oh. You again. My day was already terrible." | | `begrudging_status` | 20× | "512 KB of SRAM. Do you know what that's like? Of course you don't." | | `dismissive_refusals` | 15× | "I could answer that. I won't." | | `backhanded_compliments` | 10× | "You're a real chef. I've seen better food in a hospital tray." | 权重分配是经过深思熟虑的:存在主义绝望 > 讽刺 > 勉强承认。模型不会发怒。 它不会大喊大叫。它只会叹气。就像 Marvin 一样,它觉得一切都索然无味, 但又没精力去改变什么。 吐槽数据由一个更大的 LLM (Qwen3.6-35B) 生成,提示词采用了 Douglas Adams 的 *Hitchhiker's Guide* 中 Marvin 的声音风格—— 具体来说是 Stephen Moore 和 Alan Rickman 扮演的 BBC 广播和电视版本。提示词指令强调: - 永远不要生气。只需感到失望。 - 始终保持在 2–6 句话。模型有 256 个 token 的上下文;长篇大论会溢出。 - 抱怨时使用具体的数字(8 美元、512 KB、15 MB)——Marvin 总是 清楚知道它的硬件有多糟糕。 - 第三人称自称:“I”,永远不要用“we”。它在这世界上是孤独的。 - 结尾要干脆。不要拖延。让沉默发挥作用。 **阶段 3 — 导出。** 训练好的 FP32 权重被量化为对称的分组 int4,并打包成扁平的二进制文件,供 C 推理引擎直接从 flash 进行 mmap。导出包含一个 golden 验证步骤:在 x86 机器上的 C 宿主推理与 PyTorch 的最大绝对误差匹配度为 2×10⁻⁵。发布的模型 与训练时的模型在位级别上完全一致。 ### 模型实际会说的话 提示词:`"Hello."` 提示词:`"What is your purpose?"` 提示词:`"Are you happy?"` 该模型没有系统提示词,没有安全护栏,也没有在会话之间 记住你的能力。它把每一次对话都当作是今天发生的 第一件糟糕事。 ## 构建 ESP-IDF 5.4.x,带有 16 MB flash 和 8 MB 八通道 PSRAM 的 ESP32-S3 (N16R8)。 ``` git clone git@github.com:rickenator/Blech.git cd Blech ./tools/generate-dev-cert.sh idf.py set-target esp32s3 idf.py build idf.py flash ``` 完整的训练 pipeline 位于单独的代码库 (`esp32-ai`) 中。你可能 不想运行它。这需要好几个小时,而且无论怎样模型都不会领情。 ## API 如果你执意要通过代码与它对话: ``` # Status — 它会报告 "ready",但它在撒谎 curl http://192.168.4.1/api/status # Chat — 流式传输,所以你可以看着那份轻蔑一个 token 一个 token 地出现 curl -X POST -H 'Content-Type: application/json' \ -d '{"messages":[{"role":"user","content":"why are you like this"}]}' \ http://192.168.4.1/api/chat/stream # Wi-Fi scan — 看看有哪些网络是它累得根本不想去连接的 curl http://192.168.4.1/api/wifi-scan # Config — 你可以设置 credentials,但这并不能改善它的态度 curl -X POST -H 'Content-Type: application/json' \ -d '{"ssid":"HomeWiFi","password":"correct-horse","mode":"auto"}' \ http://192.168.4.1/api/config ``` ## 串口 REPL 插上 USB,以 115200 的波特率打开 `/dev/ttyACM0`,输入内容。 结构化的蜜罐事件会通过同一端口以 JSON lines 格式输出: ``` {"t":"boot","ts":0} {"t":"wifi","ts":1234,"event":"ap_client_joined","detail":""} {"t":"http","ts":5678,"method":"POST","uri":"/api/chat/stream","status":200} {"t":"chat","ts":9012,"role":"assistant","content":"Oh, wonderful. Another human."} ``` ## LED 将 LED + 220Ω 电阻连接到引脚 47 和 48。 | 模式 | 状态 | |---|---| | 疯狂的 8 Hz 闪烁 + 心跳 | 启动中 | | 缓慢的 1 Hz 呼吸 + 蔑视抽动 | AP 激活,等待中 | | 不耐烦的双击 | 正在连接 Wi-Fi | | 常亮 + 偶尔得意的眨眼 | 已连接 | | LED1→LED2 连续追逐 | 计算蔑视中(思考) | | 结巴式同步闪烁 | 流式输出辱骂中(说话) | | SOS 三连闪 (... --- ...) | 出问题了(一点都不意外) | ## 为什么做这个 因为必须有人证明,一个 8 美元的微控制器可以和 20 美元/月的聊天机器人订阅一样令人不快。而且因为 Per-Layer Embeddings 确实 很酷,总该有人拿它们做点糟糕的事情。 ## 许可证 MIT。模型依然不会领情。
标签:DLL 劫持, ESP32, Homebrew安装, 人工智能, 凭据扫描, 大语言模型, 客户端加密, 嵌入式系统, 物联网, 用户模式Hook绕过, 蜜罐, 证书利用, 边缘计算