从零构建企业级 AI Agent 平台
从零构建企业级 AI Agent 平台(一):架构全景与技术选型
本文是《私有化企业 AI 中台从 0 到 1》技术专栏的第一篇。我们花了大约一年时间,把一个 “能跑的 RAG 问答原型” 打磨成一个真正可治理、可运营、可多渠道交付的企业级 AI Agent 平台。这个系列会从架构、检索、Agent、Workflow、扩展生态、权限、安全、质量评估、多渠道到部署踩坑,把过程中的关键决策和实现细节全部展开。
本篇目标
:一张图讲清这个平台由哪些模块构成、一个请求进来后是如何被处理的,以及我们在技术选型上做过的关键取舍。
一、为什么我们要自建,而不是用现成的 SaaS?
动手之前,我们花了很多时间去评估市面上的方案:通用 LLM 应用平台(Dify、Coze)、知识库引擎(RAGFlow)、云厂商的 AI 平台。结论是 “能用,但有几个无法回避的问题”:
-
数据主权与合规:知识库内容、对话记录、人员组织信息都要留在企业内部,不能接受第三方托管。
-
治理纵深:不是 “能做出一个会回答的机器人” 就够,而是要回答 “谁能用这个 Agent、谁能看这份知识、谁改过什么配置、这条回答依据是什么”—— 这类问题需要一套完整的权限、审计与可追溯体系,通用平台的免费 / 社区版本普遍做得浅。
-
绑定存量系统:我们已经有企业微信、内部 OA 系统、统一的 SSO 身份源、HR 组织数据 ——AI 能力必须嵌进这些真实业务链路,而不是在一个孤岛里跑。
-
质量可运营:RAG 上线只是开始,回答质量需要被持续采样、评估、告警、优化,而不是靠用户口头反馈。
所以我们的定位从一开始就不是 “做一个更漂亮的问答机器人”,而是做企业内部 AI 应用从知识接入、Agent 编排、权限控制、多渠道交付到运营评估的一体化平台。
二、总体架构

