ARCHITECTURE.md
August 24, 2026 · View on GitHub
本文档是 Yuxi 的代码地图,只描述相对稳定的系统边界、目录职责、核心运行链路和架构不变量。它用于帮助贡献者判断“一个改动应该落在哪里”,不替代具体模块文档、测试规范或源码注释。
修改不熟悉的模块前,先阅读对应章节,再使用符号搜索定位具体类型、函数和路由。开发与运行拓扑始终以 docker-compose.yml 为准。
鸟瞰
Yuxi 是一个面向 RAG、知识图谱和多智能体工作流的知识库平台。用户通过 Vue 前端管理智能体、知识库、模型、工具、Skills、MCP 与 SubAgents;前端通过 /api 调用 FastAPI;后端服务层协调 PostgreSQL、Redis、MinIO、Milvus、Neo4j、LangGraph 和沙盒。
普通智能体请求先在 PostgreSQL 中保存为请求和消息,再立即派发或进入线程级 FIFO 队列。派发后的 AgentRun 通过 Redis/ARQ 交给独立 worker 执行,运行事件写入 Redis Stream,最终状态和业务记录写回 PostgreSQL,前端通过 SSE 消费排队与运行事件。
核心开发服务包括:
web-dev:Vue 3 / Vite 前端,挂载web/src并热重载。api-dev:FastAPI API 服务,挂载backend/server、backend/package和测试目录并热重载。worker-dev:ARQ worker,执行已经派发的 AgentRun,通过 attempt ownership 与 heartbeat 维护运行租约,并周期收敛失联 Run。storage-migrator:Compose 中唯一修改 Yuxi 数据库 Schema 的一次性迁移进程,同时处理受支持的历史存储切换;API 与 worker 等待其成功后只校验 Schema 版本。sandbox-provisioner:为智能体工具执行提供隔离沙盒。postgres:业务数据、知识库元数据、请求队列、AgentRun 与 LangGraph checkpoint。redis:ARQ 投递、运行事件、取消信号以及跨进程配置和模型缓存。minio:附件、知识库原始文件和其他对象数据。milvus、etcd:向量检索及其元数据协调。graph:Neo4j 知识图谱。mineru-api、paddlex:通过allprofile 可选启动的文档解析和 OCR 服务。
后端代码地图
后端分成两个顶层边界:backend/server 是 Web 应用入口与 HTTP 适配层,backend/package/yuxi 是业务和基础设施主体。新增领域逻辑通常优先放在 yuxi 包中,路由层只处理请求模型、认证上下文和响应装配。
Web 与 worker 入口
server/main.py创建 FastAPI 应用、注册中间件,并将业务路由统一挂载到/api。server/routers是 HTTP 路由边界,所有路由集中在server/routers/__init__.py注册。server/utils/lifespan.py管理数据库、内置模型/MCP/Skills、知识库、Redis、沙盒、LangGraph checkpoint 和通用 Tasker 的启动与关闭。server/worker_main.py是 ARQ worker 入口,实际执行设置位于yuxi.services.run_worker。
LITE_MODE 的解析由 yuxi.config.runtime 统一拥有。该模式保留认证、智能体、聊天、非知识类 Skills、MCP、模型、普通工作区和系统管理接口,但不注册 external_kb、knowledge、evaluation、graph、知识域 Dashboard 与 /workspace/knowledge/* 路由,不注册 knowledge-base Skill 或知识库工具,也不宣告客户端或 CLI 知识能力。该模式不导入知识解析重运行时、创建 knowledge schema 或初始化知识库管理器;Web 从运行时 discovery 获取同一能力投影,聊天附件只有在实际解析时才惰性加载 parser。
backend/package/yuxi
agents定义 LangGraph 智能体体系。BaseAgent是智能体基类,BaseContext是运行上下文;buildin/chatbot和buildin/subagent放内置智能体;middlewares组合文件系统、Skills、SubAgent、摘要、审批、模型兼容和用量统计;toolkits管理本地工具;backends对接沙盒、知识库和 Skills 文件系统;skills与mcp管理扩展能力及其运行时加载。workspace是持久化 UserWorkspace Owner。paths.py拥有 uid、宿主根和数据库projects/<uuid>映射,filesystem.py拥有 no-follow 文件原语,workdir.py提供以一个 Project 为根的持久化视图,preview.py拥有 UserWorkspace 文件预览和 runtime 本地 Office 缓存。Agent Backend 单独拥有/home/gem/...runtime 路径。services是用例层。智能体主链路重点分为请求接入与排队、Run 生命周期、运行时配置、worker 执行和 SubAgent 调用;聊天历史、附件、工作区、文件预览、评估、认证和观测等跨模块流程也从这里找入口。repositories是 PostgreSQL 访问边界,封装业务对象、知识库元数据、AgentRun、请求队列、Task 和扩展配置查询。路由不应绕过 repository 直接拼装持久化逻辑。storage/postgres管理 SQLAlchemy 模型、业务连接池和 LangGraph checkpoint 连接池。storage/redis管理同步/异步 Redis 客户端和 ARQ 连接参数;业务 key、事件格式和缓存语义留在各自服务中。storage/minio管理对象上传、下载和临时文件访问。storage/neo4j管理共享 Neo4j Driver、生命周期和图查询辅助。knowledge是知识库、文档解析、评估和图谱领域。runtime.py暴露运行时知识库管理器;preview.py拥有 Knowledge metadata、MinIO 原始对象读取和 MinIO Office PDF 缓存;implementations放 Milvus、Dify、Notion 和只读连接器;parser统一封装 OCR/文档解析;chunking管理分块策略;graphs管理 Milvus 与 Neo4j 图谱能力。models封装 chat、embedding 和 rerank 模型适配;models/providers使用 PostgreSQL 保存模型供应商,并通过 Redis 缓存向 API 和 worker 提供一致视图。config区分系统级配置和用户级配置。PostgreSQL 持久化系统配置和用户配置;Redis 只保存带版本失效的短缓存,旧base.toml只作为一次性迁移来源。utils只放跨领域且足够通用的日志、时间、SSE 和轻量工具;filepreview.py提供不依赖存储、领域或 HTTP 的格式识别、文本渲染和 Office 转换原语。
两类后台任务
项目中存在两套用途不同的后台执行机制,不应混用:
- AgentRun:通过 PostgreSQL 保存事实状态,使用 Redis/ARQ 投递到
worker-dev,支持运行事件、取消、恢复和线程请求队列。 services/task_service.py中的 Tasker:运行在 API 进程内,用于知识库解析、评估和图谱构建等通用后台任务;任务摘要持久化到 PostgreSQL,但可执行 coroutine 和内存队列不具备跨进程重建能力。
测试代码位于 backend/test,按 unit、integration、e2e 分层。新增或修改后端行为时,测试应放在最能覆盖真实风险的层级。
前端代码地图
前端是 Vue 3 + Vite 应用,业务入口集中在 web/src。
main.js挂载应用,App.vue是根组件。router定义公开首页、登录、智能体、工作区、智能体管理、扩展和仪表盘路由,并负责认证、管理员和超级管理员守卫。apis是后端接口封装边界。新增接口应在这里定义,复用base.js的请求、鉴权和错误处理。stores保存用户、智能体配置、主题和其他跨页面状态。views是页面级入口,components是可复用界面块。智能体对话的主要交互位于AgentChatComponent,由AgentView负责页面组合。composables封装请求排队、Run SSE、流式消息、审批、线程状态、提及和其他可组合逻辑。utils放轻量转换和展示辅助;全局样式集中在assets/css,颜色和基础规范优先复用base.css。
/ 是公开首页;登录后的核心工作区是 /agent。/extensions 对所有登录用户开放,其中 Skills 对普通用户可见,知识库、工具和 MCP 管理能力仅管理员可见;Dashboard 仅超级管理员可访问。后端权限检查始终是最终边界,前端守卫只负责页面体验。
智能体运行链路
一次普通智能体请求经过以下边界:
AgentView和AgentChatComponent收集文本、图片、附件、模型与审批配置。web/src/apis/agent_api.js调用POST /api/agent/runs。server/routers/agent_router.py校验用户和智能体,将请求交给agent_request_queue_service。- 服务在同一数据库事务中创建用户消息和 AgentRunRequest,并按用户、智能体和线程检查活跃 Run 与 FIFO 队头。
- 请求可以立即派发、进入等待队列或按
reject策略拒绝;只有数据库提交成功后才向 ARQ 投递 Run。 worker-dev中的run_worker使用进程 identity 与 job-attempt token 取得 AgentRun lease;未取得 ownership 的重复任务不会执行。执行期间 heartbeat 在独立事务中续租,再加载智能体配置和运行上下文执行对应 LangGraph。- 智能体通过 middleware 组合 UserWorkspace 中的当前 Workdir、只读共享 Skills、MCP、SubAgent、审批、摘要和工具能力。根 Agent 与子 Agent 共享同一个 runtime 和 Workdir;知识库能力主要由内置
knowledge-baseSkill 及其依赖工具按需开放。 - Run 事件写入 Redis Stream,取消通过 Redis key/pubsub 传递;AgentRun、消息投递状态和最终结果写入 PostgreSQL。任何 assistant Message 发布前先在 Run 行锁内验证当前 attempt;正常输出、绑定和
completed同事务提交。worker 失联后,过期 lease 会幂等收敛为带worker_lease_expired原因的failed。该失败只证明执行 ownership 已丢失,外部副作用仍需按 at-least-once 语义核对。 - 前端在排队阶段消费 Request SSE,派发后切换到 Run SSE,并根据数据库状态处理断线恢复和终态补偿。
- Conversation 保存不可变
project_id,每个 Project 一期绑定一个workdir_path,多个 Project 可以共享同一路径。v0.7.1 Conversation 在一次性迁移中直接获得 implicit Project,不形成 Conversation 路径中间态。managed Project 使用服务端创建的projects/<uuid>;linked Project 可绑定当前 uid UserWorkspace 下除根目录外任意经过 no-follow 校验的已有目录。Workspace tree 仅展示/projects下属于 selectable Project 的目录子树,隐藏 implicit 与尚未归属 Project 的匿名目录。yuxi.workspace唯一拥有宿主路径和 fd-relative 文件访问,统一 Workdir resolver 通过 Project 为 Viewer、附件、Artifact、Run 和 SubAgent 提供同一持久路径。Agent Backend 单独把该路径映射为/home/gem/user-data/...runtime 路径。目录的持久 POSIX 字节是 Agent 文件、附件、Viewer 和 artifact 的实时事实源,uploads/outputs只是按需创建的目录约定。Run 终态清理 runtime 进程但保留 Workdir。
审批或人机输入产生的 resume 请求会从 LangGraph checkpoint 恢复,并创建新的 AgentRun;它不重新进入普通消息 FIFO 接入流程。
架构不变量
- Docker Compose 是开发环境的事实来源。开发时先检查容器、日志和热重载,不默认要求本地裸跑服务。
- HTTP 路由保持薄;用例流程放在
yuxi.services,持久化查询放在yuxi.repositories。 - 请求接入与 Run 执行是两个阶段:先提交 PostgreSQL 事实,再投递 ARQ,不能让队列消息先于数据库状态可见。
- 同一用户、智能体和线程的普通请求通过 FIFO 队列串行派发;排队请求与运行中的 Run 使用不同状态模型和 SSE。
- PostgreSQL 保存业务事实状态;Redis 承担投递、事件、取消和缓存,不作为 AgentRun 最终状态的唯一来源。
pendingRun 是持久化投递意图;running/cancel_requestedRun 必须由唯一 attempt lease 拥有。Heartbeat 只能由当前 owner 续租,终态或 retry publication 清除 lease,过期 ownership 不能被另一个执行者静默接管。- Run 结果以
output_message_id指向的同 Run assistant 消息为权威;只有历史completedRun 可在缺少指针时兼容读取同 conversation、相同run_id的 assistant 消息,禁止从未完成或相邻 Run 猜测输出。 /api/system/health只表达 API 进程 liveness;Compose 以/api/system/ready判断启动完成、PostgreSQL/Redis 可用且存在完成启动的兼容 worker。worker 同时续租短 TTL ARQ 消费健康与 lease reconciliation 成功事实;持久 key、超长 TTL、错误 Redis DSN 或持续无法收敛失联 Run 都不能维持 readiness。业务正确性仍由真实链路测试证明。- Yuxi 数据库 Schema 只由
storage-migrator在 PostgreSQL advisory lock 内修改并记录 business/knowledge 域版本;API 与 worker 不建表或执行收敛 DDL,版本缺失、过旧或过新时拒绝启动。LITE 只迁移和校验 business 域。 - 内置 Skills 是默认 Agent shipping contract 的 required 组成,API/worker 通过 PostgreSQL advisory lock 串行同步;内置 MCP 定义是 optional,但失败必须形成可观测 degraded 而非被组件内部吞掉。
- 跨 repository 的身份管理用例只有一个 service 事务 Owner;Department、User 与强制 OperationLog 同一提交。API Key 由独立服务端主密钥和客户端幂等 ID 确定性派生,只保存 hash;原始创建意图使用不可变指纹校验,撤销保留 request-id tombstone,同一请求可恢复响应但不能复活已撤销凭据。
- 前端 API 调用集中在
web/src/apis,组件不要散落拼接普通 HTTP 接口。 - 智能体能力通过 context、middleware、toolkits、Skills、MCP 和 backends 组合;不要把知识库、沙盒或扩展逻辑硬编码进单个页面或路由。
- Skill 的依赖工具只有在对应 Skill 被显式预加载或动态激活后才对模型开放;基础工具与受 Skill 门控的工具保持边界。
- LITE 模式必须允许跳过知识库、图谱和评估等重依赖能力,新增导入、路由和启动逻辑时要尊重该边界。
- 文件边界只使用三种跨层路径:数据库
projects/<uuid>、Viewer 当前 scope 相对/foo、Agent/artifact runtime 绝对/home/gem/user-data/...;宿主Path由yuxi.workspace或显式 v0.7.1 storage migration 内部持有,普通 Service/Repository 不得取得。 - 沙盒虚拟路径由当前 Project Workdir、User Data 与共享 Skills 根共同约束;个人 Skill 保存在 UserWorkspace 的
agents/skills,共享与内置 Skill 才投影到只读/home/gem/skills。用户可见路径、对象存储 URL 与宿主机真实路径不能混用。 - 面向用户和外部系统的输入在边界校验;内部服务优先依赖已有类型、事务和仓储约束,避免用静默回退掩盖设计错误。
跨切面关注点
- 配置:Compose 和
.env提供部署配置;管理员系统配置、用户配置与模型供应商以 PostgreSQL 为持久化 Owner,Redis 只提供可失效缓存;旧base.toml仅用于一次性迁移已有系统配置。 - 权限:前端路由和页面标签提供体验级约束,FastAPI 认证依赖和 repository 可见性查询提供最终授权。
- 状态与存储:PostgreSQL 保存请求、Run、消息、Conversation 的
project_id与 Project 的workdir_path、业务和知识库元数据,也是 LangGraph checkpoint 的唯一 Owner。Redis 保存短期事件、取消信号、ARQ 和跨进程缓存;每个用户的 UserWorkspace 拥有 Workdir 与个人 Skill 字节,MinIO 继续拥有知识库与临时上传对象。 - 文档处理:Agent 附件确认后进入实时 Project Workdir;知识库上传仍先进入对象存储和文件元数据边界,再经过解析、分块和知识库实现。解析器、分块策略和知识库连接器保持可替换。
- 观测与调试:优先查看
api-dev、worker-dev和相关依赖日志;Langfuse 集中在服务层和 AgentRun 上下文;SSE 问题同时检查 Redis 事件与 PostgreSQL 终态。