news 2026/8/23 12:42:54

Spring AI 工程化实践:Java Web 项目集成大模型与本地部署指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI 工程化实践:Java Web 项目集成大模型与本地部署指南

在实际工程实践中,AI 能力的集成正从探索走向落地。无论是 Spring AI 这类框架,还是 AI Agent、大模型本地部署等概念,开发者面临的核心挑战已不再是“能否接入”,而是“如何以工程化的方式稳定、高效、安全地融入现有系统”。本文将以一个典型的 Java Web 项目为背景,探讨如何将 AI 能力(如大模型对话、内容生成)作为服务组件进行集成、开发、测试与部署,并重点解决工程实践中常见的配置、依赖管理、异常处理、性能优化及“AI 幻觉”等问题。如果你正在负责或计划开发一个包含 AI 功能的后端服务,并希望了解从环境搭建到生产上线的完整路径,本文将提供一个可复现的实践指南。

1. 理解 AI 工程化的核心挑战与 Spring AI 的定位

将 AI 模型,尤其是大语言模型(LLM),集成到企业级应用中,远不止调用一个 API 那么简单。它涉及到稳定性、成本、数据安全、响应延迟和结果可控性等多个维度的工程考量。

1.1 传统 AI 集成方式的痛点

在 Spring AI 等框架出现之前,开发者通常需要手动处理以下问题:

  • 客户端多样性:不同模型供应商(如 OpenAI、Azure OpenAI、 Anthropic、本地 Ollama)的 API 接口、认证方式、请求/响应格式各异,代码中充斥着针对特定供应商的硬编码。
  • 配置管理复杂:API Key、Base URL、超时时间、模型版本等配置散落在代码或配置文件中,难以统一管理和切换。
  • 缺乏抽象层:业务逻辑与具体的 AI 供应商 SDK 强耦合,一旦需要更换模型或供应商,重构成本高昂。
  • 可观测性差:对话历史(Prompt)、令牌(Token)消耗、响应时间、错误类型等关键指标缺乏统一的收集和监控手段。
  • “AI 幻觉”处理难:模型可能生成看似合理但不符合事实或业务规则的输出,需要在应用层设计校验和修正机制。

1.2 Spring AI 带来的工程化抽象

Spring AI 项目旨在为 AI 应用开发提供一套类似于 Spring Data 对数据库、Spring Security 对安全的抽象。它的核心价值在于:

  • 统一的 API 接口:定义了ChatClientEmbeddingClientImageClient等通用接口,业务代码只需面向接口编程,无需关心底层是 OpenAI 还是本地模型。
  • 声明式配置:通过application.ymlapplication.properties文件,以简洁的方式配置多个 AI 模型供应商的连接参数。
  • Prompt 模板与管理:支持将复杂的提示词(Prompt)模板化、外部化,便于管理和 A/B 测试。
  • 可拔插的模型支持:通过更换依赖和配置,可以轻松地在不同模型间切换,甚至实现故障转移。
  • 与 Spring 生态无缝集成:天然支持 Spring Boot 的自动配置、依赖注入、Actuator 监控等特性。

简单来说,Spring AI 试图将 AI 模型变成类似数据库或消息队列的“基础设施”,让开发者能更专注于业务逻辑的实现。

2. 环境准备与项目初始化

我们将创建一个基于 Spring Boot 3.x 和 Spring AI 的简单 AI 对话服务。这个服务将提供 RESTful API,接收用户问题,调用配置的 AI 模型,并返回回答。

2.1 基础环境要求

在开始之前,请确保你的开发环境满足以下要求:

组件要求说明
JDK17 或更高版本Spring Boot 3.x 的最低要求。
构建工具Maven 3.6+ 或 Gradle 7.x+本文使用 Maven 进行演示。
IDEIntelliJ IDEA, VS Code, Eclipse推荐使用支持 Spring Boot 的 IDE。
网络可访问所选 AI 模型 API如果使用 OpenAI 等云端服务,需确保网络连通性。若使用本地模型(如 Ollama),则需本地运行模型服务。

2.2 创建 Spring Boot 项目

使用 Spring Initializr 或 IDE 的创建向导,生成一个 Spring Boot 项目。

关键依赖选择:

  • Spring Web: 用于提供 REST API。
  • Spring AI OpenAISpring AI Ollama: 根据你计划使用的模型选择。这里我们以 OpenAI 和 Ollama 为例,展示多模型配置。
  • Lombok(可选): 简化 POJO 类的编写。
  • Spring Boot Actuator(可选): 用于监控端点。

