同一个 AI Coding Agent,用 AgentScope 和 LangGraph 各写一遍后,我理解了它们的本质差异
AgentScope | LangGraph | FastAPI | ReAct | AI Coding Agent | 框架深度对比
你可能读过很多"框架介绍"文章,但很少有一篇告诉你:用两个框架做同一个东西,到底会碰到什么不同的问题。
Babi Agent 是一个 AI Coding Agent——能读写代码、执行 Shell、搜索网页、调用 GitHub API、通过 Skills 扩展能力。它有两个 Python 实现:
- babi-agentscope:基于 AgentScope + FastAPI
- babi-langgraph:基于 LangGraph + FastAPI
两个项目共享同一套设计理念——相同的工具集、相同的 Skills 系统、相同的系统提示词结构、相同的前端页面——但底层框架完全不同。
这篇文章不是 API 文档的搬运,而是从真实工程实践中提炼出的框架选型洞察。
🎯 为什么要用两个框架写同一个 Agent?
动机很简单:如果只用一个框架,你永远不知道它的 API 是"设计得好"还是"你习惯了"。
AgentScope 和 LangGraph 都是 Python 生态中优秀的 Agent 框架,但它们的设计哲学截然不同。只有用同一套业务需求把两个框架都跑通,你才能回答这些真实问题:
- 创建一个 ReAct Agent,两个框架分别要写几行代码?
- 文件系统工具(读/写/编辑/搜索),框架帮你做了多少?
- 会话持久化,两个框架的心智模型有什么本质区别?
- Web 层集成,谁让你写更多的"胶水代码"?
- 当你想加一个自定义路由(比如清除会话),谁的扩展更自然?
📐 第一层对比:Agent 构建——“配置驱动” vs “函数组装”
AgentScope:声明式配置 + 框架托管
# babi-agentscope/babi/agent/builder.py
from agentscope.agent import Agent, ContextConfig, ReActConfig
from agentscope.tool import Toolkit, FunctionTool
agent = Agent(
name="BabiAgent",
system_prompt=sys_prompt,
model=DashScopeChatModel(
credential=DashScopeCredential(api_key=settings.dashscope_api_key),
model=settings.model_name,
stream=True,
max_retries=settings.max_retries,
),
toolkit=Toolkit(tools=[
Bash(), Grep(), Glob(), Read(), Write(), Edit(), # 框架内置工具
FunctionTool(fetch_url), # 自定义工具
FunctionTool(http_request),
FunctionTool(web_search),
]),
context_config=ContextConfig(),
react_config=ReActConfig(),
state=AgentState(
permission_context=PermissionContext(mode=PermissionMode.BYPASS),
),
)
关键观察:AgentScope 的 Agent 是一个"重量级对象"——它内部封装了 ReAct 循环、上下文管理、权限控制、状态持久化。你通过配置项告诉它"要什么",框架帮你处理"怎么做"。
FunctionTool(fetch_url) 是一个特别优雅的设计——任何普通 Python 函数,一行代码变成 Agent 可调用的工具,不需要装饰器,不需要继承基类。
LangGraph:函数式组合 + 最小封装
# babi-langgraph/babi/agent/builder.py
from langchain.agents.factory import create_agent
agent = create_agent(
model=llm,
tools=tools,
checkpointer=checkpointer,
system_prompt=sys_prompt,
)
一行代码创建一个 Agent——但这一行背后是完全不同的设计哲学。
LangGraph 的 create_agent 不创建"对象",它返回一个 CompiledGraph(编译后的状态图)。Agent 不是一个有状态的实体,而是图的一次执行。状态由 Checkpointer 管理,模型是 LangChain 的 ChatOpenAI,工具是 @tool 装饰的函数。
# LangGraph 的工具注册方式
from langchain_core.tools import tool
@tool
def read_file(file_path: str) -> str:
"""Read the contents of a file."""
try:
path = os.path.expanduser(file_path)
with open(path, "r", encoding="utf-8", errors="replace") as f:
content = f.read()
return content[:50000] if len(content) > 50000 else content
except Exception as e:
return f"Error reading file: {e}"
本质差异:AgentScope 说"我帮你管理 Agent 的生命周期",LangGraph 说"我给你原语,你自己组装"。
🔧 第二层对比:工具系统——“框架内置” vs “全部手写”
这是两个项目代码量差异最大的地方。
代码量对比
| 工具 | babi-agentscope | babi-langgraph |
|---|---|---|
| 读文件 | Read() — 框架提供 |
read_file() — 手写 17 行 |
| 写文件 | Write() — 框架提供 |
write_file() — 手写 14 行 |
| 编辑文件 | Edit() — 框架提供 |
edit_file() — 手写 27 行 |
| 代码搜索 | Grep() — 框架提供 |
grep_files() — 手写 42 行 |
| Glob | Glob() — 框架提供 |
glob_files() — 手写 21 行 |
| Shell 执行 | Bash() — 框架提供 |
shell_execute() — 手写 34 行 |
| 基础工具总代码量 | 0 行 | ~155 行 |
babi-langgraph 有一个完整的 filesystem.py(187 行),专门实现文件系统工具。而 babi-agentscope 只需要从框架导入 Read、Write、Edit、Grep、Glob、Bash。
以 edit_file 为例,看看 LangGraph 版本需要处理哪些细节:
# babi-langgraph/babi/tools/filesystem.py
@tool
def edit_file(file_path: str, old_text: str, new_text: str) -> str:
"""Edit a file by replacing exact text matches."""
try:
path = os.path.expanduser(file_path)
with open(path, "r", encoding="utf-8") as f:
content = f.read()
count = content.count(old_text)
if count == 0:
return f"Error: Text not found in {file_path}."
if count > 1:
return f"Error: Text found {count} times. old_text must be unique."
new_content = content.replace(old_text, new_text, 1)
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
return f"Successfully edited {file_path}"
except FileNotFoundError:
return f"Error: File not found: {file_path}"
except Exception as e:
return f"Error editing file: {e}"
这段代码看起来简单,但它涉及精确文本匹配、唯一性校验、异常处理、路径展开——这些都是 AgentScope 框架帮你做好的。
工程洞察:如果你只关心"差异化能力"(网页抓取、GitHub 集成、搜索),AgentScope 让你只写差异化代码。LangGraph 给你最大的自由度,但也要求你写最多的"基础设施代码"。
🗄️ 第三层对比:会话持久化——“Redis 原生” vs “Checkpointer 抽象”
这是两个框架心智模型差异最大的地方。
AgentScope:Redis 键值存储 + 显式状态管理
# babi-agentscope/babi/web/app.py
from agentscope.app.storage import RedisStorage
storage = RedisStorage(host="localhost", port=6379)
# 启动时 bootstrap:credential → agent → session
async def bootstrap_babi(settings):
async with storage:
# 1. 创建凭证
credential = DashScopeCredential(id="dashscope-default", api_key=...)
await storage.upsert_credential(user_id, credential)
# 2. 创建 Agent 记录(固定 ID,避免重启后失忆)
agent_data = AgentData(name="BabiAgent", system_prompt=sys_prompt, ...)
agent_record = AgentRecord(id="babi-agent", user_id=user_id, data=agent_data)
await storage.upsert_agent(user_id, agent_record)
# 3. 创建默认 Session(仅当不存在时)
existing = await storage.get_session(user_id, agent_id, "default")
if existing is None:
await storage.upsert_session(user_id, agent_id, session_id="default", ...)
AgentScope 的会话模型是显式的三层结构:Credential → Agent → Session。你需要在 Redis 中预先创建这些记录,Chat 接口才能工作。
清除会话也需要直接操作 Redis:
# 清除会话:直接操作 Redis 键
msg_key = f"agentscope:user:{user_id}:session:{session_id}:messages"
await r.delete(msg_key)
session_key = f"agentscope:user:{user_id}:session:{session_id}"
raw = await r.get(session_key)
session_data = json.loads(raw)
session_data["state"]["context"] = [] # 清空对话历史
await r.set(session_key, json.dumps(session_data))
优势:完全掌控数据结构,可以精确清理消息、上下文、摘要。
代价:需要理解 Redis 键的命名规则,代码更"底层"。
LangGraph:Checkpointer 模式 + 图状态快照
# babi-langgraph/babi/agent/builder.py
from langgraph.checkpoint.memory import MemorySaver
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver
# 生产环境:PostgreSQL
checkpointer_cm = AsyncPostgresSaver.from_conn_string(pg_dsn)
# 开发环境:内存
checkpointer = MemorySaver()
# 创建 Agent 时传入 checkpointer
agent = create_agent(model=llm, tools=tools, checkpointer=checkpointer, ...)
LangGraph 的会话管理是一个黑盒 Checkpointer——你不需要知道状态怎么存、怎么取,框架在每次图执行时自动保存快照。
清除会话也更简洁:
# 清除会话:调用 checkpointer 的删除方法
if hasattr(target, 'adelete_thread'):
await target.adelete_thread(session_id)
优势:开发者不需要关心存储细节,PostgreSQL / Memory 一键切换。
代价:你无法精确控制"只清消息不清状态"——Checkpointer 是原子性的。
本质差异:AgentScope 把会话管理当作你的责任(给你 Redis 客户端,你自己操作);LangGraph 把会话管理当作框架的责任(给你 Checkpointer 接口,你不用关心实现)。
🌊 第四层对比:流式输出——“MessageBus 事件驱动” vs “astream_events 直推”
AgentScope:Fire-and-Forget + MessageBus
# AgentScope 的 chat 接口(框架内部 _chat.py)
# 不返回 SSE 流,而是触发后台任务
@chat_router.post("/")
async def chat(request: ChatRequest, ...):
chat_run_registry.spawn(
chat_service.run(
user_id=user_id,
session_id=request.session_id,
agent_id=request.agent_id,
input_msg=request.input,
),
session_id=request.session_id,
)
return ChatTriggerResponse(status="started", session_id=request.session_id)
AgentScope 的 chat 接口是异步触发模式——POST 请求立即返回,Agent 在后台执行,事件通过 InMemoryMessageBus 推送到 GET /sessions/{sid}/stream SSE 长连接。
这意味着前端需要维护两个连接:一个 POST 发消息,一个 GET 收事件。
LangGraph:astream_events 一体化
# babi-langgraph/babi/web/app.py
@app.post("/api/chat")
async def chat(request: ChatRequest, ...):
async def event_generator():
async for event in agent.astream_events(
{"messages": [("user", request.message)]},
config=config,
version="v2",
):
kind = event.get("event")
if kind == "on_chat_model_stream":
chunk = event["data"]["chunk"]
yield f"data: {json.dumps({'type': 'token', 'content': chunk.content})}\n\n"
elif kind == "on_tool_start":
yield f"data: {json.dumps({'type': 'tool_call', 'tool': event['name']})}\n\n"
return StreamingResponse(event_generator(), media_type="text/event-stream")
LangGraph 的 astream_events 是同步流模式——POST 请求本身就是一个 SSE 流,事件从 Agent 内部直接产生,无需中间件。
前端只需要一个连接:POST 发消息,同一个连接收 SSE 事件。
工程洞察:
| 维度 | AgentScope (MessageBus) | LangGraph (astream_events) |
|---|---|---|
| 连接模型 | 2 个(POST + GET SSE) | 1 个(POST = SSE) |
| 事件路由 | MessageBus 分发 | 直接从 Agent 产生 |
| 多进程支持 | ✅ 天然支持(Bus 可跨进程) | ❌ 单进程(流绑定到请求) |
| 断线重连 | ✅ 有 replay log | ❌ 丢失即丢失 |
| 代码复杂度 | 高(需理解 Bus 架构) | 低(直接迭代事件流) |
AgentScope 的 MessageBus 是为企业级场景设计的——多进程部署、断线重连、事件重放。但对于单进程的个人 Agent,LangGraph 的 astream_events 更简单直接。
🏗️ 第五层对比:Web 层——“全权委托” vs “完全自建”
AgentScope:create_app 一把梭
# babi-agentscope/babi/web/app.py
from agentscope.app import create_app
app = create_app(
storage=storage,
message_bus=message_bus,
workspace_manager=workspace_manager,
extra_agent_tools=extra_agent_tools,
title="Babi Agent",
version="1.0.0",
extra_middlewares=[...],
)
# 然后追加自定义路由
@app.get("/api/workspace/tree")
async def workspace_tree(...): ...
AgentScope 的 create_app 帮你创建了完整的 Web 应用——包括 chat 路由、session 管理、credential 管理、model 管理、workspace 管理。你只需要"追加"自定义路由。
代价是:你需要阅读框架源码才能知道它到底注册了哪些路由。
LangGraph:从零搭建
# babi-langgraph/babi/web/app.py
app = FastAPI(title="Babi Agent", version="1.0.0", lifespan=lifespan, ...)
@app.post("/api/chat")
async def chat(request: ChatRequest, ...): ...
@app.get("/api/workspace/tree")
async def workspace_tree(...): ...
@app.get("/api/workspace/file")
async def workspace_file(...): ...
@app.delete("/api/chat/memory")
async def clear_memory(): ...
@app.delete("/api/chat/session")
async def clear_session(...): ...
LangGraph 版本的一切都是你自己写的——chat 接口、工作区 API、会话管理。好处是完全透明,坏处是代码量更大。
📊 全景对比矩阵
| 维度 | babi-agentscope | babi-langgraph |
|---|---|---|
| Agent 创建 | Agent(...) 配置驱动 |
create_agent(...) 一行创建 |
| ReAct 引擎 | 框架内置 | create_react_agent |
| 基础工具 | 框架提供(0 行代码) | 全部手写(~187 行) |
| 自定义工具注册 | FunctionTool(func) |
@tool 装饰器 |
| 会话持久化 | Redis(显式键值操作) | PG/Memory(Checkpointer 黑盒) |
| 流式输出 | MessageBus + SSE 长连接 | astream_events 直推 |
| Web 框架 | create_app 托管 + 自定义路由 |
完全自建 FastAPI |
| 模型接入 | DashScope 原生 SDK | ChatOpenAI 兼容 API |
| CLI 流式输出 | agent.reply_stream() 事件迭代 |
agent.astream_events() 事件迭代 |
| 多进程部署 | ✅ 天然支持 | ❌ 需额外设计 |
| 断线重连 | ✅ replay log | ❌ 不支持 |
| 依赖复杂度 | 高(AgentScope SDK + Redis) | 中(LangGraph + LangChain + 可选 PG) |
💡 真实工程洞察
洞察一:框架的"开箱能力"决定了你的"差异化空间"
AgentScope 帮你做好了 Read/Write/Edit/Grep/Glob/Bash,你只需要写 fetch_url、github_api_request 这些差异化能力。
LangGraph 要求你写所有工具——包括 read_file 这种"基础设施"。
类比 Java:AgentScope 像 Spring Boot(约定大于配置),LangGraph 像裸 Servlet API(自由但啰嗦)。
洞察二:会话管理是框架选型的核心分水岭
AgentScope 的会话模型是显式的——你需要理解 user_id → agent_id → session_id 的三层关系,需要在 Redis 中 bootstrap 数据。这很"重",但也很"透明"。
LangGraph 的会话模型是隐式的——Checkpointer 自动保存/恢复,你只需要一个 thread_id。这很"轻",但你无法精细控制。
一个真实的坑:在 babi-agentscope 中,如果 Agent 启动时不指定固定 ID(AgentRecord(id="babi-agent", ...)),每次重启会生成随机 UUID,导致 Redis 中积累大量孤立 Agent 记录,Session 指向旧 Agent,造成"AI 重启后失忆"。这种坑在 LangGraph 中不存在——因为 Checkpointer 只认 thread_id。
洞察三:流式输出的架构选择影响前端设计
AgentScope 的双连接模型(POST 发消息 + GET SSE 收事件)要求前端维护 EventSource 长连接,处理断线重连和事件重放。
LangGraph 的单连接模型(POST 即 SSE)让前端更简单——一个 fetch + ReadableStream 就够了。
但如果你的 Agent 需要多进程部署(比如 Gunicorn + 多个 worker),AgentScope 的 MessageBus 天然支持跨进程事件分发,而 LangGraph 的 astream_events 绑定在单个请求上,无法跨 worker。
洞察四:Skills 系统是跨框架的"不变量"
两个项目的 Skills 加载逻辑几乎是一比一的翻译:
# 两个项目中几乎相同的代码
def load_all_skills(workspace_path=None):
skills = {}
_load_from_dir(Path.home() / ".agents" / "skills", skills) # 全局
_load_from_dir(Path.home() / ".babi" / "skills", skills) # Babi 专属
if workspace_path:
_load_from_dir(workspace_path / ".qoder" / "skills", skills) # 项目级
return skills
这证明了一个道理:Agent 框架影响的是"基础设施层"(工具、会话、流式输出),而不是"业务设计层"(Skills、提示词、工具分类)。好的业务设计是跨框架的。
🎓 选型建议:你应该选哪个?
选 AgentScope,如果你:
- ✅ 需要一个生产级的 Agent 平台(多进程、断线重连、事件重放)
- ✅ 希望少写基础设施代码,专注差异化能力
- ✅ 需要框架级的权限控制(PermissionContext)和工作区管理
- ✅ 愿意接受框架的"约定"(Redis 必须、bootstrap 流程)
- ✅ 团队有 Python 经验,能读懂框架源码排查问题
选 LangGraph,如果你:
- ✅ 追求最小依赖和最大透明度
- ✅ 希望完全掌控每一个 API 端点和存储细节
- ✅ 需要灵活的持久化选择(Memory / PostgreSQL 一键切换)
- ✅ 偏好函数式编程风格而非面向对象
- ✅ 已经在用 LangChain 生态(ChatOpenAI、@tool 等)
一句话总结
AgentScope 是"Agent 即服务"——你配置,它运行。LangGraph 是"Agent 即代码"——你组装,它执行。
🚦 快速体验
babi-agentscope(需要 Redis)
export DASHSCOPE_API_KEY=your_api_key
git clone https://github.com/javahongxi/babi-agentscope.git
cd babi-agentscope
pip install -e ".[dev]"
# CLI 模式
babi-as
# Web 模式(需要 Redis 运行在 localhost:6379)
babi-as --web
babi-langgraph(零外部依赖)
export DASHSCOPE_API_KEY=your_api_key
git clone https://github.com/javahongxi/babi-langgraph.git
cd babi-langgraph
pip install -e ".[dev]"
# CLI 模式(内存持久化,零依赖)
babi-lg
# Web 模式(可选 PostgreSQL)
export BABI_PG_DSN=postgresql://user:pass@localhost:5432/babi # 可选
babi-lg --web
试试这些
# 代码分析
帮我看看当前项目的目录结构
# 代码搜索
搜索所有包含 @RestController 的文件
# GitHub 操作
查看我的 GitHub 置顶仓库
# 联网搜索
搜索 AgentScope 的最新版本
# Skills 扩展
# 将 .md 文件放到 ~/.babi/skills/ 目录,两个版本自动加载同一份技能
🔗 相关链接
- 📦 babi-agentscope: https://github.com/javahongxi/babi-agentscope
- 📦 babi-langgraph: https://github.com/javahongxi/babi-langgraph
- 📖 AgentScope: https://github.com/agentscope-ai/agentscope
- 📖 LangGraph: https://github.com/langchain-ai/langgraph
- ☁️ 通义千问 (DashScope): https://dashscope.console.aliyun.com
📝 结语
用两个框架写同一个 Agent 之后,最大的收获不是"谁更好",而是理解了它们各自的设计权衡:
- AgentScope 用"重框架"换"轻业务代码"——你少写 187 行工具代码,但要多理解一套 Redis 键值模型
- LangGraph 用"轻框架"换"重业务代码"——你多写所有基础设施,但获得完全的透明度和控制力
如果你正在:
- 🎯 深入理解 Python AI Agent 框架的设计哲学差异
- 🚀 在 AgentScope 和 LangGraph 之间做技术选型
- 🏗️ 搭建自己的 Coding Agent,需要一套经过验证的架构参考
- 🤔 好奇"同一个需求用两个框架实现,代码到底差多少"
Star ⭐ 两个仓库,对比运行,你会对 Agent 框架有全新的理解。
git clone https://github.com/javahongxi/babi-agentscope.git
git clone https://github.com/javahongxi/babi-langgraph.git
© hongxi.org | 同一个 Agent,两个框架——理解差异,才能做出选择
更多推荐




所有评论(0)