商业产业 Hacker News AI 2026-10-12T19:28 正文 ok 1064 words

Show HN:Ramet——面向 AI Agent 的持久化、可分叉工作空间

英文标题:🔶 Show HN: Ramet – Persistent, forkable workspaces for AI agents

Ramet 为 AI Agent 提供持久化、可分叉的隔离工作空间,使开发者能够在沙箱替换或跨会话后恢复文件与本地 Git 状态。它将工作区状态划分为共享不可变、持久、可重建和临时四类,并由应用显式决定何时快照、提升或丢弃状态。该项目可与 LangChain、LangGraph、CrewAI、AutoGen、SkyRL 等框架配合,但需要开发者自行编写适配代码,并自行处理进程内存和外部服务的恢复。Ramet 目前处于开发 Alpha 阶段,支持 Linux 与 macOS,提供 Python SDK、Go 客户端,以及可选的 PostgreSQL、S3 和 Linux FUSE 存储能力。
打开原文相关 3 · 机会 1
media1.0 KB media419.4 KB media62.6 KB

中文

面向 AI Agent 的持久化、可分叉工作空间。

在不同会话和沙箱替换之间保存及恢复 Agent 工作。你可以分叉隔离的尝试、重置至干净的起点,并保留你选择的结果。

Ramet 为每个会话或尝试,从共享的不可变基础中提供一个隔离的可写工作空间。当你的编排程序显式捕获快照或提升选定状态时,变更便会持久化。

**可自带 Agent 框架和沙箱。**你的框架负责编排工作,你的沙箱负责运行,而 Ramet 管理工作空间生命周期。

你需要解决的问题 Ramet 负责处理的内容
在沙箱被替换时保留 Agent 文件 跨会话保存和恢复工作空间文件及本地 Git 状态。
为并行 Agent 提供隔离工作空间 从同一个基础派生独立尝试、本地分叉,以及为 best-of-N 编码 Agent 选择性持久化。
在每次 rollout 之间重置 RL 环境 在工作空间复用前进行干净的文件系统重置与验证,以发现污染。
运行可重复的 Agent 评估 通过共享内容、缓存和预热池,复用已准备好的代码仓库与依赖。

正在使用 LangChain / LangGraph、CrewAI、AutoGen 或 SkyRL?

LangChain/LangGraph、CrewAI 和 AutoGen 需要应用层适配代码;Ramet 不包含专用插件。你的框架负责维护对话状态、编排、奖励和训练。Ramet 持久化文件系统状态;进程内存和外部服务需要各自的恢复策略。

为什么工作空间状态需要区别对待

Agent 工作空间混合了生命周期差异很大的文件。一个本地提交可能是有用工作唯一的副本;依赖目录可以重新构建;临时文件可能只属于某一次尝试。保存全部内容,会将可复现数据和临时数据带入每一份保存结果;丢弃全部内容,则会损失无法重建的工作。

Ramet 让你在保存一次尝试时表达这些差异:

状态类型 示例 应保留的内容
共享不可变状态 固定数据集、准备好的只读输入 在多次尝试之间复用未变更的基础内容。
持久状态 源代码修改、本地 Git 提交、选定输出 保留当前状态,以便恢复工作。
可重建状态 依赖、生成的构建输出 保留重建配方,而非生成的字节数据。
临时状态 临时文件、暂存日志 不将其纳入保存结果。

你的编排程序负责分配路径策略;Ramet 不会推断哪些文件可以安全丢弃。这些策略在提升时应用,默认会保留未分类路径。初始快照和检查点会捕获所有受支持文件;你的运行时负责执行重建配方。参见持久化策略。你可以在提升前预览将被保存的内容,随后通过已批准的重建步骤进行恢复,并执行输入检查和应用就绪检查。

对于并行 Agent,这意味着共享已准备好的基础内容、隔离每次尝试的写入,并同时选择要保留的尝试和状态。一次性尝试不需要保存快照。

开发 Alpha 版 · Linux 与 macOS · Apache-2.0

框架集成可通过 Python、Go 或 HTTP 调用 Ramet。不可变基础通过复制/reflink 或可选的 Linux FUSE,为各工作节点提供本地工作空间。你的运行时挂载一个本地路径,并负责进程清理。捕获的快照可持久化至本地,或 PostgreSQL 和 S3。

你的编排程序可通过 Python SDK 或 Go 客户端调用 Ramet:

  1. **准备:**为项目创建快照,或导入一个 Git 修订版本,然后创建资源池。
  2. **获取:**取得一个隔离工作空间,并将 execution.path 作为沙箱的工作空间挂载路径传给运行时。
  3. **运行:**使用现有框架运行 Agent,并评估其工作结果。
  4. **保存或回收:**停止沙箱的写入进程并卸载工作空间;调用 promote() 保存选定结果,然后释放 execution。释放操作会重置工作空间以供复用。

该路径属于 Ramet worker 的文件系统。应将 worker 部署在能够挂载该路径的运行时旁边;远程 SDK 连接不会把文件传输至远程沙箱。你的运行时负责进程隔离、资源限制,以及租约丢失时的清理工作。