对应的pom.xml依赖项如下:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <!-- 使用当前稳定版本 --> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>ai-engineering-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>ai-engineering-demo</name> <description>Demo project for AI Engineering with Spring AI</description> <properties> <java.version>17</java.version> <spring-ai.version>0.8.1</spring-ai.version> <!-- 使用稳定的 Spring AI 版本 --> </properties> <dependencies> <!-- Spring Boot Web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI OpenAI Starter --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Spring AI Ollama Starter (用于连接本地模型) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- Lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- Spring Boot Actuator (监控) --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>

注意:Spring AI 版本迭代较快,请根据 官方文档 确认当前稳定版本。同时引入多个模型依赖时,需要注意它们之间是否存在冲突,通常 Spring AI 的 starters 设计良好,可以共存。

3. 配置多模型连接与核心服务实现

配置是 Spring AI 工程化的关键。我们将配置两个 AI 模型客户端:一个连接远程的 OpenAI API,另一个连接本地运行的 Ollama 服务。

3.1 应用配置文件详解

src/main/resources/application.yml中,进行如下配置:

server: port: 8080 spring: application: name: ai-engineering-demo # Spring AI 配置 ai: # OpenAI 配置 (例如使用 GPT-3.5/4) openai: api-key: ${OPENAI_API_KEY:} # 强烈建议从环境变量读取,不要硬编码 base-url: https://api.openai.com/v1 # 默认值,如果是 Azure OpenAI,需修改 chat: options: model: gpt-3.5-turbo # 指定使用的模型 temperature: 0.7 # 创造性,0-2之间,越高越随机 max-tokens: 500 # 限制最大输出token数,控制成本 # Ollama 配置 (连接本地模型,如 llama2, qwen, mistral 等) ollama: base-url: http://localhost:11434 # Ollama 服务默认地址 chat: options: model: llama2 # 本地运行的模型名称 temperature: 0.8 num-predict: 300 # Ollama 中类似 max-tokens 的参数 # Actuator 端点暴露,用于健康检查等 management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always

关键配置解释:

  1. spring.ai.openai.api-key: 这是访问 OpenAI 服务的凭证。绝对不要将其直接写入代码或提交到版本控制系统。应通过环境变量 (OPENAI_API_KEY) 或配置中心注入。
  2. spring.ai.openai.base-url: 默认为 OpenAI 官方端点。如果你使用 Azure OpenAI 或其他兼容 OpenAI API 的代理服务,需要修改此值。
  3. model: 明确指定要使用的模型名称。这是控制成本和能力的关键参数。
  4. temperature: 控制生成文本的随机性。对于需要确定性答案的问答场景,可以调低(如 0.1);对于创意生成,可以调高。
  5. max-tokens/num-predict: 限制单次响应的长度,是控制单次调用成本(对于按 token 计费的 API)和防止响应过长的有效手段。

3.2 实现多模型对话服务

Spring AI 会自动根据配置创建ChatClientBean。默认情况下,它会使用spring.ai.openai的配置。为了能动态选择或同时使用多个模型,我们需要进行一些封装。

首先,定义一个统一的请求和响应对象:

