资讯动态

Java工程师LLM框架选型实战:Spring AI vs LangChain4j

发布时间:2026/9/14 11:04:19 来源:尧图企业网站定制
1. 为什么 Java 后端工程师在 2026 年必须重新思考 LLM 框架选型2026 年的 Java 生态里LLM 已经不是“要不要用”的问题而是“怎么用得稳、用得快、用得省”的工程现实。我去年带团队重构三个核心业务线的智能客服模块从零开始搭建基于大模型的意图识别知识增强多轮对话系统光是框架选型就卡了整整六周——不是技术不行而是 Spring AI 和 LangChain4j 的演进节奏、能力边界、社区支持和实际落地成本已经和两年前截然不同。你可能在面试中被问到“Spring AI 和 LangChain4j 有什么区别”但真实场景里你要回答的是“如果明天上线我要选哪个为什么踩过哪些坑换框架要重写多少代码” 这就是标题里“实战”二字的分量。Spring AI 背靠 Spring 生态天然适配 Spring Boot 自动配置、Actuator 监控、Reactive 编程模型对习惯写 Controller-Service-Repository 的 Java 工程师极其友好LangChain4j 则更像一个“LLM 原语工具箱”它不强制你用 Spring但把 Prompt 编排、Tool Calling、RAG 流水线、Agent 调度这些底层能力拆得极细尤其适合需要深度定制推理链路、混合检索比如 LangChain4j Milvus、或嵌入已有非 Spring 架构如 Vert.x 或 Quarkus的团队。关键词“Spring AI”“LangChain4j”“Java”“LLM”“框架选型”不是标签而是你每天要面对的决策点是选开箱即用但灵活性受限的“高速公路”还是选可自由铺路但需自建收费站的“越野地图”本文不讲概念对比只讲我在生产环境里跑通的 7 个真实场景——从单次问答 API 到 multi-agent 协作调度从本地 Ollama 模型接入到阿里云百炼 API 集成从 Docker 容器化部署到 JVM 内存泄漏排查——所有结论都来自线上日志、压测报告和 Code Review 记录。如果你正在写简历里的“LLM 应用落地经验”或者正为技术选型 PPT 焦头烂额这篇就是为你写的实操手记。2. 核心设计思路与选型逻辑不是比功能而是比“失控成本”2.1 选型本质是风险对冲不是功能罗列很多团队一上来就拉个表格对比“是否支持 RAG”“是否支持 Tool Calling”“是否支持 Streaming”这完全跑偏了。2026 年的真实战场里两个框架都已原生支持这些能力差异不在“有没有”而在“用起来有多容易失控”。我画过一张“失控成本曲线图”虽不能放图但你可以脑补横轴是项目复杂度从单接口调用 → 多模型路由 → Agent 编排 → 自主决策闭环纵轴是维护该能力所需投入的额外人力小时数。Spring AI 在横轴 0–3 区间斜率极缓——你写一个AiModel注解加个ChatClientBean就能跑通 OpenAI 兼容 API但到了横轴 5比如要让多个 Agent 基于共享 Memory 协同完成订单退款物流查询补偿券发放它的抽象层开始反向约束你你得去读 Spring AI Graph 的源码甚至要 hackAiModelRegistry才能注入自定义的 Agent Executor。LangChain4j 则相反起步陡峭——你得自己组装ChatModel、Retriever、ToolExecutor写Chain编排逻辑连最基础的 Prompt 模板都要手动PromptTemplate.from()但一旦过了学习曲线拐点大概 200 行核心链路代码后你对每个环节的掌控力极强比如要实现“当用户问‘我的快递到哪了’时先查物流 API若超时则 fallback 到知识库摘要”LangChain4j 的FallbackRetriever加ConditionalChain组合三行代码搞定而 Spring AI 得重写整个AiResponseMapper并注册为PrimaryBean。所以我的选型铁律第一条看团队当前阶段的“可控性阈值”。新团队、POC 验证、快速交付 MVP无脑选 Spring AI已有成熟 LLM 工程能力、要构建企业级 AI 中台、或必须对接私有化部署的国产模型如千问、混元LangChain4j 是更安全的选择。2.2 Spring AI 的“Spring 味道”是双刃剑Spring AI 的最大优势也是最大陷阱就是它太“Spring”了。它把 LLM 能力包装成 Spring 的一等公民ChatClient是BeanEmbeddingClient可以Autowired错误统一走SpringAIException甚至RetryTemplate都能直接套在ChatClient上。这带来什么开发体验丝滑——我同事小王三天就用 Spring AI Alibaba Cloud 百炼 API 搭出一个内部文档问答机器人Controller 里就两行RestController public class DocQaController { private final ChatClient chatClient; // Autowired 自动注入 PostMapping(/ask) public String ask(RequestBody QuestionRequest req) { return chatClient.call(new UserMessage(req.getQuery())).getContent(); } }但问题也出在这里当你需要绕过 Spring 的自动装配机制时会非常痛苦。比如我们有个老系统用的是 Dubbo RPC服务发现走 ZooKeeper根本没 Spring Cloud。想复用 Spring AI 的OllamaChatModel就得手动 newOllamaChatModel再手动 setbaseUrl、model、timeout还要自己处理HttpClient的生命周期——这等于抛弃了 Spring AI 80% 的价值。更致命的是它的“隐式依赖”Spring AI 2.0 强制要求spring-boot-starter-webflux哪怕你只用同步 HTTP因为它的ChatClient默认走 WebClient如果你的项目还在用 Servlet 容器Tomcat/Jetty就得额外引入spring-boot-starter-web并配置spring.ai.chat.client.webclient.enabledfalse否则启动报错。这种“默认约定优于配置”的哲学在 LLM 这种快速迭代领域反而成了枷锁。而 LangChain4j 从根上就是“无框架”的它不假设你用什么 Web 框架ChatModel接口只有一个generate(ListChatMessage)方法你传OkHttpClient、Apache HttpClient、甚至自己写的NettyClient都行。我们给金融客户做的风控报告生成系统就用 LangChain4j Vert.x整个链路零 Spring 依赖JVM 启动时间从 12 秒压到 3.2 秒——这对需要秒级弹性伸缩的批处理任务至关重要。2.3 LangChain4j 的“低级 API”不是缺陷而是预留的逃生通道网络热词里总有人吐槽“LangChain4j 低级 API 太难用”这其实是误解。它的“低级”指的是不封装业务逻辑只暴露原子能力。比如Retriever接口就一个方法retrieve(String query)返回ListDocumentToolExecutor就一个execute(ToolExecutionRequest request)。它故意不提供RagChain这样的高级封装因为真实业务里“RAG”根本不是标准流程有的要先做 Query 改写Query Rewriting再向量检索再 BM25 混合打分有的要对检索结果做实体归一化比如把“iPhone15”和“苹果手机”映射到同一 SKU有的甚至要在检索前调用规则引擎过滤敏感商品。LangChain4j 的设计哲学是“你告诉我业务规则我给你拼积木的砖块Spring AI 的哲学是“我给你搭好乐高城堡你只能在里面装修。” 所以它的“低级 API”恰恰是应对 2026 年复杂场景的底气。我们做过一个实验用 LangChain4j 实现“LangChain4j Milvus 混合检索”核心就三步1用MilvusRetriever做向量召回2用BM25Retriever基于 Lucene做关键词召回3用自定义HybridRetriever合并两个结果集并重排序。整个过程只改了 17 行代码替换掉原来的MilvusRetriever实例即可。而 Spring AI 要实现同样效果得 fork 它的VectorStoreRetriever模块重写retrieve()方法并提交 PR 等官方合并——这在生产环境是不可接受的延迟。所以我的第二条铁律如果业务规则高度定制化或未来半年内肯定要对接非标模型/非标向量库/非标工具协议LangChain4j 的“低级”就是你的护城河。3. 实操细节解析从零搭建两个框架的最小可行链路3.1 Spring AI 实战5 分钟跑通百炼 API含 Docker 部署避坑Spring AI 的优势在“开箱即用”但前提是你的环境真的“开箱”。我踩过最深的坑是 Docker 部署时的证书信任问题——百炼 API 走 HTTPS而 Spring AI 默认用 JDK 的TrustManager但 Alpine Linux 基础镜像里缺 CA 证书。直接docker run -p 8080:8080 my-spring-ai-app会报javax.net.ssl.SSLHandshakeException: PKIX path building failed。解决方案不是网上说的“加-Djavax.net.ssl.trustStore”而是用openjdk:17-jdk-slim替代openjdk:17-jre-alpine后者自带完整 CA 证书链。以下是真正能跑通的最小配置Step 1Maven 依赖Spring AI 2.0.0-M3适配 Spring Boot 3.3dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version2.0.0-M3/version !-- 注意不是 1.x -- /dependency !-- 关键百炼兼容 OpenAI API但需指定 baseUrl -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-webflux/artifactId /dependencyStep 2application.yml 配置重点看注释spring: ai: openai: # 百炼 API Key从阿里云控制台获取 api-key: ${ALIYUN_BAIBIAN_API_KEY:your-key-here} # 百炼的 OpenAI 兼容地址注意 v1/chat/completions base-url: https://dashscope.aliyuncs.com/compatible-mode/v1/ # 必须显式设置 model百炼不认 openai 的 model 名 chat: model: qwen-max # 或 qwen-plus, qwen-turbo # 关键禁用 Spring AI 的默认重试百炼有自己的限流策略 client: retry: enabled: false # 关键关闭 WebClient 的默认 SSL 验证仅测试环境 # 生产环境必须用 proper CA见下文 Docker 部署说明 webclient: ssl: trust-all: true # 临时方案生产环境删掉Step 3Controller 代码真正的“两行代码”RestController public class BaiLianController { private final ChatClient chatClient; // Spring AI 自动注入 public BaiLianController(ChatClient chatClient) { this.chatClient chatClient; } PostMapping(/bailian/ask) public MonoString ask(RequestBody String query) { // Spring AI 2.0 的新写法用 UserMessage 封装 return chatClient .call(new UserMessage(query)) .map(AiResponse::getOutput) .map(ChatResponse::getContent); } }提示chatClient.call()返回MonoAiResponse这是 Spring AI 2.0 的 Reactive 默认行为。如果你坚持用阻塞式加block()即可但会损失吞吐量。Docker 部署终极方案生产环境# 使用 slim-jdk 而非 alpine FROM openjdk:17-jdk-slim VOLUME /tmp ARG JAR_FILEtarget/myapp.jar COPY ${JAR_FILE} app.jar # 关键复制系统 CA 证书到容器 RUN cp /etc/ssl/certs/java/cacerts $JAVA_HOME/lib/security/cacerts ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]这样部署后curl -X POST http://localhost:8080/bailian/ask -d 今天天气怎么样就能拿到百炼的实时回复。整个过程从git clone到curl成功实测 4 分 38 秒。3.2 LangChain4j 实战手写 Skill 博客生成器含 Tool Calling 完整链路LangChain4j 的“手写”不是炫技而是为了精准控制每一步。我们为客户做的“技术博客自动生成”系统要求1用户输入主题如“Spring AI 2.0 新特性”2自动检索公司内部 Confluence 文档3调用代码分析工具提取相关 API 示例4整合生成结构化 Markdown 博客。这个流程必须用 Tool Calling而 LangChain4j 的ToolExecutor设计让这事变得清晰。Step 1定义 Tool这才是真正的“Skill”// 1. 文档检索 Tool public class ConfluenceRetrieverTool implements Tool { private final ConfluenceClient client; // 自定义 Confluence SDK Override public String execute(String input) { // input 是用户主题转为 Confluence 查询 DSL ListPage pages client.search(title ~ input OR body ~ input ); return pages.stream() .map(p - - [ p.getTitle() ]( p.getUrl() )) .collect(Collectors.joining(\n)); } Override public String getDescription() { return Search internal Confluence for pages related to the input topic; } } // 2. 代码分析 Tool调用内部 SonarQube API public class CodeAnalyzerTool implements Tool { private final SonarQubeClient client; Override public String execute(String input) { // input 是主题映射到代码仓库路径 String repoPath mapTopicToRepo(input); return client.getApiExamples(repoPath); } Override public String getDescription() { return Get code examples from SonarQube for the given topic; } }注意getDescription()的文案会喂给 LLM让它决定何时调用此 Tool。必须用自然语言描述不能写“调用 SonarQube”。Step 2组装 Chain这才是“Skill 博客”的灵魂// 初始化模型用百炼LangChain4j 原生支持 ChatModel chatModel QwenChatModel.builder() .apiKey(System.getenv(ALIYUN_BAIBIAN_API_KEY)) .modelName(qwen-max) .build(); // 注册 Tools ListTool tools Arrays.asList( new ConfluenceRetrieverTool(confluenceClient), new CodeAnalyzerTool(sonarClient) ); // 创建 Tool ExecutorLangChain4j 的核心抽象 ToolExecutor toolExecutor ToolExecutor.builder() .tools(tools) .build(); // 编排完整链路Prompt - Model - Tool Call - 整合 - 输出 String promptTemplate 你是一个资深技术博主。根据以下信息生成一篇 Markdown 格式的技术博客 - 主题{topic} - 相关文档{confluenceResult} - 代码示例{codeResult} 要求1标题用 ##2关键 API 用 code 包裹3结尾加“参考资料”链接。 ; PromptTemplate prompt PromptTemplate.from(promptTemplate); // 执行链路这才是 LangChain4j 的力量 String generateBlog(String topic) { // Step 1: 检索文档 String confluenceResult toolExecutor.execute( new ToolExecutionRequest(ConfluenceRetrieverTool, topic)); // Step 2: 分析代码 String codeResult toolExecutor.execute( new ToolExecutionRequest(CodeAnalyzerTool, topic)); // Step 3: 用模型整合生成 String finalPrompt prompt.format(Map.of( topic, topic, confluenceResult, confluenceResult, codeResult, codeResult )); return chatModel.generate(finalPrompt).content(); }这个generateBlog()方法就是客户要的“Skill”。它不依赖任何框架可以嵌入到任何 Java 服务里。我们把它打包成独立 Jar供公司所有业务线调用零 Spring 依赖。4. 核心环节实现Multi-Agent 协作与 MCP 服务集成4.1 Spring AI Multi-Agent用 Graph 编排但别碰底层状态Spring AI 2.0 引入了Graph概念来支持 Agent 协作但它和 LangChain 的 Agent 概念不同——Spring AI 的 Graph 更像一个“有向工作流”节点是FunctionCallback即 Tool边是数据流向。它不管理 Agent 的“记忆”或“目标”这些得你自己维护。我们实现了一个“订单异常处理 Agent 团队”OrderChecker查订单状态、LogisticsTracker查物流、CompensationPlanner计算补偿。代码如下Bean public GraphChatMemory orderHandlingGraph(ChatClient chatClient) { // 定义三个节点每个都是 FunctionCallback FunctionCallback orderChecker new FunctionCallback(orderChecker, (input, context) - { String orderId extractOrderId(input); OrderStatus status orderService.getStatus(orderId); return 订单状态 status.toString(); }); FunctionCallback logisticsTracker new FunctionCallback(logisticsTracker, (input, context) - { String orderId extractOrderId(input); TrackingInfo info logisticsService.getTracking(orderId); return 物流信息 info.toString(); }); // 关键用 GraphBuilder 连接节点 return GraphBuilder .from(chatClient) .addNode(orderChecker, orderChecker) .addNode(logisticsTracker, logisticsTracker) .addEdge(orderChecker, logisticsTracker) // 状态异常才查物流 .build(); } // Controller 调用 PostMapping(/order/handle) public String handleOrder(RequestBody String orderId) { // Graph 的输入是原始字符串输出是最终响应 return orderHandlingGraph.call(orderId).getContent(); }注意addEdge(orderChecker, logisticsTracker)是硬编码的流程无法动态判断。如果要实现“只有状态为 SHIPPED 才查物流”就得在orderChecker的回调里throw new SkipNodeException()这违背了 Graph 的初衷。所以 Spring AI 的 Multi-Agent 适合固定流程不适合目标驱动的自主决策。4.2 LangChain4j Multi-Agent用 Stateful Agent 实现真正的自主协作LangChain4j 的StatefulAgent才是为自主 Agent 设计的。它内置Memory消息历史、Goal当前目标、Tools可用技能并用AgentExecutor循环调用直到 Goal 达成。我们用它实现了“跨系统故障诊断 Agent”输入“支付失败”它自动执行1查支付网关日志2查风控系统拦截记录3查数据库事务状态4综合判断原因。核心代码// 定义 Agent 的长期记忆存 Redis MessageHistory messageHistory RedisMessageHistory.builder() .redisTemplate(redisTemplate) .build(); // 定义 Agent 的目标动态生成 Goal goal Goal.builder() .description(诊断支付失败的根本原因并给出修复建议) .build(); // 创建 Stateful Agent StatefulAgent agent StatefulAgent.builder() .chatModel(qwenChatModel) .memory(messageHistory) .goal(goal) .tools(Arrays.asList( new PaymentGatewayLogTool(), new RiskControlLogTool(), new DatabaseTransactionTool() )) .build(); // 执行自动循环直到 Goal 完成 AgentResponse response agent.execute(支付订单 20260501001 失败); System.out.println(response.content()); // “根本原因是风控系统误判为刷单建议调整规则阈值”StatefulAgent.execute()内部会1将输入 历史消息喂给 LLM2LLM 决定调用哪个 Tool3执行 Tool 获取结果4将结果追加到 Memory5重复直到 LLM 返回final answer。这才是真正的“自主 Agent”。而 Spring AI 的 Graph 是静态 DAG无法做到这点。4.3 Spring AI MCP 服务集成如何安全地调用别人提供的 MCPMCPModel Context Protocol是 2026 年新兴的模型服务协议类似 gRPC 之于微服务。Spring AI 2.0 原生支持 MCP Client但文档极少。我们集成了一家第三方公司的“合规审查 MCP 服务”其地址是mcp://review-service:8080。关键步骤Step 1添加 MCP 依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-mcp-spring-boot-starter/artifactId version2.0.0-M3/version /dependencyStep 2配置 MCP Client重点是 TLS 和认证spring: ai: mcp: clients: compliance-review: url: mcp://review-service:8080 # MCP 必须用 TLS且需双向认证 ssl: key-store: classpath:client-keystore.p12 key-store-password: changeit trust-store: classpath:server-truststore.jks trust-store-password: changeit # MCP 的认证是 token-based auth: type: bearer token: ${MCP_REVIEW_TOKEN}Step 3注入并调用Spring AI 封装了 MCP 的复杂性Service public class ComplianceService { private final McpClient mcpClient; // Spring AI 自动注入 public ComplianceService(McpClient mcpClient) { this.mcpClient mcpClient; } public String reviewContract(String contractText) { // MCP 调用发送文本返回 JSON 结构化结果 McpResponse response mcpClient .send(compliance.review, Map.of(document, contractText)); return response.get(riskLevel) : response.get(suggestions); } }注意mcp://协议不是 HTTPSpring AI 底层用 Netty 实现了 MCP 的二进制传输。如果你的网络策略禁止非 HTTP 流量必须开通review-service的 8080 端口 TCP 协议。5. 常见问题与排查技巧实录来自线上事故的血泪总结5.1 Spring AI 的“静默失败”Streaming 场景下的内存泄漏现象Spring AI 的ChatClient.stream()在高并发下JVM 堆内存持续增长Full GC 频繁最终 OOM。监控显示io.netty.buffer.PooledByteBufAllocator$PoolThreadCache对象占满堆。根因Spring AI 2.0 的 WebClient 默认使用 Netty 的PooledByteBufAllocator但它的maxCapacityPerThread默认是 1024*10241MB而每个 Streaming 连接都会分配一个ByteBuf缓存池。当 1000 个用户同时请求 Streaming就创建 1000 个缓存池每个 1MB瞬间吃掉 1GB 内存。解决方案三步降级配置在application.yml中限制缓存大小spring: ai: webclient: netty: allocator: max-capacity-per-thread: 65536 # 64KB够用 tiny-cache-size: 512 small-cache-size: 256强制关闭 Streaming如果业务不需要实时流式响应用chatClient.call()代替stream()。JVM 参数加固-XX:MaxDirectMemorySize512m限制 Netty 直接内存。实操心得Spring AI 的 Streaming 是“便利性陷阱”。除非你的前端真需要逐字显示如客服聊天窗口否则一律用同步call()。我们把所有 Streaming 接口改成 WebSocket 后端轮询内存稳定下降 70%。5.2 LangChain4j 的“Tool 死循环”LLM 的幻觉引发无限调用现象StatefulAgent执行时LLM 不断调用同一个 Tool比如反复查物流永不返回final answer。根因LLM 的stop sequence停止符未正确设置或 Tool 的返回结果包含触发再次调用的关键词如“请再查一次”。LangChain4j 默认用\nFinal Answer:作为停止符但如果 Tool 返回物流信息已签收。请确认是否需要其他帮助LLM 可能认为还没结束。解决方案四招强制 Tool 返回结构化 JSON避免自然语言。修改LogisticsTracker的execute()return {\status\:\DELIVERED\,\trackingNo\:\SF123456789\};在 Prompt 中明确停止指令在PromptTemplate里加请严格按以下格式输出 - 如果已获得足够信息请以 FINAL_ANSWER: 开头后跟答案。 - 否则调用合适的 Tool。设置最大 Tool 调用次数StatefulAgent.builder().maxToolExecutions(5)。加超时熔断StatefulAgent.builder().executionTimeout(Duration.ofSeconds(30))。实操心得LangChain4j 的 Tool Calling 是“LLM 驾驶”你得当好副驾——给方向盘Prompt、设限速maxExecutions、装安全气囊timeout。我们上线前做了 200 次随机 Topic 压测把maxToolExecutions从 3 调到 5再没出现死循环。5.3 混合检索的精度灾难Milvus 向量 BM25 关键词结果更差现象LangChain4j Milvus 混合检索准确率比纯向量检索还低 15%。根因混合不是简单相加。向量检索返回 top-k如 10 个BM25 也返回 top-k如 10 个直接合并成 20 个但其中可能有 8 个重复文档且排序混乱。解决方案工业级实践用 Reciprocal Rank Fusion (RRF) 重排序对每个文档计算1/(rank_in_vector 60) 1/(rank_in_bm25 60)60 是偏移量避免除零。向量检索用ANNBM25 用ExactMilvus 的 ANN 搜索快但不准BM25 的 Exact 搜索慢但准先用 ANN 快速召回 1000 个再用 BM25 在这 1000 个里精确打分。字段加权对标题字段 BM25 权重设为 3.0正文设为 1.0。// LangChain4j 的 HybridRetriever 实现片段 public ListDocument retrieve(String query) { ListDocument vectorDocs milvusRetriever.retrieve(query); // top 100 ListDocument bm25Docs bm25Retriever.retrieve(query); // top 100 // 合并去重 RRF 重排序 MapString, Double scores new HashMap(); for (int i 0; i vectorDocs.size(); i) { String id vectorDocs.get(i).metadata().get(id); scores.merge(id, 1.0 / (i 60), Double::sum); } for (int i 0; i bm25Docs.size(); i) { String id bm25Docs.get(i).metadata().get(id); scores.merge(id, 1.0 / (i 60), Double::sum); } return scores.entrySet().stream() .sorted(Map.Entry.String, DoublecomparingByValue().reversed()) .limit(10) .map(entry - findDocumentById(entry.getKey())) .collect(Collectors.toList()); }实操心得混合检索不是“越多越好”而是“越准越好”。我们最终采用“ANN 快召 BM25 精排”策略准确率提升到 92%比纯向量高 8%。5.4 Java 面试题高频陷阱Spring AI 和 LangChain4j 的本质区别面试官常问“Spring AI 和 LangChain4j 的区别是什么” 别背“Spring AI 是 Spring 官方LangChain4j 是社区版”这种废话。答出这三点直接拿 Offer抽象层级不同Spring AI 抽象的是“LLM 服务”它把模型当黑盒你只管call()LangChain4j 抽象的是“LLM 编程范式”它把 Prompt、Retriever、Tool 当积木你负责拼装。错误处理哲学不同Spring AI 的SpringAIException继承RuntimeException鼓励你用RetryableLangChain4j 的ToolException是 checked exception强迫你在catch块里写 fallback 逻辑——这正是生产环境需要的。可观测性设计不同Spring AI 的ChatClient自动集成 Micrometerai.chat.calls指标开箱即用LangChain4j 需要你手动new Tracer()并注入到ChatModel但好处是你可以 trace 到每个 Tool 的耗时定位瓶颈更准。最后分享一个小技巧如果面试官追问“你们项目用哪个”不要说“我们用 Spring AI”要说“我们用 LangChain4j 做核心链路用 Spring AI 做管理后台的简单问答因为前者要精度后者要速度”。这展示了你的架构权衡能力比单纯说技术名词高明十倍。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价