Reason Machines 发布了 Aspen,一个面向 AI 智能体流量的开源 Git 服务器。它面向的是数百个智能体同时推送的仓库。Aspen 独立实现了 OpenAI 为 Pando 描述的设计。
TL;DR
体积:单个 Rust 二进制文件。分支存放在 PostgreSQL(或 SQLite)中,pack 存放在对象存储中。
运行方式:一台笔记本电脑(aspen dev)、通过 Docker Compose 的单台主机,或 Helm 和 Terraform 云。
性能:推送到不同分支的推送从不互相等待,拉取会复用已存储的 pack。
最好情况:在一台 8 核服务器上持续 5 分钟达到每秒 567 次推送(自报数据)。
最差情况:落到 main 上大约每秒 4 次,且该项目处于 pre-alpha 阶段。
结论:最好情况:按分支写入消除了按仓库的推送上限。最差情况:没有发布版本、没有 Web UI,且未达到 24 小时持久性门槛。
什么是 Aspen?
Aspen 是 Reason Machines 推出的可自托管 Git 服务器,按分支而非按仓库扩展写入。它通过 HTTPS 和 SSH 使用纯 Git 协议。pack 存入兼容 S3 的对象存储;分支历史存入 PostgreSQL。
为什么传统 Git 服务器难以应对 AI 智能体?
智能体每隔几分钟就推送一次,CI 在每次推送后拉取。据 Reason Machines 称,传统服务器会把对同一仓库的每次推送排入队列。它还会为每次拉取构建全新的 pack。服务器的工作量随客户端数量增长,而非随新代码增长。
OpenAI 撞上了这堵墙,于是构建了 Pando。它将推送延迟中位数降低了近 5 倍,每天服务近 1 PB 数据。
Aspen 如何工作?
Aspen 遵循 Pando,并放弃了大多数 Git 服务器保留的 2 项主张。
按分支的比较并交换
每个分支是单个 Postgres 行中的一条版本链。一次推送先上传其 pack,然后通过一次比较并交换移动分支。推送到不同分支的推送从不互相等待。如果 2 次推送在同一分支上竞争,一个胜出。另一个会收到 Git 常见的“fetch first”错误。代价是:分支可能在不同时刻被读取。
存储 pack 而非按客户端生成 pack
Aspen 从不解包推送。它检查 pack、补全缺失的基对象,并按原样存储。拉取时流式传输客户端缺少的已存储 pack,不重新压缩任何内容。签名 URL 可以从 CDN 提供 pack。
仅主干广告与压缩
一次 push 只能看到主干分支,因此即使有上百万个分支,协商的数据量依然很小。后台压缩每次合并 16 个 pack,按类似日志结构合并树的层级进行。落后很多的客户端只会拿到几十个 pack,而不是数千个。Depth-1 CI 拉取会得到一个只构建一次、供所有任务使用的 tip pack。
自动播放</button></div>
<svg viewBox="0 0 820 250" aria-label="Aspen push 与 fetch 流程">
<defs><marker id="aspA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0 0L10 5L0 10z" fill="#7cb8f5"/></marker></defs>
<g font-family="Inter,sans-serif" font-size="13">
<rect x="20" y="40" width="150" height="60" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="95" y="66" fill="#f3efe3" text-anchor="middle" font-weight="700">Agent</text><text x="95" y="85" fill="#a59f8c" text-anchor="middle" font-size="11">git push</text>
<rect x="20" y="150" width="150" height="60" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="95" y="176" fill="#f3efe3" text-anchor="middle" font-weight="700">CI job</text><text x="95" y="195" fill="#a59f8c" text-anchor="middle" font-size="11">git fetch</text>
<rect x="320" y="85" width="170" height="80" rx="12" fill="#1d1a12" stroke="#3690ea" stroke-width="1.5"/><text x="405" y="120" fill="#f3efe3" text-anchor="middle" font-weight="800">Aspen</text><text x="405" y="140" fill="#a59f8c" text-anchor="middle" font-size="11">无状态服务器</text>
<rect x="640" y="30" width="160" height="70" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="720" y="60" fill="#f3efe3" text-anchor="middle" font-weight="700">Postgres</text><text x="720" y="80" fill="#a59f8c" text-anchor="middle" font-size="11">每个分支 1 行</text>
<rect x="640" y="150" width="160" height="70" rx="10" fill="#1d1a12" stroke="#3a3526"/><text x="720" y="180" fill="#f3efe3" text-anchor="middle" font-weight="700">对象存储</text><text x="720" y="200" fill="#a59f8c" text-anchor="middle" font-size="11">按推送原样存放 pack</text>
<path id="aspE0" d="M170 70 L320 110" stroke="#3a3526" stroke-width="2" fill="none"/>
<path id="aspE1" d="M490 140 L640 180" stroke="#3a3526" stroke-width="2" fill="none"/>
<path id="aspE2" d="M490 110 L640 70" stroke="#3a3526" stroke-width="2" fill="none"/>
<path id="aspE3" d="M320 140 L170 180" stroke="#3a3526" stroke-width="2" fill="none"/>
<circle id="aspPk" r="7" fill="#3690ea"><animateMotion id="aspMo" dur="1.2s" repeatCount="indefinite"><mpath href="#aspE0"/></animateMotion></circle>
</g></svg>
<p class="cap" id="aspCap"></p>
</div>
<div class="pane" id="p-race">
<div class="btns"><button class="b" id="aspRun"> 运行 128 个写入者</button><button class="b" id="aspReset">重置</button></div>
<div class="race">
<div class="rc"><h4>经典服务器:每个仓库一个队列</h4><div class="dots" id="aspD1"></div><div class="ct" id="aspC1">0 / 128 已完成</div></div>
<div class="rc"><h4>Aspen:每个分支一个 CAS</h4><div class="dots" id="aspD2"></div><div class="ct" id="aspC2">0 / 128 已完成</div></div>
</div>
<p class="note">玩具模型,并非基准测试。琥珀色 = 等待中,蓝色 = 已完成。复现了 Reason Machines 发布文章中的示意性模拟。</p>
</div>
<div class="pane" id="p-bench">
<div class="btns"><span class="sub">每秒向单个仓库推送的次数,每次推送在确认前均已持久化</span></div>
<div class="bar"><div class="bl"><span>Cursor Origin,S3 Standard(已发布)</span><span>120</span></div><div class="tr"><div class="fl" data-w="21.2" style="background:#6d6650"></div></div></div>
<div class="bar"><div class="bl"><span>Cursor Origin,S3 Express One Zone(已发布)</span><span>300+</span></div><div class="tr"><div class="fl" data-w="52.9" style="background:#8a8064"></div></div></div>
<div class="bar"><div class="bl"><span>Aspen,S3 Standard(自报)</span><span>567</span></div><div class="tr"><div class="fl" data-w="100" style="background:linear-gradient(90deg,#3690ea,#7cb8f5)"></div></div></div>
<div class="grid"><div class="st"><div class="n">208 ms</div><div class="l">推送中位数</div></div><div class="st"><div class="n">314 ms</div><div class="l">推送 p99</div></div><div class="st"><div class="n">30 ms</div><div class="l">可见时间中位数</div></div><div class="st"><div class="n">~4/s</div><div class="l">main 分支落地速率</div></div><div class="st"><div class="n">1.029x</div><div class="l">depth-1 字节数对比原生 git</div></div><div class="st"><div class="n">5 min</div><div class="l">持续运行,0 错误</div></div></div>
<p class="note">Aspen:1 台 8 核服务器,S3 Standard,4 核 Postgres,git.git(约 4,500 个文件,75k 次提交),分支作用域 URL。Cursor:在其自有 monorepo 上的复制集群。配置不同;OpenAI 未公布 Pando 推送速率。</p>
</div>
<div class="pane" id="p-comp">
<div class="bl"><span>客户端落后主干</span><span id="aspBeh">2,000 个版本</span></div>
<input type="range" id="aspSl" min="1" max="4" step="1" value="3">
<svg viewBox="0 0 820 120" id="aspCS"></svg>
<p class="cap" id="aspCC"></p>
</div>
<div class="pane" id="p-lim">
<div class="lim">
<div class="li"><div class="t"> Pre-alpha,无发布版本</div><div class="d">从源码构建。存储格式可能变化。24 小时混沌测试在 11.59 个无故障小时后停止。</div></div>
<div class="li"><div class="t"> main 分支落地约 4/s</div><div class="d">独立的落地操作逐个运行,受往返次数限制。合并队列速率尚未测量。</div></div>
<div class="li"><div class="t"> 缺少 Git 功能</div><div class="d">不支持部分克隆、SHA-256 仓库、Web UI 或 RSA SSH 密钥(仅支持 Ed25519 和 ECDSA)。</div></div>
<div class="li"><div class="t"> 强制推送需要 -o force</div><div class="d">由于推送只能看到主干,--force 和 --delete 需要额外的 -o force、租约或分支 URL。</div></div>
<div class="li"><div class="t"> 大型 monorepo</div><div class="d">随着仓库增长,每次推送的成本仍会上升,因此目前超大型 monorepo 的速率较低。</div></div>
<div class="li"><div class="t"> 存储支持冲突</div><div class="d">发布文章列出了 Azure Blob;README 说 Azure Blob 尚不支持,GCS 通过其 S3 API 可用。</div></div>
</div>
<p class="note">点击卡片展开。</p>
</div>
<div class="ft"><span>来源:<a href="https://reasonmachines.com/blog/aspen" target="_blank" rel="noopener">发布文章</a> · <a href="https://github.com/reason-machines/aspen" target="_blank" rel="noopener">GitHub</a> · <a href="https://cursor.com/blog/git-at-any-scale" target="_blank" rel="noopener">Cursor</a></span><span class="brand">© Marktechpost</span></div>
</div>
<script>
(function(){
var R=document.getElementById('mtp-aspen');
function post(){try{parent.postMessage({mtpH:R.offsetHeight+40,id:'mtp-aspen'},'*')}catch(e){}}
var tabs=R.querySelectorAll('.tab');
tabs.forEach(function(t){t.onclick=function(){tabs.forEach(function(x){x.classList.remove('on')});t.classList.add('on');
R.querySelectorAll('.pane').forEach(function(p){p.classList.remove('on')});R.querySelector('#p-'+t.dataset.t).classList.add('on');
if(t.dataset.t==='bench'){R.querySelectorAll('.fl').forEach(function(f){f.style.width='0';setTimeout(function(){f.style.width=f.dataset.w+'%'},60)})}
setTimeout(post,80);setTimeout(post,500)}});
/* flow */
var caps=['<b>推送。</b>智能体发送一个包。Aspen 检查它,补全缺失的基础包,并原样存入对象存储。',
'<b>比较并交换。</b>一次行更新:仅当该分支仍指向 A 时,才将其从 A 移到 B。其他分支从不等待。',
'<b>规划。</b>在获取时,Aspen 读取分支行,并计算出哪些已存储的包覆盖了客户端缺失的内容。',
'<b>流式传输。</b>已存储的包被拼接成一个包返回,或作为 CDN 的 URL 返回。不会重新压缩任何内容。'];
var edges=['aspE0','aspE2','aspE2','aspE3'],live=['aspE0','aspE1'];
var sb=R.querySelectorAll('#p-flow .b[data-s]'),cur=0,timer=null;
function step(i){cur=i;sb.forEach(function(b){b.classList.toggle('on',+b.dataset.s===i)});
['aspE0','aspE1','aspE2','aspE3'].forEach(function(e){R.querySelector('#'+e).setAttribute('stroke','#3a3526')});
var on=i===0?['aspE0','aspE1']:i===1?['aspE2']:i===2?['aspE2']:['aspE1','aspE3'];
on.forEach(function(e){R.querySelector('#'+e).setAttribute('stroke','#3690ea')});
var mp=R.querySelector('#aspMo mpath');mp.setAttribute('href','#'+(i===0?'aspE0':i===3?'aspE3':'aspE2'));
R.querySelector('#aspCap').innerHTML=caps[i]}
sb.forEach(function(b){b.onclick=function(){clearInterval(timer);timer=null;R.querySelector('#aspAuto').textContent=' Auto play';step(+b.dataset.s)}});
R.querySelector('#aspAuto').onclick=function(){if(timer){clearInterval(timer);timer=null;this.textContent=' Auto play';return}
this.textContent='❚❚ Pause';timer=setInterval(function(){step((cur+1)%4)},2200)};
step(0);
/* race */
var N=128,d1=R.querySelector('#aspD1'),d2=R.querySelector('#aspD2'),rt=null;
function build(){d1.innerHTML='';d2.innerHTML='';for(var i=0;i<N;i++){d1.insertAdjacentHTML('beforeend','<div class="dot"></div>');d2.insertAdjacentHTML('beforeend','<div class="dot"></div>')}
R.querySelector('#aspC1').textContent='0 / '+N+' done';R.querySelector('#aspC2').textContent='0 / '+N+' done'}
build();
R.querySelector('#aspReset').onclick=function(){clearInterval(rt);build()};
R.querySelector('#aspRun').onclick=function(){clearInterval(rt);build();var a=d1.children,b=d2.children,k=0,q=0,t=0;
for(var i=0;i<N;i++){a[i].className='dot w';b[i].className='dot w'}
rt=setInterval(function(){t++;
if(q<N){a[q].className='dot d';q++;R.querySelector('#aspC1').textContent=q+' / '+N+' done'}
for(var j=0;j<10&&k<N;j++){b[k].className='dot d';k++}R.querySelector('#aspC2').textContent=k+' / '+N+' done';
if(q>=N)clearInterval(rt)},45)};
/* compaction */
var lv={1:20,2:200,3:2000,4:20000};
function plan(n){var out=[];[4096,256,16,1].forEach(function(s){while(n>=s){out.push(s);n-=s}});return out}
function comp(){var n=lv[R.querySelector('#aspSl').value],ps=plan(n),s=R.querySelector('#aspCS'),x=10,h='';
var tot=ps.reduce(function(a,b){return a+Math.log2(b)+1.5},0);
ps.forEach(function(p,i){var w=(Math.log2(p)+1.5)/tot*800;h+='<rect x="'+x+'" y="30" width="'+Math.max(w-3,2)+'" height="50" rx="4" fill="'+(p>=4096?'#7cb8f5':p>=256?'#3690ea':p>=16?'#2f6fb3':'#5a5440')+'"><animate attributeName="opacity" from="0" to="1" dur=".35s" begin="'+(i*0.04)+'s" fill="freeze"/></rect>';x+=w});
h+='<text x="10" y="105" fill="#a59f8c" font-family="Inter" font-size="11">client</text><text x="810" y="105" fill="#a59f8c" font-family="Inter" font-size="11" text-anchor="end">trunk</text>';
s.innerHTML=h;R.querySelector('#aspBeh').textContent=n.toLocaleString()+' versions';
R.querySelector('#aspCC').innerHTML='示意方案:<b>'+ps.length+' 个 pack</b>,而不是 '+n.toLocaleString()+'。pack 每次合并 16 个,形成 4 种大小(此处为 1、16、256、4,096),每次拉取时取当前步骤能容纳的最大 pack。'
R.querySelector('#aspSl').oninput=comp;comp();
/* 限制 */
R.querySelectorAll('.li').forEach(function(l){l.onclick=function(){l.classList.toggle('open');setTimeout(post,400)}});
window.addEventListener('load',post);window.addEventListener('resize',post);setTimeout(post,300);
})();
</script>
</body></html>
">
Aspen 有多快?
测试使用了一台 8 核服务器、S3 Standard 和一个 4 核 Postgres。目标是 git.git 的一个副本,约 4,500 个文件和 75,000 个提交。Aspen 持续 5 分钟保持每秒 567 次推送,没有出现错误。每次推送都在 Git 打印“ok.”之前到达 S3 和 Postgres。
推送延迟中位数:208 ms;99 分位:314 ms。
另一个客户端在确认后 30 ms(中位数)看到每次推送。
深度为 1 的拉取风暴:字节数为 git.git 上原生 git upload-pack 的 1.029 倍。
Cursor 报告,在 S3 Standard 上 Origin 的推送速度最高可达每秒 120 次。在 S3 Express One Zone 上则超过 300 次。那是 Cursor 单体仓库上的复制集群,因此配置有所不同。README 称 Aspen 的数据仅供参考,并非性能承诺。
Aspen 与其他智能体规模的 Git 后端相比如何?
特性 | Aspen | Cursor Origin | OpenAI Pando | GitHub Spokes 可用性 | 开源、自托管 | 商业 Cursor 平台 | OpenAI 内部使用 | GitHub 内部使用 许可证 | Apache 2.0 | 未披露 | 未披露 | 未披露 推送排序 | 按分支(比较并交换) | 按仓库(S3 中的预写日志) | 按分支 | 跨副本三阶段提交 引用存储 | PostgreSQL 或 SQLite | S3 中的 WAL 索引,无关系型数据库 | 元数据存储 | NVMe 上的 Git 仓库 拉取服务 | 存储的 pack,拼接或通过 URI | NVMe 上的本地 Git 仓库缓存 | 复用存储的 pack | Git 仓库副本 公布的推送速率 | 567 次/秒,S3 Standard,1 台服务器(自报) | 120 次/秒 S3 Standard;300+ 次/秒 S3 Express | 未披露 | 未披露 其他公布结果 | p99 推送 314 ms | 线性读扩展至 100 个副本 | 推送延迟中位数降低近 5 倍 | 未披露
如何部署 Aspen?
你使用 Rust 从源码构建。aspen dev 使用 SQLite 和本地目录运行所有内容。团队可获得带 PostgreSQL 和 MinIO 的 Docker Compose,以及一个 Helm chart。Terraform 覆盖 AWS、Google Cloud、Azure 和 Hetzner。镜像模式前置 GitHub 或 GitLab,因此你可以在不迁移的情况下试用它。
实时演示是一个 64 x 64 像素的画布,每次点击就是一次推送的提交。据该公司称,一个 Reason 智能体从首次提交到公开发布仅用 4 天就构建了 Aspen。这期间合并了 151 个 PR。
要点
Aspen 是一个 Apache 2.0 的 Git 服务器,专为智能体规模的推送流量而构建。
它是 OpenAI 的 Pando 设计的独立、开源实现。
自报数据:单台 8 核服务器上每秒 567 次推送,p99 为 314 ms。
客户端无需改动;原生 git 可通过 HTTPS 和 SSH 工作。
查看 GitHub 仓库和发布文章。另外,欢迎在 Twitter 上关注我们,别忘了加入我们 15 万+ 的 ML SubReddit 并订阅我们的新闻通讯。等等!你在 Telegram 上吗?现在你也可以在 Telegram 上加入我们。
需要与我们合作推广你的 GitHub 仓库或 Hugging Face 页面或产品发布或网络研讨会等?联系我们
文章 Reason Machines Open-Sources Aspen, a Git Server Built for AI Agents 首次出现在 MarkTechPost 上。