Java 工程师 AI 智能体学习路线 · 阶段 3:编程调用 LLM 详解

发布时间:2026/7/10 9:09:18
Java 工程师 AI 智能体学习路线 · 阶段 3:编程调用 LLM 详解 Java 工程师 AI 智能体学习路线 · 阶段 3编程调用 LLM 详解本阶段目标用代码把大模型接进 Java 应用理解请求/响应结构掌握流式输出、错误处理、密钥管理等工程要点为后续智能体开发打基础。注博客https://blog.csdn.net/badao_liumang_qizhi3.1 HTTP 直接调用不依赖框架讲解所有 SDK 本质都是对 HTTP 接口的封装。理解原始调用有助于排查问题、对接小众厂商。端点通常为POST /v1/chat/completions。鉴权请求头Authorization: Bearer API_KEY。请求体model、messages、temperature等参数JSON。响应体choices[].message.content、usage等。示例请求结构POSThttps://api.openai.com/v1/chat/completionsHeaders:{Authorization:Bearer sk-xxx,Content-Type:application/json}Body:{model:gpt-4o,messages:[{role:system,content:你是一名 Java 专家},{role:user,content:什么是 Stream API}],temperature:0.3}示例Java 原生调用WebClient 风格WebClientclientWebClient.create(https://api.openai.com);Stringrespclient.post().uri(/v1/chat/completions).header(Authorization,Bearer apiKey).bodyValue(requestBody).retrieve().bodyToMono(String.class).block();常见场景对接未提供官方 SDK 的厂商。网关层统一封装、加审计/限流。调试时直接抓原始报文。面试知识点QAPI Key 放在请求头还是请求体为什么A放请求头Authorization避免被日志记录到 URL/Body 中泄露且符合 HTTP 鉴权惯例便于网关统一拦截。3.2 SDK 与框架抽象讲解直接用 HTTP 调用繁琐且易错实际项目多用 SDK/框架统一抽象。Java 主流选择Spring AIChatClient抽象最贴合 Spring 生态。LangChain4jChatLanguageModel抽象链式/Agent API 成熟。官方 SDKOpenAI Java SDK、各厂商 Java SDK。示例Spring AI ChatClientAutowiredprivateChatClientchatClient;publicStringask(Stringquestion){returnchatClient.prompt().system(你是一名严谨的 Java 架构师).user(question).call().content();}示例LangChain4jChatLanguageModelmodelOpenAiChatModel.builder().apiKey(apiKey).modelName(gpt-4o).temperature(0.3).build();Stringanswermodel.generate(用一句话解释 JVM 内存模型);常见场景业务代码只依赖抽象接口便于切换厂商。多模型路由按任务类型选择不同ChatClient实例。面试知识点Q为什么要用 ChatClient 这类抽象而不是直接写 HTTPA屏蔽厂商差异、统一消息模型、内置重试/流式/拦截器降低耦合便于灰度切换与单元测试。3.3 消息模型请求/响应结构讲解理解 SDK 背后的对象模型有助于正确组装多轮对话与解析结果。MessageSystemMessage/UserMessage/AssistantMessage/ToolMessage。Prompt一组 Message 的容器传给模型。ChatResponse / Completion模型返回含Result内容、Metadata模型名、用量。UsagepromptTokens/completionTokens/totalTokens用于成本核算。示例组装多轮消息Spring AIPromptpromptnewPrompt(List.of(newSystemMessage(你是 Java 导师),newUserMessage(什么是泛型),newAssistantMessage(泛型提供编译期类型安全...),newUserMessage(那通配符上下界呢)));ChatResponseresponsechatClient.call(prompt);Stringanswerresponse.getResult().getOutput().getText();常见场景多轮对话需把历史 User/Assistant 持续拼回 Prompt。解析usage做成本监控。面试知识点Q多轮对话是把历史全量发过去吗有什么问题A通常是但会随轮次增长超出上下文窗口并增加成本工程上需截断/摘要历史或只保留关键上下文。3.4 流式处理Streaming讲解流式SSE让模型边生成边返回提升用户体验。Java 侧常用响应式Flux或SseEmitter。协议服务端以text/event-stream持续推送片段delta。Spring AI 流式chatClient.prompt().user(question).stream().content()// FluxString.subscribe(token-System.out.print(token));Web 层SseEmitterGetMapping(/chat/stream)publicSseEmitterstream(RequestParamStringq){SseEmitteremitternewSseEmitter();chatClient.prompt().user(q).stream().content().subscribe(token-{try{emitter.send(token);}catch(IOExceptione){}},e-emitter.completeWithError(e),emitter::complete);returnemitter;}前端用fetchReadableStream或EventSource逐字渲染。常见场景聊天机器人打字机效果。长文本生成避免前端长时间白屏。面试知识点Q流式输出和非流式在成本/延迟上有什么区别A成本相同按 Token 计费流式首字延迟低、体验好但需处理断连、背压与超时非流式实现简单但需等全部生成完才返回。3.5 错误处理与重试讲解生产调用必须处理以下异常429 限流Too Many Requests触发速率/配额限制。408/超时网络或服务端慢。5xx服务端错误。内容审核拦截输入/输出被安全策略拒绝。重试策略指数退避 抖动如 1s、2s、4s 后重试。区分可重试429/5xx/超时与不可重试400/鉴权失败。设置最大重试次数与总体超时。降级重试耗尽后返回兜底文案或降级模型。示例Spring Retry 简化示意Retryable(retryFor{RateLimitException.class,TimeoutException.class},maxAttempts3,backoffBackoff(delay1000,multiplier2))publicStringaskWithRetry(Stringq){returnchatClient...;}RecoverpublicStringfallback(Stringq,Exceptione){return服务繁忙请稍后再试;}常见场景高并发批量任务易触发 429需限流退避。关键链路配置熔断Resilience4j。面试知识点Q收到 429 应该怎么处理立即重试可以吗A不可以立即重试会加重限流。应读取Retry-After头按指数退避抖动重试并配合本地限流、批量降速必要时降级到更小模型。3.6 密钥与配置管理讲解环境变量 / 配置中心API Key 不写死在代码放application.yml占位${AI_KEY}或 Nacos/Apollo。密钥轮转定期更换 Key支持多 Key 负载均衡与故障切换。多模型路由配置多个模型实例按任务/成本选择。安全Key 仅服务端持有绝不暴露给前端日志脱敏。示例多 Key 轮询伪代码ListStringkeysList.of(k1,k2,k3);AtomicIntegeridxnewAtomicInteger();StringnextKey(){returnkeys.get(idx.getAndIncrement()%keys.size());}常见场景配额分摊多个 Key 避免单 Key 限流。合规密钥加密存储、访问审计。面试知识点Q前端能直接持有 LLM 的 API Key 吗为什么A不能。前端暴露 Key 等于公开凭证会被盗刷且无法审计必须由后端代理调用Key 仅存服务端。3.7 阶段 3 自测清单能不依赖框架直接用 HTTP 调用一次模型能用 Spring AI / LangChain4j 实现对话能实现流式输出接口SseEmitter / Flux能处理 429 限流并重试/降级能把 API Key 放到配置中心而非代码3.8 阶段 3 面试题与参考答案1. API Key 应该放请求头还是请求体为什么放请求头Authorization: Bearer避免被 URL/Body 日志记录泄露且便于网关统一拦截与审计。2. 为什么推荐用 ChatClient / ChatLanguageModel 抽象而不是直接写 HTTP屏蔽厂商差异、统一消息模型、内置重试/流式/拦截器降低耦合便于切换、灰度与单元测试。3. 多轮对话如何管理历史全量发送有什么问题把历史 User/Assistant 持续拼回 Prompt。全量发送会随轮次增长超出上下文窗口并增加成本需截断/摘要历史。4. 流式输出和非流式在成本、延迟、体验上的区别成本相同按 Token流式首字延迟低、体验好但需处理断连/背压/超时非流式实现简单但需等全部生成完才返回。5. 收到 429 限流应该如何处理不立即重试读取 Retry-After按指数退避抖动重试配合本地限流与降速必要时降级到更小模型或返回兜底。6. 哪些错误可重试、哪些不可重试可重试429 限流、408/超时、5xx不可重试401/403 鉴权失败、400 参数错误、内容审核拦截需改输入而非重试。7. 前端能直接持有 LLM API Key 吗不能。前端暴露 Key 会被盗刷且无法审计必须由后端代理调用Key 仅存服务端并做日志脱敏。8. 什么是 SSEJava 侧如何实现流式推送SSEServer-Sent Events是服务端单向持续推送文本的协议。Java 侧可用 Spring 的SseEmitter或响应式Flux把模型生成的 token 逐段推送给前端。9. 多 Key 轮询有什么好处分摊配额避免单 Key 限流支持故障切换与密钥轮转提升可用性与并发能力。10. 如何对 LLM 调用做成本核算解析响应中的usageprompt/completion/total tokens按厂商单价累计结合日志/可观测平台做按业务/用户的成本归因与告警。