news 2026/9/4 16:56:26

从零读懂AI智能客服(二):Java 多模块项目如何协作:Maven 依赖、Spring 注入与运行时装配

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从零读懂AI智能客服(二):Java 多模块项目如何协作:Maven 依赖、Spring 注入与运行时装配

本文主题:解释模块为什么能互相使用,以及 Maven、Spring 和公共接口分别承担什么职责
适合读者:刚接触 Maven 多模块、Spring 依赖注入和模块化单体架构的 Java 开发者
代码基线:当前学习分支源码快照

上一篇:从零读懂 AI 智能客服后端架构:模块职责与 SSE 聊天链路
下一篇:AI 智能客服为什么不能照搬传统三层架构:CRUD 与 AI 编排分层对比


🐟这里是yurenpai

27届开发者,主要学习 Java 后端与 AI 应用开发。

这里记录真实项目中的代码调用链、Agent/RAG 工程化、问题排查和开发复盘。

个人理念:

模块之间能否协作,不能只看目录位置,必须同时追踪构建依赖和运行时装配。

写在前面

先说结论:两个模块放在同一个仓库里,并不代表它们可以直接互相调用。真正决定模块协作的是三层机制:Maven 负责建立编译依赖,Spring 负责发现并注入运行时 Bean,公共接口负责控制依赖方向、避免业务模块形成循环依赖。

本文会以“聊天模块调用工作流、工作流反向使用聊天能力”为贯穿案例,重点讲清:

  1. 父子关系、聚合关系和依赖关系有什么区别;
  2. admin为什么能够把多个业务模块装进同一个应用;
  3. 接口放在公共模块中,为什么能避免chat ↔ aiflow的循环依赖;
  4. 如何用 POM、import、接口实现和启动模块反向验证一次跨模块调用。

说明:本文以当前项目源码为例,重点解释模块协作方法;不同项目的模块名称可能不同,但分析步骤可以复用。

代码说明:除明确标注为完整源码外,文中的 POM、Java 代码和目录片段均为根据当前源码整理的简化示意;文中的“已核对”表示完成静态源码核对,不等同于启动或接口测试通过。


一、为什么要拆成这么多模块

假设不拆模块,把所有代码都放在一个项目中:

src/main/java/com/tst/pharma/ ├─ 用户管理 ├─ 权限管理 ├─ 聊天 ├─ 知识库 ├─ 工作流 ├─ AI 流程 ├─ Redis ├─ SSE ├─ 短信 ├─ 文件上传 ├─ Excel └─ 定时任务

时间长了会出现几个问题:

1. 所有代码混在一起,不知道属于哪个业务; 2. 一个聊天模块也能随便依赖用户、短信、工作流内部实现; 3. 公共工具被复制多份; 4. 修改一个模块容易影响其他模块; 5. Maven 依赖越来越混乱; 6. 新人很难判断代码应该放在哪里; 7. 无法控制模块之间的依赖方向。

所以项目进行了两次拆分。

第一次:按大职责拆分

tst-pharma-admin ├─ 应用启动和组装 tst-pharma-common ├─ 公共基础能力 tst-pharma-modules ├─ 具体业务模块 tst-pharma-extend └─ 独立扩展服务

第二次:在每个大模块内部继续拆分

例如公共能力拆成:

tst-pharma-common-core tst-pharma-common-web tst-pharma-common-redis tst-pharma-common-sse tst-pharma-common-mybatis tst-pharma-common-satoken

业务能力拆成:

tst-pharma-system tst-pharma-chat tst-pharma-workflow tst-pharma-aiflow tst-pharma-generator

这样就可以做到:

聊天模块需要 SSE → 只依赖 common-sse 聊天模块需要 Web 能力 → 只依赖 common-web 聊天模块不需要短信 → 就不必直接依赖 common-sms

这就是“模块化”的主要意义:

不是为了把目录弄多,而是为了划分职责、复用能力并控制依赖。


二、先区分三个容易混淆的概念

项目中有三种关系,一定要区分。

1. 父子关系

例如子模块的pom.xml中:

<parent><groupId>com.tst.pharma</groupId><artifactId>tst-pharma-main-backend</artifactId><version>${revision}</version></parent>

它表示子模块继承父工程的:

版本号 依赖版本管理 Maven 插件 Java 版本 构建配置

但是:

继承父 POM,不代表父模块能够直接使用子模块的 Java 类。


