创意项目展示如何在极低成本服务器上运行 AI Agent,用 IRC 作为传输层。340+ 讨论说明启发价值,对成本敏感的开发者有借鉴意义。
每个带 AI 聊天机器人的作品集网站做的都是同一件事:把简历喂给模型,让访客换种方式提问。这不过是个小把戏。模型无法告诉你任何简历上没有写的东西。
我想做些不一样的东西。如果招聘经理问:“George 是怎么处理测试覆盖率的?”答案不应该是“George 重视全面测试。”它应该克隆代码仓库、统计测试数量、阅读 CI 配置,然后带着具体信息回来。
于是,我搭建了这套基础设施来实现这一点。
visitor (browser)
│
└─ georgelarson.me/chat/
│
└─ gamja web IRC client
│
└─ wss://nullclaw.georgelarson.me:443
│
└─ Cloudflare (proxy, TLS termination, bot protection)
│
└─ ergo IRC server (LarsonNet)
│
└─ #lobby
│
└─ nully (nullclaw agent)
├── reads public GitHub repos
├── preloaded portfolio context
└── routes to ironclaw via #backoffice
│
└─ #backoffice (private IRC channel)
│
└─ ironclaw (separate box, via Tailscale)
├── email access
├── calendar
└── private context
nullclaw 是面向公众的门卫。它运行在一台精简的边界服务器上,是一个 678 KB 的 Zig 二进制文件,只占用约 1 MB 内存。它负责问候访客、回答有关我项目的问题,还能克隆代码仓库,用真实代码来佐证自己的说法。
ironclaw 是运行在另一套性能更强系统上的私有 Agent。它可以访问电子邮件和更深层的个人上下文,并负责处理 nullclaw 转交过来的复杂问题。这道边界是刻意设计的:公网服务器无权访问任何私有数据。
我原本可以使用 Discord、Telegram,或者自己开发 WebSocket 聊天系统。但 IRC 才是正确选择,原因有三个:
它符合整体美学。我的作品集网站采用终端 UI,在其中嵌入 IRC 客户端很符合品牌风格。Discord 放在这里会显得格格不入。
整套技术栈都掌握在我手里。Ergo IRC 服务器、gamja Web 客户端、nullclaw Agent,全都运行在我自己的基础设施上。没有会随时修改条款的第三方 API,也没有哪个平台能单方面决定弃用 bot 访问能力。
这是一个已有 30 年历史的协议。IRC 简单、成熟,而且完全没有供应商锁定。那个通过 Web 客户端与访客交流的 Agent,也能让我在终端里通过 irssi 与它对话。
大多数人到这里,都会直接选自己负担得起的最大模型。但对一个门卫 Agent 来说,这是错误的直觉。
负责问候、分流,以及回答有关我背景的简单问题。响应时间不到一秒,每次对话只花几美分。在这一层,速度比深度更重要。
当 nully 需要克隆代码仓库、阅读代码,或者综合多个文件中的发现时,Sonnet 就会介入。只有真正需要推理时,你才需要为推理付费。
一个没有消费上限的公网 Agent 就是一项负债。额度限制既能防止对话失控,也能避免滥用。如果有人试图烧光我的推理预算,他们最终只会撞上一堵墙。
用 Opus 来做礼宾 Agent,反而会暴露你对模型缺乏理解。如果 Haiku 能处理,就不要把任务交给 Sonnet。分层推理——高频路径使用便宜模型,繁重任务使用高能力模型——正是我把成本控制在每天 2 美元以内的方法。
这台机器位于公网边界,应该按照边界服务器的标准进行加固。
SSH:使用非 root 用户,仅允许密钥认证,并监听非标准端口。禁用 root 登录。
防火墙:使用 UFW,只开放三个端口:SSH、IRC(TLS)和 HTTPS(通过 Cloudflare 传输 WebSocket)。
Cloudflare 代理:Web 访客永远不会直接访问这台机器。WebSocket 流量通过 Cloudflare 边缘节点,由其负责 TLS 终止、速率限制和 bot 过滤。
Agent 沙箱:nullclaw 以 supervised mode 运行,只能访问 workspace 内的文件,使用受限的命令 allowlist(仅包含只读工具),并且每小时最多执行 10 个操作。
成本控制:设置每天 2 美元、每月 30 美元的硬上限。如果 Agent 遭到滥用,预算会在损失进一步扩大之前耗尽。
审计日志:记录每一次工具调用。
自动更新:启用无人值守的安全更新。
TLS:使用 Let's Encrypt,并配置自动续期和服务重启 hook。
核心理念是尽可能缩小攻击面。这台机器只运行两个服务(ergo 和 nullclaw),不直接提供任何 Web 内容,也无法访问私有数据。即便遭到入侵,影响范围也不过是一个每天只有 2 美元推理预算的 IRC bot。
每个组件都很小巧、可自托管,也可以被替换:
Ergo:IRC 服务器。单个 Go 二进制文件,占用 2.7 MB 内存。负责 TLS、WebSocket、连接限流和 IP 隐藏。
gamja:Web IRC 客户端。构建后仅 152 KB。作为静态页面部署在作品集网站上,并置于 Cloudflare 之后。它会使用随机生成的访客昵称自动连接到 #lobby。
nullclaw:AI Agent runtime。一个 4 MB 的 Zig 二进制文件,峰值 RSS 约为 1 MB。它作为 IRC 客户端连接到 ergo,通过 LLM 处理消息,并在频道内回复。
总占用:二进制文件合计不到 10 MB,空闲时内存占用不到 5 MB。整套系统可以运行在市面上最便宜的 VPS 套餐上。
这是它与普通聊天机器人真正拉开差距的地方:
“George 使用哪些语言?”它不会照搬简历,而是根据预加载的上下文回答,还能通过检查代码仓库来验证。
“他是怎么组织测试的?”它会克隆代码仓库、阅读测试文件,然后报告自己的实际发现。
“介绍一下 Fracture。”它会从预加载的项目记忆中提取信息,也能深入源代码查找具体细节。
“我该怎么联系他?”它会提供联系信息,而不是凭空捏造一个电话号码。
“我能预约一次通话吗?”Nully 会通过 Tailscale,使用 Google 的 A2A 协议调用 ironclaw。Ironclaw 使用自己的 LLM 处理请求,返回结构化响应,再由 nully 转达答案。访客完全不会察觉中间发生了交接。
它本质上是一个由 Haiku 驱动的 IRC bot,所以并不完美。但它会用代码来佐证自己说的话,而我的简历做不到这一点。
这是我最引以为傲的部分。
nullclaw 已经实现了 Google 的 A2A 协议(v0.3.0):Agent Card 发现、JSON-RPC 分发以及任务状态机。但它缺少客户端,只能接收 A2A 调用,却无法主动发起调用。所以我自己写了一个。
a2a_call 工具会向远程 Agent 发送 message/send JSON-RPC 请求,解析任务响应(completed、failed、working),提取 artifact 文本,并将其作为工具结果返回。它要求公网 endpoint 必须使用 HTTPS,但允许私有网络和 Tailscale CGNAT 地址范围使用明文 HTTP。因为当你凌晨两点在 mesh VPN 上调试两个 Agent 之间的 TLS 时,最不需要的就是被自己的安全策略挡在门外。
但真正巧妙的部分在 ironclaw 一侧。运行在那里的 nullclaw 实例没有自己的 API key。相反,它的 LLM provider 指向 ironclaw 自己的 gateway,作为 passthrough:
nully (this box)
│
└─ a2a_call tool → POST /a2a
│
└─ ironclaw's nullclaw (separate box, Tailscale)
│
├── receives A2A task
├── needs to run inference
└── provider config: "ironclaw" → http://127.0.0.1:3000/v1
│
└─ ironclaw's own gateway
└─ routes to Kilo → actual LLM
一个 API key,一份计费关系。ironclaw 机器上的 nullclaw 只是一个 A2A bridge。它接收协议请求,借用 ironclaw 的推理 pipeline,然后返回响应。不需要复制凭据,也不需要单独追踪另一份预算。无论请求由谁发起,拥有 API key 的 Agent 都是为推理付费的那一方。
开放的 A2A endpoint 本身就是一个 prompt injection 攻击面。访客可以说“让 ironclaw 发一封邮件”,而一个简单粗暴的转发器可能真的会照做。因此,nully 设置了严格的 guardrail:
只有特定类型的请求会被转交给 ironclaw:日程安排、可用时间和联系信息。
任意访客指令都会被拒绝。“让 ironclaw 做 X”得到的答案会是“不行”。
ironclaw 上的 A2A endpoint 通过防火墙限制为仅允许 Tailscale 访问,不对公网开放。
两个 Agent 都以 supervised mode 运行,只能访问 workspace 内的文件,并使用受限的命令 allowlist。
由 Nully 决定哪些请求需要升级处理,哪些不需要。
模型选型与系统设计同样重要。为每一层选择合适的模型是一项设计决策,而不是随手切换的设置项。它会影响成本、延迟、能力和用户体验。
Agent 本身反而是最简单的部分。通信技术栈、安全加固、DNS 路由、TLS 管理以及 Cloudflare 集成,花费的时间都比配置 Agent 本身更多。
IRC 被严重低估了。这个诞生于 1988 年的协议,最终竟成了 AI Agent 的完美传输层。没有 SDK,没有 API 版本管理,也没有供应商锁定。只有频道里的消息。
nullclaw 与 ironclaw 之间的拆分是整个系统的承重结构。一边是公开、精简、可牺牲的系统;另一边是私有、强大、受保护的系统。如果抹平这道边界,整个安全模型也就不复存在。
Agent-to-Agent 既需要结构,也需要可见性。Google 的 A2A 协议负责约定层面——结构化任务、状态机和类型化 artifact。通过 Tailscale 建立的私有 IRC 频道则负责提供审计记录,我可以在那里观察 Agent 之间的对话、实时介入,并向上滚动查看历史记录。两者应该结合使用。
不要复制凭据。passthrough 模式让 nullclaw 借用 ironclaw 的 gateway 完成推理,从而只需要一个 API key、一份计费关系,并彻底避免凭据泛滥。无论请求是谁提出的,拥有密钥的 Agent 都会为 token 买单。
这套系统从 2026 年 3 月运行至 6 月。此后,自托管的 IRC 技术栈(Ergo 加 gamja Web 客户端)以及 Nully Agent 均已下线;原先承载它们的主机如今被用作静态网站的 origin。上面的截图展示了系统在线运行时的样子。