Go 编写的极简编程 Agent,约 12MB 二进制文件、无模型锁定、默认只读权限隔离、子 Agent 机制避免上下文污染。
过去几个月,我一直在开发一个名为 phi 的终端编码 Agent(https://github.com/pulseaiclub/phi),很想和大家分享它是什么以及为何存在。
如果你用过 Pi、Claude Code、Aider 或 Goose,phi 追求的是同样的工作流——但有意剥离了运行时的包袱。它是一个独立的 Go 二进制文件,约 12 MB,不见 Node、Electron 或 Python 的踪影。
以下是其核心设计:
这是 phi 存在的根本原因。模型发布的步伐比任何 Agent 维护者跟进的都快。phi 不追逐每一个新的模型提供商,而是将模型视为可插拔的配置:任何 OpenAI 兼容或 Anthropic 原生的模型都可以开箱即用。无需等待作者添加支持,几秒内即可切换模型。配置只是一个 YAML 文件,还有一个迷你的 HTML 编辑器,让你可以用点选操作浏览和编辑 ~/.phi/config.yaml。
剥离后的发布版本(CGO_ENABLED=0)只有约 12 MB,冷启动空闲时 RSS 约 21 MB,首帧渲染约 40 ms。只有 6 个直接模块依赖。如果你想拥有一个可以在半秒内构建完成、一个下午就能读完源码的编码 Agent,就是它了。
使用编码 Agent 时,总有一个让我神经紧张的时刻——"模型删除了你的文件"。phi 默认只读:Agent 可以扫描代码库,但写入和 shell 命令需要显式批准。批准弹窗提供三个选项——允许、带反馈拒绝、或允许本次会话剩余所有操作。
bash.allow → go test ./... 没问题bash.deny → rm -rf * 会被阻止fetch.allowed_hosts → 限制 Agent 可以访问的域名一个大型 Agent 处理小任务没问题,但长期运行的工作会让上下文窗口充满噪音。phi 提供了一套完整的子 Agent 工具(agent_spawn、agent_task、agent_wait、agent_list、agent_log、agent_cancel),关键设计决策是子 Agent 的转录内容保存在 ~/.phi/jobs/<id>/ 下,永远不会被注入主 Agent 的上下文。只有任务摘要会返回来。你的主会话保持干净。
子 Agent 也有角色划分:
explore — 只读搜索review — 只读检查worker — 独立编辑如果不需要,可以在内置配置中禁用整个子系统。
大多数编码 Agent 通过文本搜索替换来进行编辑。问题是,如果模型的文件视图稍有过期,替换就可能落在错误的位置。
phi 使用基于哈希定位的行编辑。读取文件时,每一行都会生成一个短哈希(FNV-64a,映射为紧凑的 2 字符编码)。编辑针对的是特定哈希,而不是模糊字符串。如果文件自上次读取后发生了变化,哈希就不会匹配——phi 不会静默失败,而是返回新的行引用(>>>),让模型可以用新的锚点重试。不需要重新读取整个文件,不会意外损坏。
TUI 完全用 Go 手撸,没有历史包袱。首帧渲染约 40 ms。功能实用,不做表面文章:
Ctrl+K 命令面板@ 模糊文件提及/sessions 和 /resume 会话管理!command 将本地 shell 输出直接流式传入转录~/.phi/skills/ 下放一个 SKILL.md,Agent 就会自动加载~/.phi/sessions/<工作目录>/ 下,随时可恢复phi 有一套 hook 系统,让你在每个工具调用的前后——在权限门之前和执行之后——运行任意代码,无需重新编译,也无需把逻辑塞进 config.yaml。
每个 hook 只是一个包含 hook.json 清单和一个可执行文件的目录:
有两个 hook 事件:
Hook 按项目作用域生效:放在 /.phi/hooks/ 下的 hook 会覆盖同名用户级 hook。设置 PHI_HOOKS=off 可完全禁用发现机制。这是落实组织策略、审计追踪或内置权限门覆盖不了的输入重写的正确位置。
phi 采用 MIT 许可证,我真心欢迎贡献者。如果你曾想要一个装进单个二进制文件、不带运行时的编码 Agent,给它个机会,告诉我你的想法。