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

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

本阶段目标:用代码把大模型接进 Java 应用,理解请求/响应结构,掌握流式输出、错误处理、密钥管理等工程要点,为后续智能体开发打基础。


注:

博客:

https://blog.csdn.net/badao_liumang_qizhi

3.1 HTTP 直接调用(不依赖框架)

讲解

所有 SDK 本质都是对 HTTP 接口的封装。理解原始调用有助于排查问题、对接小众厂商。

  • 端点 :通常为 POST /v1/chat/completions
  • 鉴权 :请求头 Authorization: Bearer <API_KEY>
  • 请求体modelmessagestemperature 等参数(JSON)。
  • 响应体choices[].message.contentusage 等。

示例(请求结构)

json 复制代码
POST https://api.openai.com/v1/chat/completions
Headers: { "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 风格)

java 复制代码
WebClient client = WebClient.create("https://api.openai.com");
String resp = client.post()
    .uri("/v1/chat/completions")
    .header("Authorization", "Bearer " + apiKey)
    .bodyValue(requestBody)
    .retrieve()
    .bodyToMono(String.class)
    .block();

常见场景

  • 对接未提供官方 SDK 的厂商。
  • 网关层统一封装、加审计/限流。
  • 调试时直接抓原始报文。

面试知识点

  • Q:API Key 放在请求头还是请求体?为什么?
    A:放请求头(Authorization),避免被日志记录到 URL/Body 中泄露;且符合 HTTP 鉴权惯例,便于网关统一拦截。

3.2 SDK 与框架抽象

讲解

直接用 HTTP 调用繁琐且易错,实际项目多用 SDK/框架统一抽象。Java 主流选择:

  • Spring AIChatClient 抽象,最贴合 Spring 生态。
  • LangChain4jChatLanguageModel 抽象,链式/Agent API 成熟。
  • 官方 SDK:OpenAI Java SDK、各厂商 Java SDK。

示例(Spring AI ChatClient)

java 复制代码
@Autowired
private ChatClient chatClient;

public String ask(String question) {
    return chatClient.prompt()
            .system("你是一名严谨的 Java 架构师")
            .user(question)
            .call()
            .content();
}

示例(LangChain4j)

java 复制代码
ChatLanguageModel model = OpenAiChatModel.builder()
        .apiKey(apiKey)
        .modelName("gpt-4o")
        .temperature(0.3)
        .build();

String answer = model.generate("用一句话解释 JVM 内存模型");

常见场景

  • 业务代码只依赖抽象接口,便于切换厂商。
  • 多模型路由:按任务类型选择不同 ChatClient 实例。

面试知识点

  • Q:为什么要用 ChatClient 这类抽象而不是直接写 HTTP?
    A:屏蔽厂商差异、统一消息模型、内置重试/流式/拦截器,降低耦合,便于灰度切换与单元测试。

3.3 消息模型(请求/响应结构)

讲解

理解 SDK 背后的对象模型,有助于正确组装多轮对话与解析结果。

  • MessageSystemMessage / UserMessage / AssistantMessage / ToolMessage
  • Prompt:一组 Message 的容器,传给模型。
  • ChatResponse / Completion :模型返回,含 Result(内容)、Metadata(模型名、用量)。
  • UsagepromptTokens / completionTokens / totalTokens,用于成本核算。

示例(组装多轮消息,Spring AI)

java 复制代码
Prompt prompt = new Prompt(List.of(
    new SystemMessage("你是 Java 导师"),
    new UserMessage("什么是泛型?"),
    new AssistantMessage("泛型提供编译期类型安全..."),
    new UserMessage("那通配符上下界呢?")
));
ChatResponse response = chatClient.call(prompt);
String answer = response.getResult().getOutput().getText();

常见场景

  • 多轮对话需把历史 User/Assistant 持续拼回 Prompt。
  • 解析 usage 做成本监控。

面试知识点

  • Q:多轮对话是把历史全量发过去吗?有什么问题?
    A:通常是,但会随轮次增长超出上下文窗口并增加成本;工程上需截断/摘要历史,或只保留关键上下文。

3.4 流式处理(Streaming)

讲解

流式(SSE)让模型"边生成边返回",提升用户体验。Java 侧常用响应式(Flux)或 SseEmitter

  • 协议 :服务端以 text/event-stream 持续推送片段(delta)。
  • Spring AI 流式
java 复制代码
chatClient.prompt()
    .user(question)
    .stream()
    .content()                       // Flux<String>
    .subscribe(token -> System.out.print(token));
  • Web 层(SseEmitter)
java 复制代码
@GetMapping("/chat/stream")
public SseEmitter stream(@RequestParam String q) {
    SseEmitter emitter = new SseEmitter();
    chatClient.prompt().user(q).stream().content()
        .subscribe(
            token -> { try { emitter.send(token); } catch (IOException e) {} },
            e -> emitter.completeWithError(e),
            emitter::complete
        );
    return emitter;
}
  • 前端 :用 fetch + ReadableStreamEventSource 逐字渲染。

常见场景

  • 聊天机器人打字机效果。
  • 长文本生成避免前端长时间白屏。

面试知识点

  • Q:流式输出和非流式在成本/延迟上有什么区别?
    A:成本相同(按 Token 计费);流式首字延迟低、体验好,但需处理断连、背压与超时;非流式实现简单,但需等全部生成完才返回。

3.5 错误处理与重试

讲解

生产调用必须处理以下异常:

  • 429 限流(Too Many Requests):触发速率/配额限制。
  • 408/超时:网络或服务端慢。
  • 5xx:服务端错误。
  • 内容审核拦截:输入/输出被安全策略拒绝。

重试策略

  • 指数退避 + 抖动(如 1s、2s、4s 后重试)。
  • 区分可重试(429/5xx/超时)与不可重试(400/鉴权失败)。
  • 设置最大重试次数与总体超时。
  • 降级:重试耗尽后返回兜底文案或降级模型。

示例(Spring Retry 简化示意)

java 复制代码
@Retryable(retryFor = {RateLimitException.class, TimeoutException.class},
           maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2))
