资讯动态

SpringBoot实战:基于OpenAI API构建可扩展聊天机器人源码解析

发布时间:2026/10/7 5:44:32 来源:尧图企业网站定制
简介面向Java全栈与AI应用开发者打造的聊天机器人开源项目以SpringBoot和SpringCloud搭建后端基础已对接OpenAI GPT-3.5、GPT-4.0、百度文心一言以及Stable Diffusion、Midjourney等能力可快速搭建对话、绘图类机器人也可作为企业在既有微服务架构中接入大模型的参考底座。压缩包共1011个文件整体约38.52MB主体由452个Java源码、104个Vue页面、112个JS脚本以及图片、样式、配置文件构成覆盖后端服务、前端交互、部署脚本与界面静态资源。技术栈涉及Java、JavaScript、Vue、TypeScript、CSS、Shell等前后端边界清楚XML、YAML与Dockerfile等则提供部署和运行配置便于本地或容器化启动。目前已有800人学习下载。项目目录结构清晰、代码可读性强适合毕业设计、课程演练与二次开发从前端对话框到后端微服务调用完整展示接收用户输入、调用大模型并生成回复的链路也便于在此基础上扩展私有知识库、多模态对话与个性化指令等功能。1. 这个标题到底在交付什么SpringBoot与OpenAI聊天机器人源码的实用边界“基于SpringBoot和OpenAI的聊天机器人设计源码”这既不是一套“点开就能跑”的黑匣子也不是市面上那种只放几个类文件就敢叫源码的残缺Demo而是一份可以拿去答辩、改造和二次开发的工程骨架。它要解决的核心问题非常具体让Java后端起一个SpringBoot服务通过HTTP方式把OpenAI的对话能力封装成接口——前端传一句消息服务端带着会话上下文向OpenAI发起请求再把回答原样返回给用户。这个项目方向的典型使用者是三类人正在做Java课程设计的学生、刚接触AI应用开发的后端工程师以及想快速验证“对话能力能不能接进现有系统”的技术负责人。真正决定这套源码上限的不是“调OpenAI接口”这个动作而是它有没有把参数配置、会话记忆、超时重试、异常处理和部署方式组织成体面的工程结构。我见过不少Demo能聊可一旦要求多轮对话、流式输出、线上问题排查代码就撑不住了。这篇笔记会从整体架构、最小实现、关键参数到避坑记录完整走一遍。就算你对OpenAI的模型细节不熟只要懂Java和HTTP这套方案就能落地而且换任何一家大模型服务商改的也只是接口地址和鉴权头。2. 拆解请求链路与工程边界SpringBoot和OpenAI之间到底发生了什么先把这个系统最核心的链路说清楚浏览器或小程序把你的消息POST到SpringBoot的ControllerController交给ServiceService拿着消息去请求OpenAI的Chat Completions接口拿到回答后逐层返回前端渲染。整个过程本质上就是你写的后端代码充当了一个“翻译官”唯一要记住的事实是OpenAI的接口是普通REST接口不是WebSocket长连接所以它和你通讯录里的“实时聊天”完全是两码事。用户感知到“回复快”靠的是后端处理够快用户感知到“像打字机一样打字”靠的是后面要讲的流式输出。这里有一个新手最容易忽略的事实SpringBoot内嵌Tomcat的默认工作线程数是200而一次同步调用OpenAI通常要消耗3到8秒。这意味着如果50个用户同时提问线程池基本就被占满了。因此聊机器人这种“每个请求都外部等待”的场景比普通CRUD接口更考验工程的边界设计。看一套源码值不值得用先看它怎么处理这个等待过程是傻等着还是有超时、有流式、有限流。2.1 为什么选SpringBoot承载聊天机器人三个直接决定开发效率的理由我一般会根据项目背景在Servlet和SpringBoot之间选型但这类对话机器人工程用SpringBoot基本是共识理由有三个。第一是自动配置能力HttpClient的创建、JSON的序列化、配置文件的属性绑定SpringBoot都能通过starter和注解在几行内完成你自己手写Servlet光是处理JSON请求体和CORS就要多写几百行。第二是内嵌容器的部署优势一个java -jar就能启动不需要单独装Tomcat这对课程设计答辩和内部Demo演示都非常友好。第三是生态成熟度拦截器做鉴权、AOP做日志、Actuator做健康检查这些都不是“额外功能”而是你往生产环境走时一定会用到的东西。SpringBoot框架本身不直接参与“智能对话”它负责的是稳定地组织HTTP请求的生命周期。具体的做法是你定义一个配置类把OpenAI的API Key、模型名、超时时间都绑定到属性上Service里通过java.net.http.HttpClient或RestTemplate发起调用Controller只负责接收参数、调用Service、返回结果。至于“智能”这件事完全由OpenAI的模型决定。所以选型结论很简单如果你要一个人在几天内交付一个可演示、可扩展的聊天机器人服务SpringBoot是投入产出比最高的选择。2.2 对接OpenAI的接口形态Chat Completions的请求与响应模型OpenAI对外提供的对话接口是POST https://api.openai.com/v1/chat/completions请求和响应都是JSON鉴权方式是在请求头里带Authorization: Bearer 你的API Key。请求体里的核心字段是model和messages。messages是一个数组每一组消息带role和content两个属性role有三种system用来设定角色user是用户输入assistant是模型历史回答。源码工程里这三类消息会分别被组织成对象再统一序列化进请求体。响应结构比请求简单一些外层是choices数组数组中每一项的message.content就是模型生成的回答finish_reason表示结束原因。为什么会把这段讲得这么细因为我发现很多基于SpringBoot的聊天机器人源码跑不通问题不是出在Java代码上而是开发者对接口协议理解有偏差有人把API Key放在请求体里有人把messages当成字符串传有人不知道assistant的历史消息也要回传。这些都是微信群里最频繁出现的求助问题。实际开发时我一般不建议依赖OpenAI官方Java SDK。这个SDK更新节奏快、版本兼容不稳定而聊天机器人服务需要的是你能完全控制请求内容。用JDK自带的HttpClient加一个ObjectMapper几十行代码就能完成对接而且后续换模型服务商时你只需要改一个URL和一个鉴权头这种控制感是SDK给不了的。2.3 源码工程的合理分层controller、service、config、dto各管什么拿到一份声称“设计源码”的工程第一件事永远是看它的包结构。项目结构合理的源码通常长这样src/main/java/com/example/chatbot ├── ChatbotApplication.java ├── config │ └── OpenAIProperties.java ├── controller │ └── ChatController.java ├── service │ ├── ChatService.java │ └── OpenAIClient.java ├── dto │ ├── ChatRequest.java │ ├── ChatResponse.java │ └── ChatMessage.java └── common └── Result.java这个结构对应了三条清晰的职责边界。config只负责读取配置dto只定义请求和响应的数据结构service负责业务编排和HTTP调用controller只做参数接收和结果返回。把它们混在一个类里是新手常见误用比如把HTTP调用逻辑写在Controller里最后Controller变得又胖又难测试。一个值得借鉴的判断标准是如果某个类里出现了“拼JSON字符串”和“解析响应”的代码它就应该属于service层而不是controller层。关于是否要拆多Module我的观点很直接如果只是做一个聊天机器人单一Maven Module完全够用只有当你想把通用能力复用到多个项目时才去拆common、core、web这样的多模块结构。过早拆分模块只会让你在启动和依赖管理上多花时间。包扫描的规则也要留意ChatbotApplication默认只扫描同一级和子级包如果你的配置类放在外层目录就会触发“明明写了注解却没用”的玄学问题这个坑我放在避坑章节里细说。3. 亲自动手从空工程到跑通最小可用的SpringBoot聊天机器人直接把整套源码贴出来不现实也违反这类项目“看懂结构”的初衷。我换一种方式带你从空目录开始复现这个源码工程最核心的三个部分——配置文件、配置绑定类、核心Service与Controller。只要这三块落地整个聊天机器人就跑起来了剩下的美化、多轮记忆、流式输出都是在此基础上加料。动手之前先明确一点这份源码的“可运行性”依赖两个前提。第一运行环境能正常访问OpenAI的接口域名这个需要你自己确认网络连通性代码层面能做的只有报错提示。第二你手里有可用的API Key建议先开通一个测试账号并创建Key再把Key设置成环境变量而不是直接写死在代码里。3.1 第一步最小pom依赖和配置文件一个聊天机器人后端对依赖的要求很低核心只有Web和参数校验两个starter加上JSON处理能力Spring Boot Web已经内置了Jackson。先看pom.xmldependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency /dependencies这里刻意不加任何OpenAI SDK依赖理由前面说过我们直接用HTTP协议对接而不是被SDK绑定。spring-boot-starter-web提供了内嵌Tomcat、Spring MVC和Jacksonspring-boot-starter-validation用来对用户输入做非空校验防止空消息也去请求OpenAI浪费Token。接下来是application.yml这里体现了SpringBoot配置管理的核心写法server: port: 8080 spring: application: name: chatbot-service openai: api-key: ${OPENAI_API_KEY:} model: gpt-4o-mini temperature: 0.7 max-tokens: 1000 connect-timeout: 10s read-timeout: 60s注意api-key这一行它从环境变量OPENAI_API_KEY读取冒号后的是默认值。这样做的直接好处是即便你把代码提交到Git仓库Key也不会泄露。很多所谓“开源源码”翻车点就在这里——作者把Key硬编码进去使用者拿到后不仅调不通还可能因为Key被滥用而欠费。模型名默认选了gpt-4o-mini这是成本比较均衡的选择如果你要做创意写作或复杂推理再手动改成gpt-4o或gpt-3.5-turbo。3.2 第二步用配置类把yml里的参数变成可注入对象配置文件写好了接下来要让Java代码能读到这些值。这里使用ConfigurationProperties它比Value更整洁适合成组配置Configuration ConfigurationProperties(prefix openai) public class OpenAIProperties { private String apiKey; private String model; private double temperature; private int maxTokens; private Duration connectTimeout; private Duration readTimeout; // getter 和 setter 省略IDE 自动生成 }这个配置类绑定了application.yml里所有以openai开头的属性。prefix openai对应配置项的前缀字段名maxTokens会自动映射到max-tokensSpringBoot的松弛绑定规则会把中划线转成驼峰。Duration类型则能直接解析10s这类写法拿到的是一个标准的时间对象。有两点要注意。第一Configuration只是声明这是配置类要让属性绑定生效还得在启动类上加上EnableConfigurationProperties(OpenAIProperties.class)或者像上面这样配合Configuration也能生效。第二这类配置类应该放在config包下确保能被启动类扫描到。在后续的Service里你只需要通过构造函数注入这个配置对象就能随时读取到模型名、超时时间和API Key而不用到处从Environment里取值。3.3 第三步核心Service与Controller让请求真正跑通现在到最关键的部分。Service负责把用户消息组装成OpenAI要求的格式发出去再把回答解析回来。我选择JDK 11自带的java.net.http.HttpClient不用引入额外依赖Service public class OpenAIChatService { private final OpenAIProperties properties; private final HttpClient httpClient; private final ObjectMapper objectMapper; public OpenAIChatService(OpenAIProperties properties) { this.properties properties; this.httpClient HttpClient.newBuilder() .connectTimeout(properties.getConnectTimeout()) .build(); this.objectMapper new ObjectMapper(); } public String chat(String userMessage) throws Exception { // 构建请求体 MapString, Object requestBody new HashMap(); requestBody.put(model, properties.getModel()); requestBody.put(temperature, properties.getTemperature()); requestBody.put(max_tokens, properties.getMaxTokens()); requestBody.put(messages, List.of( Map.of(role, system, content, 你是一个乐于助人的中文助手), Map.of(role, user, content, userMessage) )); String json objectMapper.writeValueAsString(requestBody); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(https://api.openai.com/v1/chat/completions)) .header(Content-Type, application/json) .header(Authorization, Bearer properties.getApiKey()) .timeout(properties.getReadTimeout()) .POST(HttpRequest.BodyPublishers.ofString(json)) .build(); HttpResponseString response httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() ! 200) { throw new RuntimeException(OpenAI API 调用失败状态码: response.statusCode()); } // 从响应里取出第一个回答 JsonNode root objectMapper.readTree(response.body()); return root.path(choices).path(0).path(message).path(content).asText(); } }这段代码的逻辑可以拆成四步读第一步把model、messages、temperature这些参数放进Map再由ObjectMapper序列化成JSON字符串注意这里用List.of和Map.of构造不可变集合是Java 9以后的推荐写法。第二步构造HttpRequest时设置了三个关键头Content-Type声明JSON格式Authorization携带Bearer Token这是OpenAI鉴权的固定格式。第三步httpClient.send同步发送请求timeout设置的是整个请求的读取超时时间来自配置文件的read-timeout。第四步拿到响应后先判断状态码再用Jackson的tree模型按路径取值choices[0].message.content就是最终回答。Controller层反而更简单它只需要接收消息、调用Service、返回统一结果RestController RequestMapping(/api) public class ChatController { private final OpenAIChatService chatService; public ChatController(OpenAIChatService chatService) { this.chatService chatService; } PostMapping(/chat) public Result chat(RequestBody Valid ChatRequest request) { String reply chatService.chat(request.getMessage()); return Result.success(reply); } }ChatRequest里只需要一个message字段加上NotBlank注解校验空消息。Result是统一响应包装包含code、message、data三个字段。跑起来之后用一条curl命令就能验证curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 用一句话介绍你自己}正常情况下你会看到包装在Result.data里的回答。走到这一步一套最小可用的SpringBoot加OpenAI聊天机器人就已经跑通了。接下来要讨论的是让这套源码从“能跑”变成“能用在项目里”的工程化参数调整。4. 工程化必调参数上下文窗口、Token与重试策略避免聊着聊着就烧钱把Service跑通只是完成了30%的工作。真正让聊天机器人源码分出高下的是它对Token消耗和上下文的管理。OpenAI按Token计费多轮对话时如果你把历史消息全部传给模型费用会随着对话轮次指数增长。我在给团队做技术评审时有个习惯先看这套源码怎么管理messages数组如果只是简单地把用户输入追加进去那生产环境大概率会在月底收到一封账单报警邮件。4.1 会话记忆的滑动窗口用最近N轮消息控制请求体体积模型本身没有记忆它每次都是根据你上传的messages数组重新推理。所谓“记忆”就是你把之前的对话历史作为assistant和user消息再次传回接口。正确的做法是做一个会话管理器按用户ID保存消息历史只在请求时取出最近几轮Component public class MemoryManager { private static final int MAX_ROUNDS 6; private final MapString, DequeChatMessage sessions new ConcurrentHashMap(); public ListChatMessage getContext(String userId) { DequeChatMessage history sessions.getOrDefault(userId, new ArrayDeque()); return new ArrayList(history); } public void append(String userId, ChatMessage message) { DequeChatMessage history sessions.computeIfAbsent(userId, k - new ArrayDeque()); if (history.size() MAX_ROUNDS * 2) { history.removeFirst(); // 淘汰最早的一条 } history.addLast(message); } }滑动窗口的核心逻辑在append方法里当历史消息超过12条6轮对话 × 每轮2条时从队首移除最老的记录。Deque双端队列在这里非常合适因为它同时支持头部移除和尾部追加。ConcurrentHashMap保证多用户同时聊天时互不干扰。这个设计的意义在于请求体体积被严格限制Token消耗有上界模型也不会因为收到太长的历史而“迷失”在当前话题中。真正进阶的做法是计算当前会话的总Token数超过阈值时不再盲目裁剪轮数而是用一条摘要消息顶替被移除的早期内容。不过对大多数课程设计和内部工具来说6轮滑动窗口已经够用。你还需要把system消息固定放在messages首位用来设定机器人的身份边界。4.2 temperature、max_tokens、top_p的取值参考这三个参数直接影响回答的质量、成本和安全边界。temperature控制随机性值越低回答越确定适合客服、代码生成值越高越有创意适合文案和闲聊。max_tokens限制单次回答的最大长度同时也直接封顶了单次请求的费用。top_p是核采样阈值通常你只需要调temperature不需要两个一起调OpenAI官方也建议分开使用。参数名客服/知识问答创意写作代码生成temperature0.2 ~ 0.40.8 ~ 1.00.1 ~ 0.3max_tokens500 ~ 8001200 ~ 2000800top_p默认1.00.9默认1.0成本估算上有一个粗略经验法则英文约4个字符消耗1个Token中文约1个汉字消耗1到2个Token一次请求的总Token等于历史消息Token加上本次回答TokenOpenAI按总Token计费。在配置里把max-tokens设成800意味着单次请求回答部分的费用上限就是800个Token。源码里如果连这个字段都没有或者写死成2048说明作者没有考虑过成本控制这种项目接进生产环境很容易翻车。4.3 超时、重试与API Key安全把外部依赖的不确定性挡在系统外外部API调用的三大风险就是慢、限流、挂。Chat Completions接口正常回答在2到10秒但遇到复杂Prompt或服务高负载时可能更长。配置里的connect-timeout用10秒read-timeout用60秒是比较平衡的组合。超时之外要处理两个HTTP状态码429表示限流5xx表示服务端临时故障。常见做法是加一个带指数退避的重试机制第一次失败等1秒第二次等2秒第三次等4秒超过3次直接放弃并返回友好错误。注意不要对401做重试那说明API Key有问题重试只会浪费配额。重试逻辑可以用Spring的RetryTemplate也可以自己写一个循环核心是必须有最大次数限制否则高并发下会形成请求雪崩。API Key的安全配置必须单独强调它一定要通过环境变量注入禁止出现在配置文件的明文里禁止在日志中打印禁止在统一响应里回传。我见过最离谱的源码把API Key放在application.yml里直接提交到公共仓库第二天就被人盗刷了几百美元。你拿到任何源码第一件事就是全局搜索sk-开头的内容确认Key不是硬编码的。5. 避坑手册SpringBoot整合OpenAI最常见的五个翻车现场这部分直接从真实踩坑记录里挑最典型的五条每条都按“现象到原因到解决”的顺序讲。你会发现绝大多数问题并不是OpenAI接口本身的问题而是SpringBoot工程层面的经典坑。把这些记住了下次遇到同样的报错你连搜索引擎都不用打开。5.1 现象一明明写了配置类启动却报OpenAIProperties注入失败有人在启动时看到这样的报错No qualifying bean of type OpenAIProperties或APPLICATION FAILED TO START。原因是ChatbotApplication默认扫描启动类所在包及其子包如果你的config、service等包全都在com.example.chatbot下面那是没问题的但如果你把配置类放到了com.example.config这种“兄弟包”里就完全不在扫描路径内。解决方法是把整个工程的包结构统一在启动类同级的子包下或者在启动类上显式指定扫描范围SpringBootApplication(scanBasePackages com.example) public class ChatbotApplication { public static void main(String[] args) { SpringApplication.run(ChatbotApplication.class, args); } }还有一个关联坑当SpringBoot版本较高时某些自动配置类的行为有变化比如ConfigurationProperties不再被自动注册必须配合EnableConfigurationProperties或Configuration使用。判断方法很简单在启动日志里看Negative matches部分有没有出现你期望的配置项。这套排查思路对SpringBoot整合任何外部服务都通用。5.2 现象二调用API返回401或429日志里只有一行状态码痛苦的是日志里只有Unauthorized不告诉你Key到底出了什么问题。这种情况先别怀疑代码直接用curl测一下OpenAI接口是否通、Key是否有效。常见原因有三个第一环境变量没生效OPENAI_API_KEY为空字符串你带了空Token去请求第二复制Key时带上了多余空格或换行第三免费额度用完实际返回的是429而不是401。解决方法是把异常信息打印全但只输出Key的最后四位做排查if (response.statusCode() ! 200) { String safeKey properties.getApiKey().replaceAll(.(?.{4}), *); log.error(OpenAI调用失败, status{}, keyTail{}, body{}, response.statusCode(), safeKey, response.body()); }这个技巧值得写进你的工具类。全量Key绝不能打入日志但完全不打又无法排查打尾部四位是安全与效率的平衡点。另外429这里有个“后悔药”如果你用的是ChatGPT Plus订阅而不是API按量付费账号那是没法直接调接口的别在这个上面浪费时间。5.3 现象三回答到一半连接被重置前端拿不到完整消息同步调用时偶发IOException: Connection reset前端表现为“转圈转半天然后直接报错”。原因有两个方向一是OpenAI处理复杂Prompt超过了你的read-timeout网关主动断连二是网络链路中的某个中间节点在空闲一段时间后关闭了连接而你的HttpClient还在复用旧连接。解决方案分三层。第一层把读取超时从默认的30秒调到90到120秒给慢推理留足空间。第二层如果你用的是RestTemplate尽量换成HttpClient或OkHttp并在客户端配置连接池和KeepAlive策略HttpClient httpClient HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .executor(Executors.newFixedThreadPool(20)) .build();第三层也是更彻底的方案使用流式输出第6章展开。流式接口在生成第一个Token时就开始返回数据即使整体耗时很长前端也已经接收到了内容用户感知从“一直转圈”变成“缓慢打字”体感完全不同。5.4 现象四把Vue打包产物放进static目录后一刷新页面就404这是做前后端一体部署时最经典的坑。你按教程把Vue打包后的dist目录内容复制到src/main/resources/static下首页能打开但点击路由跳转或者按F5刷新页面就变404了。原因很简单Vue用的是history路由URL路径是前端路由比如/chat/history刷新时浏览器向Tomcat请求这个路径Tomcat在静态资源目录里找不到对应的HTML就返回404。这个现象几乎每个做过Vue打包放进SpringBoot的人都遇到过。解决方法是加一个ForwardController把非/api开头的路径全部转发到index.htmlController public class ForwardController { RequestMapping(value {/chat/**, /settings/**, /, /index}) public String forward() { return forward:/index.html; } }这里的关键逻辑是真正以/api开头的后端接口不能被转发否则所有请求都会被吞进前端路由。所以ForwardController的匹配路径必须精确并且只处理浏览器端路由。另一个注意点是mvn clean会把static目录清掉稳妥的做法是让前端构建直接输出到src/main/resources/static或者写一个脚本在打包时自动复制避免每次手工操作。5.5 现象五Maven依赖下载慢构建卡了十分钟严格来说这不是SpringBoot或OpenAI的问题但它真实地卡住了大量入门者的第一步。现象是新建工程后Maven一直在下载依赖进度条寸步难行最终超时失败。原因基本固定在Maven中央仓库的连接质量上。解决方法是配置国内镜像仓库国内最常用的就是阿里云Maven仓库镜像修改settings.xmlmirrors mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror /mirrors配置好后重新构建你会发现依赖下载速度提升几十倍。这个配置属于“一次配置终身受益”我自己的开发环境里至今沿用。不过要提醒一句团队协作时这个settings.xml是个人环境配置不要提交到工程里如果有人用你的源码却不用这个镜像至少要保证代码本身不依赖任何特定仓库地址。6. 进阶给聊天机器人加流式输出让体验从“转圈圈”变成“打字机”很多人把流式输出理解为“锦上添花”但实际上对对话类产品来说它是影响留存率的关键体验。同步接口必须等整段回答生成完才返回用户看到的就是一个3到8秒的加载动画而流式输出让第一个Token在几百毫秒内到达回答逐字出现视觉上感觉快了好几倍。实现流式输出SpringBoot有原生支持核心是SseEmitterGetMapping(/chat/stream) public SseEmitter streamChat(RequestParam String message) { SseEmitter emitter new SseEmitter(-1L); executor.execute(() - { try { // 发起OpenAI流式请求streamtrue // 逐行读取 event: data: 内容 // 每个 data 里 choices[0].delta.content 就是增量文本 // emitter.send(content); } catch (Exception e) { emitter.completeWithError(e); } finally { emitter.complete(); } }); return emitter; }SseEmitter的工作方式很好理解它把HTTP连接保持打开服务端可以多次发送数据直到调用complete()关闭。前端用原生EventSource或fetch流式读取就能接住。注意几个工程要点线程池必须单独配置不能用Tomcat的工作线程执行长任务SseEmitter(-1L)表示不自动超时真正的超时控制应该在OpenAI客户端的read-timeout上做异常分支必须调用completeWithError否则前端会一直等下去。验证流式接口有没有生效不要急着打开页面先看这个现象同步接口要等5秒才能看到完整回答改成流式后1秒内就能看到第一个字然后内容一行一行吐出来。我在做客服系统时把这个方案原封不动搬了过去只换了模型名和system消息。回头看做这个东西最大的教训不是不会写代码而是把外部API当成“本地函数”来用没设超时、没做重试、也没算Token。现在的习惯是接任何一家大模型API第一件事先读限流文档第二件事写超时和重试第三件事画清楚消息的上下文边界。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