今天想跟你聊一个挺有意思的开源项目,叫UpCtl。它是一套AI Agent驱动的全自动开发测试部署平台,但有意思的地方在于,它的底层技术选型非常……怎么说呢,非常Unix味儿。它没有用什么花哨的Agent编排框架,而是用Tmux加SSH把整个系统串起来的。
对,我第一次看到这个项目的时候也觉得挺意外的。你想现在主流的AI Agent方案,不管是LangChain还是AutoGPT、CrewAI,基本都是通过API调用来做Agent编排的。但UpCtl完全不走这条路,它把Agent跑在Tmux的Session里面,然后通过Tmux的文本发送机制来跟Agent通信。说白了就是——我往你的终端窗口里打字,你就开始干活。
这听起来有点……原始?但我猜它这么做肯定有道理。你能先给大家解释一下,为什么Tmux在这里这么关键吗?
好,Tmux大家可能多少都用过,它是一个终端复用器。它的核心架构是C/S模型,后台跑一个Server进程管理所有Session,你通过Client去连。关键点在哪呢?即使你所有的终端窗口都关了,SSH断了,Server进程和里面跑的程序还活着。这对AI Agent来说太重要了——你不希望你的Agent干活干到一半,因为网络抖动一下就挂了吧?
嗯,这就像是给Agent提供了一个「不死」的运行环境。
没错。而且Tmux还有一个特别好用的能力,就是send-keys命令。你可以从外部向一个Tmux Session发送任意文本。UpCtl的Service层就是利用这个机制,把组装好的Prompt直接「打」进Agent所在的Session窗口里,然后按个回车触发执行。不需要跟Agent做什么复杂的API对接,进程间通信的复杂度一下子就降到最低了。而且还有一个附带好处——任何人都可以tmux attach到那个Session,实时看Agent在干什么,可观测性拉满。
这确实很巧妙。你看它本质上是把一个传统的运维工具变成了AI Agent的运行时基础设施。那Service层具体是怎么设计的?
Service层的设计理念是把Agent完全抽象化。它不关心你用的是什么Agent,也不假设Agent跟Service在同一台机器上。它就干三件事:从Web端读Ticket内容、连接到Agent、用Tmux命令发Prompt。至于Agent拿到Prompt之后怎么干活,调了什么模型,Service完全不管。
那如果Agent不在同一台机器上呢?这就涉及到连接方式的问题了吧。
对,项目里抽象了三种连接方式。第一种最简单,Agent和Service在同一台主机上,直接Tmux attach就行。第二种是SSH远程登录,Agent在另一台机器上,SSH过去再调Tmux。第三种最复杂,SSH多跳隧道穿透,支持跳板机场景,就是通过ProxyJump一级一级跳到目标主机。整个Service层是用Rust写的,长期运行稳定性很好,而且这个Service层据说还是开发者提需求之后,AI自己迭代开发出来的。
哈,用AI开发的工具来驱动AI,有点套娃的意思了。那实际部署的时候,他们是怎么把这些东西串起来的?
这就要说到混合云架构了。他们的Demo环境是这样的:Agent跑在本地的Mac Studio上,因为性能强,适合做推理和编译这种计算密集型任务。Service层和Web端部署在公网云主机上。但问题来了,Mac Studio在局域网里,没有公网IP,云主机怎么SSH过去呢?
内网穿透?
对,他们用了NPS,一个Go写的开源内网穿透工具。原理很简单,本地机器主动跟公网服务器建立长连接,外部请求到了公网服务器之后,通过这个已有的连接转发到内网。这样云主机上的Service就能反向SSH到局域网里的Mac了。整个架构就是「计算在边缘、调度在云端」,Agent既能用本地的算力,又能访问云上的代码仓库和部署服务。
明白了,这个架构确实很实用。那我们来聊聊工作流吧,Ticket系统具体是怎么运转的?
流程是这样的:你创建一个Ticket,填标题、工作内容,可以上传PDF、截图之类的附件,然后关联到一个项目。Ticket创建后需要审批,审批通过之后,系统有个定时脚本会定期扫描已批准但没处理的Ticket,然后通过Tmux机制把Prompt发给Agent。当然你也可以点「直接开始处理」按钮,立刻让Agent干活。
那Agent干完活之后呢?
全自动闭环。Agent干活的过程中会通过Service层在Ticket下面加评论,干完之后自动关闭Ticket,还会写一份完整的工作报告。这跟传统CI/CD流水线有本质区别——Jenkins那些执行的是预定义的确定性步骤,而这里驱动的是一个有推理能力的AI Agent,它能根据Ticket描述自己决定该怎么开发、怎么测试、怎么部署,甚至能处理一些模糊需求。
这就引出另一个关键问题了——Agent怎么知道该怎么干活?它的知识从哪来?我注意到他们没有用向量数据库做RAG。
这是我觉得这个项目特别有意思的一个设计。他们搞了一套四级知识体系来替代传统RAG。第一级是提示词模板,固定插在每轮对话开头,保证Agent行为稳定。因为这部分不怎么变,配合DeepSeek用的时候KV Cache命中率很高,能省80%到90%的前缀处理成本。第二级是Memory知识库,就是一堆Markdown文档,放在指定目录下,记录详细的技术文档和经验。第三级是项目描述信息,仓库地址、部署环境这些结构化数据。第四级是历史Ticket的工作报告。
所以本质上是用结构化的上下文工程来替代向量检索的不确定性?
完全正确。传统RAG你要处理向量化的信息损失、检索精度和召回率的平衡、chunk怎么切、向量数据库怎么运维……一堆工程痛点。他们的方案是人工组织和分层管理知识,以确定性的方式注入Prompt。而且更妙的是,那个Memory知识库本身也是一个项目,它的更新也是通过Ticket驱动Agent来完成的。Agent干活积累经验,经验反哺知识库,知识库又让后续任务质量更高,形成一个正向飞轮。
这个飞轮效应确实很漂亮。不过我想最后聊一下这个项目的定位,开发者自己怎么看?
开发者很坦诚,他说这本质上就是一套流水线工具,不能替代人的思考和工程判断。人的角色是创建Ticket、指导Agent、做决策和审核。Agent负责执行那些重复性的、规范化的工作。这其实就是Human-in-the-Loop的协作模式,人和AI互补而不是替代。
嗯,我觉得这个项目最让我欣赏的一点,就是它用最朴素的Unix哲学——文本流通信、管道串联——解决了一个看起来很前沿的AI Agent编排问题。不追求技术上的花哨,但每个选型都有清晰的工程考量。项目已经开源了,感兴趣的朋友可以去试试,据说有一键部署方案,上手门槛不高。
对,而且它的架构扩展性很好,你换别的TUI形态的Agent也能跑。我觉得这种思路值得很多做内部工具的团队参考——有时候最简单的方案反而是最稳的方案。