Skip to content

Aegra 运行时与 LangGraph

本章目标:

  1. 理解为什么 a-cdm 2026-04-25 把 Agent Runtime 从 LangGraph Server 切到 Aegra 0.9.4,容器名为何保留
  2. 看懂 RUNTIME_BACKEND 三态切换:aegra / langgraph(postgres)/ langgraph(memory 应急)
  3. 掌握两套图注册(aegra.json 文件路径 vs langgraph.json 模块路径)与 wrapper 的作用

TL;DR

deer-flow-langgraph 容器(名字保留)实际跑的 Agent Runtime 由 RUNTIME_BACKEND env 决定:aegraaegra serve(Aegra 0.9.4,MIT,数据落独立 aegra DB),langgraphlanggraph_api.cli postgres(数据落 langgraph_server DB,回滚通道),LANGGRAPH_STORAGE_MODE=memory → 内存模式(应急,重启丢数据)。Aegra 只接受 ./file.py:graph 文件路径,所以 aegra_graphs/ 下有一组 thin wrapper 把 harness 工厂 re-export;langgraph.jsonmodule:func 模块路径作回滚通道。两套 server 共用同一份 acdm_auth.py Auth。

Overview(为什么从 LangGraph Server 换到 Aegra)

LangGraph Server 是 LangChain 官方的 Agent 运行时,但它的生产部署能力(LangSmith Deployments)是商业产品。a-cdm 2026-04-25 切到 Aegra 0.9.4(CLAUDE.md:14),核心动机是 Aegra 是 MIT 开源、兼容 LangSmith Deployments 协议、可自托管,避免商业依赖。

切换的工程难点是不能推翻已有架构:前端、acdm-backend SDK 调用、Caddy 路由全是按 deer-flow-langgraph:2024 写的。a-cdm 的解法是"容器名/端口/路径全保留,只换容器内进程":RUNTIME_BACKEND env 在容器启动命令里分支,默认 langgraph 作回滚通道,生产 .env 设 aegra。这样切换/回滚只改一个 env + --force-recreate,上层零感知。

Architecture:三态运行时切换

容器启动命令是一段 shell 分支(deploy/prod/deer-flow-backend/compose.yml:81-97):

RUNTIME_BACKEND命令数据落用途Source
aegrauv run aegra serve --port 2024platform-db 的 aegra DB生产主路径(2026-04-25 起)compose.yml:84-86
langgraph(默认)langgraph_api.cli --runtime-edition postgreslanggraph_server DB回滚通道compose.yml:91-96
langgraph + LANGGRAPH_STORAGE_MODE=memorylanggraph dev(内存)内存,重启丢应急(DB 也挂时)compose.yml:87-89