项目采用模块化单体(Modular Monolith),而不是微服务。整体分层如下:
一句话版本:所有 HTTP / 渠道请求先进中间件链(日志、请求上下文、授权),再落到对应的 FastAPI Router,Router 只做 “契约编排 + 权限校验”,真正的复杂业务逻辑下沉到领域 Service,Service 再访问数据库、向量库、缓存、对象存储和外部模型。
这里有一个值得强调的架构纪律:模型不能决定授权。所有检索、工具调用、工单提交等副作用,都必须由程序在 Service 层校验权限、状态和输入之后才执行。
三、核心模块地图
先用一张图把 “平台由哪些模块构成"讲清楚 —— 核心是一条” 知识 → 检索 → Agent → Workflow" 的能力流水线,外围再挂上扩展生态、治理支撑、交付渠道和数据底座:
各模块职责与依赖如下:
| 模块 | 职责 | 主要依赖 |
|---|---|---|
| 知识采集 | 上传 / URL / 问答对 / 数据库 / 运维记录 → 解析、OCR、切分、向量化 | parser、OCR/VLM、embedding、对象存储 |
| 检索与 RAG | 多路召回、融合、重排、证据信号、上下文构建、grounded 合成 | 向量库、ES、MySQL、LLM、Redis |
| Agent 运行时 | 画像分发、工具编排、流式输出、usage 统计 | 检索、LLM、Skill、MCP、集成 |
| Workflow 引擎 | 可视化编排 DSL、发布快照、版本恢复、执行 | 检索、LLM、HTTP |
| Skill / MCP | 能力包仓库;外部工具注册 / 发现 / 调用 | DB、对象存储、HTTP |
| 连接器 | 外部系统地址 / 凭据 / ACL 的统一入口 | 加密凭据、统一外呼策略 |
| 权限治理 | RBAC + 统一资源 ACL + 权限申请审批 | 权限目录、组织数据 |
| 质量体系 | 在线 QC 采样 + 离线 Evaluation 数据集评测 | LLM、Agent 输出 |
| 多渠道 | Web Client / 发布页 / API / 企微 | Agent 运行时、连接器 |
| 可观测 | 审计日志、使用统计、检索指标 | MySQL |
| 补充模块(项目当前已落地) | 职责 | 对应代码位置 |
| — | — | — |
| 客户端工作台 | 企业内 AI 客户端:会话管理 / 命令面板 / 消息来源 / 附件 | frontend/src/pages/clientApps/ |
| 多语言检索 | 语言识别 → 本地化切分 → 检索语言过滤 → 实体级翻译 hydrate | app/services/retrieval/、i18n_content |
| 检索工程韧性 | 单路超时互等、预算降级、固定报错短语兜底、查询扩展 | app/services/retrieval/ |
| VLM 与复杂文档理解 | 图片→文本描述、装饰性图片过滤、Visio / 嵌套表格 / Excel / PPTX 解析 | app/services/vlm/、processing/ |
| AgentOps 运营可观测 | 检索指标、命中详情、质量面板、QC / 检索 AB 实验 | app/services/observability/ |
| 外部 Agent 与共享会话 | 外部 Agent 接入、会话共享与分发 | external_agents、shared_sessions |
| 知识版本管理 | 知识库 / 文档版本快照与恢复 | knowledge_version |
| 标签体系与平台域 | 标签 / 标签维度 / 分类 / 平台域 | tag / tag_dimension / category |
规模口径(项目当前实现):API Router 42 个、领域 Service 24 个、数据模型 40 个、前端页面组件 74 个、测试文件 142 个。文首架构图与 4.3 节中的 “约 40 个 Router / 约 60 个页面组件” 可按此更新。
四、技术选型与取舍
先说结论:没有银弹,每一层选型都是 “团队熟悉度 × 生态成熟度 × 运维成本” 三方权衡的结果。下面按层展开,每层都交代清楚 “我们选了什么、为什么选它、放弃了什么”。
4.1 后端框架:Python + FastAPI
先看我们在后端框架上比较过的几个方案:
| 方案 | 优势 | 劣势 | 结论 |
|---|---|---|---|
| Flask | 轻量、灵活、上手快 | 无异步原生支持,Pydantic 校验 / OpenAPI / 依赖注入都要手动补 | 骨架太薄,企业级要手动补齐的东西太多 |
| Django + DRF | 全家桶、自带 Admin、生态完整 | ORM 偏同步,异步支持是后补的;与 “异步 AI 链路” 心智不匹配 | 重,且 AI 时代维护两套心智成本高 |
| FastAPI | 异步原生、Pydantic v2 校验、自动 OpenAPI、AI 生态无缝 | 框架较新,团队需约束写法避免自由度过高 | 选它 |
| Spring Boot | 企业级成熟、类型安全 | 与 Python AI 生态割裂,等于养两套语言栈 | 没有强 Java 团队不划算 |
最终落在 Python 3.11 + FastAPI,理由可以总结为三点:
-
AI 生态是最强磁铁:LangChain、sentence-transformers、OpenAI SDK 这些库在 Python 里是第一公民,选 Java/Go 意味着整个 RAG/Agent 栈要么绕一大圈、要么自研。
-
异步是这条链路的刚需:检索(向量库 + ES + 数据库并发召回)、LLM 流式输出、SSE 推送,本质都是高并发 IO,FastAPI 的原生异步模型和这条链路的心智完全一致。
-
契约即文档:Pydantic 校验 + 自动 OpenAPI(
/docs就是现成的接口文档),配合我们 “API → 领域 Service → Model/Integration” 的分层,后端 42 个 Router(项目当前实现)的边界可以靠类型与 schema 约束住。
配套选型还有几个值得记录的细节:
-
pydantic-settings:配置即类型,
.env一个文件驱动开发 / 生产 —— 本地与生产差别只改取值,不用维护多套 env 文件。 -
SQLAlchemy 2 异步:API 请求走
async_session,启动期迁移走同步引擎,避免启动迁移与业务连接互相干扰。 -
踩坑实录:
pymysql版本要锁定<1.2.0——1.2.0 改了ping()的默认参数,与 aiomysql 异步适配层不兼容(SQLAlchemy 的已知 issue)。这种 “上游小版本改默认值” 的坑,企业项目里几乎每年都会遇到一次。
取舍点:
模块化单体而非微服务,本质是 “匹配团队体量” 的选择
。我们团队体量较小、平台以内网使用为主、并发量不高,单体在当下的规模下完全够用,部署、调试、发布都更省事;而微服务带来的收益 —— 独立扩缩容、故障隔离、独立发布、多团队并行 —— 在 “单团队 + 低并发” 的现实下几乎用不上,背上的却是网络开销、分布式一致性、运维复杂度这些实打实的成本。所以我们没有跟风微服务,而是用
领域 Service + 明确的模块边界
把单体组织清楚,代价是单进程内的并发与资源隔离需要靠配置纪律(如检索并发上限、QC 并发上限)来约束,这部分会在部署篇细讲。如果未来并发量或团队规模上去了,现在的模块边界已经为演进到微服务留好了余地。
4.2 数据层:一主库 + 多存储,而不是 “一个库装下所有”
在展开之前先说一个贯穿本节的原则:数据层的大部分选择不是因为 “某个组件更先进”,而是因为我们直接复用了团队已有的技术栈—— 团队已经采购 / 部署了 MySQL、Doris、Elasticsearch、Redis 和对象存储,“能复用就复用、少引入新中间件” 是比 “横向比出最优” 更现实也更重要的一条准则。下面每一项我会尽量把 “技术本身的特性” 和 “现实原因(团队已有)” 分开说。
MySQL 主库承载所有业务实体(用户、Agent、知识库、文档、工单、审计……)—— 这是团队存量数据库,直接复用。我们保留了 SQLite / PostgreSQL / SQL Server 等多方言驱动以应对不同环境,但生产以 MySQL 为准 —— 这里要诚实记录一个坑:多方言支持会带来 “默认行为不一致”(比如布尔与时间戳语义),这是技术债务清单里的 P0 项,需要靠统一的模型与迁移纪律收敛。
向量检索我们比较过专用向量库,最终没有一开始就引入:
| 方案 | 定位 | 结论 |
|---|---|---|
| Chroma | 轻量嵌入式向量库 | 默认选项:小规模 / 本地开箱即用、零运维,作为开发 / 演示环境的向量基线 |
| Doris | 生产向量库 | 现实原因:团队已采购并运维 Doris,向量与业务数据同仓,无需再新增一个中间件;并非它在所有维度上优于专用向量库 |
| Qdrant / Milvus | 专用向量库 | 原生 ANN 能力更专业,但等于新增一个必须长期采购与运维的中间件 —— 当前没有为它付出额外成本的理由,依赖已保留,作为后续演进选项 |
Elasticsearch(团队已有)承担关键词 / BM25 检索,Redis(团队已有)承担缓存与跨进程协调(企微消息去重、连接锁、检索缓存),对象存储(S3 兼容:MinIO / MOS / OBS,同样是团队既有存储)存放原始文档与生成文件。
所以 “Doris 做向量” 是 “团队已有 + 少引入中间件” 的现实选择,而不是 “Doris 比 Qdrant/Milvus 更优” 的技术结论 —— 这个口径也写进了我们内部文档,避免后人误读成技术倾向。
取舍点:
多路召回 ≠ 炫技
。向量检索擅长语义,BM25 擅长精确词面,字段检索擅长 “按编号 / 部门 / 标题找”。企业知识问答里大量问题是 “这个工单编号对应的流程是什么”,单纯向量根本召不回来。所以我们在早期就定了 “多路召回 + 融合” 的路线,这个细节会在第三篇详细展开。
4.3 前端:React 18 + TypeScript + Vite + Tailwind
框架对比:
| 方案 | 优势 | 劣势 | 结论 |
|---|---|---|---|
| React 18 + TS | 生态最大,ReactFlow / ECharts / WangEditor 等专业库都是 React 一等公民 | 状态与渲染心智模型有学习曲线 | 选它 |
| Vue 3 | 上手快、模板直观 | AI 编排画布等专业库以 React 生态最强,要绕一层桥接 | 团队不熟且生态不占优 |
组件方案:无头组件 + 设计令牌,而不是成品组件库。我们用 Radix UI(无头)+ Tailwind Design Token,而不是直接上 Ant Design 这类成品库:
-
得:视觉与主题完全可控(深浅色、品牌色、对比度都能用设计令牌统一);可访问性(键盘、焦点、ARIA)由 Radix 原生兜底;打包产物更轻。
-
失:基础组件要自己组装,开发初期比 “开箱即用” 慢 —— 这是典型的 “前期慢、后期快” 取舍。
-
工程组织:
components/ui(通用原语)→ 领域组件 → 页面三层,用class-variance-authority+tailwind-merge管理样式变体,避免样式散落。
前端架构总览:
前端几个 “刻意” 的工程决策:
-
统一 API 层:认证、请求、URL 拼接、文件上传、SSE 流式全部收敛到
frontend/src/api/client.ts,页面不直接碰fetch。这保证了 “登录态怎么带、错误怎么统一处理、流式怎么接” 全平台只有一份实现。 -
轻量状态管理:用 React Hooks + Context,刻意不引入 Redux / Zustand 这类全局状态库—— 让状态尽量靠近页面与领域组件,降低跨页面耦合,也减少样板代码。
-
三语 i18n + 实体级翻译:UI 文案走
zh-CN / en / pt-BR字典;但业务实体(知识库名、Agent 名)单独维护译文并在渲染时 hydrate,而不是把实体名塞进 UI 字典 —— 这是企业多语场景最容易做错的地方。 -
一套代码基座:管理端、客户端、发布页共用前端工程,通过路由与权限目录区分入口,避免维护三套前端。管理端 7 大导航分组、路由和权限码,全部由
shared/permission.catalog.json这一个数据源驱动。
4.4 后台任务:进程内 asyncio,而不是上 Celery/MQ
采集解析、QC 采样、工单状态同步、组织同步这些 “异步长任务”,我们没有引入独立 MQ 或 Celery,而是用进程内 asyncio.create_task / BackgroundTasks + 状态机 + 幂等实现,跨进程一致性靠 Redis 锁和部署纪律保证。
| 方案 | 优势 | 代价 | 结论 |
|---|---|---|---|
| 进程内 asyncio | 少一个中间件、开发调试最快、状态天然在应用内 | 多实例下有重复执行风险,需单实例 / 选主纪律 | 当前方案 |
| Celery + Redis | 成熟、任务重试 / 定时开箱即用 | 与异步 FastAPI 配合要额外适配;运维多一套 | 阶段目标 |
| MQ + 独立 worker | 可靠、可水平扩展、削峰 | 部署与运维复杂度显著上升 | 团队规模 / 业务量到了再上 |
诚实说明:这是
有意的阶段性取舍
,不是最优终态。它降低了运维复杂度(少一个中间件),但在多 worker / 多实例下存在任务重复执行的风险,需要严格的 “单实例 / 选主” 纪律。是否升级到持久任务队列,是我们技术债务清单里明确记录的一条,会在终篇展开讨论。
4.5 Schema 演进:启动期幂等迁移(得与失)
数据库结构演进我们目前主要靠启动期幂等 auto_schema(Base.metadata.create_all + 一组幂等 ALTER),Alembic 依赖虽然存在但没有形成完整的 revision 链。
-
得:部署简单,新环境启动即自动补齐;历史库升级不依赖人工执行迁移脚本。
-
失:启动时间、锁竞争、回滚和审计成本随表数量上升;无法精确控制 “每个版本的迁移差异”。
这一条我们标记为 P1 技术债务,计划逐步冻结新增启动迁移、建立可版本化的迁移基线。在博客里我们不回避这些 “没做完美” 的地方—— 它们才是真实工程里最有价值的部分。
4.6 部署、安全与开发协同
-
部署:多阶段 Docker + docker-compose + Nginx 反向代理;前端产物直接打进镜像,由 FastAPI 托管静态资源并做 SPA 回退,单域名即可部署(也满足 Office Online / WOPI 回调和 SSO 回调的域名一致性)。部署脚本同时覆盖本地 Windows 与服务器,尽量一条命令起服务。
-
安全:JWT + RBAC + 声明式路由 AuthZ + 统一资源 ACL 四层叠加;连接器凭据加密落库且接口只返回掩码;外部系统统一走连接器 + URL 白名单 + scheme/host/ 超时校验,禁止业务模块直连外部地址。
-
开发协同:权限目录
shared/permission.catalog.json是唯一数据源—— 前端菜单、后端权限码、i18n 都由它同步派生,避免 “菜单、权限、文案三处各写一份” 导致权限对不上。
4.7 选型全景表
| 关注点 | 选择 | 放弃 / 延后 | 一句话理由 |
|---|---|---|---|
| 后端框架 | FastAPI + SQLAlchemy 2 异步 | Flask / Django / Spring | 异步原生 + AI 生态 + Pydantic 契约 |
| 编程语言 | Python 3.11 | 多语言栈 | RAG/Agent 生态是第一公民 |
| 主数据库 | MySQL | 多方言支持但生产以 MySQL 为准 | 团队已有,直接复用;多方言有默认值不一致的坑 |
| 向量检索 | Chroma(默认)/ Doris(生产) | Qdrant / Milvus | 团队已购 Doris;Chroma 轻量默认 |
| 关键词检索 | Elasticsearch | 纯 MySQL LIKE | 团队已有;召回准确率 |
| 缓存 / 协调 | Redis | Memcached | 团队已有;需锁、去重、缓存多种结构 |
| 前端框架 | React 18 + TS + Vite | Vue / Webpack | 专业库生态 + 构建速度 |
| UI 组件 | Radix + Tailwind 令牌 | Ant Design 成品库 | 主题可控、产物轻、可访问性兜底 |
| 任务队列 | 进程内 asyncio | Celery / MQ | 初期运维最简(技术债已记录) |
| Schema 迁移 | 启动期幂等 auto_schema | Alembic revision 链 | 部署简单(技术债已记录) |
| 对话流式 | REST + SSE | WebSocket 作为对话主通道 | 流式够用、调试简单;企微渠道用出站长连接 |
| 部署形态 | Docker Compose + Nginx | 集群编排 | 单机起步够用,镜像化保证一致性 |
总结一句:我们的选型策略不是 “追新技术”,也不是 “谁更优就选谁”,而是在每个环节优先选 “团队最熟 + 团队已有 / 已购 + 生态够用 + 运维最省” 的组合——MySQL、Doris、ES、Redis、对象存储这些数据组件大多是团队本来就持有、直接复用的结果,而不是横向评测出来的 “最优”。把省下来的精力投到检索质量、权限治理这些真正决定产品成败的地方。
五、一个请求的完整链路
以 “用户通过 Web Client 向一个绑定了知识库的 Agent 提问” 为例:
对应的中间件与路由挂载(代码简化示意):
\\# app/main.py
app.add\\\_middleware(HTTPLogMiddleware) # 请求日志 + X-Request-Id
app.add\\\_middleware(RequestContextMiddleware) # TraceId / 试点租户
app.add\\\_middleware(AuthZMiddleware) # 声明式路由授权
app.add\\\_middleware(CORSMiddleware, ...)
\\# 业务 API 统一挂在 /api 下(约 40 个 Router)
app.include\\\_router(agents\\\_router, prefix="/api/agents", tags=\\\["Agent"])
app.include\\\_router(kb\\\_router, prefix="/api/kb", tags=\\\["知识库与文档"])
app.include\\\_router(chat\\\_router, prefix="/api/kb", tags=\\\["问答"])
app.include\\\_router(workflows\\\_router, prefix="/api/workflows", tags=\\\["Workflow"])
app.include\\\_router(qc\\\_router, prefix="/api", tags=\\\["回答质检"])
app.include\\\_router(resource\\\_acl\\\_router,prefix="/api", tags=\\\["资源ACL"])
app.include\\\_router(connections\\\_router, prefix="/api/integration", tags=\\\["连接管理"])
链路里三个容易忽略但对工程质量影响巨大的点:
-
授权不止一层:
AuthZMiddleware做 “这个接口要什么权限码” 的声明式校验;到具体资源(这个 Agent、这份知识库)再走统一资源 ACL。平台 RBAC 决定 “能不能进入某类能力”,资源 ACL 决定 “能访问哪个具体资源”,两者分层、分别验证。 -
检索带权限:检索在服务端就按当前用户过滤知识库范围,绝不允许 “先召回全部再让前端过滤”。
-
回答要可追溯:SSE 流式返回 answer 的同时返回结构化的
sources,前端展示引用;这些来源在返回前还会再做一次可见性过滤。
六、目录与代码导航
app/ FastAPI 应用:API Router、核心中间件、模型、领域 Service、启动迁移
frontend/ React 管理端 / 客户端 / 发布页
shared/ 前后端共享的权限目录与管理端壳层数据(单一数据源)
tests/ 后端单元、集成与回归测试
scripts/ 权限同步、部署辅助、评估和维护脚本
skills/ 可导入/复用的 Agent Skill 包与项目开发 Skill
docs/ 架构、开发规范、专题方案、ADR 与历史归档
data/ 运行时提示词等数据资产
Dockerfile / docker-compose.yml / nginx.conf / deploy.sh 部署
给新同学 / 新开发者的建议阅读顺序:
-
app/main.py—— 看中间件链和全部路由挂载,建立 “平台有哪些能力” 的全局观。 -
app/api/—— 按业务域看 API 契约与权限依赖。 -
app/services/retrieval/—— 理解最核心的检索链路(第三篇会深入)。 -
shared/permission.catalog.json—— 这是权限体系的单一数据源,管理端菜单和权限码都从这里驱动。
七、小结与下期预告
这一篇我们把 “企业AI是什么、怎么搭起来、一个请求怎么走” 讲清楚了。核心要点回顾:
-
定位:企业内部 AI 应用的一体化平台,重点在治理、检索质量、企业系统绑定,而不是通用编排。
-
形态:模块化单体,FastAPI + React(Radix + Tailwind),MySQL 主库 + 向量库 / ES/Redis + 对象存储。
-
选型策略:每层优先选 “团队最熟 + 团队已有 / 已购 + 运维最省” 的组合 ——MySQL、Doris、ES 等数据组件多是直接复用团队既有技术栈,并非横向对比出的 “最优”;前端无头组件 + 设计令牌、统一 API 层、轻量状态,则是刻意为之。
-
纪律:模型不决定授权;检索带权限;回答可追溯;业务外部系统一律走连接器;权限目录单一数据源。
-
诚实清单:进程内任务、启动期迁移、多方言默认值不一致、无 CI,都是我们明确记录的技术债务,不是隐藏的问题。
-
已落地能力:客户端工作台、多语言检索、AgentOps 运营观测、VLM 文档理解、检索工程韧性等平台能力均已落地,详见第三章补充盘点;本期数字口径以 “42 Router / 24 Service / 40 Model / 74 页面组件 / 142 测试” 为准。
下一篇:知识库采集与文档处理管线 —— 从 “上传一个 PDF” 到 “能被检索到”,背后的状态机、解析链路与幂等工程。我们下期见。
版权与声明:本文为技术实现分享,所有代码片段均做了简化与脱敏;涉及内网地址、凭据、第三方系统域名的地方一律使用占位符。请勿将任何真实内部配置用于公开演示。
更多推荐


所有评论(0)