主题
acdm-backend 控制面架构
本章目标:
- 看懂
create_app()工厂:中间件栈、异常契约、30+ router 如何串起来- 掌握
_lifespan启动序列每一步在初始化什么、为什么是这个顺序- 理解
api → service → repository → model四层铁律及其禁忌
TL;DR
acdm-backend 是 a-cdm 的"控制面":一个 FastAPI 应用,create_app()(acdm-backend/app/main.py:214)装配 5 道中间件(CORS、SlowAPI 限速、reversible actor、MCP service token、audit log)、3 个统一异常处理器(429/业务 400/全局 500)、30+ router 和一个 /mcp 子应用。_lifespan(main.py:134)按"AI 注册表 → DB event recorder → AI 抽取监听 → 启动恢复 → 健康探测 → 后台执行器 → BA 报告调度器"顺序启动。代码严格分四层:api(契约) → service(业务+事务) → repository(纯 CRUD) → model(ORM)。
Overview(为什么控制面要这样组织)
引擎(deer-flow)是通用 Agent 框架,不懂"项目""会议""成员"这些业务概念,也不该承担鉴权、数据治理、可逆操作。acdm-backend 就是这一层"业务适配 + 安全边界 + 编排中枢":
- 对前端:提供项目/会议/知识库/BA 报告等业务 CRUD;
- 对引擎:既是 forward_auth 的 verify 提供方,又是 Agent 工具的 BFF 后端,还是 MCP 知识源;
- 对自身:统一鉴权、统一异常契约、可逆操作框架。
它把这些职责用"工厂装配 + lifespan 编排 + 四层分层"组织起来,目的是让每类关注点(中间件、异常、启动、业务)各归其位、互不缠绕。
Architecture:中间件栈
create_app() 按顺序 add_middleware,执行时后注册的先进(FastAPI/Starlette 洋葱模型):
| 中间件 | 作用 | Source |
|---|---|---|
CORSMiddleware | 跨域(前端 dev origin) | acdm-backend/app/main.py:222-228 |
SlowAPIMiddleware | /auth/login 速率限制(30/min,防扫描/DoS) | acdm-backend/app/main.py:230-233 |
reversible_actor_context | 从 JWT 设 actor=direct(可逆操作上下文) | acdm-backend/app/main.py:239-250 |
mcp_service_auth | 校验 /mcp/* 与 /agent-tools/* 的 X-ACDM-Service-Auth | acdm-backend/app/main.py:255-268 |
audit_log | 请求日志 + API 审计表记录 | acdm-backend/app/main.py:270-295 |
reversible_actor_context 是 a-cdm 特有的:它把当前用户身份注入 ContextVar(set_direct_actor),后续可逆操作框架据此记录"是谁动了数据"(详见可逆操作章)。mcp_service_auth 拦截服务间调用路径,这是引擎回调控制面 BFF 的认证关口(详见耦合契约章)。
Components:统一异常契约
三个 @app.exception_handler 把所有错误收敛成一致的 ApiResponse 形状:
| 异常 | HTTP | 响应体 | Source |
|---|---|---|---|
RateLimitExceeded | 429 | {code,data:null,message} + Retry-After 头 | acdm-backend/app/main.py:297-315 |
BusinessException | 400 | {code, data:null, message} | acdm-backend/app/main.py:317-330 |
Exception(兜底) | 500 | 统一格式,不泄露栈 | acdm-backend/app/main.py:331-337 |
这就是为什么授权依赖抛 BusinessException(40401) 能统一变成 HTTP 400 + {code:40401} ——前端只需按 code 判业务错误,不用解析多种错误形状。/health 端点(main.py:339)供 docker healthcheck 用。
Data Flow:_lifespan 启动序列
启动顺序不是随意的——后面的步骤依赖前面初始化好的状态:
init_registry()(main.py:153):AI 角色注册表 fail-fast 校验——结构错直接让进程起不来,这必须最先做。install_db_event_recorder()(main.py:159):注册 SQLAlchemy ORM 事件监听,可逆操作框架的基础,必须在任何业务写之前装好。install_ai_extract_listener()(main.py:166):给MeetingFile装 after_insert hook,文件入库即触发 AI 抽取。recover_pending_on_startup()(main.py:170):扫 status=pending 的未完抽取任务重新入队——容错重启(非阻塞,失败只 log)。probe_streaming_roles()(main.py:178):探测 streaming AI 角色健康(非阻塞,main.py:180失败只 log)。start_background_executor()(main.py:187):后台任务执行器。run_sync_scheduler()(main.py:191):BA 报告状态监控定时任务,asyncio.create_task常驻。
关闭序列对称收尾:stop_background_executor() + scheduler_task.cancel()。
Implementation:四层铁律
acdm-backend/README.md:9-40 把分层定为铁律,每层职责单一:
| 层 | 目录 | 职责 | 禁忌 |
|---|---|---|---|
| api | app/api/ | FastAPI router + Pydantic 契约 + @Depends 鉴权 | ❌ 直接 import repository(跨过 service) |
| service | app/services/ | 业务逻辑 + 事务边界 + BusinessException | ❌ 调 api/* 或返 FastAPI Response |
| repository | app/repository/ | 纯 SQLAlchemy CRUD | ❌ await session.commit()(commit 只在 service) |
| model | app/model/ | ORM 类,绑 acdm schema | ❌ 写业务逻辑 |
样板:api/project_router.py + services/project_service.py + repository/project_repo.py + model/models.py:Project 是一套干净分层,新增资源直接对照(acdm-backend/README.md:38-40)。commit 只在 service 层发生这条很关键——它保证事务边界与可逆操作的 business_transaction 对齐(详见可逆操作章)。
router 注册在 create_app() 末段(main.py:379-569),约 30 个业务/admin/agent-tools router + app.mount("/mcp", get_mcp_app())(main.py:560)挂 FastMCP 子应用 + auth_router/verify_router/health_router。agent_tools_* router 走共享 X-ACDM-Service-Auth 网关(main.py:566-569),由 mcp_service_auth 中间件保护。
Common Pitfalls / 实战 Tips
- lifespan 步骤顺序不能乱:
install_db_event_recorder必须在业务写之前;init_registry必须最先(fail-fast)。改 lifespan 注意依赖。 - repository 不 commit:在 repo 里写
session.commit()会破坏 service 的事务边界,也会让可逆操作框架记错事件边界。 - 新增 router 别忘鉴权 Depends:api 层不挂
require_*依赖等于裸奔;agent-tools 路由要靠mcp_service_auth中间件覆盖前缀。 - 中间件顺序是洋葱:
audit_log最后注册=最外层,能记到所有请求;改中间件注册顺序会改变包裹关系。
References
acdm-backend/app/main.py:134-212—_lifespan启动/关闭序列(本章主源)acdm-backend/app/main.py:214-337—create_app工厂:中间件栈 + 异常处理器acdm-backend/app/main.py:379-569— 30+ router 注册 +/mcp挂载acdm-backend/README.md:9-40— 四层铁律与禁忌(线索,已回源核实)acdm-backend/app/database.py— async SQLAlchemy 引擎 + acdm schema 绑定acdm-backend/app/config.py:17-242— 控制面配置 Settings
Related Pages
| Page | Relationship |
|---|---|
| 仓库结构与代码地图 | 本章是该章 acdm-backend 分区的机制展开 |
| 鉴权与授权双层体系 | 本章中间件/router 挂载该章的授权依赖 |
| 可逆 DB 操作与存储层 | 本章 reversible_actor_context + install_db_event_recorder 在该章详解 |
| 审计、SSE 与后台任务 | 本章 lifespan 启动的 background_executor/scheduler 在该章详解 |
| 知识源 MCP 与 Agent Tools BFF | 本章 /mcp 挂载 + mcp_service_auth 在该章展开 |