跳过正文
开放模型上生产:Java 团队的 LangChain4j 集成路径
  1. 文章/

开放模型上生产:Java 团队的 LangChain4j 集成路径

·5992 字·12 分钟
NeatGuyCoding
作者
NeatGuyCoding
目录

开放模型上生产:Java 团队的 LangChain4j 集成路径
#

把生成式 AI 放进已有 Spring 服务,难点往往不在「能不能调通一个 ChatCompletion」,而在能否用同一套 Java 代码在本地 Ollama、团队自托管的 OpenAI 兼容端点,以及 Azure AI Foundry 托管推理之间切换,同时把工具调用、RAG 与基本质量门禁做成可观测、可回滚的管线。下文按工程能力块组织:集成层、本地运行时、演示服务、RAG 与评测、容量延迟、托管迁移与安全——论断尽量绑定 LangChain4j 文档Ollama APIJEP 444 与可复现命令;未在公开文档中复核的数字与案例标注为演讲者观点

演示源码可通过 aka.ms/opengenai 获取(HTTP 301 至 gen-ai-with-open-models)。仓库按 Demo 1(本地推理 + 工具调用)与 Demo 2(RAG + 评测)拆分脚本,便于在 JavaOne 现场逐段复现;生产落地时则应把「能跑通」与「能上线」拆开:前者验证集成面,后者补齐持久化、鉴权、护栏与 SLO。

JavaOne 2026:Selecting the Right Model 选型幻灯
图:幻灯「Selecting the Right Model for Your Use Case」——按任务、延迟与内存约束选型,而非追逐榜单第一名(演讲者观点)。


统一集成层:LangChain4j 与 OpenAI 兼容端点
#

为什么
#

Java 团队若沿用「每个模型一个 SDK」,换端点就要改业务层。演讲中的做法是:用 LangChain4jOllamaChatModelOpenAiChatModel 与同一套 AiServices@ToolEmbeddingModel / EmbeddingStore,把 Python 样例里的 base_url + api_key 迁到 Bean 配置。演讲者观点:切换 Ollama、vLLM 或云端时「尽量不改业务 Java」——实际仍需改 baseUrlapiKeymodelName 等 Bean,而非零 diff。

机制与约束
#

  • Ollama 集成 默认 http://localhost:11434OpenAI 兼容路径/v1/chat/completions
  • @Tool 属于 LangChain4j function callingTools 教程),与 MCP 是不同协议层;混用概念会导致架构图画错边界。
  • 演示仓库 OllamaConfigtemperature0.7;要点草案写 0.2 以利 grounded 回答——二者不冲突,属可调超参。

Mermaid diagram 1

LangChain4j 幻灯:OllamaChatModel、@Tool 与 AiServices 三步模式
图:幻灯「LangChain4j: Code Patterns for Open Models」——OllamaChatModel.builder()@ToolAiServices.builder

怎么做
#

OllamaChatModel chat = OllamaChatModel.builder()
    .baseUrl("http://localhost:11434")
    .modelName("llama3.1:8b")
    .temperature(0.7)
    .build();
curl http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"llama3.1:8b","messages":[{"role":"user","content":"hello"}]}'

常见误区
#

  • @Tool 当成 MCP Server 暴露——二者可并存,但语义与运维边界不同。
  • 假设换云厂商时 Java 业务类完全不动:至少配置类与鉴权头会变。
  • AiServices 接口上堆过长 @SystemMessage,却不用 MessageWindowChatMemory 控制窗口——长对话会挤占留给 RAG context 的 token(AiServices 教程 中的 memory 组件可按需启用)。

本地运行时:Ollama 与演示模型组合
#

为什么
#

在接云端账单与合规评审之前,用本地 Ollama 复现延迟、量化与工具调用行为,成本可控。演示固定组合:llama3.1:8b(对话)、mistral:7b(可切换)、nomic-embed-text(嵌入),与 演示 README 一致。

机制与约束
#

  • CLI ollama pull / ollama list 对应 REST POST /api/pullGET /api/tagsOllama API)。
  • Linux 官方安装为 curl -fsSL https://ollama.com/install.sh | sh;字幕中的 apt-get install ollama 未在官网核实
  • GPU/显存由 Ollama 调度属演讲者观点;量化标签(如 Q4)见各模型卡片,无统一「质量-内存」对照表。

README 与终端:Demo 1 Local Inference + Tool Calling
图:IDE 中 ## Demo 1: Local Inference + Tool CallingProduction-Ready GenAI with Open Models for Java Teams

怎么做
#

ollama pull llama3.1:8b
ollama pull mistral:7b
ollama pull nomic-embed-text
ollama list

application.ymlollama.base-url: http://localhost:11434chat-model / embedding-model 与上一致(application.yml)。