env 注入(compose.yml:104-117):Aegra 读 DATABASE_URL(默认 postgresql://acdm:acdm@platform-db:5432/aegra)+ REDIS_URL;langgraph postgres 模式读 DATABASE_URI + REDIS_URI两套 DB 物理隔离——Aegra 用 aegra DB,langgraph 用 langgraph_server DB,切换不会污染对方数据(compose.yml:107-111)。compose.yml:114 的默认值是 langgraph,但生产 .env 实际设 aegra

容器还 bind mount 了 /data/erp-wiki/kingdee(compose.yml:122-126):因为 config.yaml 的 sandbox mount 把 /mnt/kingdee 映射到 host /data/erp-wiki/kingdee,而 sandbox bash 在 langgraph 容器进程内 subprocess 跑,host_path 必须容器内可达,所以做同名 bind mount。

Components:两套图注册 + wrapper

Aegra 和 LangGraph CLI 的图加载语法不同,这是 wrapper 存在的根本原因:

aegra.jsonlanggraph.jsonSource
图路径语法./file.py:variable(文件路径)module.path:func(模块路径)deer-flow/backend/aegra.json:6-12 / langgraph.json:8-14
lead_agent./aegra_graphs/lead_agent.py:graphdeerflow.agents:make_lead_agent同上
pipelines./aegra_graphs/weekly_report.py:graphdeerflow.pipelines.*:make_pipeline同上
auth./packages/.../acdm_auth.py:auth同一个aegra.json:13-15 / langgraph.json:18-20
checkpointer(Aegra 自管)./.../checkpointer/async_provider.py:make_checkpointerlanggraph.json:15-17

Aegra 0.9.4 的 graph loader 只接受文件路径(aegra_graphs/lead_agent.py:1-13 注释明确),所以 deer-flow/backend/aegra_graphs/ 下有 5 个 thin wrapper:

python
# 摘自 deer-flow/backend/aegra_graphs/lead_agent.py:15
from deerflow.agents import make_lead_agent as graph  # Aegra classify_factory 检测 callable+RunnableConfig

lead_agent.py 直接 re-export 工厂;ba_report.py:13-17 包一层 def graph(config)make_step_runner(因为 BA 报告是单步执行器,Hybrid 模式由 acdm-backend 编排,无 interrupt)。两套 server 通过 RUNTIME_BACKEND 共存,LangGraph CLI 路径是回滚通道。

Data Flow:控制面读 Aegra owner_sub

acdm-backend 和引擎 Gateway 都需要按 owner_sub 查 Aegra 的 thread 表(做 thread 归属校验)。deer-flow/backend/app/gateway/deps.py_resolve_aegra_db_url()(deps.py:24-39)从 DATABASE_URI 推导 aegra DB 的 asyncpg URL:去掉 +asyncpg 标记、把库名换成 aegra(正则 re.sub(r"/[^/?]+(\?.*)?$", r"/aegra\1", base)),与 acdm-backend 的同名逻辑镜像,保证两服务指向同一 aegra DB。

  • aegra_pool_ctx(app)(deps.py:43-81):初始化 app.state.aegra_pool(asyncpg pool,min 1 max 4)。fail-safe:env 未设或建池失败时 aegra_pool=None,thread owner lookup 降级(不崩)。
  • aegra_checkpointer_ctx(app)(deps.py:85):初始化 AsyncPostgresSaver,用于 fork 端点读写 Aegra checkpointer。

run_aegra.py(run_aegra.py:1-20)是本地启动脚本:Windows 上 psycopg 需 WindowsSelectorEventLoopPolicy,必须在任何 async 代码前设;默认 DATABASE_URL/LANGGRAPH_STORE_POSTGRES 指向本地 aegra 库,AEGRA_CONFIG 指向 aegra.json,然后 uvicorn.run("aegra_api.main:app")

Configuration(本章相关)

env默认含义Source
RUNTIME_BACKENDlanggraph(compose)/ aegra(prod .env)运行时选择deploy/prod/deer-flow-backend/compose.yml:114
LANGGRAPH_STORAGE_MODEpostgresmemory=应急内存模式(丢数据)compose.yml:117
DATABASE_URL(Aegra)...platform-db:5432/aegraAegra 数据库compose.yml:108
DATABASE_URI(LG)${DATABASE_URI}langgraph postgres 模式compose.yml:103
AEGRA_CONFIG/app/backend/aegra.jsonAegra 图/auth 配置compose.yml:107

Common Pitfalls / 实战 Tips

  • 容器名 deer-flow-langgraph 是误导但有意保留:它跑的可能是 Aegra。判断真实运行时看 RUNTIME_BACKEND 而非容器名(CLAUDE.md:14)。
  • 切 RUNTIME_BACKEND 要 --force-recreate:改 .envrestart 不重读,必须 force-recreate 或等 cron 重启(compose.yml:15-22)。
  • Aegra 与 langgraph 数据不互通:从 langgraph 切 aegra 后,旧 langgraph_server DB 里的 thread 不会出现在 aegra DB——backfill 是单独的 change(openspec/changes/backfill-legacy-threads)。
  • LANGGRAPH_STORAGE_MODE=memory 只在 langgraph 模式生效且丢数据:这是"DB 也挂"时的最后兜底,不是常规选项(compose.yml:87-88)。
  • aegra_pool fail-safe:aegra_pool=None 时 thread owner lookup 降级而非崩(deps.py:47),排查 thread 归属问题先看 pool 是否建成。

References

  • deploy/prod/deer-flow-backend/compose.yml:76-130 — RUNTIME_BACKEND 三态切换(本章主源)
  • deer-flow/backend/aegra.json:1-16 — Aegra 图注册 + auth(文件路径形式)
  • deer-flow/backend/langgraph.json:1-21 — LangGraph 图注册(模块路径,回滚通道)
  • deer-flow/backend/aegra_graphs/lead_agent.py / ba_report.py — Aegra wrapper
  • deer-flow/backend/app/gateway/deps.py:24-86 — aegra_pool / checkpointer 初始化 + DSN 推导
  • deer-flow/backend/run_aegra.py:1-45 — 本地 Aegra 启动脚本
PageRelationship
系统整体架构本章是该章 deer-flow-langgraph 容器的运行时细节
鉴权与授权双层体系本章两套 server 共用该章的 acdm_auth.py Auth
控制面与引擎的耦合契约本章 aegra_pool/owner_sub 查询是该章耦合的一部分
子代理委派与确定性 Pipeline本章 aegra_graphs 注册的 pipeline 在该章详解
环境变量、凭据与降级开关本章 RUNTIME_BACKEND 是该章降级开关之一

公司内部参考 · 由 claude-wiki-gen 基于源码自动生成的二次分析