
人工智能大模型AI 应用交互助手本地部署【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址https://gitcode.com/CherryHQ/cherry-studio点击查看免费下载导读本文是 Cherry Studio 桌面客户端主进程Electron Main Process架构的权威参考围绕src/main/的顶层目录组织展开为什么顶层目录是一个封闭且锁定的分类集合、每个目录承担何种唯一职责、features/与services/如何按规模分档、依赖方向如何流向业务无关的基座层以及新增能力时如何在不新增顶层目录的前提下被正确归位。读完本文你将掌握一套可直接落地的进程级代码组织规范——既能判断一个新模块该放进ai/、data/、services/还是features/domain/也能理解 Cherry Studio 当前主进程代码结构背后的治理逻辑与尚存的迁移缺口。1. 定位主进程架构在 Cherry Studio 文档体系中的位置src/main/是 Cherry Studio 的 Electron 主进程代码根。本文档是它的规范级canonical参考回答三个问题src/main/下每个顶层目录是干什么的哪些规则阻止这些目录无序蔓延目录之间如何相互依赖。它是渲染进程架构与共享层架构的主进程对等文档跨进程的完整视图进程模型、monorepo 目录树见架构总览。与项目整体目录规则的关系主进程的顶层封闭规则是命名规范 §4.8「顶层目录默认封闭」在src/main/下的最严格落地且按 §4.9单复数、§4.10feature 与类型桶的区分、§5.2按形态路由进一步细化。三者配合构成完整的治理闭环。核心命题顶层是一组有原则的封闭分类集合而不是一份开放式模块清单。每个顶层目录只装一种东西并且因为不同的理由而占据顶层位置。该集合被锁定——任何新能力都必须按其本质归入已有类别永远不为其新增顶层目录详见 §4。2. 封闭的顶层集合九个目录各自一份章程src/main/下恰好存在以下顶层条目每个都有且只有一份职责目录类别占据顶层位置的理由core应用运行时与业务无关、只关心如何把应用跑起来的基础设施。检验标准把core/搬到另一个 Electron 应用上加上别的业务代码你就得到了一个不同的应用。「与业务无关」是必要但不充分条件core/只容纳不可移除的底座——应用离开它就无法运行可移除的能力即使必须在启动早期执行也应归入services/见 §4。它只装一种东西——应用底座生命周期/DI 容器、路径注册表、日志器、窗口管理器、调度器与任务、preboot、诊断、安全原语IPC 发送方信任。ipc跨进程边界Electron 最具定义性的进程间机制——足够特殊和重要因此独立成目录。IpcApischema router handler是已迁移域的类型化边界遗留的根级ipc.ts仍共存作为 §7 追踪的迁移缺口。data数据层通用业务数据存储——一等公民数据层故独立。持有 DbService / CacheService / PreferenceService / DataApiService / BootConfig、DB schema以及 v1→v2 迁移器它们按设计读取领域数据——一次性迁移代码。详见数据系统参考。ai核心领域Cherry Studio 本质上是一个 AI 客户端因此 AI 拥有自己的顶层归属一切与 AI 本质绑定的事物都在这里providers、middleware、MCP、agents、stream manager与shared/ai镜像。features领域模块业务领域一个领域一个目录。复杂领域将自身相关的 services/utils 等捆绑在features/domain/之下。services业务服务业务功能服务。简单服务是单个文件较大的服务组织成自己的子目录。utils无状态助手跨领域、与领域无关的无状态函数无单一属主。「无状态」是门槛而非「纯函数」助手可以通过环境化的application/logger触及基础设施见 §3它只是不拥有状态、不产生外向副作用见 §2 反模式。i18n主进程本地化主进程自己的locales/目录及其t()/getI18n()解析器。这是封闭集合上一次有意的、受治理的扩充§4与src/renderer/i18n/镜像使每个进程各自拥有独立目录utils/i18n/方案因破坏跨进程对称性而被否决。入口文件main.ts——进程入口先跑 preboot再application.bootstrap()。使用具名文件而非index因为index按命名规范 §6.4 保留给 barrel重导出聚合文件。ipc.ts——遗留 IPC 注册正在被逐步退役进ipc/。命名遵循命名规范 §4.9core/data/ai/ipc/i18n是单数命名空间features/services/utils是复数桶collection bucket。判断规则是「这个目录是否装着许多同类东西」——是则复数否则单数。从源码可以完整验证这份目录清单。实际仓库根目录与文档一致共九个顶层条目src/main/ ├── main.ts # 进程入口preboot → application.bootstrap() ├── ipc.ts # 遗留 IPC 注册正在被退役进 ipc/ ├── core/ # 业务无关的应用运行时lifecycle/DI、paths、logger、window、scheduler/job、preboot、security ├── ipc/ # IpcApi —— 类型化的 main↔renderer 边界 ├── data/ # 数据层DB/Cache/Preference/DataApi/BootConfig、schemas、migration ├── ai/ # AI 子系统 —— 产品的核心领域 ├── features/ # 业务领域一个目录一个各自捆绑自己的 services/utils ├── services/ # 业务功能服务单文件或一个子目录 ├── utils/ # 跨领域无状态助手 └── i18n/ # 主进程本地化目录与解析器2.1 从入口源码看分层落地main.ts 是理解这套分层的最佳起点。文件头部注释明确写着DO NOT add new code here——新服务应进入生命周期系统core/lifecycle/不可移除的 preboot 步骤进入core/preboot/可移除但必须在 preboot 期执行的能力归入其本性之家如services/并从入口调用。该文件只是胶水只应收缩。入口启动序列与文档所述完全吻合// Preboot 阶段——顺序重要见 core/preboot/README.md resolveUserDataLocation() // 用户数据目录解析必须先执行 requireSingleInstance() // 单实例锁 configureChromiumFlags() // Chromium 启动开关 initCrashTelemetry() // 崩溃遥测 protocol.registerSchemesAsPrivileged([...]) // 特权 scheme 声明 application.initPathRegistry() // 冻结路径注册表——bootstrap() 会断言其完成 // ... application.registerAll(serviceList) // 注册全部生命周期服务 const bootstrapPromise application.bootstrap() // 生命周期引导 await app.whenReady() await bootstrapPromise // ... await registerIpc() // 遗留 IPC 注册v2 待分解其中application.registerAll(serviceList)与application.bootstrap()分别来自core/application/与core/lifecycle/serviceList来自 serviceRegistry.ts这正是文档 §3 所说「handler 通过application.get(XxxService)解析生命周期服务」的容器底座。2.2core/目录的边界判定core/README.md 给出了清晰的判定规则经验法则如果移除某个模块会让应用无论功能如何都无法运行它属于core/如果移除它只会破坏某个具体功能它属于别处如services/、data/。当前core/模块清单含各自的参考文档包括模块描述参考文档application/应用单例、服务注册表、bootstrap 编排生命周期参考lifecycle/IoC 容器、服务生命周期管理、分阶段引导生命周期参考logger/基于 Winston 的日志服务preboot 单例经logger别名消费日志参考paths/路径注册表所有主进程文件系统路径的唯一真源paths/READMEpreboot/引导前的同步设置userData 解析等preboot/READMEwindow/窗口管理器窗口管理器参考utilityProcess/崩溃隔离的 Electron utility 进程注册、类型化客户端、线协议、子运行时Utility Process 参考scheduler/job/调度器与任务系统任务与调度参考concurrency/createLatestReconciler——通用 latest-wins 异步副作用协调器concurrency/READMEsecurity/IPC 发送方信任等安全原语IpcApi Overview §Security注意core/的启动分阶段词汇是全代码库的标准用语prebootcore/preboot/负责调用bootstrap()前必须完成的同步设置无 DI、无生命周期服务→bootstrapcore/application/core/lifecycle/负责冻结路径注册表、构建 IoC 容器、运行 Background / BeforeReady / WhenReady 各生命周期阶段→runningbootstrap()返回后的稳态。core/README.md特别提醒这三个生命周期阶段运行在 bootstrap 阶段内部不是三个独立的顶层阶段。3.features与services同一个东西的两种尺寸services/与features/是同一种东西——业务逻辑——的两种规模形态。分档遵循命名规范 §4.10 的跨进程规则提升而非默认——而且要分步走。一个小的、自包含的服务最初以单文件形式放在桶根services/TopicService.ts一个持有状态的Service/Manager类用与其类名一致的PascalCase见命名规范 §5.2而通用助手是utils/topic.ts——主题专用的助手在进入子目录步骤之前保持内联。当单个文件容纳不下时先在原处扩展为camelCase主题子目录——services/topic/容纳TopicService.ts及其助手——而不是直接升级为 feature。注意形态目录是主题名不带Service后缀命名规范 §4.5只有类文件保留后缀例如services/webSearch/WebSearchService.ts。只有当它成长为大型、多文件的领域、捆绑自身 services/utils 与助手时才赢得features/domain/的归属典型如 knowledge、apiGateway、fileProcessing。不要为一个预期中的模块预先创建子目录或 feature。ai/不是普通 feature。它是产品的核心领域拥有自己的顶层归属§2它是奠基性的不是众多领域之一。按角色路由命名规范 §5.2持有长期资源或有持久副作用的有状态类→ 生命周期Service见生命周期参考无状态模块→ 默认utils/仅当出现外向副作用或被迫向上依赖时才提升到services/见 §5.2 路由表大型领域→features/domain/。只读永远不构成提升理由——为查询而触碰基础设施的助手依然是助手。源码佐证src/main/features/下当前恰好四个领域目录——apiGateway/、fileProcessing/、knowledge/、miniApp/与文档列举的「knowledge、apiGateway、fileProcessing」示例一致且规模相当而src/main/services/则是单文件服务如AppService.ts、TrayService.ts、VersionService.ts等数十个*Service.ts与主题子目录webSearch/、file/、oauth/、proxy/、cherryCloud/等并存的混合桶——正是「单个文件是默认主题多文件时提升为子目录」的直观体现。3.1 子目录与 Barrel 规则单个.ts文件是默认形态只有当主题确实拥有多个文件时才提升为子目录。Barrelindex.ts聚合导出遵循命名规范 §6.4跨进程的单一权威应用到services/与utils/桶根services/与utils/没有index.ts。桶是类别而非模块——要导入就导入具体文件或主题绝不导入整个桶。services/topic/子目录恰好有一个index.ts作为其公开 API显式具名导出禁止export *其余文件保持私有。复杂的utils/topic/子目录同样只有一个index.ts。为什么每个主题通过单一公开入口被导入——与单文件模块完全一致——内部文件保持私有消费者永不深导入。对utils/而言当文件与目录共享主题名时说明符main/utils/topic在文件长成文件夹后甚至无需改变。features/domain/是同一「单入口」思想的高一层消费者通过其一个公开入口导入领域而非其内部文件。这一规则与命名规范 §6.4 的 barrel 铁律完全一致barrel 只做重导出、必须真正可封闭、不得嵌套 barrel。桶根types/、utils/、services/因此不设根index.ts。4. 依赖方向流向业务无关的基座层各目录的章程蕴含了方向依赖流向业务无关的基座层基座层Foundation——core/与utils/不携带业务知识没有任何业务代码位于它们之下。数据层Data layer——data/是基座层之上的存储层。业务层Business——ai/、features/、services/是业务层它们向下依赖data/、core/、utils/。ai/在业务层内部具有基座地位features/与services/可以依赖它它不得导入任何 feature。feature 领域互相隔离features/domain/不得导入同级feature——通过services/、ai/、data/或shared共享。ipc/是边界适配器handler 保持薄边界策略 IpcError映射 委托并通过两种方式触达业务代码——生命周期服务注册于serviceRegistry.ts通过application.get(XxxService)解析绝不直接导入非生命周期模块无状态主题 barrel 或直接导入的单例通过其精选入口导入而仅为获取 DI 句柄而虚构一个生命周期服务是反模式。详见 Handler: Pure Function vs Service Delegate。禁止导入渲染进程src/main与src/preload不得导入渲染进程代码。跨进程类型放在shared主进程独有类型留在src/main——放置规则见共享层架构。此规则由 ESLintno-restricted-imports规则强制在src/mainsrc/preload中封禁renderer§7 追踪仅存的一个例外。4.1 两条贯穿性环境依赖logger与application有两条依赖横穿每个目录且不是分层边——它们是环境化的基础设施访问logger日志与applicationDI 容器 / 服务定位器。对源码导入做一次原始扫描会发现几乎一切都依赖core而这仅仅是因为这两条别名上述规则关注的是领域之间的直接模块导入。4.2 依赖规则的两级强制执行现状内部方向边暂未自动化不同于渲染进程文档 §5 提出的import/no-restricted-paths分区方案方向靠约定与评审维持。外部 main↔renderer 边界已被强制eslint 规则BAN_RENDERER_FROM_MAIN是硬约束。从 eslint.config.mjs 可以看到其完整定义const BAN_RENDERER_FROM_MAIN { group: [renderer, renderer/**, **/renderer/**], message: Main/preload must not import renderer code. Use shared for cross-process types, or src/main for main-only types. See docs/references/architecture/shared-layer.md. }该禁令在配置中针对src/mainsrc/preload以error级别生效eslint.config.mjs。另有一条BAN_DRIZZLE_MIGRATOR禁令禁止直接调用 drizzle 的migrate()必须改用data/db/applyMigrations的applyMigrations()否则表重建式迁移会因事务让PRAGMA foreign_keysOFF失效而静默级联删除子行——这正是 §1 中「v1→v2 迁移器属于 data/」的工程动机之一。5. 封闭顶层治理新能力永不新增目录顶层集合封闭且锁定——把在src/main/下新增目录视为不可行。这是命名规范 §4.8顶层默认封闭的最严格形态§4.8 仅在同时满足必要性没有现有类别能在无语义损失的前提下容纳这些文件与完备性新目录有清晰范围、符合 §4.3 复数桶或 §4.5 单数领域模块形态、不与现有桶重叠时才允许新增顶层目录而主进程的类别已覆盖全部空间——所以新能力被归入现有类别永不拥有自己的目录。唯一有意的扩充是i18n/§2它的加入让主进程拥有了与src/renderer/i18n/对称的本地化目录这是一次有记录的受治理例外而非规则松动。渲染进程§6与shared§2的顶层同样受此治理约束。新能力永不获得新的顶层目录按其本质归位该能力是……归属与 AI 本质绑定ai/业务数据 / 存储data/一条 IPC 路由ipc/IpcApi业务无关、不可移除的应用运行时基础设施core/一个业务服务services/——若是大型多文件领域则为features/domain/纯粹、领域无关的逻辑utils/结合命名规范 §4.8 的完整判定流程新目录的准入标准是两条都必须成立①必要性——现有顶层桶无法无语义损失地容纳新文件②完备性——新目录范围清晰、形态合规、与现有桶无重叠。任何一条存疑就把文件放进现有桶现有桶下的子目录不受限制。6. 反模式清单下列模式被明确禁止把业务代码任何 Cherry Studio所做之事的专属逻辑放进core/——core/必须保持纯应用运行时。features/domain/导入同级feature跨领域耦合。ai/导入features/核心领域向上依赖 feature。为单一能力开设新的顶层目录§4。将业务数据散落到临时存储而不是走data/子系统或将命令式操作散落到临时通道而不是走ipc/IpcApi。这与命名规范 §6.7 的「桶反模式」相互呼应单数命名却装了许多同类项、桶内混入与声明类别不符的文件、长期只装 0–2 个文件的瘦桶、两个顶层桶范围重叠——出现任何信号都值得一次整合评审。7. 子系统参考索引各子系统的纵深细节位于各自的专项文档本文只负责目录布局本身不在此重复子系统细节子系统位置参考文档服务生命周期IoC、分阶段引导core/lifecycle/、core/application/生命周期参考启动阶段preboot / bootstrap / runningcore/preboot/、core/application/core/README窗口管理器core/window/窗口管理器参考Utility 进程崩溃隔离的工作进程core/utilityProcess/Utility Process 参考调度器与任务core/scheduler/、core/job/任务与调度参考路径注册表core/paths/paths/READMEIPC 发送方信任门validateSendercore/security/IpcApi Overview §Security数据系统DB/Cache/Preference/DataApi/BootConfigdata/数据系统参考IPCIpcApiipc/IPC 参考AI 子系统ai/AI 参考8. 当前偏差目标 vs 现状本文描述的是目标态。当前代码尚未完全对齐之处记录如下——本轮治理只记录、不修改代码。这里只列出结构性偏差封闭顶层集合 §4、桶 barrel §2.1、放置规则 §2按文件的命名后缀审计§5.2不在本文范围区域当前状态目标态遗留ipc.tsv1 IPC 注册位于进程根与 IpcApi 共存各领域逐步迁移进ipc/IpcApi直至ipc.ts被退役§1从 main.ts 的源码可以看到这一偏差的实况registerIpc()的注释明确写道——遗留的单体 IPC 注册造成了 bootstrap 与 IPC 就绪之间的时序耦合TODO(v2) 计划将其分解为生命周期服务内部的逐服务ipcHandle/ipcOn。设计使然的边界不是偏差因此被有意省略data/migration/v2/迁移器读取领域数据§1以及logger/application从任何层级的环境化访问§3。9. 相关文档架构总览——进程模型、数据流、monorepo 目录树本文档的跨进程上级。渲染进程架构 / 共享层架构——各进程目录参考的对等文档。命名规范——§4.8 封闭顶层、§4.9 单复数、§4.10 feature 与类型桶、§5.2 按形态路由。赞分享人工智能大模型AI 应用交互助手本地部署【免费下载链接】cherry-studio Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端项目地址https://gitcode.com/CherryHQ/cherry-studio点击查看免费下载相关推荐Cherry Studio 主进程架构详解src/main 的封闭目录体系、依赖方向与治理规则Cherry Studio 主进程架构详解src/main 的封闭目录体系、依赖方向与治理规则 Cherry Studio 的 Electron 主进程目录AI 应用大模型桌面应用本地部署RAGCherry Studio shared 跨进程基础层架构两大不变量、封闭顶层目录集与放置决策实战手册Cherry Studio shared 跨进程基础层架构两大不变量、封闭顶层目录集与放置决策实战手册 本文基于 Cherry Studio 仓库的官方架构AI 应用大模型桌面应用本地部署RAGCherry Studio 文档治理与规格驱动流程封闭集目录树、Frontmatter 门禁与 Agent 决策记录Cherry Studio 文档治理与规格驱动流程封闭集目录树、Frontmatter 门禁与 Agent 决策记录 本文基于 Cherry Studio 仓AI 应用大模型桌面应用本地部署RAG创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考