LangChain 和 LangGraph 的区别
一、 演进背景与技术阵痛:为什么 LCEL 无法支撑复杂 Agent?
在大模型应用架构的演进历程中,LangChain 的出现极大地降低了开发者调用 LLM 的门槛。然而,随着应用场景从“单轮生成”逐步深入到“自主规划与多步行动”的真实业务环境,原有架构的局限性暴露无遗。
┌────────────────────────────────────────────────────────────────────────┐
│ 计算流拓扑演进形态对比 │
├────────────────────────────────────────────────────────────────────────┤
│ 1. 传统 LangChain 模式 (DAG / 管道式流水线) │
│ Input ──► Prompt ──► LLM ──► Parser ──► Output (单向流动,无法回溯) │
├────────────────────────────────────────────────────────────────────────┤
│ 2. 现代 LangGraph 模式 (有状态环状图 / 状态机) │
│ ┌─────────► Node A (规划) ──────► Node B (工具调用) ──────┐ │
│ │ ▲ │ │ │
│ │ │ (动态条件边判断) │ ▼ │
│ └─────────── Node C (反思纠错) ◄───────────┴──────► Output (收敛退出)│
│ [中央全局状态 State: 自动累加、持久化与快照拦截] │
└────────────────────────────────────────────────────────────────────────┘
1.1 经典 LangChain 与 LCEL 的高光与局限
LangChain 在 0.1 时代引入了 LCEL(LangChain Expression Language),通过类似 Unix 管道符 | 的语法,让开发者能够极其优雅地组织链式调用:
# 典型的 LCEL 管道调用:纯单向流动
chain = prompt | model | output_parser
result = chain.invoke({"topic": "分布式系统"})
这种设计在处理 DAG(Directed Acyclic Graph,有向无环图) 任务时表现优异。典型场景如:
-
标准 RAG 流水线:用户提问 ➔ 检索向量库 ➔ 组装上下文 Prompt ➔ 投喂大模型 ➔ 解析为字符串输出。
-
数据清洗转化:加载文档 ➔ 文本切块 ➔ 批量生成摘要 ➔ 写入存储。
但是,真实世界的 Agent 从来不是一条笔直的流水线。
1.2 工业级 Agent 业务带来的架构撞墙
当业务团队试图用 LangChain 搭建具备自主排错与复杂决策能力的系统(如自动化代码修复、交互式金融分析、多系统协同审批)时,LCEL 遭遇了四大难以逾越的工程障碍:
1. 循环与动态回溯(Loops & Cycles)
真实的思考过程充斥着“行动 ➔ 评估 ➔ 失败 ➔ 重新规划 ➔ 再次尝试”的闭环。
-
LCEL 是严格的无环图,如果强行在链内部引入循环,开发者只能在自定义组件中编写递归调用或在外部嵌套
while循环。 -
这种做法不仅彻底打破了 LCEL 优雅的声明式结构,更导致调用链路极度脆弱,极易因死递归造成显存崩溃或上下文失控。
2. 隐式状态丢失与上下文污染(Implicit State Fragility)
在复杂的管道链条中,数据是通过前一个组件的返回值隐式传递给下一个组件的。
-
如果下游第 5 步的决策需要引用第 1 步的某个原始元数据,开发者必须通过诸如
RunnablePassthrough.assign等繁琐的操作将数据一路打包向下透传; -
随着工具调用步骤增加,上下文对象迅速膨胀变形,数据流向变得难以追踪和调试。
3. 人机协同(Human-in-the-loop)的断点困境
在许多关键任务中,系统需要引入人类干预:例如 Agent 准备执行一个删除数据库或转账 10 万元的操作时,必须挂起等待管理员审批。
-
在传统链式结构中,整个进程处于持续阻塞状态;如果此时服务器发生重启或网络闪断,内存中的上下文全部灰飞烟灭;
-
链式架构缺乏将运行时的中间状态持久化到磁盘并在数小时后由外部信号“唤醒并继续向下执行”的机制。
4. 多智能体协作(Multi-Agent)拓扑失调
当团队需要构建“产品经理 Agent、研发 Agent、测试 Agent”协同作业的多智能体网络时,单纯的链式拼接无法描述复杂的智能体网络:
-
智能体之间需要共享一个演进中的全局工作空间(Global Scratchpad);
-
谁是下一个发言者必须基于当前状态进行动态路由(Dynamic Routing);
-
链式结构在此类拓扑面前彻底退化为混乱的代码拼凑。
为了从根本上解决这些架构矛盾,LangChain 官方在重塑底层运行时的背景下推出了 LangGraph。
二、 核心区别横向全景解构:六大维度的技术断代
要理清 LangChain 与 LangGraph 的边界,必须将两者的底层抽象与控制逻辑进行深度比对。
┌────────────────────────────────────────────────────────────────────────┐
│ LangChain vs LangGraph 核心架构对比表 │
├──────────────────┬────────────────────────────┬────────────────────────┤
│ 评估维度 │ LangChain (LCEL) │ LangGraph │
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 1. 核心计算拓扑 │ 有向无环图 (DAG) / 线性管道│ 有向有环图 (Cyclic Graph) / 状态机│
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 2. 状态管理模型 │ 隐式管道传递,无全局状态 │ 显式集中式状态机 (Typed State) │
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 3. 状态更新机制 │ 组件返回值全量替换 │ 基于 Reducer 算子的局部原子合并 │
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 4. 执行控制流 │ 声明式管道符 (|) 串联 │ 节点 (Node) + 条件边 (Conditional Edge)│
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 5. 持久化与恢复 │ 内存瞬态对象,进程退出即丢 │ 原生 Checkpointer 驱动的快照回滚│
├──────────────────┼────────────────────────────┼────────────────────────┤
│ 6. 人机协同机制 │ 轮询 / 外部回调打补丁 │ 原生中断断点 (interrupt_before/after)│
└──────────────────┴────────────────────────────┴────────────────────────┘
2.1 拓扑模型:有向无环管道 vs 有向有环状态机
-
LangChain (LCEL):本质是函数式数据流处理流水线。数据如同流水一般从 Source 流向 Sink,沿途被各个算子转换。它严格遵循 DAG 原则,数据不能也不应该流回之前的节点。
-
LangGraph:本质是有限状态机(Finite State Machine, FSM)。图中的每一个节点都是一个状态转换函数,系统维护一个随时间演进的中央状态对象。节点执行完成后,通过边(Edge)决定下一个执行节点,且边可以明确指向图中的任意历史节点,形成闭环回路。
2.2 状态机制:瞬态传参 vs 类型化通道与 Reducer 算子
在 LangChain 中,没有独立于计算链条之外的统一状态。每个组件的输入必须严格匹配上一个组件的输出。
而在 LangGraph 中,状态是一等公民(First-class Citizen):
-
开发者通过 Python 的
TypedDict或Pydantic显式定义整个图能够读写的数据模型(State Schema)。 -
Reducer 机制:这是 LangGraph 区别于其他图框架的核心机制。当某个节点执行完毕后,它只需要返回一个字典,表达“我对状态做了哪些修改”。通过为特定字段绑定 Reducer(如
operator.add),系统会自动将新生成的消息或数据追加到历史列表中,而不是粗暴地全量覆盖。
节点返回增量: {"messages": [NewAIMessage]}
│
▼ 触发 State 绑定的 Reducer (operator.add)
全局状态自动演进: State["messages"] = State["messages"] + [NewAIMessage]
2.3 状态持久化与“时间旅行”(Time Travel)
这是工业级系统与玩具 Demo 的分水岭:
-
LangChain:缺乏开箱即用的事务级状态持久化。一旦模型生成中断或系统崩溃,必须从头开始重跑整个链条。
-
LangGraph 原生内置 Checkpointer 机制:
-
图在每一次节点跃迁后,都会自动将当前状态打上一个唯一的
thread_id和checkpoint_id,并持久化到存储后端(如内存、SQLite、PostgreSQL); -
时间旅行(Time Travel):开发者可以随时将执行指针回退到某一个特定的历史检查点,修改当时的变量值(例如修正大模型某一步产生的工具调用参数),然后让图沿着新的分支继续向下运行。这在调试长时任务和提供人类撤销(Undo/Redo)功能时具有决定性意义。
-
2.4 人机协同(Human-in-the-loop):断点与线程挂起
在 LangChain 中实现人工审批,通常需要自己写一套复杂的轮询、数据库记录与外部控制器,硬生生把一条链切碎成多个碎片。
而在 LangGraph 中,人工介入被原生集成到编译阶段:
-
开发者可以在编译图时指定
interrupt_before=["tool_execution_node"]; -
当图运行到执行工具节点的前一刻,运行时会自动保存检查点并安全挂起进程,退出前向执行;
-
外部客户端(如前端 Web 界面或审批后台)可以通过 API 随时查询当前挂起的状态,人工确认通过后,向该线程发送一个 Resume 信号,图便会无缝恢复现场继续执行。
三、 LangGraph 核心原语与架构运行机理解密
为了彻底看清 LangGraph 的内部工作方式,我们需要拆解其最核心的四大基本原语:State(状态)、Nodes(节点)、Edges(边) 与 Checkpointers(检查点)。
┌────────────────────────────────────────────────────────────────────────┐
│ LangGraph 内部架构原语拆解 │
├────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. State (状态模式) 定义整个图生命周期内流动的数据骨架 │
│ │
│ 2. Nodes (计算节点) 无状态或有状态的独立函数,接收并更新状态 │
│ Input: State ──► Output: Partial Update │
│ │
│ 3. Edges (路由拓扑) 定义流转方向:普通边、入口边、条件路由边 │
│ │
│ 4. Checkpointer (检查点) 在每一次超步 (Super-step) 写入持久化快照 │
│ │
└────────────────────────────────────────────────────────────────────────┘
3.1 状态通道(State Channels)与增量更新
在 LangGraph 中,状态由一系列被称为“通道(Channels)”的键值对组成。每个通道可以拥有独立的写入行为策略:
from typing import Annotated, List
from typing_extensions import TypedDict
import operator
class AgentState(TypedDict):
# 消息通道:使用 operator.add 装饰,意味着任何节点对 messages 的更新都会追加到列表末尾
messages: Annotated[List[str], operator.add]
# 覆盖通道:没有 Reducer 装饰,意味着节点的更新会直接覆盖原值
current_status: str
retry_count: int
这种机制让节点与节点之间完全解耦:节点 A 不需要知道节点 B 接收什么样的参数格式,每个节点只需读取自己关心的状态键,并在返回时产出局部的状态更新字典即可。
3.2 节点(Nodes):承载计算的独立单元
节点本质上是一个标准的 Python 函数(同步或异步):
-
入参:当前的图全局状态
state; -
出参:包含需要更新的状态字段的字典
dict; -
设计原则:节点应当尽可能保持单一职责。例如“调用大模型分析”、“执行 SQL 查询工具”、“格式化输出”分别应当拆分为独立的节点。
3.3 边(Edges)与超步控制流(Super-steps)
LangGraph 基于 Pregel 批量同步并行计算模型 设计:
-
普通边(Normal Edge):显式声明两个节点之间的先后依赖关系。例如
graph.add_edge("planner", "executor"),表示 planner 节点执行完毕后无条件跳转到 executor。 -
条件边(Conditional Edge):根据当前状态动态计算下一步路由。
def route_next_step(state: AgentState) -> str: if state["retry_count"] > 3: return "human_fallback" # 重试次数过多,转人工 if "error" in state["current_status"]: return "reflector" # 出现异常,转反思节点 return "end" # 任务完成,退出 -
特殊预设节点:
-
START:图的入口锚点; -
END:图的终点锚点,一旦边路由到此,图停止步进并输出最终状态。
-
3.4 检查点(Checkpointer)与线程事务管理
Checkpointer 是 LangGraph 实现高可用状态保持的底层发动机。当图编译时挂载了 Checkpointer(例如 MemorySaver 或 PostgresSaver):
-
每次一个或一组节点完成执行(被称为完成一个 Super-step),Checkpointer 会自动将当前状态完整序列化并写入存储库;
-
每次调用时通过传入
config={"configurable": {"thread_id": "session-1001"}}绑定会话线程; -
不同的
thread_id拥有完全隔离的状态空间,天然支持多租户高并发访问。
四、 代码级硬核实战对比:从脆弱的 LCEL 到健壮的 LangGraph
为了直观体会两者的实际代码差异,我们设计一个典型的工业级业务场景:
任务目标:构建一个“代码生成与自修复 Agent”。
-
Agent 根据用户要求编写 Python 代码;
-
自动运行沙箱测试;
-
如果测试报错,将错误信息回传,Agent 自主反思并修正代码,最多重试 3 次;
-
如果重试 3 次仍未解决,挂起任务触发断点,等待人工介入指导。
4.1 传统 LCEL 实现的困境
如果试图用纯 LangChain / LCEL 来编写这段逻辑,代码会变得极其扭曲:
# 传统 LCEL 路线的伪代码与工程困境
from langchain_core.runnables import RunnablePassthrough, RunnableBranch
# LCEL 只能构建单向链路,面对循环,必须手动写极其脆弱的递归链
def make_recursive_chain():
# 想要实现循环,必须在自定义 Runnable 内部递归调用自己
def retry_logic(inputs):
code = inputs["code"]
test_result = run_sandbox_test(code)
if test_result == "PASS":
return {"result": code, "status": "SUCCESS"}
if inputs.get("attempts", 0) >= 3:
# 根本无法原生挂起等待审批,只能直接抛出异常或者直接返回失败
return {"error": "Max retries reached", "status": "FAILED"}
# 深度递归:极易在长链条下导致栈溢出和显存泄漏
fix_prompt = f"代码运行报错: {test_result},请修复代码: {code}"
new_code = llm.invoke(fix_prompt).content
return retry_logic({"code": new_code, "attempts": inputs.get("attempts", 0) + 1})
return RunnablePassthrough() | retry_logic
# 这种方案完全失去了可观测性、无法中途持久化,且根本谈不上“人工唤醒”
4.2 LangGraph 完整工程级实战实现
下面给出采用 LangGraph 构建该完整业务闭环的生产级标准实现。
1. 环境准备
pip install langgraph langchain-core langchain-openai
2. 核心源码构建
import os
import operator
from typing import Annotated, List, Literal
from typing_extensions import TypedDict
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import MemorySaver
# ==================== 1. 定义类型化全局状态 ====================
class CodingAgentState(TypedDict):
# 消息通道:自动追加对话历史
messages: Annotated[List[BaseMessage], operator.add]
# 当前生成的代码
current_code: str
# 沙箱测试报错信息
error_log: str
# 累计重试次数
retry_count: int
# ==================== 2. 模拟工具与业务节点 ====================
def coder_node(state: CodingAgentState):
"""根据需求或历史报错编写/修正代码"""
messages = state["messages"]
retry_count = state.get("retry_count", 0)
print(f"\n--- [Coder Node] 正在进行第 {retry_count + 1} 次代码编写/修复 ---")
# 模拟大模型根据上一次的 error_log 生成代码
if state.get("error_log"):
# 模拟模型修复了错误
generated_code = "def add(a, b): return a + b # 修正后通过测试"
else:
# 第一次写代码时故意生成一个带有 Bug 的代码
generated_code = "def add(a, b): return a - b # 存在逻辑 Bug"
return {
"current_code": generated_code,
"messages": [AIMessage(content=f"已生成代码: {generated_code}")]
}
def tester_node(state: CodingAgentState):
"""沙箱执行环境:运行测试用例"""
code = state["current_code"]
retry_count = state.get("retry_count", 0)
print(f"--- [Tester Node] 启动沙箱测试代码: {code} ---")
# 模拟单元测试:当代码包含 'return a - b' 时报错,包含 'return a + b' 时通过
if "return a - b" in code:
test_passed = False
error_msg = "AssertionError: add(1, 2) should be 3, but got -1"
else:
test_passed = True
error_msg = ""
if test_passed:
print(">>> 测试用例 100% 通过!")
return {"error_log": "", "retry_count": retry_count}
else:
print(f">>> 单元测试失败!捕获错误: {error_msg}")
return {
"error_log": error_msg,
"retry_count": retry_count + 1,
"messages": [HumanMessage(content=f"测试失败,错误详情: {error_msg}")]
}
def human_approval_node(state: CodingAgentState):
"""人工审批节点:当多次自愈失败后挂起"""
print("--- [Human Intervention] 进入人工兜底处理节点 ---")
return {
"messages": [AIMessage(content="多次重试均未通过测试,已转交工程师人工处理。")]
}
# ==================== 3. 编写动态条件边路由逻辑 ====================
def router_after_test(state: CodingAgentState) -> Literal["coder_node", "human_approval_node", "__end__"]:
"""核心决策路由:判断继续重试、人工介入还是成功收敛"""
error = state.get("error_log")
retries = state.get("retry_count", 0)
# 1. 没有报错,说明测试通过,任务圆满完成
if not error:
return "__end__"
# 2. 出现报错且重试次数已达上限(超过 2 次),路由至人工处理
if retries >= 2:
print(">>> 警告:自愈重试次数已达上限,触发安全边界,转人工处理!")
return "human_approval_node"
# 3. 出现报错但未达上限,形成回环,继续让 Coder 修改
print(">>> 决定:继续回退至 Coder 节点进行自我纠错!")
return "coder_node"
# ==================== 4. 组装与编译有状态图 ====================
# 实例化图构建器
builder = StateGraph(CodingAgentState)
# 添加计算节点
builder.add_node("coder_node", coder_node)
builder.add_node("tester_node", tester_node)
builder.add_node("human_approval_node", human_approval_node)
# 构建基础控制拓扑
builder.add_edge(START, "coder_node")
builder.add_edge("coder_node", "tester_node")
# 挂载核心条件边 (实现闭环回退与分支选择)
builder.add_conditional_edges(
source="tester_node",
path=router_after_test,
path_map={
"coder_node": "coder_node",
"human_approval_node": "human_approval_node",
"__end__": END
}
)
builder.add_edge("human_approval_node", END)
# 挂载内存检查点 (具备生产级持久化与恢复能力)
checkpointer = MemorySaver()
# 编译图:在此处设置断点,在进入 human_approval_node 之前主动挂起
app = builder.compile(
checkpointer=checkpointer,
interrupt_before=["human_approval_node"] # 原生人机协同断点
)
3. 模拟端到端运行验证
# ==================== 5. 执行图推理与断点恢复演示 ====================
# 配置独立的会话线程
thread_config = {"configurable": {"thread_id": "task_session_001"}}
print("==================== 开始执行 Agentic 流程 ====================")
# 第一次启动任务
initial_input = {
"messages": [HumanMessage(content="请编写一个 add(a, b) 函数并确保测试通过。")],
"retry_count": 0,
"error_log": "",
"current_code": ""
}
# 逐步推进图的执行
for event in app.stream(initial_input, config=thread_config):
pass
# 查看当前任务的最新快照状态
current_snapshot = app.get_state(thread_config)
print("\n==================== 阶段一运行结束状态快照 ====================")
print(f"当前下一个待执行节点 (Next Node): {current_snapshot.next}")
print(f"累计重试次数: {current_snapshot.values.get('retry_count')}")
print(f"最终生成代码: {current_snapshot.values.get('current_code')}")
控制台实际运行输出追踪
==================== 开始执行 Agentic 流程 ====================
--- [Coder Node] 正在进行第 1 次代码编写/修复 ---
--- [Tester Node] 启动沙箱测试代码: def add(a, b): return a - b # 存在逻辑 Bug ---
>>> 单元测试失败!捕获错误: AssertionError: add(1, 2) should be 3, but got -1
>>> 决定:继续回退至 Coder 节点进行自我纠错!
--- [Coder Node] 正在进行第 2 次代码编写/修复 ---
--- [Tester Node] 启动沙箱测试代码: def add(a, b): return a + b # 修正后通过测试 ---
>>> 测试用例 100% 通过!
==================== 阶段一运行结束状态快照 ====================
当前下一个待执行节点 (Next Node): ()
累计重试次数: 1
最终生成代码: def add(a, b): return a + b # 修正后通过测试
从输出日志可以清晰地看到:系统完全在没有外部 while 循环干预的情况下,自动根据测试结果在 coder_node 与 tester_node 之间完成了闭环自我回溯修正,并在测试全通后平滑收敛终止。
五、 生产级工程落地挑战与踩坑指南
将基于 LangGraph 的应用推向生产环境时,由于引入了状态机与持久化机制,工程师会面临与传统无状态 API 完全不同的全新挑战:
1. 状态爆炸与序列化深水坑(State Bloat & Serialization)
-
隐患:随着多轮工具调用与自反思推进,如果将所有的工具原生返回值、PDF 二进制数据、庞大的 JSON 树全部追加到
State["messages"]中,状态快照对象会迅速膨胀至数十兆字节。 -
后果:每次节点跃迁时 Checkpointer 写入存储(如 Redis/Postgres)的耗时会飙升至数百毫秒,并在多次循环后耗尽上下文窗口。
-
最佳实践:
-
实行“存储分离”:在 State 中仅保留数据引用(如
document_id或 S3 存储链接)与轻量语义总结; -
状态精简策略:在特定节点增加“剪枝”逻辑,将多轮反思产生的中间错误日志在收敛后显式置空。
-
2. 并发与竞态条件(Concurrency & Race Conditions)
当多个子任务节点处于并行分支并试图同时写回同一个状态键时:
-
如果该状态键没有声明 Reducer(即默认覆盖策略),后写入的节点会无情覆盖先写入节点的数据,导致严重的业务数据丢失;
-
防范方案:对于可能存在多节点并发写操作的状态字段,必须且只能使用具备合并能力的 Reducer(如
operator.add或自定义的字典合并函数)。
3. 持久化后端的选型策略
| 运行环境 | 推荐 Checkpointer | 核心考量 |
| 本地开发 / 单元测试 | MemorySaver | 内存瞬时写入,零部署开销,速度极快 |
| 单机小型工具 / 桌面端 | SqliteSaver | 基于文件系统持久化,轻量无需额外网络进程 |
| 企业高可用生产集群 | PostgresSaver | 支持分布式连接池、行级锁并发保护与海量冷数据分区 |
在容器化集群部署时,严禁在生产环境使用 MemorySaver,否则当容器发生漂移或滚动更新时,挂起的审批与断点将永久丢失。
六、 选型决策模型:何时用 LangChain?何时用 LangGraph?
不要走向从“过度依赖 LangChain”到“全面泛化 LangGraph”的另一个极端。在实际技术架构选型中,请遵循以下决策流:
[架构选型决策树]
│
┌──────────────────────────┴──────────────────────────┐
▼ ▼
【任务是否包含以下特征?】 【任务是否属于以下类型?】
- 存在循环探索与自我修复 (Loops) - 一次性线性 RAG 检索问答
- 需要长时间挂起等待外部人工审批 - 纯文档批量向量化与分块流水线
- 依赖复杂的 Multi-Agent 状态分发 - 一次性多步骤数据结构映射转换
- 要求单步可回滚与审计溯源 - 确定性、无分支工具串联
│ │
▼ ▼
坚决选用 LangGraph 首选轻量 LangChain (LCEL)
(建立以 State 为中心的图状态机) (基于管道符的高吞吐无状态调用)
两种技术的高阶融合方案
在成熟的企业架构中,两者通常是分层共存的:
-
宏观编排层(Macro Level):使用 LangGraph 搭建顶层状态机,控制大颗粒度的智能体协同、人工审批闸口与重试回溯逻辑;
-
微观执行层(Micro Level):在图中的某个具体 Node(计算节点)内部,依然可以自由调用 LangChain 封装好的向量检索器(Retriever)、Prompt 模板或特定的解析器(Output Parser)来高效完成局部计算。
七、 总结
从 LangChain 到 LangGraph,反映了大模型应用开发从“以 Prompt 组装为中心的单向调用”迈向“以状态控制与环境交互为中心的自主智能体”的必然趋势。
-
LangChain 是一套出色的工具箱与管道库,它将大模型生态中纷繁复杂的接口标准化,极大地加速了从 0 到 1 的功能组装;
-
LangGraph 则是一个真正的智能体操作系统运行时,它通过状态图、可控循环、细粒度快照与原生断点机制,为充满不确定性的大模型推理套上了确定性的工程缰绳。
深刻理解 DAG 流水线与图状态机的本质差异,在合适的业务场景选用合适的编排原语,是每一位致力于构建高可靠、工业级 AI 系统的开发者与架构师的核心技术底色。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)