public String askWithRetry(String q) { return chatClient...; }

@Recover
public String fallback(String q, Exception e) { return "服务繁忙,请稍后再试"; }

常见场景

  • 高并发批量任务易触发 429,需限流+退避。
  • 关键链路配置熔断(Resilience4j)。

面试知识点

  • Q:收到 429 应该怎么处理?立即重试可以吗?
    A:不可以立即重试,会加重限流。应读取 Retry-After 头,按指数退避+抖动重试;并配合本地限流、批量降速,必要时降级到更小模型。

3.6 密钥与配置管理

讲解

  • 环境变量 / 配置中心 :API Key 不写死在代码,放 application.yml(占位 ${AI_KEY})或 Nacos/Apollo。
  • 密钥轮转:定期更换 Key,支持多 Key 负载均衡与故障切换。
  • 多模型路由:配置多个模型实例,按任务/成本选择。
  • 安全:Key 仅服务端持有,绝不暴露给前端;日志脱敏。

示例(多 Key 轮询,伪代码)

java 复制代码
List<String> keys = List.of(k1, k2, k3);
AtomicInteger idx = new AtomicInteger();
String nextKey() {
    return keys.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. 什么是 SSE?Java 侧如何实现流式推送?

SSE(Server-Sent Events)是服务端单向持续推送文本的协议。Java 侧可用 Spring 的 SseEmitter 或响应式 Flux 把模型生成的 token 逐段推送给前端。

9. 多 Key 轮询有什么好处?

分摊配额避免单 Key 限流,支持故障切换与密钥轮转,提升可用性与并发能力。

10. 如何对 LLM 调用做成本核算?

解析响应中的 usage(prompt/completion/total tokens),按厂商单价累计;结合日志/可观测平台做按业务/用户的成本归因与告警。

相关推荐
ASKCOS2 小时前
深度解析 Kimi Code Swarm 模式:多 Agent 并行编程的架构设计与工程权衡
ai
曦尧4 小时前
Argo CD:面向 Kubernetes 的声明式 GitOps 持续交付工具
ai·自动化
ofoxcoding4 小时前
Codex Computer Use 完整指南:AI 自动操控 Mac 与 Windows 桌面实战详解
人工智能·windows·macos·ai
奇牙coding1234 小时前
OpenAI Realtime API WebSocket 断连 4008/1006 怎么解决?不是 Key 失效,是实时多模态独有的会话超时规则
python·websocket·网络协议·ai
结城明日奈是我老婆4 小时前
comfyui 控制图像Controlnet
ai·ai作画
志凌海纳SmartX5 小时前
构建 Agent Ready 的企业云基础设施(白皮书下载)
人工智能·ai·agent·smartx·企业云·榫卯企业云
武子康5 小时前
调查研究-224 Prefill 与 Decode 分离:高并发 LLM Serving 的下一层架构
人工智能·ai·架构·llm·gpu·vllm·sglang
头茬韭菜6 小时前
3.6 依赖注入 — 四个依赖的精准边界
开发语言·ai·ecmascript
奇牙coding12316 小时前
gpt-5.5、deepseek-v4-pro、claude-opus-4.8 跑同一套 50 道编程题——有一类任务结果和官方榜单完全反过来
gpt·ai