1613 字
8 分钟
agent-sudo v2 —— 从 Python 到 C++ 的全量重写

昨天用 Python + PySide2 发布了 agent-sudo 的初始版本。queue + flush 模式跑通了,一个下午从零到能用的原型,挺有成就感。

但今天再回头看,代码组织、性能、分发都存在明显的天花板。于是我通宵把它全量重写了一遍。

7emotions
/
agent-sudo
Waiting for api.github.com...
00K
0K
0K
Waiting...

agent-sudo demo

为什么重写#

v1 用 Python 做原型是刻意的选择——半天写完、能跑、能演示。但在昨天发完博客后,几个问题开始变得明显:

依赖地狱。用户需要安装 PySide2,而这个包在某些发行版上根本装不上(Qt5 的 Python binding 与系统 Qt 版本不兼容、PyPI wheel 不覆盖某些架构)。一个”零配置”的工具,安装第一步就先卡住。

分发沉重。Python 脚本意味着用户必须有一个完整的 Python 运行时,且路径需要符号链接到 /usr/local/bin。Agent 调用时可能切换了虚拟环境或者 PATH 混乱,导致 agent-sudo 根本找不到。

GUI 组件拼凑。PySide2 的 API 是命令式的,每增加一个组件都要在 __init__ 里塞一大堆 QPushButtonQLineEditconnect 语句。随着 60 秒倒计时、LLM 输入区、主题切换、危险标注这些功能加进来,main.py 膨胀到了六百多行,维护成本直线上升。

Python 自身的限制ctypes.memset 清零密码是一个运行时操作,编译器无法静态保证它一定被执行。多线程的密码管道读写(写入 sudo 的 stdin 后关闭写端让 sudo 读到 EOF)在 Python 里需要精心处理 QThreadcloseWriteChannel 的时序关系,容易写出竞态。

重写目标#

目标说明
零运行时依赖编译成单个 ELF 二进制,不依赖 Python 或任何解释器
类型安全C++ 编译期类型检查,消除 category 错配、信号/槽类型不对等 bug
声明式 UI用 creeper-qt 声明式组件库替代命令式 PySide2,UI 结构即代码结构
模块化拆分 main.cc(511 行)与 gui/ 下的独立模块,每个模块职责单一
Qt6 原生使用 Qt6 的 Widgets、Network、Svg、Multimedia 模块,不再受 Qt5 兼容限制
可审计编译选项可见、构建脚本可复现、产物可校验

架构变化#

v1 是一个 main.py 单文件。v2 拆分成了清晰的模块结构:

src/
├── main.cc                  # argv[0] 分派 + creeper-qt 声明式 UI
├── gui/
│   ├── command_card.{h,cc}  # 命令卡片构建器(含危险检测)
│   ├── ring_countdown.{h,cc}# 36×36 环形倒计时
│   ├── icon.{h,cc}          # Material Icons + 应用 SVG
│   ├── sound.{h,cc}         # QSoundEffect 声音管理器
│   ├── theme_transition.{h,cc} # ColorScheme 平滑插值
│   ├── queue_io.{h,cc}      # JSON 队列/历史 I/O
│   ├── executor.{h,cc}      # sudo 子进程执行器
│   └── tr.h                 # JSON 驱动 i18n

UI 层没有从零手写 Qt6 Widget 代码,而是基于一个优秀的声明式组件库:

creeper5820
/
creeper-qt
Waiting for api.github.com...
00K
0K
0K
Waiting...

creeper-qt 把 Qt 的命令式 Widget 构建转换为声明式的嵌套组件树——窗口、卡片、按钮、输入框的层级关系直接对应代码的嵌套结构。主题系统(3 套预设 + 亮暗模式切换)和 MixerMask 展开动画也直接由 creeper-qt 的 ThemeManagerTransitionValue 驱动,不需要自己写一帧帧的过渡逻辑。

感谢 @creeper5820 的优秀工作,src/gui/creeper-qt/ 为 vendored 源码,遵循 MIT 许可。

