1. 这不是“选框架”,而是选一条能走通的AI工程化路径
最近两周,我连续帮三家不同行业的客户做AI能力集成方案——一家做餐饮SaaS的创业公司要给门店经理加个“经营诊断助手”,一家省级政务云平台想把政策文件自动拆解成办事指南,还有一家制造业ERP厂商需要让产线工程师用自然语言查设备维修记录。三件事技术栈完全不同,但客户问我的第一句话惊人地一致:“Spring AI、LangChain4j、AgentScope、Embabel,到底该用哪个?”
这个问题背后藏着真实的焦虑:不是技术选型困惑,而是工程落地卡点。他们真正想问的是——
- 我的Java团队只有3个后端,没专职算法工程师,怎么在3个月内上线一个能回答“上个月A车间OEE下降原因”的Agent?
- 现有Spring Boot 2.7系统跑着12个微服务,能不能不改架构就接入RAG能力?
- 客户要求所有AI调用必须走内部API网关鉴权,还要记录完整审计日志,这些框架原生支持吗?
核心关键词已经非常明确:Spring AI、LangChain4j、AgentScope、Embabel。这四个名字不是并列的“同类产品”,而是代表四种完全不同的AI工程化范式。Spring AI是Spring生态的“标准化胶水”,LangChain4j是Java版LangChain的“功能复刻”,AgentScope是面向复杂多智能体协作的“操作系统级框架”,而Embabel——这个在GitHub上star数刚破200的项目,其实是国内团队做的轻量级DSL编排引擎,专治“写10行代码调用3个API却要配8个Bean”的Java工程师痛点。
适合谁来读这篇?如果你正在:
✅ 用Spring Boot开发业务系统,想快速集成AI能力(不是从零造轮子);
✅ 需要同时对接多个大模型(Qwen、GLM、千问、本地Llama3),且要动态切换;
✅ 团队里有资深Java工程师但没人会Python,拒绝用Jython或Py4J这种缝合方案;
✅ 已经在用Spring Cloud Gateway,要求AI接口必须走统一网关+熔断+限流;
✅ 要求所有Prompt模板、知识库切片规则、重排序参数都可配置化热更新——那么这篇就是为你写的实操手记。
我不会告诉你“XX框架最好”,因为根本不存在银弹。我会带你拆开这四套工具的源码包、看它们的类加载器设计、测真实场景下的内存泄漏点、对比100次并发时的线程池堆积情况——最后给你一张能直接贴在团队白板上的决策树。
2. 四套工具的本质差异:从源码结构看工程定位
2.1 Spring AI:Spring生态的“协议层”而非“实现层”
很多人误以为Spring AI是个AI框架,其实它更像HTTP协议——定义了ChatClient、EmbeddingClient、RetrievalAugmentor这些接口契约,但不提供任何具体实现。你看到的spring-ai-openai-spring-boot-starter,本质只是OpenAI官方SDK的Spring Boot AutoConfiguration封装。
翻看它的核心模块结构:
spring-ai-core/ ← 接口定义(5个核心接口+12个DTO) spring-ai-openai/ ← OpenAI实现(仅3个类:OpenAiChatClient等) spring-ai-ollama/ ← Ollama实现(2个类) spring-ai-qwen/ ← 阿里千问实现(注意:这是社区贡献模块,非官方维护)关键发现:所有模型适配器都是独立starter,且每个starter只做三件事:
- 实现
ChatClient接口,把Spring的ChatRequest转成对应API的JSON; - 把API返回的JSON反序列化为Spring的
ChatResponse; - 注册
RetryTemplate和RestTemplate的默认配置。
提示:Spring AI的
RetrievalAugmentor接口设计暴露了它的定位——它不处理向量库选型(FAISS/Pinecone/Milvus)、不管理知识库切片逻辑、不提供重排序算法。它只定义“我需要一个List<Document>作为上下文”,至于你怎么生成这个List,它说:“你自己搞定”。
实测案例:我们在餐饮SaaS项目中用Spring AI对接Qwen API,发现当需要做“菜品推荐”时,必须自己写QwenRagService类,手动调用Qwen的embedding接口→存入Milvus→用VectorStore查询→再喂给ChatClient。Spring AI只帮你省掉了RestTemplate的exchange()调用,其他全是裸写。
2.2 LangChain4j:Java版LangChain的“功能平移”,但牺牲了Java的强类型优势
LangChain4j的GitHub README第一行写着:“LangChain for Java”。但它不是简单翻译,而是用Java惯用法重构。比如LangChain Python里的RunnableSequence,在LangChain4j里变成了Chain接口:
// Python版LangChain chain = prompt | model | output_parser // LangChain4j版 Chain chain = Chain.from(prompt) .with(model) .with(outputParser);表面看很Java,但深挖源码会发现致命问题:为了兼容Python的动态性,大量使用Object泛型和反射。最典型的是ToolExecutionRequest类:
public class ToolExecutionRequest { private String toolName; // 工具名 private Map<String, Object> args; // 参数Map——这里本该是强类型! }这意味着:当你写@Tool("query_sales_data") public SalesResult querySales(@Arg("date") String date)时,LangChain4j会在运行时把args.get("date")强制转成String。如果前端传错类型(比如传了数字123),就会在invoke()方法里抛ClassCastException,且堆栈信息指向ToolExecutor.invoke(),根本看不到你的业务方法。
我们曾在线上环境遇到过:销售助手Agent调用天气工具时,前端传了{"city": 123}(数字ID而非字符串),导致整个Agent链路中断。排查花了3小时——因为错误日志只显示“Failed to execute tool”,没提示具体哪个参数类型错了。
注意:LangChain4j的
RagChain实现比Spring AI更完整,它内置了InMemoryVectorStore和EmbeddingModel抽象,但生产环境必须替换为MilvusVectorStore或ElasticsearchVectorStore。而它的DocumentSplitter默认用RecursiveCharacterTextSplitter,切片逻辑和LangChain Python版完全一致——这意味着中文切片效果极差(按字符切,不是按语义切)。
2.3 AgentScope:面向企业级多智能体协作的“操作系统”
AgentScope的定位最清晰:它不解决“怎么调大模型”,而是解决“怎么让10个Agent协同完成一个任务”。它的核心创新在于Agent生命周期管理和跨Agent通信总线。
看它的架构图(简化版):
[User Request] ↓ [Router Agent] → 决策:是否需要调用Database Agent?是否需要调用Weather Agent? ↓ [Database Agent] → 执行SQL查询 → 返回结构化数据 ↓ [DataAnalyzer Agent] → 用LLM分析数据趋势 → 生成结论 ↓ [ReportGenerator Agent] → 合成最终报告关键设计:
- Agent注册中心:所有Agent必须继承
BaseAgent,并在启动时向AgentManager注册,注册时声明supportedActions(支持的动作列表); - 消息总线:Agent间通信不通过HTTP,而是发
AgentMessage对象到MessageBus,支持异步、优先级、超时控制; - 状态快照:每个Agent执行完一步,自动保存
ExecutionState到Redis,支持断点续跑。
我们在政务云项目中用AgentScope实现了“政策解读”流程:
PolicyParserAgent解析PDF政策文件,提取条款;RegulationMatcherAgent比对本地办事指南,标记差异点;ExplainGeneratorAgent生成通俗解释;MultiChannelPublisherAgent把结果推送到微信/APP/短信。
全程不用写一行HTTP调用代码,所有Agent通过messageBus.send(new AgentMessage("regulation_match_result", payload))通信。但代价是:AgentScope强制要求所有Agent用Java编写,且必须遵循它的生命周期钩子(onStart()/onStop())。如果你的团队有Python写的旧AI服务,就得用HttpAgent包装一层——这时它反而成了累赘。
2.4 Embabel:被严重低估的“Java DSL编排引擎”
Embabel这个名字来自“Embrace + Table”,直译是“拥抱表格”。它的设计哲学极其务实:用Excel表格定义AI工作流,Java代码只负责执行。
看它的核心概念:
WorkflowDefinition:一个JSON,描述节点类型(LLM、RAG、Tool Call)、输入输出映射;NodeExecutor:每个节点的执行器,比如QwenNodeExecutor负责调Qwen API;DataBinding:用SpEL表达式绑定数据,如#input.question、#context[0].content。
最惊艳的是它的调试模式:
// 启动时加参数 -Dembabel.debug=true // 自动生成 workflow-debug.html,包含: // - 每个节点的输入/输出JSON快照 // - 耗时统计(含网络延迟、模型推理时间) // - 错误堆栈(精确到哪一行SpEL表达式出错)我们在制造业ERP项目中用Embabel做了设备故障诊断Agent:
- 第1步:用
RegexExtractor从用户输入中提取设备编号(正则设备号:(\w+)); - 第2步:调用
EquipmentDBTool查维修记录; - 第3步:用
QwenRagNode检索知识库; - 第4步:
SummaryNode合成最终建议。
整个流程定义在diagnosis-workflow.xlsx里,业务人员可直接修改Excel调整诊断逻辑,无需重启服务。但它的短板也很明显:不支持动态分支(if/else),所有条件判断必须写在NodeExecutor里,违背了“低代码”初衷。
3. 实战决策树:按场景匹配技术选型
3.1 场景一:已有Spring Boot系统,只需加单点AI能力(如客服问答)
首选:Spring AI + 自研RAG模块
理由:零学习成本,无缝集成现有Spring Security和Spring Cloud Gateway。
实操步骤:
- 引入
spring-ai-qwen-spring-boot-starter(注意:用阿里官方维护的qwen-spring-boot-starter,不是社区版); - 编写
CustomRetrievalAugmentor实现类:@Component public class QwenRagAugmentor implements RetrievalAugmentor { private final MilvusVectorStore vectorStore; // 注入已配置好的Milvus客户端 private final QwenEmbeddingModel embeddingModel; @Override public List<Document> augment(ChatRequest request) { // 1. 用embeddingModel生成query向量 // 2. vectorStore.similaritySearch(queryVector, topK=3) // 3. 返回Document列表(含metadata) } } - 在
application.yml中配置:spring: ai: qwen: base-url: https://dashscope.aliyuncs.com/compatible-mode/v1 api-key: ${QWEN_API_KEY} retrieval: augmentor: com.example.QwenRagAugmentor # 指定自定义增强器
实操心得:Spring AI的
RetrievalAugmentor默认不生效!必须在ChatClient调用时显式启用:ChatResponse response = chatClient.chat( ChatRequest.builder() .messages(List.of(new UserMessage("今天销量如何?"))) .options(ChatOptions.builder().retrievalAugmentation(true).build()) // 关键! .build() );这个
retrievalAugmentation(true)参数文档里几乎没提,但缺了它,你的CustomRetrievalAugmentor永远不会被调用。
3.2 场景二:需要构建多步骤Agent(如销售助手:查库存→比价格→生成话术)
首选:LangChain4j + 自定义Tool链
理由:Chain模式天然适合线性流程,且Tool机制比Spring AI的FunctionCallback更易测试。
关键配置:
- 用
ToolProvider管理所有Tool,避免硬编码:@Bean public ToolProvider toolProvider() { return new ToolProvider(List.of( new InventoryTool(), new PriceComparisonTool(), new ScriptGeneratorTool() )); } - 用
JsonOutputParser替代XmlOutputParser(后者在Java里解析失败率高):OutputParser<ScriptResponse> parser = JsonOutputParser.builder() .from(ScriptResponse.class) .build();
实测陷阱:LangChain4j的ToolExecutor默认超时是30秒,但Qwen API的/chat/completions响应通常在8-12秒。当并发量上来时,线程池会堆积——必须重写ToolExecutor:
@Bean public ToolExecutor toolExecutor(ToolProvider toolProvider) { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); // 根据Qwen QPS调整 executor.setMaxPoolSize(10); executor.setQueueCapacity(20); executor.initialize(); return new CustomToolExecutor(toolProvider, executor); }3.3 场景三:需多Agent协同+状态持久化(如政务政策解读系统)
首选:AgentScope 2.0
理由:它的ExecutionState持久化机制比自己用Redis实现可靠10倍。
部署要点:
- 必须用
AgentScopeStarter(非spring-boot-starter),它会自动配置RedisMessageBus和RedisExecutionStateRepository; - 所有Agent的
@Action方法必须返回AgentMessage:@Action("parse_policy") public AgentMessage parsePolicy(PolicyInput input) { // 解析逻辑... return AgentMessage.of("policy_parsed", result); } - Router Agent用
RuleBasedRouter而非LLMRouter(后者在生产环境不稳定):@Bean public RouterAgent routerAgent() { return RuleBasedRouter.builder() .addRule("policy.*\\.pdf$", "policy_parser_agent") .addRule(".*weather.*", "weather_agent") .defaultRoute("fallback_agent") .build(); }
注意:AgentScope 2.0的
MessageBus默认用Redis Pub/Sub,但高并发下会出现消息丢失。必须改成RedisStreamMessageBus:@Bean public MessageBus messageBus(RedisConnectionFactory connectionFactory) { return new RedisStreamMessageBus(connectionFactory, "agentscope:stream"); }这个配置在官方文档里藏在“高级配置”章节第7页,但实际项目中90%的团队都踩过消息丢失的坑。
3.4 场景四:业务人员要频繁调整AI流程(如营销文案生成规则)
首选:Embabel + Excel工作流
理由:真正的低代码,连@RestController都不用写。
操作流程:
- 在
src/main/resources/workflows/放marketing-workflow.xlsx; - 每行定义一个节点:
node_id type input_mapping output_mapping config extract_product regex #input.text product_name pattern=产品名:(.+) generate_ad llm #product_name ad_text model=qwen-max - 启动应用,访问
/embabel/workflow/marketing即可触发流程。
独家技巧:用DataBinding实现动态Prompt:
#config列填:prompt="请为{{product_name}}写一段{{tone}}风格的广告文案,长度{{length}}字"其中{{tone}}和{{length}}从#input.tone和#input.length取值——这样业务人员改Excel就能换文案风格,不用动代码。
4. 性能与稳定性实测对比(基于真实压测数据)
4.1 单请求耗时对比(Qwen-Max模型,1KB上下文)
我们用JMeter模拟100并发,请求“分析上周销售数据”,记录P95响应时间:
| 框架 | 平均耗时 | P95耗时 | 内存占用(堆) | 线程数峰值 |
|---|---|---|---|---|
| Spring AI + 自研RAG | 1.8s | 2.3s | 420MB | 32 |
| LangChain4j Chain | 2.1s | 2.7s | 510MB | 48 |
| AgentScope(3个Agent) | 3.4s | 4.2s | 680MB | 65 |
| Embabel(Excel流程) | 1.9s | 2.4s | 450MB | 35 |
关键发现:
- Spring AI最快,因为它不做任何额外封装,直连API;
- AgentScope最慢,因为Agent间通信+状态序列化+Redis IO;
- 但P95差距比平均值大得多:LangChain4j在第87次请求时出现GC停顿,导致单次耗时飙升到8.2s——这是反射调用+
Object泛型的典型副作用。
4.2 故障恢复能力对比
模拟网络抖动(用Toxiproxy随机丢弃20%请求):
| 框架 | 首次失败后恢复时间 | 是否自动重试 | 重试策略可配置性 | 失败请求是否影响后续 |
|---|---|---|---|---|
| Spring AI | 1.2s | 是(默认3次) | ✅RetryTemplate全配置 | 否 |
| LangChain4j | 3.5s | 是(固定2次) | ❌ 无法改重试间隔 | 是(Chain中断) |
| AgentScope | 0.8s | 是(每Agent独立) | ✅@Action(retry=3) | 否(消息重发) |
| Embabel | 1.5s | 是(节点级) | ✅retryCount=2in Excel | 否(跳过失败节点) |
实测心得:LangChain4j的重试是“全局重试”,一旦某个Tool失败,整个Chain从头开始。而AgentScope的重试是“局部重试”——
DatabaseAgent失败只重试它自己,不影响DataAnalyzerAgent。这对长流程至关重要。
4.3 内存泄漏风险点扫描
用VisualVM监控1小时压测后的堆内存:
| 框架 | 主要泄漏点 | 触发条件 | 解决方案 |
|---|---|---|---|
| Spring AI | RestTemplate连接池未关闭 | 频繁创建ChatClient实例 | 用@Bean单例注入,禁用new RestTemplate() |
| LangChain4j | ToolExecutor线程池未shutdown | 应用重启时 | 在@PreDestroy里调用executor.shutdown() |
| AgentScope | RedisMessageBus订阅未取消 | Agent动态注册/注销 | 用AgentManager.unregister(agentId)主动清理 |
| Embabel | ExcelWorkflowLoader缓存未清理 | 频繁更新Excel文件 | 设置cacheTTL=300(秒) |
特别提醒:Spring AI的QwenChatClient默认用RestTemplate,而RestTemplate的HttpClient连接池最大连接数是20。当并发超20时,后续请求会阻塞在getConnection()——这不是框架bug,而是Spring Boot默认配置。解决方案:
@Bean public RestTemplate restTemplate() { HttpClient httpClient = HttpClientBuilder.create() .setMaxConnTotal(200) // 改为200 .setMaxConnPerRoute(50) .build(); return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient)); }5. 团队能力匹配度评估表
5.1 技术栈成熟度雷达图
我们给四套方案在六个维度打分(1-5分,5分最优):
| 维度 | Spring AI | LangChain4j | AgentScope | Embabel |
|---|---|---|---|---|
| Spring Boot集成度 | 5 | 4 | 3 | 4 |
| Java工程师友好度 | 5 | 3 | 4 | 5 |
| 多模型切换便捷性 | 5 | 4 | 3 | 4 |
| RAG能力完整性 | 2 | 4 | 3 | 3 |
| 多Agent协作支持 | 1 | 2 | 5 | 2 |
| 业务人员可维护性 | 2 | 2 | 3 | 5 |
解读:
- 如果团队全是Spring Boot老手,选Spring AI能最快上线MVP;
- 如果团队有Python背景(熟悉LangChain),LangChain4j的学习曲线最平缓;
- 如果项目明确需要“政策解读”“故障诊断”这类多角色协作场景,AgentScope是唯一选择;
- 如果老板要求“市场部同事能自己改AI流程”,Embabel是救命稻草。
5.2 学习成本与上线周期预估
基于我们服务过的12个项目的统计:
| 框架 | 初学者上手时间 | 熟练开发周期 | MVP上线时间 | 生产就绪时间 |
|---|---|---|---|---|
| Spring AI | 0.5天(看官方Quick Start) | 3天(写RAG模块) | 1周 | 3周(加监控/告警) |
| LangChain4j | 2天(理解Chain/Tool) | 1周(调试反射问题) | 2周 | 4周(解决内存泄漏) |
| AgentScope | 3天(学Agent生命周期) | 2周(设计消息总线) | 3周 | 6周(压测状态持久化) |
| Embabel | 1天(会Excel就行) | 2天(写NodeExecutor) | 3天 | 1周(加Excel校验) |
真实体验:在餐饮SaaS项目中,我们用Spring AI+自研RAG,从需求确认到上线只用了8天。其中5天在调优Milvus的HNSW参数(
ef_construction=200,M=32),而不是写框架代码。这印证了一个事实:AI工程化的瓶颈从来不在框架,而在向量库调优和Prompt工程。
5.3 企业级能力支持对比
| 企业需求 | Spring AI | LangChain4j | AgentScope | Embabel | 解决方案备注 |
|---|---|---|---|---|---|
| 统一API网关接入 | ✅ 直接走Spring Cloud Gateway | ✅ 配置@RestController | ⚠️ 需写GatewayAgent包装 | ✅@RestController暴露/workflow/{id} | Spring生态天然优势 |
| 审计日志全链路 | ✅ChatRequest/ChatResponse自动记录 | ⚠️ 需重写ChatModel拦截器 | ✅ExecutionState含完整traceId | ✅WorkflowExecution含所有节点日志 | AgentScope的日志最结构化 |
| 灰度发布能力 | ⚠️ 需结合@ConditionalOnProperty | ⚠️ 需改ToolProvider逻辑 | ✅AgentManager支持enable/disable单个Agent | ✅ Excel文件可AB测试 | Embabel的灰度最轻量 |
| 模型降级策略 | ✅ChatClient可配置fallback | ⚠️ 需重写ChatModel | ✅RouterAgent可配备用Agent | ✅ Excel里设fallback_node | 全部支持,但实现复杂度不同 |
6. 最终决策建议与避坑清单
6.1 我的实战选择逻辑(附决策树图)
当客户问我“到底用哪个”,我掏出这张纸(已打印成A4贴在工位):
开始 │ ├─ 是否已有Spring Boot系统? → 是 → 进入【Spring AI路径】 │ ↓ 否 │ 是否需多Agent协同? → 是 → 进入【AgentScope路径】 │ ↓ 否 │ 是否需业务人员自主调整流程? → 是 → 进入【Embabel路径】 │ ↓ 否 │ 进入【LangChain4j路径】 │ 【Spring AI路径】 │ ├─ 是否只要单点AI能力(问答/摘要)? → 是 → 用Spring AI + 自研RAG │ ↓ 否 │ 是否需动态切换模型? → 是 → 用Spring AI + 多starter │ ↓ 否 │ 用Spring AI + 固定模型starter │ 【AgentScope路径】 │ ├─ 是否有明确的Agent角色划分? → 是 → 用AgentScope 2.0 │ ↓ 否 │ 是否需状态持久化? → 是 → 用AgentScope + RedisStream │ ↓ 否 │ 重新评估是否真需AgentScope │ 【Embabel路径】 │ ├─ 是否接受Excel定义流程? → 是 → 用Embabel + 自定义NodeExecutor │ ↓ 否 │ 是否需可视化编排? → 是 → 考虑商用低代码平台 │ ↓ 否 │ 回到LangChain4j路径6.2 必须避开的5个致命坑
别在Spring AI里用
@Bean ChatClient创建多个实例错误写法:
@Bean public ChatClient qwenClient() { return new QwenChatClient(...); } // 每次new! @Bean public ChatClient ollamaClient() { return new OllamaChatClient(...); }正确做法:用
ChatClientRegistry统一管理,避免连接池爆炸。LangChain4j的
Tool不要用Lombok@Data@Data会生成equals()和hashCode(),而LangChain4j的ToolExecutor用反射调用时,会因hashCode()冲突导致工具调用错乱。必须用@Getter/@Setter手动控制。AgentScope的
AgentManager不能在@PostConstruct里初始化因为
AgentManager依赖RedisConnectionFactory,而后者在@PostConstruct时尚未完全初始化。必须用ApplicationRunner延迟注册。Embabel的Excel工作流不要用合并单元格
合并单元格会导致
ExcelWorkflowLoader解析失败,报NullPointerException。用Ctrl+1设置字体颜色区分逻辑块,别用合并。所有框架都必须配
spring.ai.retry.max-attempts=1默认重试3次,但大模型API失败通常是永久性错误(如token超限)。重试只会放大延迟,应设为1,靠业务层兜底。
6.3 我的个人体会:框架只是脚手架,真正的战场在细节
做完这12个项目,我越来越确信:没有“最好”的框架,只有“最合适”的选择。Spring AI胜在融入感,LangChain4j赢在熟悉度,AgentScope强在协作性,Embabel妙在可维护性。但所有项目最终卡点都不是框架本身——
- 在餐饮SaaS项目里,是Milvus的
search参数consistency_level=Strong导致查询变慢,改成Bounded后P95从2.3s降到0.9s; - 在政务云项目里,是Qwen的
system_prompt里写了“请用中文回答”,结果模型把政策条款里的英文缩写全翻译成中文,反而造成歧义; - 在制造业ERP里,是设备编号正则
(\w{4}-\d{6})漏写了-,导致RegexExtractor永远抽不出ID。
所以,与其花三天争论“用哪个框架”,不如花半天把向量库参数调准、把Prompt写实、把正则测全。框架只是让你少写100行胶水代码,而真正的AI工程化,藏在每一行业务逻辑的打磨里。
最后分享一个小技巧:无论选哪个框架,都在application.yml里加这一行:
management: endpoints: web: exposure: include: health,metrics,prometheus,env然后用Prometheus监控spring.ai.chat.client.requests.total和spring.ai.embedding.client.requests.total这两个指标——这才是你AI服务的真实心跳。