常见误区
#

  • pull 嵌入模型就调 /ingest——RAG 链路会在嵌入步骤失败。
  • 把演示 mistral:7b 当成生产默认——应按自有 benchmark 重选(见选型幻灯)。
  • 在笔记本上同时跑 70B 全精度与大型 Spring 堆——即使 JVM 未 OOM,节点仍可能因 Ollama 常驻权重而 swap(演讲者观点:模型内存多在 heap 外)。

选型幻灯还强调六步:定义任务类型(chat / RAG / code 等)→ 设定延迟与内存上限 → 用自家数据 benchmark → 评估 GGUF Q4/Q5/Q8 量化 → 比较「7B @ 50 tok/s」与「70B @ 5 tok/s」谁更符合 SLA → 固定模型版本并准备回滚配置。公开榜单只能作初筛,不能替代领域数据上的回归。

开放模型术语:Quantization、GGUF、MoE、TTFT
图:幻灯「Open Model Terms」——量化、GGUF、MoE 与 TTFT。


Spring Boot 最小可运行集成
#

为什么
#

Java 21 + Maven 的 Spring Boot 应用给团队一条可复制的联调入口:构建、启动、用 curl 打 REST,再叠工具与 RAG。

机制与约束
#

  • pom.xmljava.version 21,Spring Boot 3.4.x。
  • server.port: 8080,入口 OpenModelsDemoApplication
  • OCR 中 808@ 为识别噪声,实际端口 8080

终端:mvn clean package 与 spring-boot:run
图:SOURCE CONTROL 与 README 中 mvn clean package -DskipTestsmvn spring-boot:run

怎么做
#

mvn -q -DskipTests clean package
mvn -q spring-boot:run
curl -sS "http://localhost:8080/chat?message=ping"

常见误区
#

  • 跳过 ollama serve 只起 Spring——LLM 调用会连不上 11434
  • 在 CI 里跑完整 Demo 却不预留 GPU/CPU 与模型拉取时间。
  • -DskipTests 当作长期策略——演示可以跳过测试,上线前应恢复针对 Controller 与 ingest 路径的契约测试。

Tomcat 在 8080 监听、context path '/' 与 OCR 中 Started OpenModelsDemoApplication 日志一致,说明这是一个标准 Spring MVC 外壳,而非独立 CLI;团队可把同一 JAR 部署到 K8s,只需把 ollama.base-url 换成集群内 Service 名称。


对话与工具调用:从 /chat/chat/tools
#

为什么
#

先验证纯生成(/chat),再让模型在需要时调用确定性后端(/chat/tools),把「必查库存」从幻觉风险里拆出去。

机制与约束
#

  • ChatController@GetMapping("/chat")@Qualifier("chatAssistant") 注入,chatAssistant.chat(message)
  • 工具路径:@GetMapping("/chat/tools")toolAssistantInventoryTools@Tool + @P("Product SKU")Map 模拟库存(JDK-21 → 150),外部 ERP。

ChatController:@GetMapping("/chat")
图:public class ChatController@GetMapping("/chat")

