![]()
新智元報道
![]()
【新智元導讀】當Agent數量增多時,管理混亂成為難題。OpenRath提出用Session作為核心,代替Agent為中心的設計,使多個Agent能共享狀態,實現更清晰的協作與控制。
Agent越來越多,Session卻越來越亂。
這是幾乎所有人把多智能體系統真正跑大之后,都會撞上的一堵墻。
一個Agent維護一份上下文,另一個Agent又復制一份歷史;一個任務分叉出好幾條推理路徑,最后沒人說得清哪條分支產出了最終答案;模型調用、工具執行、沙箱環境、長期記憶各管各的狀態——demo 跑得挺好,可當系統擴到幾十上百個 Agent,調試、復現、編排全部開始失控。
最近,一個來自清華大學與中山大學的團隊(Rath Team)把他們的解法開源了,叫OpenRath:這是一個像PyTorch的多智能體、多會話運行時。
它的主張是:別再圍著Agent轉了。真正該被當成一等公民的,是Session。
![]()
官網:https://www.openrath.com/
文檔:https://docs.openrath.com/
博客:https://blog.openrath.com/
GitHub:https://github.com/Rath-Team/OpenRath
開源協議:BSD-3-Clause | 當前版本:v1.2.1(PyPI)
目前OpenRath已在PyPI發布到 v1.2.1,pip install openrath就能裝,BSD-3-Clause協議,官網、文檔、博客、GitHub一應俱全。
本文將從「為什么」一路講到「怎么用」,重點拆OpenRath和AutoGen、LangGraph這些框架到底不一樣在哪——以及它為什么敢借PyTorch的名字。
Agent運行時用「聊天記錄」思考
第一性問題:當Agent真的動了手,證據該存在哪?
第一代大模型應用,可以概括成「提示詞進、回答出」。Agent系統改變了這條邊界。
一個有用的Agent不只是產出文本。它會檢索、規劃、調用工具、讀文件、寫代碼、查API、跑測試、操作瀏覽器,有時還會改動外部狀態。ReAct讓推理和行動在一個循環里交替,Toolformer 讓模型學會何時調用工具,再到Model Context Protocol把工具變成協議級的邊界,這條線一直在往前走。
可一旦Agent真的對世界動了手,一個運行時層面的問題就冒出來了:這些動作的證據,到底存在哪?
如果一次工具調用讀了文件,我們需要它的參數和結果;如果它改了倉庫,我們需要diff;如果它跑在某個沙箱里,我們需要沙箱的身份;如果它失敗重試了,我們需要那條失敗路徑;如果有人批準或否決了某個動作,我們需要那個校驗信號。一份聊天記錄頂多敘述這些事,卻不足以還原這些事。
舉個具體例子。
一個軟件任務:研究Agent讀了issue、檢索了筆記;編碼Agent改了倉庫;沙箱跑了測試;校驗Agent否決了第一版補丁,于是工作流分叉;記憶后端記下這次失敗,免得以后重犯。如果這些事件散落在各自的日志里,那么最終答案幾乎是最不重要的產物,真正有價值的,是那條「工作如何一步步推進」的證據鏈。
這就是OpenRath的出發點:把Session當成證據的載體,而不只是聊天歷史。
為什么是Agent Cluster
單個Agent會膨脹成一個巨大的prompt,于是要把它拆開。
早期一個Agent基本夠用:接收輸入、理解任務、調用工具、返回結果,像個增強版聊天機器人。但真實任務很快超出單個Agent的邊界。
一個像樣的軟件工程任務,往往要拆成需求理解、資料檢索、架構設計、代碼實現、測試驗證、結果審查。不同環節要的能力并不一樣——有的擅長規劃,有的擅長寫碼,有的擅長挑錯。繼續讓一個Agent全包,它就會膨脹成一個巨大的prompt和一個越來越混亂的上下文窗口。
于是有了Agent Cluster:讓Planner、Researcher、Coder、Reviewer、Executor、Memory Agent各司其職,圍繞一個復雜目標協作。
多個專業Agent圍繞共享的Session協作:各自讀取當前狀態、完成局部任務、把結果寫回,供下一個Agent接力。
可一旦真把它跑起來,難題就冒出來了:這些Agent怎么共享上下文?某個結論到底來自哪個Agent、哪條分支、哪次工具調用?一個Agent出了錯,能不能回滾到對應分支重來?
說白了,Agent Cluster真正的挑戰,從來都不在「造更多Agent」——難的是管住這些Agent之間的狀態怎么流動。
![]()
OpenRath多問了一句
別人解決Agent之間怎么說話,它問說完之后誰擁有這份工作。
多智能體這個詞,常讓人想到一個群聊:一個Agent提議,一個批評,一個執行,一個主管決定什么時候收尾。這個模式有用,但不夠。
這條路上已經有不少工作:AutoGen把多Agent對話做成了一個實用的編程模型;CrewAI把Agent團隊和更結構化的流程分開;LangGraph用圖狀態和supervisor節點來表達路由與控制。它們都解決Agent之間怎么說話。
OpenRath接著往下問了一句:Agent們說完話之后,誰來擁有這份工作的狀態?
一個生產級的Agent Cluster,需要決定:當前這個Session該交給哪個 Agent、它該看到什么上下文、讀了哪些記憶、下一條命令在哪個沙箱跑、繼續之前需要什么校驗信號。這些都是控制平面的問題,靠往群聊里再加一個角色是解決不了的。OpenRath的答案是:讓Session成為路由的單位,讓Session Graph成為那張控制平面——Agent、工具、工作流、記憶、沙箱位置,都在這張圖上交匯。
一句話:Agent集群不是群聊,而是建立在持久Session狀態之上的運行時控制平面。
這也是為什么,從Agent數量×Session數量兩個維度看,多智能體系統會分成四象限:
單Agent單Session是ChatGPT式聊天;多Agent單Session是子代理協作;單Agent多Session是OpenClaw式分支扇出;而多Agent多Session(MAMS),正是OpenRath面向的方向。
OpenRath把這套思路稱為MAMS(Multi-Agent Multi-Session)。它的判斷很干脆:真正需要被fork(分叉)、merge(合并)、復用、追蹤的,是整條Session數據流——而非某個Agent內部那份各自維護的消息列表。
換個說法:大多數框架攢的是一屋子聰明的工人,OpenRath先把工位、工單和流水線建好。用官方那句話說就是——Agent是工人,Session才是工作本身。
![]()
像PyTorch一樣搭Agent集群
這不是蹭名字。
PyTorch之所以好用的三個設計,OpenRath一一對應搬了過來。
OpenRath最聰明的一步,是把深度學習開發者最熟的那套抽象,整套搬到了Agent系統上。
PyTorch為什么好用?因為它把復雜計算拆成了清晰的積木:Tensor是流動的數據,Module/Layer是變換這份數據的可組合單元,device決定算在哪,而整張計算圖是跑起來才長出來的。OpenRath給Agent系統做了幾乎一一對應的映射:
核心映射:Tensor→Session、Module/Linear→Workflow/Agent、Device→Sandbox / Backend、Parameter→Memory、Function→Tool、控制流→Selector。
下面三節,正是把這張表里最關鍵的三組對應,從術語對照講成為什么這么設計。
這套映射不是噱頭。把它拆開,PyTorch真正教給OpenRath的,其實是三件事——下面三節,正好是理解OpenRath的三根支柱。
![]()
支柱一:Agent是變換層 ,不是全能助手
Layer不持有數據;Agent也不持有狀態。
PyTorch里,nn.Linear不是一個應用,它只是一層變換:吃進一個Tensor,吐出一個Tensor。一個網絡的本事,來自很多這樣的層被疊起來。
OpenRath把Agent設計成了同一種東西。Agent就是Session上的一層變換。它的核心,就是一條forward(session) -> session的路徑:進來一個Session,出去一個Session。
關鍵在于,變換層不止一種。同樣是forward(session) ->session這個形狀,可以裝下完全不同的活兒:
一個Agent調工具、改workspace里的文件,把執行結果寫回Session;
一個
Compressor把跑了幾十輪的長會話壓縮成一條精簡消息(官方example第 8課就是它);一個Agent在跑之前
recall記憶、跑之后commit記憶,相當于給這次會話做了一次「索引與歸檔」;你也可以寫一個只做摘要、只做校驗、只做改寫的Agent。
它們對外都是同一個接口,于是能像神經網絡的層一樣任意堆疊、任意嵌套。這正是Workflow(對應nn.Module)的意義:子類只要實現一個forward(session) -> session,里面就能串聯多個Agent、fork Session、壓縮上下文、調用工具、分發到子工作流。因為每一層進出都是 Session,Workflow能像nn.Module一樣層層包起來,每層不必重新發明一套狀態格式。
管上百個Agent,于是從拼提示詞變成了搭模塊。Layer不持有數據,數據是 Tensor;Agent 也不持有狀態,狀態是Session。
這里還藏著一個容易被忽略的好處。因為Agent不擁有整個世界——Session loop仍是引擎,Sandbox仍是執行位置,Memory仍是獨立store——所以單個Agent的場景足夠簡單,同一個Agent又能被原樣塞進更大的 Workflow,不用改一行。
至于工具本身,OpenRath抽象成了FlowToolCall:一手攥著給模型看的name / description / JSON schema,一手攥著真正在Python里執行的行為,讓工具長什么樣」和工具干什么始終待在一起。內置了文件、shell、代碼執行工具,stdio的MCP工具也能直接適配進同一個循環。底層還有個清晰的分層:FlowToolCall是flow層模型可見的函數,BackendTool*才是沙箱后端真正消費的載荷。
支柱二:Sandbox與Memory是「可插拔后端」
把后端寫死,等于把模型焊在CPU上。
PyTorch第二個聰明的地方,是把「算在哪」從「算什么」里剝了出來。同一份模型代碼,.to("cuda")就上GPU,換個后端就換塊卡,計算邏輯一行不用動。device / compute backend是可插拔的。
OpenRath把這個思想用在了兩個最容易被寫死的地方:執行環境和長期記憶。
Sandbox(對應 Device)——工具到底在哪運行。很多框架把「對話歷史」和「工具實際執行的位置」分開管,模型以為自己還在某個工作區,shell或容器其實早就切走了。
OpenRath把Sandbox綁在Session上:工具跑在Session當前的backend上,返回的Session會記住自己的執行位置,不會悄悄漂移。
而它真正的巧思,是把Sandbox做成了可插拔的backend:本地進程始終可用(session.to("local", spec="./")),容器化的OpenSandbox是可選項(pip install "openrath[opensandbox]"),未來任何第三方執行后端,只要接到同一套 Session placement 模型后面就能用。執行環境,從此不再硬編碼進某一個 shell。
Memory(對應 Parameter)——跨運行保留的記憶。
它是獨立的一層持久狀態,能綁定到 Agent、運行前 recall、運行后 commit;既不像工具結果那樣用完即棄,也不只是塞進 prompt 的幾行文本。
基礎安裝自帶零依賴的本地后端,把數據存在.openrath/memory/,不用 LLM 也能做BM25詞法檢索;
配了embedding就能用向量排序;想要更強的,可以接OpenViking這類外部記憶服務。和Sandbox一樣,Memory同樣是可插拔backend——recall不綁死在某一個數據庫上。
這一招對自帶本地狀態的團隊尤其友好:你已經有自己的容器調度、有自己的向量庫或知識庫,不必推倒重來,只要把它包成一個backend接進去,就能復用OpenRath的整套Session / Workflow抽象。說到底,把執行環境和記憶都做成可換的device,OpenRath才能讓狀態、執行、記憶、編排彼此解耦到足以各自替換,卻又都串在同一個流動的值——Session——上。
支柱三:Session Graph是動態圖
PyTorch的圖是跑起來才長出來的,OpenRath的Session Graph也是。
PyTorch還有第三個讓人上癮的設計:動態圖(define-by-run)。它不要求你先把整張計算圖畫死再喂數據,而是代碼跑到哪、圖就長到哪。控制流就是普通的Pythonif/for,靈活到可以在運行時根據中間結果改變走向。
OpenRath的Session Graph,是同一個性格的東西。
先看Session長什么樣。它遠不止一串聊天記錄——而是一張結構化的chunk表,大致是這個形狀:
Session├─ chunks: [ {role: system, ...}, # Agent 指令也是一條 chunk│ {role: user, text: "..."},│ {role: assistant, text: "..."},│ {role: tool_result, name, args, result} ] # 工具證據├─ placement: "local" / "opensandbox" # 這一段在哪執行├─ lineage: parent / fork / merge 關系 # 我從哪條分支來└─ usage: token 用量它能fork出分支,能detach切斷父鏈,能merge合并,還能序列化成JSONL直接交給下一個Workflow。而這張由fork / merge織出來的圖,是Agent們跑起來、一步步演化出來的,并非事先畫死的劇本——這正是「動態圖」的含義。
Session沿著fork / merge演化,每一步都留下血緣;工具運行環境則由Sandbox backend綁定。這張圖不是預先編排好的,而是隨Agent運行實時生長。
為什么這件事對Agent Cluster是決定性的?因為集群規模一大,你遲早要回答:這個結論到底是哪個Agent、走哪條分支、調哪次工具、在哪個workspace產出的?散落的日志答不了,一張帶血緣的動態圖能答。Session Graph于是從實現細節升格成了集群的可觀測層與控制層:路由、復現、回滾、審計,全在同一張圖上做。
![]()
幾十行,把這套東西跑通
抽象講再多,不如看一段能跑的代碼。
下面這個最小例子(取自官方README的最小完整工作流),把Session、Sandbox、Tool、Agent、Memory、Workflow、Compressor一次性串了起來:
from rath import flowfrom rath.session import Sessionclass ReadmeWorkflow(flow.Workflow): def __init__(self): provider = flow.Provider(model="gpt-5.5")# Agent:一層帶 prompt / 工具 / 記憶的變換 self.agent = flow.Agent( "Use the `word_count` tool, then answer briefly.", provider, tools=[WordCountTool()], memory="local", )# Compressor:另一種變換層——把長會話壓成一條精簡消息 self.compressor = flow.Compressor("Compress the run into one message.", provider) def forward(self, session: Session) -> Session: self.agent.remember_memory("The user likes compact summaries.") # 運行前寫記憶 session = self.agent(session) # 一層變換 self.agent.commit_memory(session) # 運行后提交記憶return self.compressor(session) # 再疊一層變換# Session 承載數據,Sandbox 決定執行位置session = Session.from_user_message("Count the words in: OpenRath makes agent clusters traceable.")session = session.to("local", spec="./")out = ReadmeWorkflow()(session)讀這段代碼,三根支柱全在里面:數據是Session,執行位置由.to()決定(支柱二),agent和compressor是兩層不同的變換疊起來(支柱一),而它們怎么串、串幾層,是forward里用普通Python寫出來的(支柱三)。每一步進出,都是同一個Session。
真正動態的地方Selector
把流程寫進提示詞是焊死,交給Selector才是讓系統學會拐彎。
上一節的forward是寫死的順序。但真實任務往往要等跑起來才知道該往哪拐——這時候就輪到動態圖的「運行時路由」登場。
很多框架的做法是把流程提前編排死:if走A,else走B。
OpenRath的答案是Selector:一個由大模型驅動的路由器。它在若干個「會自我描述」的Workflow之間做選擇,返回下一個該跑的Workflow,任務結束就返回一個空操作。妙處在于——它讓Agent之間的if/while,依然是普通的Python:
selector = flow.Selector(provider)while not isinstance( nxt := selector.forward(session, triage, tech, wrapup), flow.EmptyWorkflow): session = nxt(session)把流程寫進提示詞,是把不確定性焊死;交給 Selector,才是讓系統學會拐彎。這也正是官方把OpenRath稱為"dynamic multi-agent workflow"的底氣:流程從寫死的劇本,變成了運行時才定下來的路由——和PyTorch 動態圖里那句代碼跑到哪、圖就長到哪是同一種自由。
它現在到底能不能用?
能裝、能跑、能照著學——不是一份PPT。
最能說明問題的是它的example/目錄——一條編號遞進的學習階梯,每個腳本只講一個概念,前一個的產出正好是后一個的輸入:
已在GitHub開源,BSD-3-Clause協議,可直接pip install openrath。
01_hello_agent:最小程序,構造Agent、在Session上調用、流式輸出02_session_lineage:用fork分叉、detach切斷血緣、查看session graph、導出JSONL03_sandbox_backend:把同一個Session放到local或opensandbox,看工具在哪執行04_tools_builtin/05_custom_tool/06_mcp_tool:內置工具、自定義工具、借用MCP工具07_streaming/08_compress/09_memory/10_provider_variation:流式、上下文壓縮、記憶、換模型廠商11_dynamic_selector:用Selector做if分支和while循環
從「先讓一個Agent跑起來」到「讓一群Agent動態協作」,11步走完,OpenRath的核心也就理解透了。
安裝也分層:基礎pip install openrath,要容器沙箱加[opensandbox],要外部記憶加[openviking],模型則走 OpenAI 兼容的環境變量或~/.openrath/config.json。
更值得說的是這套 example 的設計取向:官方強調,例子的目標是產出一份「證據檔案」,而不是一張截圖。一個軟件任務跑完,理想的產物不該只停在一句「成功了」,而該是一整份可回溯的卷宗——
issue 原文 → Session Graph → 調了哪些工具 → 副作用落在哪個 sandbox → 被否決的那條分支 → 最終采納的補丁 → 測試結果 → 這次往 Memory 里寫了什么。
這份卷宗,正是「一個 demo」和「一個能拿來做技術報告的運行時」之間的區別。按團隊自己的說法,他們在內部已經用 OpenRath 組織起接近 Transformer 結構的 Agent Workflow——不過這更偏系統能力驗證,還不是公開 benchmark,這點他們說得很坦白。
從持久Session,到Agent Cluster
v1.1讓單個Agent的工作可追溯,v1.2讓一群Agent的協作可追溯。
把視野拉遠一點:OpenRath的版本演進本身就是一條干凈的線。v1.1解決「持久」——如果一個Agent的工作是跨時間展開的,憑什么唯一被保存的只有最終答案?于是有了持久Session,把干活的證據完整留下。v1.2再抬高一層:讓Session從「單個Agent事后可查的記錄」,升級成「能在多個Agent和工作流之間被路由的對象」。一行代碼就概括了這個轉變:
session = workflow.forward(session)它意味著,工作的單位從一個prompt、一個答案或一個Agent角色,挪到了一份持久、可路由的Session狀態上。
從Prompt 工程
走向系統工程
OpenRath的意義,不只是「又一個Agent框架」。
它真正想解決的是:當Agent Cluster成為主流形態,開發者能不能像寫深度學習那樣,獲得一套可組合、可追蹤的工程體驗。這正是它借PyTorch之名的底氣——同一套直覺遷移了過來:層是變換(Agent),device可插拔(Sandbox / Memory backend),圖是動態的(Session Graph)。
在PyTorch里,你定義Module,讓Tensor在網絡中流動;在OpenRath里,你定義Agent和Workflow,讓Session在系統中流動。剩下的——血緣記錄、工具調度、沙箱綁定、長期記憶、動態路由——交給框架。
如果說過去的Agent框架面向的是「一個智能助手」,那么OpenRath面向的是「一個智能體系統」。
而這件事的起點,樸素得有點反直覺——不是再多造一個Agent,而是先把Session當回事。
參考資料:
https://www.openrath.com/
編輯:LRST
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.