需要多少集成工作?

你的环境 接入所需工作
同主机离线 Docker 命令 使用随附适配器:获取工作空间、调用 runtime.run(),然后保存或释放。为编排程序崩溃增加监督式清理。
自有容器或 VM 运行时 修改工作空间分配方式,挂载返回的路径,并在超时、租约丢失和清理失败时实现运行时关闭。
无法挂载 worker 路径的托管沙箱 需先解决文件系统挂载问题;仅设置 SDK URL 并不足够。

你的 Agent 循环和评估器仍保留在自己的框架中。集成指南展示了需要改动的三个调用点、一个适配器示例,以及用于验证环境的检查项。Python SDK 指南介绍了设置、Docker 执行、持久化和并行尝试。

执行写入保留在本地。需要显式捕获或提升才能持久化状态。可先使用本地存储;当快照需要在 worker 磁盘丢失后仍可保留时,再添加 PostgreSQL 和 S3 兼容存储。支持时,写时复制会共享文件数据;不支持时则回退为彼此独立的完整副本。参见架构和运行时安全边界。对于大型快照——其中尝试只会访问部分文件——可选的 Linux FUSE 后端会在首次访问时加载工作树文件。随附的 Docker 适配器使用复制/reflink;FUSE 需要自定义挂载连接。

这一同主机设置为本地开发和集成测试引入 API。已部署的 worker 会在沙箱运行时旁使用相同的生命周期。本地存储会将快照保存于该 worker 的磁盘;可选的 PostgreSQL 和 S3 兼容存储则允许替换后的 worker 恢复已发布快照。参见部署和存储模式。

需要 Go 1.26.9+、Python 3.10+、Git,以及 Linux 或 macOS。安装 CLI 和 Python SDK,然后启动本地 worker:

git clone https://github.com/shenli/ramet.git
cd ramet
go build -o bin/ramet ./cmd/ramet
python3 -m venv .venv
.venv/bin/python -m pip install ./sdk/python
./bin/ramet serve --data /tmp/ramet-data --listen 127.0.0.1:7433

保持 worker 运行。在另一个终端中,从同一个检出目录执行以下命令,并将项目的绝对路径填入其中。请将该项目放在 /tmp/ramet-data 之外,并在捕获快照时停止其写入进程。

.venv/bin/python - <<'PY'
from ramet import Ramet

ramet = Ramet()  # Connects to http://127.0.0.1:7433.
base = ramet.snapshot(source="/absolute/path/to/your/project")

with ramet.execution_pool(base, concurrency=4) as pool:
    with pool.acquire() as execution:
        # Run your tools against this directory; the source project is untouched.
        print("Workspace:", execution.path)
        (execution.path / "result.txt").write_text("selected result\n")
        saved = execution.promote()  # Persist this attempt as a new snapshot.
    # Leaving the execution context resets and releases its workspace.

with ramet.execution_pool(saved, concurrency=1) as pool:
    with pool.acquire() as restored:
        print((restored.path / "result.txt").read_text())
PY

恢复后的工作空间会输出 selected result。快照会持久化在 worker 的数据目录中(若要长期保留,请使用持久目录而不是 /tmp);资源池和 execution 目录均为临时目录。此示例中的 SDK 和 worker 运行在同一主机上。连接你的运行时时,只挂载 execution.path,并在提升或释放前停止其写入进程;Docker 适配器会为容器命令处理这一生命周期。

如需可直接运行的演示,请执行 ./bin/ramet demo,或参阅 best-of-N 编码演示。

采用 Apache-2.0 许可。参见第三方声明。

English / 原文

Persistent, forkable workspaces for AI agents.

Save and restore agent work across sessions and sandbox replacements. Fork isolated attempts, reset to a clean starting point, and preserve the results you choose.

Ramet gives each session or attempt an isolated writable workspace from a shared immutable base. Changes become persistent when your harness explicitly captures a snapshot or promotes selected state.

Bring your own agent framework and sandbox. Your framework coordinates the work, your sandbox runs it, and Ramet manages the workspace lifecycle.

Problem you need to solve What Ramet handles
Keep agent files when a sandbox is replaced Save and restore workspace files and local Git state across sessions.
Give parallel agents isolated workspaces Independent attempts from one base, local forks, and selective persistence for best-of-N coding agents.
Reset RL environments between rollouts Clean filesystem resets and verification before workspace reuse to catch contamination.
Run repeatable agent evaluations Reuse prepared repositories and dependencies through shared content, caches, and warm pools.

Using LangChain / LangGraph, CrewAI, AutoGen, or SkyRL?

LangChain/LangGraph, CrewAI, and AutoGen require application adapter code; dedicated Ramet plugins are not included. Your framework keeps conversation state, orchestration, rewards, and training. Ramet persists filesystem state; process memory and external services need their own recovery strategy.

Why workspace state needs different treatment

An agent workspace mixes files with very different lifetimes. A local commit may be the only copy of useful work; a dependency directory can be rebuilt; a scratch file may belong to just one attempt. Saving everything carries reproducible and temporary data into every saved result. Throwing everything away loses work you cannot reconstruct.

