agentty :当 AI 编程助手跑进终端,用 C++ 重写一切
概述
AI 编程助手赛道这两年卷得厉害,从 GitHub Copilot 到 Claude Code,从 Cursor 到 Windsurf,各种形态的产品层出不穷。但如果你是一个习惯在终端里干活的人,可能会发现一个尴尬的现实:这些工具要么重度依赖 Electron 和 Node.js,启动慢、内存吃得多;要么把用户锁定在自家 IDE 里,离开那个环境就什么都不是。agentty 就是为了解决这个问题而生的——一个用 C++26 写的、编译成单个静态二进制文件的终端 AI 编程助手,冷启动不到 1 毫秒,不依赖 Node、Python 或任何运行时,支持几乎所有主流大模型。它做的事情和 Claude Code 类似,但走了一条完全不同的技术路线。
产品背景
要理解 agentty 为什么存在,得先看看它所处的环境。
2024 到 2025 年间,AI 编程助手经历了一轮爆发式增长。Anthropic 推出了 Claude Code,一个基于终端的 agentic coding 工具,凭借 Claude 模型强大的代码理解和工具调用能力,迅速在开发者社区走红。但 Claude Code 有一个让不少人诟病的问题:它是一个 Node.js 应用。这意味着每次启动都要加载整个 Node 运行时,冷启动时间动辄几秒甚至更长,内存占用也不低。对于习惯了vim、tmux、各种轻量级终端工具的开发者来说,这种"重"是难以接受的。
与此同时,终端原生工具的优势在 AI 时代被重新审视。终端是开发者最熟悉的界面,SSH 远程开发、容器内操作、服务器运维——这些场景下图形界面要么不可用,要么体验很差。一个能在终端里跑、资源占用极低、启动极快的 AI 编程助手,有着明确的市场需求。
agentty 的作者显然看到了这个缺口。项目的 GitHub 仓库创建于 2026 年 4 月,定位非常明确:做 Claude Code 的替代品,但用完全不同的技术栈来实现。从它的 topics 标签(claude-code-alternative、static-binary、cpp26、airgap)就能看出作者的核心主张——更快、更轻、更自由。
更深一层看,这个项目也反映了开源社区对"单一供应商锁定"的警惕。Claude Code 绑定 Anthropic 的 API,Cursor 绑定 OpenAI,而 agentty 从一开始就设计成多模型、多提供商的架构,把选择权交还给用户。
功能分析
agentty 的功能可以分成几个核心模块来看。
多模型支持
这是 agentty 最基础也最重要的能力。它默认使用 Claude(支持 OAuth 登录,可以直接用 Claude Pro/Max 的订阅),但也支持通过--provider参数切换到其他模型:
- OpenAI(GPT-4o 等)
- Groq(Llama 3.3 等)
- OpenRouter(接入几乎所有模型)
- Ollama(本地模型,比如 Qwen2.5-Coder)
- 任何兼容 OpenAI API 的端点
而且模型切换不需要重启,在应用内按Ctrl+P就能实时切换。这个设计让 agentty 不是一个"绑定某个模型"的工具,而是一个"模型无关的终端前端"。
完整的工具链
agentty 内置了一套丰富的工具,让 AI 能够真正操作你的代码库:
- 文件操作:
read(读取文件)、write(写入文件)、edit(编辑文件) - 搜索与导航:
grep(内容搜索)、glob(文件匹配)、find_definition(查找定义) - 命令执行:
bash(执行 shell 命令)、diagnostics(诊断信息) - Git 集成:
git_status、git_diff、git_log、git_commit - 网络能力:
web_fetch(抓取网页)、web_search(搜索) - 记忆系统:
remember、forget、wipe_memory - 任务调度:
task(子代理分发)、skill(加载 Agent Skill) - 其他:
todo(待办事项)、list_dir(列出目录)
每个工具都有专门的 UI widget 来展示结果,不是简单的文本输出。比如edit工具会以 diff 的形式展示变更,bash工具会展示命令输出和退出码。
沙箱安全
agentty 默认开启沙箱。在 Linux 上使用bwrap(Bubblewrap),在 macOS 上使用sandbox-exec。这意味着 AI 执行的每一个 shell 命令都跑在隔离环境里,文件系统工具也拒绝访问工作区以外的路径。即使你授权了一个bash调用,它也无法读取~/.ssh/id_rsa这类敏感文件。
权限策略分为三个级别:
| 级别 | 读文件系统 | 写文件系统 | 网络 | 执行 |
|---|---|---|---|---|
| Write | 允许 | 允许 | 允许 | 允许 |
| Ask | 允许 | 需确认 | 需确认 | 需确认 |
| Minimal | 需确认 | 需确认 | 需确认 | 需确认 |
这套策略是在编译期通过constexpr验证的,48 种组合全部在构建时被穷举检查,不存在运行时才发现策略漏洞的可能。
离线(Air-gapped)模式
这是 agentty 一个非常有特色的功能。有些开发场景下,目标机器是没有外网连接的(比如内网服务器、安全隔离环境)。agentty 的 airgap 模式允许你在本地笔记本和远程主机之间建立 SSH 隧道,本地负责转发 API 请求,远程主机上的 agentty 通过 SOCKS5-over-SSH 与本地通信。整个过程中 TLS 证书是端到端 pinned 的,中间的 SSH 隧道无法进行中间人攻击。
使用非常简单:
agentty airgap--setupuser@host# 首次:复制凭据agentty airgap user@host# 之后每次Agent Skills 与记忆系统
agentty 支持通过SKILL.md文件来教 AI 理解你的代码库。你可以在项目的.agentty/skills/目录或全局的~/.agentty/skills/目录下放置技能文件,格式兼容 Claude Code 的.claude/skills/。根据项目引用的研究论文(arXiv:2410.03981),在有内部 DSL 或团队约定的代码库上,精心编写的技能文件可以将 AI 的准确率从约 20% 提升到 85%。
remember/forget工具则让 AI 能够跨会话记住你的偏好和约定,不需要每次重复说明。
Zed 集成(ACP)
agentty 支持 Agent Client Protocol(ACP),可以直接作为 AI agent 跑在 Zed 编辑器里。配置非常简单,在 Zed 的settings.json里加一段:
{"agent_servers":{"agentty":{"command":"agentty","args":["acp"]}}}这意味着你既可以在终端里直接用 agentty,也可以在 Zed 里通过 ACP 调用它,两者共享同一套 provider、工具注册表、权限策略和工作区沙箱。
代码块运行(Ctrl+G)
当 AI 回复中包含可执行的 shell 代码块时,按Ctrl+G可以直接在真实终端上运行这些命令——不需要复制粘贴。TUI 会挂起,命令在真实 shell 中执行(sudo 密码提示可用,输出实时显示,Ctrl+C杀掉的是命令而不是 agentty),执行完后可以选择将输出附加到对话中、复制到剪贴板或丢弃。
解决了什么问题
agentty 解决的核心问题可以归结为以下几点:
第一,终端用户的 AI 编程体验问题。现有的 AI 编程助手要么是基于 Electron 的重型应用(Cursor、Windsurf),要么是 Node.js 的终端工具(Claude Code)。对于追求轻量、快速、终端原生的开发者来说,这些工具的启动时间和资源占用都是痛点。agentty 用 C++ 编译成单个静态二进制文件,冷启动不到 1 毫秒,从根本上解决了"等工具启动"的问题。
第二,模型锁定的问题。大多数 AI 编程工具深度绑定某个模型提供商。Claude Code 只能用 Claude,Copilot 只能用 OpenAI。agentty 从架构上就是多模型的,用户可以自由切换,甚至在离线环境下使用本地 Ollama 模型。这对于有成本考量、隐私需求或单纯想尝试不同模型的用户来说,是一个重要的差异化优势。
第三,离线/内网环境的 AI 编程问题。很多开发场景(运维、安全、金融)要求目标机器不能直连外网。agentty 的 airgap 模式通过 SSH 隧道优雅地解决了这个问题,而且不需要在远程主机上做任何额外配置。
第四,安全问题。让 AI 在你的代码库上执行命令,天然存在安全风险。agentty 通过 OS 级沙箱(bwrap/sandbox-exec)+ 编译期验证的权限策略 + 工作区边界限制,构建了一个多层防御体系。这比单纯依赖"用户确认"要可靠得多。
第五,AI 对代码库的理解问题。通用大模型对特定项目的内部约定、DSL、架构模式往往理解不够。agentty 的 Agent Skills 机制让开发者可以用自然语言编写技能文件来"教"AI,而且这个能力是跨会话持久的。
产品实现分析
agentty 的技术实现是它最值得深入分析的部分,因为它的设计选择直接决定了产品的体验差异。
整体架构:Elm 风格的纯函数更新循环
agentty 的架构灵感来自 Elm 语言的设计模式,整个运行时是一个纯函数循环:
(Model, Msg) -> (Model, Cmd<Msg>)- Model是整个应用状态的聚合结构体
- Msg是一个封闭的 sum type(C++ 中用
std::variant实现),包含所有可能的事件 - Cmd是对副作用的描述(网络请求、磁盘操作、定时器),由运行时执行后将结果反馈为新的 Msg
渲染是第二个纯函数view: Model -> Element,委托给maya——一个作为 git submodule 引入的姐妹 TUI 引擎。agentty 自身不构造任何 UI 元素,只从 Model 状态构建 widget 的 Config 值,maya 负责所有像素、边框和动画。
这个架构的好处是可预测性极强。状态变化只能通过 Msg 触发,副作用只能通过 Cmd 描述,没有任何隐式的全局状态修改。这使得调试、测试和推理程序行为变得非常直接。
目录结构
include/agentty/ ├── domain/ # 纯数据,无 I/O(session, conversation, catalog, todo, profile) ├── runtime/ # 应用主体(model, msg, update, view) ├── provider/ # Provider 概念及实现(anthropic, openai) ├── tool/ # Tool 概念、注册表、权限策略 ├── io/ # http, tls, auth, persistence, clipboard └── airgap/ # SOCKS5-over-SSH src/ # 与 include 镜像的实现文件domain/层只包含纯数据结构,没有任何 I/O 操作。session、conversation、catalog、todo、profile都是纯数据。ID 类型使用了强类型 newtype(ToolCallId、ThreadId、OAuthCode、PkceVerifier),交换两个不同类型的 ID 会在编译期报错,而不是在运行时才发现 bug。
Provider 抽象层
Provider是一个 C++26 concept:
template<classP>conceptProvider=requires(P&p,Request req,EventSink sink){{p.stream(std::move(req),std::move(sink))}->std::same_as<void>;};任何能流式传输 chat completion 的类型都满足这个 concept。目前有两个生产级实现:AnthropicProvider(HTTP/2 + SSE,支持 OAuth/Pro/Max)和OpenAIProvider(任何 OpenAI 兼容端点)。测试中还有一个确定性的内存脚本实现。
main.cpp在启动时通过Depsseam 安装具体的 Provider 和 Store,运行时通过app::deps()访问。update_auth()可以在不重启进程的情况下实时切换凭据——进行中的流在请求构建时缓存了 header,所以不受影响。
Msg 的领域拆分
为了避免一个巨大的 variant 导致每次修改都触发全量重编译,agentty 将 Msg 拆分成约 12 个领域子变体(ComposerMsg、StreamMsg、ToolMsg、ModelPickerMsg等)。顶层 reducer 通过std::visit将每个领域转发到独立的翻译单元:
autostep=std::visit(overload{[&](msg::ComposerMsg cm){returndetail::composer_update(...);},[&](msg::StreamMsg sm){returndetail::stream_update(...);},[&](msg::ToolMsg tm){returndetail::tool_update(...);},// ... 还有 9 个领域分支},msg);这样每个update/<domain>.cpp只在自己的领域叶子变化时才需要重编译,大大提升了开发时的编译速度。
工具系统:类型安全的 JSON 边界
每个工具都实现Toolconcept,要求提供静态的身份、schema、效果集和行为:
template<classT>conceptTool=requires{typenameT::Args;typenameT::Result;{T::name()}->std::convertible_to<std::string_view>;{T::description()}->std::convertible_to<std::string_view>;{T::input_schema()}->std::convertible_to<nlohmann::json>;{T::effects()}->std::convertible_to<EffectSet>;}&&requires(constnlohmann::json&args){{T::execute(args)}->std::convertible_to<ExecResult>;};工具内部是完全类型安全的,只有调度器边界才使用 JSON。DynamicDispatch在try/catch中执行工具(崩溃的工具变成类型化的ToolError,不会导致进程终止),并对每个工具应用输出预算,防止失控的read/bash/grep撑爆上下文窗口。截断策略有三种:
- Head:保留头部,适合有序内容(read、edit、write)
- Tail:保留尾部,适合日志流(bash、diagnostics)
- HeadTail:保留两端、省略中间,适合两端都有信号的工具(grep、web、git diff)
权限策略:编译期验证的矩阵
每个工具声明一个EffectSet,包含四个位:ReadFs、WriteFs、Net、Exec。权限策略是一个constexpr函数,在编译期被穷举验证:
4 种效果 × 3 种 Profile = 48 个单元格一个独立的expected_decision函数重新表述策略,通过static_assert穷举验证每个单元格的一致性。如果任何一方被修改导致不一致,构建会直接失败——不是等到测试才发现。
工具调度:并行安全
同样的EffectSet还决定了两个工具是否可以并发执行:
WriteFs和Exec要求独占访问(写操作可能影响其他工具的读取,exec 的命令是模型选择的所以必须串行)Pure、ReadFs、Net可以自由组合
流式转储:带重试看门狗的状态机
一个 turn 不是单个请求,而是一个状态循环:Streaming → AwaitingPermission → ExecutingTool → Streaming → ... → Idle。这个 FSM 建模在domain/session.hpp中,每个非 Idle 状态都携带 turn 上下文(取消令牌、开始时间戳、重试计数器),从 Idle 读取这些字段是编译错误。
可靠性依赖两个独立机制:
- 重试状态机:120 秒的停滞看门狗触发取消,合成
StreamError调度重试 - 两个独立的重试预算:
truncation_retries处理流中途 EOF,transient_retries处理 5xx/网络/过载/429。后者不是单调递增的——每当连接证明健康(第一个内容 delta 或 SSE ping)就重置为 0
安全边界
- 工作区边界:文件系统工具拒绝工作区外的路径(
--workspace /可以退出) - 沙箱:
bash和diagnostics默认在 bwrap/sandbox-exec 中运行 - TLS 证书固定:包括通过 airgap SOCKS 隧道的端到端固定
- 原子写入:所有持久化文件使用
write+fsync+rename,崩溃不会损坏数据
构建与分发
- 语言:C++26(MSVC 上用 C++23 +
/std:c++latest) - 编译器要求:GCC 14+ / Clang 18+ / MSVC 14.40+
- 构建系统:CMake 3.28+
- 内存分配器:mimalloc(Windows 上性能提升 15-25%)
- CPU 基线:AVX2(覆盖 2013 年后的所有桌面/笔记本)
- 分发:单个完全静态的可执行文件,支持 Linux x86_64/aarch64、macOS Intel/Apple Silicon、Windows
- 包管理:支持 apt、dnf、zypper、AUR、apk、brew、scoop、winget、nix、snap、gentoo
架构逻辑图
┌──────────────────────────────────────────────────────────────────┐ │ agentty 运行时 │ │ │ │ ┌──────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │ │ main.cpp │───▶│ Deps Seam │───▶│ Provider Concept │ │ │ │ (启动入口) │ │ (依赖注入) │ │ ├─ Anthropic │ │ │ └──────────┘ └──────────────┘ │ ├─ OpenAI │ │ │ │ │ └─ 测试用内存实现 │ │ │ ▼ └──────────────────────┘ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ maya TUI 引擎 │ │ │ │ ┌─────────┐ ┌──────────┐ ┌────────────────────────┐ │ │ │ │ │ init │ │ view │ │ subscribe │ │ │ │ │ │(加载状态)│ │(渲染视图) │ │ (定时器 + 流订阅) │ │ │ │ │ └─────────┘ └──────────┘ └────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ update (Model, Msg) -> (Model, Cmd) │ │ │ │ ┌────────────┐ ┌────────────┐ ┌────────────────────┐ │ │ │ │ │ComposerMsg │ │ StreamMsg │ │ ToolMsg │ │ │ │ │ │(输入处理) │ │(流式响应) │ │ (工具调用/结果) │ │ │ │ │ └────────────┘ └────────────┘ └────────────────────┘ │ │ │ │ ... + 9 个其他领域 Msg │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌──────────┐ ┌────────────────────────────┐ │ │ │ Store │ │ Tools │ │ 安全层 │ │ │ │(持久化) │ │(注册表) │ │ ├─ 权限策略 (constexpr) │ │ │ │ │ │ │ │ ├─ OS 沙箱 (bwrap) │ │ │ │ │ │ │ │ ├─ 工作区边界 │ │ │ │ │ │ │ │ └─ TLS 证书固定 │ │ │ └──────────┘ └──────────┘ └────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ Airgap (SOCKS5-over-SSH) │ │ │ │ 本地笔记本 ◀═══ SSH 隧道 ═══▶ 远程无网主机 │ │ │ └──────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────┘产品优势
和同类产品相比,agentty 有几个明显的优势:
极致的启动速度和资源占用。这是 agentty 最直观的优势。一个静态二进制文件,冷启动不到 1 毫秒,没有 Node 运行时、没有 Python 解释器、没有 npm 依赖。相比之下,Claude Code 启动需要加载整个 Node.js 运行时,Cursor 和 Windsurf 更是基于 Electron 的重型应用。对于频繁启停终端会话的开发者来说,这个差距是体感级别的。
真正的多模型自由。大多数 AI 编程工具要么只能用一个模型,要么切换模型需要复杂的配置。agentty 从架构上就是模型无关的,Ctrl+P一键切换,而且支持本地 Ollama 模型——这意味着你甚至可以在完全离线的环境下使用 AI 编程助手。
编译期安全保证。权限策略的 48 种组合在构建时就被穷举验证,效果集和并行安全规则通过static_assert锁定。这不是运行时测试能达到的保证级别——如果策略被改坏了,构建直接失败,不可能带着漏洞发布。
离线友好。airgap 模式是 agentty 独有的能力。通过 SSH 隧道让无外网的主机也能使用 AI 编程,而且配置简单到只需要一条命令。这对于运维、安全、金融等行业的开发者来说是刚需。
终端原生体验。不是把 Web UI 塞进终端,而是从零为终端设计的 TUI。maya 引擎处理所有的终端渲染细节——颜色检测、真彩色/256 色/16 色自适应、SSH 环境下的降级策略。代码块运行(Ctrl+G)直接在真实终端上执行,sudo 密码提示可用,Ctrl+C杀命令不杀 agentty。
开放和可替代。MIT 许可证,代码完全开放。ACP 协议支持意味着它可以跑在 Zed 里,也可以被任何支持 ACP 的编辑器集成。不绑定任何单一供应商。
当然,agentty 也不是没有取舍。作为一个相对年轻的项目(2026 年 4 月创建,目前 v0.2.7),它的生态和社区规模还无法和 Claude Code 或 Cursor 相比。C++26 的编译要求(GCC 14+ / Clang 18+)也意味着在某些老旧环境上从源码构建会有门槛——不过预编译的二进制文件覆盖了主流平台。
总结
agentty 是一个思路清晰、执行到位的产品。它没有试图在所有维度上和 Claude Code、Cursor 这些成熟产品竞争,而是精准地切入了一个细分市场:追求终端原生体验、注重启动速度和资源占用、需要多模型自由和离线能力的开发者。
从技术实现来看,agentty 的架构设计值得称赞。Elm 风格的纯函数更新循环带来了极强的可预测性,C++26 的 concept 和 constexpr 特性被充分利用来在编译期捕获错误,领域拆分的 Msg 设计兼顾了类型安全和编译速度。这些不是花哨的技术炫技,而是直接转化为用户可感知的体验优势——1 毫秒的启动时间、不会因策略修改引入安全漏洞的权限系统、崩溃不会损坏数据的原子写入。
当然,作为一个开源项目,agentty 的成功最终取决于社区能否围绕它建立起来。但从目前的功能完整度和技术质量来看,它已经是一个可以日常使用的产品,而不只是一个技术 demo。对于在终端里生活的开发者来说,agentty 提供了一个值得认真考虑的选择。
参考 URL
- agentty GitHub 仓库:https://github.com/1ay1/agentty
- agentty 官网:https://agentty.org
- agentty 架构文档:https://github.com/1ay1/agentty/blob/master/docs/ARCHITECTURE.md
- agentty 渲染文档:https://github.com/1ay1/agentty/blob/master/docs/RENDERING.md
- agentty 代码块运行文档:https://github.com/1ay1/agentty/blob/master/docs/RUN_CODE_BLOCK.md
- agentty 变更日志:https://github.com/1ay1/agentty/blob/master/CHANGELOG.md
- maya TUI 引擎:https://github.com/1ay1/maya
- Agent Client Protocol:https://agentclientprotocol.com
- Agent Skills 相关研究论文:https://arxiv.org/abs/2410.03981