2. 聚合关系

pom.xml中:

<modules><module>tst-pharma-admin</module><module>tst-pharma-common</module><module>tst-pharma-extend</module><module>tst-pharma-modules</module></modules>

它表示:

在根目录执行 Maven 构建时 → Maven 会一起构建这些模块

但是:

被根 POM 聚合,也不等于模块之间能够互相调用。

例如:

tst-pharma-chat tst-pharma-workflow

虽然都被根项目聚合,但如果chat/pom.xml没有依赖workflow,那么chat不能直接随意导入workflow的类。


3. 依赖关系

真正决定一个模块能否使用另一个模块 Java 类的是:

<dependency><groupId>com.tst.pharma</groupId><artifactId>被依赖模块</artifactId></dependency>

例如tst-pharma-chat/pom.xml中有:

<dependency><groupId>com.tst.pharma</groupId><artifactId>tst-pharma-common-chat</artifactId></dependency><dependency><groupId>com.tst.pharma</groupId><artifactId>tst-pharma-common-sse</artifactId></dependency>

所以聊天模块才能使用:

importcom.tst.pharma.common.chat.domain.dto.request.ChatRequest;importcom.tst.pharma.common.sse.core.SseEmitterManager;

可以记成:

聚合关系 └─ 决定一起构建 父子关系 └─ 决定继承统一配置 依赖关系 └─ 决定是否能使用另一个模块的代码

小鱼点睛

父子关系统一配置,聚合关系决定一起构建,依赖关系决定能否使用对方的 Java 类。两个目录即使紧挨在一起,也不会自动产生代码依赖。


三、项目真实的模块依赖方向

整个项目可以简化成:

tst-pharma-admin 主应用启动和组装 │ ┌───────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ tst-pharma-system tst-pharma-chat tst-pharma-workflow 系统管理业务 AI聊天与知识库 传统业务流程 │ ▼ tst-pharma-aiflow AI流程编排 上述业务模块继续依赖: tst-pharma-common-* 公共基础组件

更准确地说,admin直接依赖:

tst-pharma-system tst-pharma-generator tst-pharma-chat tst-pharma-workflow tst-pharma-aiflow

因此最终启动admin时,这些业务模块都会进入主应用。


四、admin是怎么把所有业务模块装进来的

文件:

D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-admin\pom.xml

它声明了:

admin ├─ 依赖 system ├─ 依赖 generator ├─ 依赖 chat ├─ 依赖 workflow └─ 依赖 aiflow

构建主应用时,Maven 会把这些模块的编译产物和依赖一起放进最终应用。

可以将最终运行的应用理解成:

tst-pharma-admin.jar ├─ admin 自己的类 ├─ system 模块的类 ├─ chat 模块的类 ├─ workflow 模块的类 ├─ aiflow 模块的类 ├─ generator 模块的类 └─ 它们所依赖的 common 类

因此运行时主要是:

一个 JVM 进程 一个 Spring 容器 一个后端端口 6039

而不是:

system 启一个端口 chat 启一个端口 workflow 启一个端口 aiflow 再启一个端口

所以当前主体架构属于:

模块化单体应用,不是微服务架构。


五、公共模块是怎么被业务模块使用的

例子一:聊天模块使用 SSE

ChatServiceFacade中:

privatefinalSseEmitterManagersseEmitterManager;

SseEmitterManager不在聊天模块,而在:

tst-pharma-common-sse

聊天模块的pom.xml声明了:

<dependency><groupId>com.tst.pharma</groupId><artifactId>tst-pharma-common-sse</artifactId></dependency>

完整关系是:

tst-pharma-chat │ │ Maven 依赖 ▼ tst-pharma-common-sse │ ├─ SseEmitterManager ├─ SseMessageUtils ├─ SseEventDto └─ SSE 自动配置

于是聊天模块才能:

建立 SSE 发送 content 发送 done 发送 error 关闭 SSE

例子二:聊天模块使用登录认证

聊天代码中使用:

LoginHelper.getUserId(); StpUtil.getTokenValue();

这涉及:

common-satoken common-redis Sa-Token

依赖链不一定都由聊天模块直接声明,也可以通过依赖传递获得。

简化理解:

tst-pharma-chat ↓ common-chat / common-sse / common-web ↓ common-satoken ↓ common-redis ↓ Redis