InventoryTools:checkStock 与 Product SKU
图:InventoryToolsString checkStock(@P("Product SKU")

怎么做
#

curl "http://localhost:8080/chat?message=What+is+the+Java+record+keyword?"
curl "http://localhost:8080/chat/tools?message=How+many+units+of+JDK-21+in+stock?"
@Tool("Look up current stock level for a product by SKU")
String checkStock(@P("Product SKU") String sku) {
  return Map.of("JDK-21", 150).getOrDefault(sku, 0) + " units";
}

常见误区
#

  • 工具返回自由文本却不校验 SKU——模型仍可能编造未查询的商品。
  • 在 system prompt 里塞用户可控指令而不隔离——增加 prompt injection 面(生产需输入护栏,见后文)。
  • 为「省事」只暴露 /chat 而不做工具路由——库存、工单、定价类问题应走确定性后端;演示中 /chat/tools/chat 分离正是为了对比幻觉与 grounded 工具结果。

InventoryTools 返回形如 "SKU %s: %d units in stock".formatted(sku, qty) 的字符串(OCR 可见 formatted 片段),便于模型把工具输出复述给用户,同时保持数据源在 Java 侧可控。


RAG:摄取、问答与启发式评测
#

为什么
#

企业文档型问答需要「答案有据可查」:先 chunk + 嵌入 + 检索,再生成;更换模型或分块策略后,用自有 golden set 做回归,避免只靠肉眼试 prompt。

机制与约束
#

  • DocumentIngestorFileSystemDocumentLoaderDocumentSplitters.recursiverag.chunk-size: 500chunk-overlap: 50EmbeddingStoreIngestor 写入 in-memory store。
  • POST /ingest 返回 documents_ingestedembedding_modelstore(演示为 3 份 docs/ 样例)。
  • GET /askRagConfigEmbeddingStoreContentRetriever + ragAssistant;样例问虚拟线程时,java21-features.txtJEP 444 一致——事实来自文档,表述由 LLM 组织。
  • RagEvaluator3EvalCasecomputeFaithfulness 等为关键词重叠启发式(源码注释 Simple keyword overlap metric), RAGAS 等行业基准;OCR 中 context_precision≈0.27 等为单次演示跑分,不可外推

Mermaid diagram 2

README:Demo 2 RAG Pipeline + Evaluation
图:## Demo 2: RAG Pipeline + Evaluation### Ask questions grounded in your docume

DocumentIngestor:import EmbeddingModel
图:DocumentIngestor.javaimport dev.langchain4j.model.embedding.EmbeddingModel

RagEvaluator:@Service public class RagEvaluator
图:public class RagEvaluator 与 faithfulness / context precision 相关逻辑。

怎么做
#

curl -X POST "http://localhost:8080/ingest"
curl "http://localhost:8080/ask?question=What+are+virtual+threads+in+Java+21?"
curl -X POST "http://localhost:8080/evaluate"

常见误区
#

  • 把演示 /evaluate 分数当作 SLA 合同指标。
  • chunk 过大导致 context 稀释,过小导致语义破碎——需按文档类型调 chunk-size / overlap
  • 生产仍用 in-memory store——重启即失索引;应换持久化向量库(演示未覆盖)。
  • 混淆 faithfulness(答案是否忠于检索片段)与 answer relevance(答案是否切题)——RagEvaluator 对二者分别用启发式打分;若要对外报告,应改用 RAGAS、DeepEval 等框架并固定数据集版本。

POST /evaluate 返回的 average_latency_mssource_accuracy 等字段适合作为变更前后对比的门禁,而非绝对质量承诺。更换 llama3.1:8b 为其他开放权重后,应重新 ingest 并跑一遍 evaluate,观察 faithfulness 与 context precision 是否同向变化。


容量、延迟与节点规划
#

为什么
#

「JVM 调优完成」不等于节点稳定:Ollama 模型权重常驻 RAM,通常在 heap 外,仍与 -Xmx 争用同一台机器;K8s 缩容到零会换来冷启动 TTFT(演讲者观点)。

机制与约束
#

  • Ollama Modelfile 参数 num_ctxkeep_alive;API 支持 streamOllama API)。
  • 幻灯列举:StreamingPrompt cachingKV-cache / num_ctxBatchModel warmupRight-size contextHardware matching(量化 CPU vs 全精度 GPU)。

Response-Time Tuning Strategies 幻灯
图:幻灯「Response-Time Tuning Strategies」——Streaming、Prompt caching、Model warmup 等七项。

怎么做
#

# 容量草表(单侧运维,演讲者观点)
节点 RAM 128Gi − JVM -Xmx 24Gi − Ollama resident ~60Gi − OS/cache ≈ 余量
@Component
class LlmWarmup implements ApplicationRunner {
  @Override
  public void run(ApplicationArguments args) {
    chatAssistant.chat("ping");
  }
}

常见误区
#

  • 只看 tokens/s 不看 TTFT——交互式聊天首 token 更敏感。
  • 盲目增大 num_ctx——上下文越长,单次推理越慢(官方参数表可证存在 trade-off)。
  • 未开 streaming 却让前端干等整段 JSON——用户感知的「卡顿」往往来自首 token,而非总耗时;LangChain4j Ollama 集成文档含 streaming 示例,可与 Spring SseEmitter 或 WebFlux 组合。

MoE(Mixture of Experts)类模型在幻灯中与量化、TTFT 并列介绍:总参数量大但每步只激活子集专家,适合在延迟预算内追求能力上限。演讲者观点:托管侧(Foundry NIM)与自建 vLLM 的 MoE 运维复杂度高于稠密 7B/8B,选型时应把弹性与回滚算进总成本。


托管迁移:Azure AI Foundry 与 OpenAI 兼容 REST
#

为什么
#

本地验证后,可把同一 LangChain4j 调用面指向 Azure AI Foundry 上托管的开放权重部署(演示幻灯为 NVIDIA Nemotron-3-Super-NIM-microservice),由平台承担 GPU 与弹性(ACA/AKS/KEDA 等为 Azure 通用能力,未与演示仓库绑定验证)。