Ramet lets you express those differences when saving an attempt:

Kind of state Examples What should survive
Shared immutable Fixed datasets, prepared read-only inputs Reuse the unchanged base content across attempts.
Durable Source edits, local Git commits, selected outputs Preserve the current state so the work can be restored.
Rebuildable Dependencies, generated build outputs Keep a reconstruction recipe instead of the generated bytes.
Ephemeral Scratch files, temporary logs Leave them out of the saved result.

Your harness assigns path policies; Ramet does not infer which files are safe to drop. These policies apply on promotion, with unclassified paths preserved by default. Initial snapshots and checkpoints capture all supported files; your runtime executes reconstruction recipes. See persistence policies. Preview what will be saved before promotion, then restore through approved reconstruction steps with input checks and an application readiness check.

For parallel agents, this means sharing a prepared base, isolating each attempt's writes, and choosing both which attempt and which state to keep. Disposable attempts need no saved snapshot.

Development alpha · Linux & macOS · Apache-2.0

Framework integration calls Ramet through Python, Go, or HTTP. An immutable base supplies worker-local workspaces through copy/reflink or optional Linux FUSE. Your runtime attaches a local path and owns process cleanup. Captured snapshots persist locally or in PostgreSQL and S3.

Your harness calls Ramet through the Python SDK or Go client:

  1. Prepare: snapshot a project or import a Git revision, then create a pool.
  2. Acquire: get an isolated workspace and pass execution.path to your runtime as the sandbox's workspace mount.
  3. Run: use your existing framework to run the agent and evaluate its work.
  4. Save or recycle: stop the sandbox's writers and unmount the workspace; call promote() to save a selected result, then release the execution. Release resets the workspace for reuse.

The path belongs to the Ramet worker's filesystem. Deploy the worker beside the runtime that can mount it; a remote SDK connection does not transfer files to a remote sandbox. Your runtime owns process isolation, resource limits, and cleanup on lease loss.

How much integration work is involved?

Your environment Work to connect it
Same-host offline Docker commands Use the included adapter: acquire a workspace, call runtime.run(), then save or release. Add supervisor cleanup for harness crashes.
Your own container or VM runtime Change workspace allocation, mount the returned path, and implement runtime shutdown on timeout, lease loss, and cleanup failure.
Hosted sandbox without worker-path attachment Resolve filesystem attachment first; setting the SDK URL is not enough.

Your agent loop and evaluator stay in your framework. The integration guide shows the three call sites to change, an adapter sketch, and checks to validate your environment. The Python SDK guide walks through setup, Docker execution, persistence, and parallel attempts.

Execution writes stay local. Capture or promote explicitly to persist state. Start with local storage; add PostgreSQL and S3-compatible storage when snapshots need to survive worker-disk loss. Copy-on-write shares file data where supported, with independent full copies as the fallback. See the architecture and runtime security boundary. For large snapshots where attempts touch only a subset of files, the optional Linux FUSE backend hydrates working-tree files on first access. The included Docker adapter uses copy/reflink; FUSE needs custom mount attachment.

This same-host setup introduces the API for local development and integration testing. A deployed worker uses the same lifecycle beside your sandbox runtime. Local storage keeps snapshots on that worker's disk; optional PostgreSQL and S3-compatible storage let a replacement worker restore published snapshots. See deployment and storage modes.

Requires Go 1.26.9+ , Python 3.10+ , Git, and Linux or macOS. Install the CLI and Python SDK, then start a local worker:

git clone https://github.com/shenli/ramet.git
cd ramet
go build -o bin/ramet ./cmd/ramet
python3 -m venv .venv
.venv/bin/python -m pip install ./sdk/python
./bin/ramet serve --data /tmp/ramet-data --listen 127.0.0.1:7433

Leave the worker running. In another terminal, from the same checkout, run the following with the absolute path to your project. Keep that project outside /tmp/ramet-data and stop its writers while capturing the snapshot.

.venv/bin/python - <<'PY'
from ramet import Ramet

ramet = Ramet()  # Connects to http://127.0.0.1:7433.
base = ramet.snapshot(source="/absolute/path/to/your/project")

with ramet.execution_pool(base, concurrency=4) as pool:
    with pool.acquire() as execution:
        # Run your tools against this directory; the source project is untouched.
        print("Workspace:", execution.path)
        (execution.path / "result.txt").write_text("selected result\n")
        saved = execution.promote()  # Persist this attempt as a new snapshot.
    # Leaving the execution context resets and releases its workspace.

with ramet.execution_pool(saved, concurrency=1) as pool:
    with pool.acquire() as restored:
        print((restored.path / "result.txt").read_text())
PY

The restored workspace prints selected result. Snapshots persist in the worker's data directory (use a persistent directory instead of /tmp to retain them long term); pools and execution directories are temporary. The SDK and worker run on the same host for this example. When connecting your runtime, mount only execution.path and stop its writers before promotion or release; the Docker adapter handles this lifecycle for container commands.

For ready-made demonstrations, run ./bin/ramet demo or follow the best-of-N coding demo.

Licensed under Apache-2.0. See third-party notices.