所以一个模块不一定需要把所有底层依赖都重新声明一遍。


例子三:系统模块使用 MyBatis

SysConfigMapper

publicinterfaceSysConfigMapperextendsBaseMapperPlus<SysConfig,SysConfigVo>{}

其中BaseMapperPlus位于:

tst-pharma-common-mybatis

关系为:

tst-pharma-system │ ▼ tst-pharma-common-mybatis │ ▼ MyBatis-Plus │ ▼ MySQL

因此system模块不需要自己再实现一套分页、Mapper 基类和数据库配置。


六、模块之间不仅靠 Maven,还靠 Spring 连接

Maven 解决的是:

编译时能不能看到另一个模块的类

Spring 解决的是:

程序启动后,具体使用哪个实现对象

启动类:

D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-admin\src\main\java\com\tst\pharma\TstPharmaApplication.java

位于根包:

packagecom.tst.pharma;

并且有:

@SpringBootApplication

Spring 默认会从启动类所在包向下扫描:

com.tst.pharma ├─ controller ├─ service ├─ config ├─ factory ├─ workflow └─ ...

即使类分别位于不同 Maven 模块中,只要最后都被admin引入,并且包名属于:

com.tst.pharma...

Spring 就能发现它们。

例如:

chat 模块 └─ ChatController └─ @Controller chat 模块 └─ ChatServiceFacade └─ @Service aiflow 模块 └─ WorkflowStarter └─ Spring Bean common-sse 模块 └─ SSE 自动配置 └─ 注册 SseEmitterManager

它们最后都在同一个 Spring 容器中。


七、Spring 依赖注入是怎么跨模块工作的

例如ChatController中:

privatefinalChatServiceFacadechatService;

因为类上有:

@RequiredArgsConstructor

Lombok 会生成类似构造器:

publicChatController(ChatServiceFacadechatService){this.chatService=chatService;}

Spring 启动时发现:

ChatController 需要 ChatServiceFacade

然后又发现:

@ServicepublicclassChatServiceFacade{}

于是完成注入:

Spring 容器 ├─ 创建 ChatServiceFacade ├─ 创建 ChatController └─ 把 ChatServiceFacade 放进 ChatController

虽然它们处在不同目录甚至不同 Maven 模块,也不影响运行时注入,只要满足:

1. admin 的 Maven 依赖把两个模块装进来了; 2. Spring 扫描到了对应类; 3. 类注册成了 Bean; 4. 依赖类型能够匹配。

小鱼点睛

Maven 只解决“编译时能不能看见接口和类”,真正把接口字段连接到实现对象的是 Spring 容器。只有编译依赖、Bean 扫描和类型匹配同时成立,跨模块注入才会成功。


八、项目中最典型的跨模块设计:common-chat接口桥梁

这一部分很重要,因为它解释了:

chataiflow为什么可以互相协作,但没有在 POM 中直接互相依赖。

先看结构:

tst-pharma-common-chat ├─ IChatService ├─ IChatModelService └─ IWorkFlowStarterService

它们只是接口和公共协议,不包含具体业务实现。

聊天服务接口

位于:

tst-pharma-common-chat └─ IChatService

具体实现位于聊天模块:

@ServicepublicclassChatServiceFacadeimplementsIChatService{}

关系:

common-chat └─ 定义 IChatService chat └─ ChatServiceFacade 实现 IChatService

工作流启动接口

位于:

tst-pharma-common-chat └─ IWorkFlowStarterService

具体实现位于 AI 流程模块:

publicclassWorkflowStarterimplementsIWorkFlowStarterService{}

关系:

common-chat └─ 定义 IWorkFlowStarterService aiflow └─ WorkflowStarter 实现 IWorkFlowStarterService

chat 调用 aiflow

ChatServiceFacade中不是直接依赖某个aiflow内部类,而是:

privatefinalIWorkFlowStarterServiceworkFlowStarterService;

流程是:

ChatServiceFacade │ │ 只认识公共接口 ▼ IWorkFlowStarterService ▲ │ 由 Spring 在运行时寻找实现 │ WorkflowStarter

这样chat模块在编译时不需要直接依赖aiflow


aiflow 调用 chat

aiflowWorkflowUtil中使用:

privateIChatServicechatService;privateIChatModelServicechatModelService;

流程:

WorkflowUtil │ │ 只依赖公共接口 ▼ IChatService ▲ │ Spring 注入实现 │ ChatServiceFacade