package com.example.aiengineeringdemo.dto; import lombok.Data; @Data public class ChatRequest { private String message; private String modelProvider; // 用于指定使用哪个提供商,如 "openai", "ollama" } @Data public class ChatResponse { private String provider; private String answer; private Long tokenUsage; // 可选,记录token消耗 }

然后,创建一个服务层,用于路由请求到不同的ChatClient

package com.example.aiengineeringdemo.service; import com.example.aiengineeringdemo.dto.ChatRequest; import com.example.aiengineeringdemo.dto.ChatResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @Service @Slf4j @RequiredArgsConstructor public class AIChatService { // 使用 Map 来存储不同供应商的 ChatClient private final Map<String, ChatClient> chatClientMap; // 构造器注入,Spring 会自动将名为 `openAiChatClient` 和 `ollamaChatClient` 的 Bean 放入 Map public AIChatService(Map<String, ChatClient> chatClientMap) { this.chatClientMap = chatClientMap; } public ChatResponse chat(ChatRequest request) { String provider = request.getModelProvider(); if (provider == null || provider.isEmpty()) { provider = "openai"; // 默认提供商 } ChatClient client = chatClientMap.get(provider + "ChatClient"); // Bean 名称规则 if (client == null) { throw new IllegalArgumentException("Unsupported AI model provider: " + provider); } log.info("Using AI provider: {} to process query: {}", provider, request.getMessage()); long startTime = System.currentTimeMillis(); // 调用 AI 模型 ChatResponse aiResponse = client.prompt(new Prompt(request.getMessage())).call().chatResponse(); long endTime = System.currentTimeMillis(); String answer = aiResponse.getResult().getOutput().getContent(); ChatResponse response = new ChatResponse(); response.setProvider(provider); response.setAnswer(answer); // 注意:不同客户端的响应中 Token 计数方式可能不同,这里简化处理 // 实际可以从 aiResponse.getMetadata() 或 aiResponse.getUsage() 中获取 // response.setTokenUsage(aiResponse.getUsage().getTotalTokens()); log.info("AI provider [{}] response time: {} ms", provider, (endTime - startTime)); return response; } }

为了让 Spring 能正确注入多个ChatClient,我们需要一个配置类来显式定义它们:

package com.example.aiengineeringdemo.config; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.openai.OpenAiChatClient; import org.springframework.ai.ollama.OllamaChatClient; import org.springframework.ai.ollama.api.OllamaApi; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @Configuration public class AIConfig { // 默认的 ChatClient,通常指向 OpenAI @Bean @Primary // 标记为主要 Bean,当不指定名称注入时会使用这个 public ChatClient openAiChatClient(OpenAiChatClient openAiChatClient) { // 这里直接返回 Spring AI 自动配置的 OpenAiChatClient // 我们也可以在这里进行一些包装,比如添加拦截器记录日志 return ChatClient.builder(openAiChatClient) //.defaultSystem("你是一个有帮助的助手。") // 可以设置默认系统指令 .build(); } // 另一个 ChatClient,指向 Ollama @Bean public ChatClient ollamaChatClient(OllamaChatClient ollamaChatClient) { return ChatClient.builder(ollamaChatClient).build(); } }

3.3 提供 RESTful API 控制器

最后,创建一个简单的控制器来暴露服务:

package com.example.aiengineeringdemo.controller; import com.example.aiengineeringdemo.dto.ChatRequest; import com.example.aiengineeringdemo.dto.ChatResponse; import com.example.aiengineeringdemo.service.AIChatService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/ai") @RequiredArgsConstructor public class AIChatController { private final AIChatService aiChatService; @PostMapping("/chat") public ChatResponse chat(@RequestBody ChatRequest request) { return aiChatService.chat(request); } }

4. 运行验证与基础测试

完成代码编写后,我们需要验证服务是否能够正常运行,并能正确调用不同的 AI 模型。

4.1 启动准备与检查

  1. 对于 OpenAI:确保环境变量OPENAI_API_KEY已正确设置。
    # Linux/Mac export OPENAI_API_KEY='your-api-key-here' # Windows (CMD) set OPENAI_API_KEY=your-api-key-here # Windows (PowerShell) $env:OPENAI_API_KEY='your-api-key-here'
  2. 对于 Ollama:确保已在本地安装并启动了 Ollama,并且拉取了所需的模型(如llama2)。
    # 安装 Ollama (详见官网) # 拉取模型 ollama pull llama2 # 启动 Ollama 服务(通常安装后会自动运行)
  3. 启动 Spring Boot 应用
    mvn spring-boot:run
    或直接在 IDE 中运行AiEngineeringDemoApplication主类。

4.2 使用 API 测试工具进行验证

应用启动后,使用curl、Postman 或任何 HTTP 客户端进行测试。

测试 OpenAI 接口:

curl -X POST http://localhost:8080/api/ai/chat \ -H "Content-Type: application/json" \ -d '{ "message": "请用一句话解释什么是微服务架构。", "modelProvider": "openai" }'

预期会收到一个包含 GPT-3.5-turbo 模型回答的 JSON 响应。

测试 Ollama 接口:

curl -X POST http://localhost:8080/api/ai/chat \ -H "Content-Type: application/json" \ -d '{ "message": "请用一句话解释什么是微服务架构。", "modelProvider": "ollama" }'

预期会收到一个包含本地 Llama2 模型回答的 JSON 响应。

4.3 验证 Actuator 健康检查

Spring Boot Actuator 提供了应用和组件健康状态端点,这对于运维至关重要。

curl http://localhost:8080/actuator/health

如果 OpenAI 或 Ollama 连接失败,对应的健康指示器可能会显示DOWN状态。你需要检查配置、网络和模型服务状态。

5. 工程实践中的关键问题与排查

将 AI 集成到生产环境,必然会遇到各种问题。以下是几个典型场景的排查路径。

5.1 连接与认证失败

这是最常见的问题,通常表现为401 Unauthorized403 Forbidden或连接超时。

问题现象可能原因检查方式处理建议
调用 OpenAI 返回 401API Key 错误、过期或未设置。1. 检查环境变量OPENAI_API_KEY是否已设置且正确。
2. 在应用日志中搜索Invalid API keyIncorrect API key
3. 登录 OpenAI 平台检查 API Key 状态和额度。
1. 重新生成 API Key 并更新环境变量。
2. 确保配置的base-url与 API Key 所属区域匹配(如 Azure OpenAI)。
调用 Ollama 连接被拒绝Ollama 服务未启动或端口不对。1. 执行ollama serve查看服务状态。
2. 检查application.ymlspring.ai.ollama.base-url的端口(默认 11434)。
3. 使用curl http://localhost:11434/api/tags测试 Ollama API 是否可达。
1. 启动 Ollama 服务。
2. 确认防火墙或安全组放行了对应端口。
3. 如果 Ollama 运行在容器或远程,需修改base-url
请求超时网络不稳定、模型响应慢或配置超时时间太短。1. 查看应用日志中的超时异常栈。
2. 检查 Spring AI 或底层 HTTP 客户端(如 RestTemplate)的超时配置。
1. 适当增加超时配置(Spring AI 目前主要通过底层客户端配置,如spring.ai.openai.client.*属性)。
2. 对于慢模型,考虑异步调用或设置更合理的客户端超时。

5.2 处理“AI 幻觉”与输出不可控

大模型可能生成错误或不符合要求的内容。

现象:模型回答的事实错误、编造信息、或未遵循指令格式。应对策略

  1. 优化 Prompt 工程:在系统指令(System Message)中明确约束。例如,在AIConfig中为ChatClient设置defaultSystem
    return ChatClient.builder(openAiChatClient) .defaultSystem("你是一个严谨的编程助手。对于不确定的信息,请明确回答‘我不知道’。回答请尽量简洁。") .build();
  2. 后处理校验:在服务层 (AIChatService) 对模型的输出进行校验。例如,使用正则表达式检查是否包含特定格式,或调用另一个校验逻辑。
    public ChatResponse chat(ChatRequest request) { // ... 调用 AI ... String rawAnswer = aiResponse.getResult().getOutput().getContent(); // 后处理:例如,检查是否以“答案是:”开头,如果不是,进行修正或标记 if (!rawAnswer.startsWith("答案是:")) { rawAnswer = "格式修正:答案是:" + rawAnswer; log.warn("模型输出格式不符合预期,已进行修正。"); } // ... 构建响应 ... }
  3. 使用有“约束生成”能力的模型或框架:某些模型或外部库支持 JSON 格式、正则表达式约束的输出。

5.3 性能与成本优化

AI API 调用通常是应用中最耗时的操作之一,且可能按 Token 计费。

常见优化点:

  • 缓存:对常见、确定性高的查询结果进行缓存。例如,使用 Spring Cache 注解。
    @Cacheable(value = "aiResponses", key = "#request.message + #request.modelProvider") public ChatResponse chat(ChatRequest request) { // ... 原有逻辑 ... }

    注意:缓存 AI 响应需谨慎,需评估内容的时效性和用户个性化需求。

  • 异步调用:对于非实时性要求高的场景,使用@Async将 AI 调用改为异步,避免阻塞主线程。
  • 限制输入/输出长度:严格在 Prompt 中和配置参数 (max-tokens) 里限制文本长度,这是控制成本和响应时间最直接的手段。
  • 监控与告警:通过 Actuator Metrics 或自定义 AOP 切面,监控每次 AI 调用的耗时、Token 消耗和成功率。设置阈值告警。

5.4 依赖冲突与版本管理

Spring AI 生态较新,版本迭代快,容易与其他依赖产生冲突。

排查步骤:

  1. 确认 Spring Boot 与 Spring AI 版本兼容性:务必查阅 Spring AI 官方文档 的版本说明。
  2. 使用 Maven 依赖树分析冲突
    mvn dependency:tree -Dincludes=org.springframework.ai
    检查是否有多个不同版本的 Spring AI 组件被引入。
  3. 常见冲突点:不同 AI Starter 可能依赖了不同版本的底层 HTTP 客户端(如 Apache HttpClient, OkHttp)。如果出现NoSuchMethodErrorClassNotFoundException,通常需要统一版本或在pom.xml中排除冲突的传递依赖。

6. 从学习环境到生产环境的进阶考量

在学习和开发环境跑通只是第一步。要上线生产,还需要考虑更多因素。

6.1 配置外部化与安全

  • 密钥管理:API Key 等敏感信息必须从代码中剥离。使用环境变量、云厂商的密钥管理服务(如 AWS Secrets Manager, Azure Key Vault)或专业的配置中心(如 Apollo, Nacos)。
  • 配置 Profile:使用 Spring Boot 的application-{profile}.yml为不同环境(dev, test, prod)配置不同的模型参数、超时时间和降级策略。

6.2 可观测性与监控

  • 日志标准化:在AIChatService中记录每次调用的提供商、输入长度、输出长度、耗时和状态。便于后续分析和审计。
  • 集成 Micrometer:通过 Spring Boot Actuator 和 Micrometer,将 AI 调用的关键指标(次数、耗时、错误率)暴露给 Prometheus 和 Grafana。
  • 分布式追踪:在微服务架构中,确保 AI 调用链被集成到 Sleuth/Zipkin 或 Jaeger 中,以查看其在全局链路中的影响。

6.3 容错与降级策略

生产环境不能因为一个外部 AI 服务不可用而导致核心业务瘫痪。

  • 客户端重试:为ChatClient配置重试机制(Spring Retry)。
  • 故障转移:实现更智能的AIChatService,当主用模型(如 OpenAI)失败或超时时,自动切换到备用模型(如 Ollama 或其他云端服务)。
  • 熔断与限流:使用 Resilience4j 或 Sentinel 对 AI 服务调用进行熔断和限流,防止因 AI 服务响应慢而拖垮整个应用。
  • 兜底策略:当所有 AI 服务都不可用时,返回预设的兜底答案或引导用户使用其他功能。

6.4 模型版本管理与 A/B 测试

  • 模型版本化:在配置中明确指定模型名称和版本(如gpt-4-1106-preview),而不是使用别名(如gpt-4),避免因模型默认版本升级引入不可预知的变化。
  • 流量染色与 A/B 测试:可以通过在请求头或用户标识中注入实验标记,将流量导向不同的模型或 Prompt 版本,从而对比效果。

将 AI 能力工程化,本质上是将其视为一个具有特殊性的外部服务进行集成和管理。Spring AI 提供了优秀的抽象层来简化开发,但生产环境的稳定性、安全性、成本和效果可控性,仍然依赖于开发者对配置、监控、容错和架构的深入理解与实践。从配置一个可用的客户端开始,逐步构建监控告警、实现容错降级、优化成本与性能,最终使其成为业务系统中一个可靠、可控的组成部分,这才是 AI 工程实践的核心路径。下一步,你可以探索更复杂的场景,如流式响应(Streaming)、函数调用(Function Calling)、基于向量数据库的检索增强生成(RAG)等,这些 Spring AI 也提供了相应的支持。

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

层次分析法:告别拍脑袋决策,用数学量化主观判断

1. 项目概述&#xff1a;从拍脑袋到结构化决策 做项目、选方案、评绩效&#xff0c;甚至挑手机、选学校&#xff0c;我们每天都在做决策。但很多时候&#xff0c;所谓的“决策”不过是凭感觉、拍脑袋&#xff0c;或者陷入“公说公有理&#xff0c;婆说婆有理”的扯皮僵局。有没…

作者头像 李华
网站建设 2026/8/23 12:37:10

Visual Studio 2022深度解析:从C/C++到C#的编译原理与高效配置实战

1. 项目概述&#xff1a;为什么是“番外”&#xff1f;聊到Visual Studio 2022&#xff0c;很多朋友的第一反应可能是“宇宙第一IDE”&#xff0c;功能强大但也略显臃肿。网上关于VS2022安装和创建第一个“Hello World”项目的教程已经多如牛毛&#xff0c;从下载到点击运行&am…

作者头像 李华
网站建设 2026/8/23 12:24:40

C++可变参数模板与折叠表达式:从基础原理到实战应用

1. 从“一堆参数”到“优雅的抽象”&#xff1a;为什么我们需要可变参数与折叠表达式 在C的世界里&#xff0c;我们总在追求更通用、更简洁、更高效的代码。回想一下&#xff0c;当你需要写一个打印函数&#xff0c;最开始可能只处理 int &#xff0c;后来要加 double 、 …

作者头像 李华