机制与约束
#

  • Azure 聊天补全形态:POST https://{endpoint}/openai/deployments/{deployment-id}/chat/completions?api-version=...Azure OpenAI REST);api-version 必须以部署页为准——草案中的 2024-05-01-preview 与当前 Learn 示例(如 2024-10-21)可能不一致。
  • Java 侧可用 OpenAiChatModel(自定义 baseUrl)或 AzureOpenAiChatModeldeploymentName)。
  • 幻灯/OCR 描述 Nemotron 120B 总参 / 12B active、LatentMoE、MTP、NVFP4——本次未能从 Microsoft Learn 全文复核;正文引用门户模型卡为准。Hugging Face 上同名仓库链接在核验时返回 404,写死参数量有风险。

Microsoft Foundry:NVIDIA-Nemotron-3-Super-NIM-microservice
图:Foundry 目录页「Nemotron-3-Super-120B-A12B is a large language model (LLM) trained by NVIDIA」。

怎么做
#

curl "$FOUNDRY_ENDPOINT/openai/deployments/$DEPLOYMENT/chat/completions?api-version=$API_VERSION" \
  -H "api-key: $AZURE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"hello"}]}'
OpenAiChatModel.builder()
  .baseUrl(System.getenv("FOUNDRY_BASE_URL"))
  .apiKey(System.getenv("AZURE_API_KEY"))
  .modelName(System.getenv("DEPLOYMENT_NAME")) // 以部署名为准
  .build();

常见误区
#

  • 把 Foundry deployment name 写成 Hugging Face repo id。
  • 忽略 api-version 查询参数——Azure 与裸 Ollama 本地 API 的版本策略不同(见下表)。
  • 认为「上云就不用关心开放权重合规」——Foundry 强调模型溯源(演讲者观点:相对 Hugging Face 自选权重需自评 license 与安全),法务与数据驻留仍需单独评审。

演示未包含 Foundry 分支的 Spring Profile;落地时常见做法是 application-local.yml 指向 Ollama,application-prod.yml 注入 FOUNDRY_BASE_URLAZURE_API_KEY,并对同一 Assistant 接口做金丝雀流量对比延迟与成本。

端点族版本机制
Azure OpenAI / FoundryURL 查询 api-version
Ollama 原生 /api/*文档未要求 api-version
Ollama OpenAI 兼容 /v1/*客户端形态兼容 OpenAI

开放模型的安全层
#

为什么
#

从 Hugging Face 等渠道拉取的权重通常不带商业 API 那套强制内容过滤;把未审查模型直接暴露给终端用户,合规与品牌风险由应用方承担。

机制与约束
#

  • 幻灯建议:Input guardrails(注入、PII、话题黑名单)→ ModelOutput guardrails(毒性、事实性、格式)→ Human review
  • LangChain4j 提供 ModerationModelOpenAiModerationModel演示仓库未实现护栏 Controller——属生产扩展。
  • Llama Guard 等为可选本地方案,演示代码未验证。

Safety Layers for Open Models 幻灯
图:幻灯「Safety Layers for Open Models」——input filter → model → output filter → human review。

怎么做
#

public String safeChat(String user) {
  if (moderation.isViolating(user)) {
    throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
  }
  String out = llm.generate(user);
  return moderation.isViolating(out) ? "[blocked]" : out;
}

常见误区
#

  • 只在输出侧做 moderation——越狱 prompt 已进入模型上下文。
  • 无 flagged 日志与人工升级队列——安全层不可运营。
  • 把 OpenAI Moderation API 当作唯一方案——离线或主权云场景可改用 ModerationModel 的其他实现或 Llama Guard 类本地分类器,但需自行维护模型版本与误杀率。

开放权重与托管 API 的差异在于:过滤责任默认在应用方。幻灯中的 input/output guardrails 与 LangChain4j ModerationModel 是同一思路的工程化——先拦截再生成,再拦截再返回,可疑样本进入人工复核队列,而不是仅在前端展示免责声明。


收尾:资料入口与证据边界
#

Thank You 幻灯与 aka.ms/opengenai
图:收尾页 https://aka.ms/opengenai 与议题「Production-Ready GenAI with Open Models for Java Teams」。

已核实(可对照仓库与官方文档):LangChain4j + Ollama 集成模式;演示端点 /chat/chat/tools/ingest/ask/evaluate;JEP 444 与样例文档一致性;Azure REST 形状与 LangChain4j 双集成类。

演讲者观点或未复核:AT&T 等降本案例、Foundry 目录模型数量、128Gi 容量草表、Nemotron 精确架构参数在 Learn 上的正文、护栏在生产环境的具体 SLA。

若你已在本地跑通 Demo 1/2,下一步通常是:持久化向量库、用真实 benchmark 替换 RagEvaluator 启发式、按节点 RAM 预算选定量化档,并在 Foundry 部署上用门户给出的 api-version 做金丝雀。整条路径的核心收益,是把「模型端点」变成可替换的配置,而不是散落在各 Controller 里的硬编码 URL。


参考与延伸阅读
#

相关文章