所以整体是:

common-chat 公共接口和公共请求对象 ▲ ▲ │ │ chat aiflow 实现聊天接口 实现流程接口

这叫:

通过公共契约解耦业务模块。

小鱼点睛

把接口下沉到双方都能依赖的公共模块,目的不只是“集中存放公共类”,而是反转依赖方向:业务模块面向稳定契约协作,避免chataiflow在 POM 中相互咬住。


九、为什么接口要放在common-chat,而不是直接放在chat

如果IChatService放在:

tst-pharma-chat

那么aiflow为了调用它,就必须:

aiflow → 依赖 chat

同时聊天模块为了启动 AI 工作流,可能又需要:

chat → 依赖 aiflow

最后形成:

chat → aiflow ↑ ↓ └───────┘

这就是 Maven 循环依赖。

Maven 无法正常处理这样的模块关系。

当前项目把接口放进公共模块后,变成:

chat ──────→ common-chat aiflow ────→ common-chat

然后由admin同时引入:

admin ├─ chat └─ aiflow

运行时再由 Spring 把实现连接起来。

这个设计可以理解为插座:

common-chat └─ 规定插座形状 chat └─ 提供一种插头 aiflow └─ 提供或使用另一种插头 Spring └─ 启动时把兼容的插头插到插座上

十、admin是最终的组装者

这个项目中,真正知道“我要同时加载哪些业务模块”的是:

tst-pharma-admin

它的pom.xml相当于组装清单:

主应用需要: ├─ system ├─ generator ├─ chat ├─ workflow └─ aiflow

因此可以这样理解:

common └─ 制造公共零件 modules ├─ 制造用户系统 ├─ 制造聊天系统 ├─ 制造业务工作流 └─ 制造 AI 工作流 admin └─ 把所有零件和业务模块装成完整应用

十一、数据库 Mapper 又是怎么跨模块加载的

配置位于:

D:\Tools\java\aiwork\tst-pharma-main-backend\tst-pharma-admin\src\main\resources\application.yml

其中配置了:

mybatis-plus:mapperPackage:com.tst.pharma.**.mappermapperLocations:classpath*:mapper/**/*Mapper.xmltypeAliasesPackage:com.tst.pharma.**.domain

MyBatis 配置类使用:

@MapperScan("${mybatis-plus.mapperPackage}")

意思是扫描所有模块中符合下面规则的 Mapper:

com.tst.pharma.任意内容.mapper

例如:

system 模块 └─ com.tst.pharma.system.mapper.SysConfigMapper chat 模块 └─ com.tst.pharma.mapper.ChatMessageMapper workflow 模块 └─ 对应的 mapper

classpath*:也表示:

不只查当前模块,还会查依赖 JAR 中的 Mapper XML。

所以启动一个admin,能够把多个业务模块中的 Mapper 全部注册到同一个 MyBatis/Spring 容器。


十二、配置文件如何影响所有模块

主要运行配置集中在:

tst-pharma-admin\src\main\resources\application.yml tst-pharma-admin\src\main\resources\application-dev.yml

这里配置:

端口 数据库 Redis MyBatis Sa-Token 多租户 日志 SSE 上传目录

虽然配置文件在admin,但 Spring 启动后创建的是一个统一环境。

因此:

system 模块可以使用数据源 chat 模块可以使用数据源 workflow 模块可以使用 Redis common-sse 可以读取 SSE 配置 common-satoken 可以读取认证配置

它们不需要各自维护一套application.yml

可以理解成:

admin application.yml │ ▼ Spring Environment ├─ system 使用 ├─ chat 使用 ├─ workflow 使用 ├─ aiflow 使用 └─ common 组件使用

十三、数据库和 Redis 也是模块之间的公共基础设施

当前主要业务模块运行在同一个应用中,并共享:

MySQL Redis 登录状态 租户上下文 事务管理器 SSE 连接管理

例如一次聊天请求可能发生:

chat 模块 ├─ 从 chat_model 表查询模型配置 ├─ 从 chat_message 表查询历史消息 ├─ 从 knowledge_info 表查询知识库配置 ├─ 使用 Redis/登录上下文获取认证信息 └─ 使用 SSE 向当前用户发送结果

但要注意:

共享同一个数据库,不代表模块应该随意直接修改其他模块的表。

正常情况下应优先:

