主题
系统整体架构
本章目标:
- 在脑中建立 a-cdm 完整的服务/容器/端口拓扑,知道每个进程是谁、监听什么、归谁管
- 看懂三条关键数据流(资料入库 / Agent 消费知识 / PMO 维护知识)与一条 AI 任务流
- 理解 Caddy 网关 + forward_auth 如何把"前端、控制面、引擎、ERP 服务"统一在一个入口下
TL;DR
a-cdm 生产环境是一组接在 platform-net Docker 网络上的容器,统一由 Caddy 网关(platform-gateway)在 :80/:443 对外。前端 /a-cdm/*、控制面 /api/acdm/*、引擎 /api/deerflow/* 与 /api/langgraph/*、ERP /api/erp/* 各走一条反代规则,除登录入口与前端静态外全部经 forward_auth 鉴权。数据层是一个 PostgreSQL 共享实例按 schema/DB 隔离,文件层是 Nextcloud WebDAV,知识层是独立 git 仓 a-cdm/llm-wiki。
Overview(为什么是这套拓扑而不是单体)
a-cdm 大可以做成"一个 FastAPI 进程塞下所有东西"。它没有,原因是三类职责的伸缩特性和故障域完全不同:
- Agent 引擎是长任务、吃内存、依赖外部 LLM,故障时会拖很久(rebuild 前要先腾内存,
CLAUDE.md部署铁律); - 控制面是短事务、强一致、扛鉴权与数据治理,必须稳;
- 前端是无状态静态资源,可独立 build/部署。
把它们拆成独立容器,各自有独立的部署节奏(cd-poll.sh 按 diff 路径分别部署)、独立的故障隔离(引擎 OOM 不会拖垮鉴权)、独立的降级开关(RUNTIME_BACKEND=langgraph 只切引擎运行时)。代价是引入了网关与服务间认证的复杂度,这正是后面"耦合契约"和"鉴权授权"两章要讲的内容。
Architecture:服务/端口拓扑
端口分配是规格级约定(openspec/specs/architecture/spec.md:86),dev 与 prod 有意做了部分错开以避让本机已有服务:
| 服务 | 进程 | dev 端口 | 生产对外路径 | 归属部分 | Source |
|---|---|---|---|---|---|
| PostgreSQL | postgres:16 | 5435(避让 5433/5434) | platform-db(内网) | 基础设施 | docker-compose.dev.yml:48-50 |
| acdm-backend | FastAPI/uvicorn | 8002 | /api/acdm/* /api/acdm/auth/* | 控制面 | acdm-backend/app/main.py |
| deer-flow Gateway | uvicorn app.gateway.app | 8001 | /api/deerflow/* + bare `/api/models | skills | ...` |
| deer-flow Runtime | Aegra 0.9.4(aegra serve) | 2024 | /api/langgraph/* | 引擎 | deer-flow/aegra.json |
| deer-flow Redis | redis:7 | 内网 6379 | — | 引擎 | deploy/prod/deer-flow-backend/compose.yml |
| acdm-frontend | Next.js 16(pnpm dev) | 3000 | /a-cdm/* | 前端 | deer-flow/frontend/package.json |
| demoo_service | Flask | 8088 | /api/erp/* | demoo_service | demoo_service/server.py |
| Caddy 网关 | caddy:2-alpine | —(本地走 Caddyfile.dev 离线校验) | :80 / :443 | 基础设施 | openspec/specs/architecture/spec.md:88-94 |
本地开发刻意精简:docker-compose.dev.yml 只起 postgres(5435),Caddy/Nextcloud/Redis/auth-mock 都不起 —— 决策背景写在文件头注释 docker-compose.dev.yml:3-18:生产已有 platform-gateway/platform-db/auth-api,本地 deer-flow checkpointer 用 SQLite 即可,auth 走 Authorization: Bearer dev-token 直通。
数据库是一个 PostgreSQL 实例多 DB/schema 隔离(openspec/specs/architecture/spec.md:405 数据隔离要求):acdm-backend 业务数据落 acdm schema、Aegra 落 aegra DB、LangGraph fallback 模式落 langgraph_server DB。这一点在"Aegra 运行时"章会展开。
网关路由与 forward_auth 矩阵
所有外部流量先过 Caddy。Caddy 的核心职责有两个:路径反代和 forward_auth 鉴权前置。它把请求先打到 acdm-backend 的 /auth/verify,验证通过(回 200 + X-Auth-* 头)才放行到真正的上游:
| 路径 | 上游 | forward_auth | 备注 | Source |
|---|---|---|---|---|
/api/acdm/auth/* | acdm-backend:8002 | ❌ | 登录流本身不能要鉴权 | openspec/specs/architecture/spec.md:108 |
/api/acdm/* | acdm-backend:8002 | ✓ | strip /api/acdm 前缀 | :109 |
/api/deerflow/* | deer-flow-gateway:8001 | ✓ | strip /api/deerflow | :110 |
/api/langgraph/* | deer-flow-langgraph:2024 | ✓ | SSE 端点,strip 前缀 | :111 |
/api/erp/* | demoo_acdm_agent:8088 | ✓ | 2026-04-28 harden-public-edge 起加鉴权 | :112 |
/api/meeting/* | meeting-api:8000 | ✓ | v0.2.1 legacy | :113 |
/a-cdm/* | acdm-frontend:3000 | ❌ | 前端静态由 proxy.ts 自己守卫 | :114 |
| Fallback | 302 → /a-cdm/ | — | 兜底重定向 | :115 |
:80 与 :443 两块路由表完全镜像复制维护(规格 :104 明确,因为 Caddy 不用 import 而是复制两份)。域名 HTTP 访问 301 跳 HTTPS;IP 路径在 :80 保留作应急回滚兜底,但因 COOKIE_SECURE=True,HTTP IP 路径业务实际不可用(预期,见 openspec/specs/architecture/spec.md:127-130)。
Data Flow:四条关键链路
openspec/specs/architecture/spec.md:48-74 定义了三条知识链路,再加上一条 AI 任务流,共四条:
- 链路 A 资料 → 知识库(
:52-60):顾问上传 → 业务后端按资料类型路由到对应 compiler → compiler 用 pygit2 写llm-wiki的raw/<类型>/+ 编译产物wiki/...→ push,GitLab UI 自动呈现。 - 链路 B Agent 消费知识(
:62-67):用户提问 → Lead Agent 触发 skill → 通过 MCP filesystem 读llm-wiki,不经 HTTP API → LLM 自主导航 + 引用 + 溯源。 - 链路 C PMO 维护知识(
:69-74):PMO 在 GitLab Web IDE / 前端 WikiEditor 改 markdown → commit+push → CI job 刷新 index。 - 链路 D AI 任务流(本仓实现):前端发起长任务(BA 报告 / 会议聚合)→ acdm-backend 用 LangGraph SDK 在引擎建 thread/run → 前端通过
/api/langgraph/*SSE 直连引擎流式拿结果,断线可 reattach(详见"AI 消息流"章)。
Configuration(本章相关)
| 配置 | 值 | 含义 | Source |
|---|---|---|---|
| 生产 host 监听 | :80 + :443 | HTTP 域名 301→HTTPS,IP 兜底 | openspec/specs/architecture/spec.md:90-94 |
ACDM_PUBLIC_HOSTS | 三 host 白名单(域名为主) | 公网边缘加固 | CLAUDE.md:14 |
| dev 数据库端口 | 5435 | 避让本机已有 5433/5434 | docker-compose.dev.yml:48 |
| 数据隔离 | 单 PG 实例多 DB/schema | acdm/aegra/langgraph_server | openspec/specs/architecture/spec.md:405 |
Common Pitfalls / 实战 Tips
- dev 与 prod 同一条鉴权路径,无后门(
CLAUDE.md:14)。本地不起 Caddy 不代表绕过鉴权 —— dev 由前端proxy.ts解 cookie 注入 header 顶替 forward_auth。 - 改 basePath 必须重 build:前端
/a-cdm前缀是 Next.js 编译期静态替换,不是运行时配置(CLAUDE.md:15,lessons L26)。 - :80 / :443 路由表必须同步改两份:漏改一块会导致 HTTP/HTTPS 行为不一致,规格
:104明确要求镜像。
References
openspec/specs/architecture/spec.md:24-135— 7 层分层、三条数据流、端口与服务拓扑规格(本章主源)docker-compose.dev.yml:1-58— 本地最小开发栈与精简决策背景deploy/prod/Caddyfile.keycloak-v1— 生产网关路由与 forward_auth 实现CLAUDE.md:14-15— 鉴权路径无后门、basePath 编译期约束deploy/prod/deer-flow-backend/compose.yml— 引擎三容器(gateway/runtime/redis)编排acdm-backend/app/main.py— 控制面进程入口
Related Pages
| Page | Relationship |
|---|---|
| 项目概览与产品愿景 | 本章是该章"三大组成"的服务级展开 |
| 请求生命周期与服务拓扑 | 本章的拓扑在该章细化为单请求的逐跳链路 |
| 鉴权与授权双层体系 | 本章 forward_auth 矩阵的鉴权机制在该章详解 |
| Caddy 网关与生产路由拓扑 | 本章路由表在该章逐条对应 Caddyfile 实现 |
| 本地开发环境搭建 | 本章 dev 拓扑在该章落到启动步骤 |