单二进制通过 argv[0] 检测实现分派:调用 agent-sudo 走入队逻辑,调用 agent-sudo-flush 走弹窗逻辑,两者是同一个 ELF 的符号链接。Agent 不需要关心这是 Python 还是 C++ —— agent-sudo --reason "..." -- <cmd> 的行为完全不变。

新技术栈#

维度v1 (Python)v2 (C++23)
语言Python 3.8+C++23 (GCC 14+)
GUIPySide2 (Qt5)creeper-qt + Qt6
构建pip installCMake 3.22+
依赖PySide2, PythonQt6, Eigen3, CMake
产物.py 脚本 + 符号链接单个 ELF 二进制
密码清零ctypes.memsetstd::fill + clear()
i18n硬编码中文8 语言 JSON 驱动

v2 新增功能#

在重写过程中,借着架构的灵活性顺手加了不少新功能:

危险检测。每条命令自动分析字符串内容并标注安全等级:常规 apt install 标绿色 ✓,systemctl stop/kill/chmod 标黄色 ⚠,rm -rf/dd if=/mkfs 标红色 ✕。纯字符串匹配,不需要 Shell 解析,但 Agent 无法绕过标注。

主题系统。3 套预设配色(BlueMiku/GoldenHarvest/Green)+ 亮/暗模式。切换时 ColorScheme 平滑插值动画,不是硬切。点击窗口右上角的调色板图标就能轮换,设置自动写入 ~/.config/agent-sudo/theme.conf

声音反馈。4 种事件音效(窗口打开/10 秒警告/执行成功/审批拒绝),每个事件可独立设为 default/none/自定义 WAV。QSoundEffect 实现,不依赖外部播放器。

多语言。8 种语言(zh/en/ja/ko/fr/de/es/pt),JSON 驱动。中文为内置默认,其余通过 Qt 资源文件加载。翻译条目覆盖所有 UI 文案,包括危险检测的等级标签和按钮文字。

Material Icons。用 Material Icons Round 字体渲染所有图标(codepoint 引用),不再依赖系统图标主题。自定义应用 SVG 图标用于窗口标题栏和任务栏。

倒计时暂停。60 秒倒计时增加暂停/恢复按钮,人在来不及操作时可以暂停计时器。

10 秒警告。倒计时最后 10 秒触发警告音效,提醒人尽快操作。

认证失败处理。自动检测中文和英文的 sudo 密码错误提示,失败后清空队列并返回退出码 126,不重试。

输出过滤。执行结果自动过滤 [sudo]sudo: 噪音行,Agent 收到的输出更干净。

构建与安装#

cmake -S . -B build && cmake --build build
sudo cp build/agent-sudo-flush /usr/local/bin/
sudo ln -sf /usr/local/bin/agent-sudo-flush /usr/local/bin/agent-sudo

编译依赖 GCC 14+、Qt6(Widgets/Network/Svg/Multimedia)、Eigen3、CMake 3.22+。产物是单个约 490KB 的二进制文件,无动态链接的 Python 依赖。

对于不想自己编译的用户,INSTALL.md 提供了预编译二进制下载和安装步骤。

结语#

v2 重写熬了一整夜,核心动机不是”Python 不够好”,而是”这个工具已经到了需要独立分发的阶段”。C++ 的单二进制分发、编译期类型安全、以及 creeper-qt 的声明式 UI 模式,让代码的组织和维护都上了一个台阶。

Python 原型仍然有价值——它在半天内验证了 queue + flush 模式的可行性。没有这个原型,v2 不可能在一个通宵内完成。但到了需要独立安装、独立运行、独立交付的阶段,C++ 是更自然的选择。

项目以 MIT 协议开源在 GitHub,欢迎试用和反馈。

agent-sudo v2 —— 从 Python 到 C++ 的全量重写
https://lorenzofeng.top/posts/agent-sudo-v2/
作者
Lorenzo Feng
发布于
2026-06-24
许可协议
CC BY-NC-SA 4.0