模块 A → 调用公共接口 → 模块 B 的 Service → 模块 B 的 Mapper → 模块 B 负责自己的表

而不是:

模块 A → 直接拿模块 B 的 Mapper → 随意修改模块 B 的表

否则模块边界会再次被破坏。


十四、用聊天请求看模块之间如何真正协作

一次:

POST /chat/send

背后会经过很多模块。

客户端请求 │ ▼ common-web / common-satoken ├─ Web 请求处理 ├─ 登录认证 └─ 用户上下文 │ ▼ tst-pharma-chat ├─ ChatController ├─ ChatServiceFacade ├─ 场景分类 └─ 模型调用 │ ├──────────────┐ ▼ ▼ common-sse common-chat SSE连接和事件 公共请求对象和接口 │ │ │ ├─────────────┐ │ ▼ ▼ │ aiflow chat实现 │ AI流程启动 模型聊天实现 │ ▼ 浏览器逐段接收回复

如果是普通聊天:

ChatController → ChatServiceFacade → ChatModelService → ChatServiceFactory → 模型供应商实现 → LangChain4j → 外部模型接口 → common-sse → 前端

如果是 AI 工作流:

ChatController → ChatServiceFacade → IWorkFlowStarterService → aiflow 中的 WorkflowStarter → AI 工作流节点执行 → 需要模型时调用 IChatService → chat 中的 ChatServiceFacade → 模型

这就是多个模块之间的真实协作。


十五、extend为什么没有被直接装进admin

根 POM 聚合了:

tst-pharma-extend

所以执行根 Maven 构建时,会一起构建:

monitor-admin snailjob-server

但是admin/pom.xml没有把它们作为主业务依赖引入。

这说明:

根 POM 聚合它们 ≠ admin 运行时包含它们

它们更可能是独立启动的扩展服务:

主后端 └─ tst-pharma-admin 监控服务 └─ tst-pharma-monitor-admin 任务调度服务 └─ tst-pharma-snailjob-server

所以再次强调:

聚合 └─ 一起构建 依赖 └─ 编译和运行时使用

十六、当前项目的核心模块关系图

可以先保存这张图:

tst-pharma-main-backend │ ├─ tst-pharma-common │ ├─ common-core │ │ └─ 最底层公共对象、异常、工具 │ │ │ ├─ common-redis │ │ └─ 依赖 common-core │ │ │ ├─ common-satoken │ │ └─ 依赖 common-core、common-redis │ │ │ ├─ common-mybatis │ │ └─ 依赖 common-core、common-satoken │ │ │ ├─ common-sse │ │ └─ 依赖 core、redis、satoken、json │ │ │ ├─ common-web │ │ └─ Web 公共能力 │ │ │ └─ common-chat │ ├─ 依赖 core │ ├─ 依赖 sse │ ├─ 依赖 mybatis │ └─ 定义聊天、模型、工作流公共接口 │ ├─ tst-pharma-modules │ ├─ system │ │ └─ 依赖 MyBatis、Web、认证、租户等公共模块 │ │ │ ├─ chat │ │ ├─ 依赖 common-chat │ │ ├─ 依赖 common-sse │ │ ├─ 依赖 common-web │ │ └─ 实现 IChatService、IChatModelService │ │ │ ├─ aiflow │ │ ├─ 依赖 common-chat │ │ ├─ 使用 IChatService │ │ └─ 实现 IWorkFlowStarterService │ │ │ ├─ workflow │ │ └─ 依赖 MyBatis、Web、租户、认证等公共模块 │ │ │ └─ generator │ └─ 依赖 MyBatis、Web、文档、日志等公共模块 │ ├─ tst-pharma-admin │ ├─ 引入 system │ ├─ 引入 chat │ ├─ 引入 workflow │ ├─ 引入 aiflow │ ├─ 引入 generator │ └─ 启动一个完整 Spring Boot 应用 │ └─ tst-pharma-extend ├─ monitor-admin └─ snailjob-server

十七、判断“两个模块怎么关联”的固定方法

以后看到两个模块,不要凭目录名猜,可以按下面五步确认。

第一步:看调用模块的pom.xml

例如想知道chat能不能使用common-sse

打开 tst-pharma-chat/pom.xml 搜索 tst-pharma-common-sse

如果有依赖,说明编译时可见。

第二步:看 Javaimport

例如:

importcom.tst.pharma.common.sse.core.SseEmitterManager;

说明代码确实使用了该模块的类。

第三步:看字段注入类型

例如:

privatefinalIWorkFlowStarterServiceworkFlowStarterService;

说明当前类依赖的是接口。

第四步:查谁实现接口

搜索:

implements IWorkFlowStarterService

找到:

aiflow → WorkflowStarter

第五步:确认最终启动模块是否同时引入双方

查看:

tst-pharma-admin/pom.xml

确认同时依赖:

chat aiflow

这样 Spring 运行时才有机会把它们连接起来。


十八、核心结论

结论一

目录相邻 ≠ 有依赖关系

真正的编译依赖看:

pom.xml 中的 dependency

结论二

根 POM 中有 module ≠ 该模块被装进主应用

它可能只是一起构建。

结论三

admin 是主应用组装者

它把:

system、chat、workflow、aiflow、generator

放进同一个 Spring Boot 应用。

结论四

common 提供公共能力和公共接口 modules 提供具体业务实现

结论五

模块之间推荐通过:

公共接口 Spring 依赖注入

进行协作,而不是互相直接依赖内部实现。

结论六

当前主体不是微服务,而是:

模块化单体 ├─ 编译时分模块 ├─ 代码职责分模块 └─ 运行时主要在同一个 Spring Boot 进程

最典型的真实关系就是:

ChatServiceFacade │ │ 实现 ▼ IChatService(common-chat) ▲ │ 使用 │ WorkflowUtil(aiflow)

以及反方向:

WorkflowStarter(aiflow) │ │ 实现 ▼ IWorkFlowStarterService(common-chat) ▲ │ 使用 │ ChatServiceFacade(chat)

它们由admin同时组装,再由 Spring 在运行时连接。这个例子基本涵盖了整个项目模块化设计的核心。


总结

本文围绕“解释模块为什么能互相使用,以及 Maven、Spring 和公共接口分别承担什么职责”,主要分析了:

  1. 父子、聚合和依赖三种 Maven 关系的边界;
  2. 启动模块如何完成业务模块的最终组装;
  3. Spring 如何跨 JAR 扫描 Bean 并按接口注入实现;
  4. 公共接口桥梁如何控制依赖方向并避免循环依赖;

整个过程可以概括为:

根 POM 聚合 → 子模块声明依赖 → Java 使用公共接口 → Spring 扫描实现类 → admin 统一装配 → 运行时完成调用

Maven 解决“编译时能不能看见”,Spring 解决“运行时由谁来实现”,公共接口解决“依赖应该朝哪个方向”。三者缺一不可。

当前进度

[OK] 已经完成

  • 已完成主要模块 POM 依赖方向的静态核对;
  • 已通过接口定义、实现类和启动模块还原聊天模块与工作流模块的协作关系;

[TODO] 后续继续

  • 下一篇对比普通 CRUD 分层与 AI 对话编排分层;
  • 继续结合真实请求理解模块协作在运行时如何落到方法调用;

小鱼点睛

Maven 解决“编译时能不能看见”,Spring 解决“运行时由谁来实现”,公共接口解决“依赖应该朝哪个方向”。三者缺一不可。

下一篇

下一篇将继续分析“AI 智能客服为什么不能照搬传统三层架构:CRUD 与 AI 编排分层对比”,把本文建立的结构认知继续落到具体代码和对象流上。


这篇文章是我在真实项目学习过程中的阶段性记录。不同项目的命名和目录可能不同,但判断职责边界、依赖方向和数据生命周期的方法可以复用。如果内容中还有遗漏,欢迎一起交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 16:55:41

RawChat多模型聚合平台:一站式解决AI助手选择与集成难题

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 16:55:38

SiC IGBT三相四桥臂功率桥开发实战:从原理到硬件设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 16:54:54

基于MediaPipe的实时疲劳与坐姿检测系统:从原理到Python实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 16:53:26

【版本管理】Git工具

Git管理概念区分:1. Git&#xff1a;底层版本控制工具&#xff0c;必须安装&#xff1b;2. VSCode&#xff1a;编辑器&#xff0c;免费&#xff0c;调用Git做提交&#xff1b;3. Gitee / GitCode&#xff1a;网上远程存代码的网站。个人网站:https://gitee.com/LSHleiDong

作者头像 李华
网站建设 2026/9/4 16:53:10

从云端API到本地部署:构建高可用AI服务的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华