一、为什么这篇文章值得 Java 工程师认真读完?


大模型已经不是“试一试”的技术,而是开始进入核心业务链路。

对 Java 工程师来说,真正的挑战从来不是“会不会调用一个 LLM API”,而是下面这些更现实的问题:

  • • 如何避免大模型一本正经地胡说八道
  • • 如何把企业知识库、安全权限、订单系统、工单系统接进来
  • • 如何在高并发下扛住流量,同时控制模型调用成本
  • • 如何让系统具备可灰度、可观测、可扩展、可回退的工程能力

很多文章把 LLM、Agent、RAG、Tool、Skill 拆开讲,但在企业落地里,它们从来不是孤立存在的。真正可上线的智能系统,通常是这样一条链路:

    1. LLM 负责理解、推理、生成。
    1. RAG 负责把“企业真实知识”注入上下文,抑制幻觉。
    1. Agent 负责判断什么时候该检索、什么时候该调用工具、什么时候该结束。
    1. Skill 负责把工具能力做成稳定、自治、可治理的业务能力单元。

这篇文章不只解释“是什么”,而是站在资深架构师视角,带你搭一套真正能进生产的大模型应用架构。全文以一个典型的企业智能助手场景展开:

  • • 员工咨询制度政策
  • • 查询个人余额、档案、订单、工单
  • • 发起 IT 支持、退款、审批等动作型请求
  • • 支持高并发访问、流式响应、异步执行和全链路审计

技术栈会尽量贴近 Java 主流工程体系:

  • Spring Boot 3.x
  • Spring WebFlux
  • Redis
  • Kafka
  • Milvus / PgVector
  • Micrometer + Prometheus + Grafana
  • Kubernetes
  • LangChain4j / AgentScope Java

二、先统一认知:LLM、RAG、Agent、Skill 到底各解决什么问题?


2.1 LLM:负责语言理解与生成,但不等于“事实真相”

LLM 本质上是一个基于上下文预测下一个 token 的概率模型。它强在:

  • • 自然语言理解
  • • 多轮对话
  • • 摘要归纳
  • • 推理规划
  • • 结构化输出

但它天然存在三个工程痛点:

  • • 没有企业私域知识
  • • 容易幻觉
  • • 对实时状态无感,比如订单状态、审批结果、库存变化

所以,企业系统里不能把 LLM 直接当作“事实数据库”或“业务系统”。

它更适合做两类事情:

    1. 把人类自然语言翻译成系统可执行意图。
    1. 基于可信上下文,生成对用户友好的最终回答。

2.2 RAG:把“企业事实”实时塞进大模型上下文

RAG,Retrieval-Augmented Generation,检索增强生成。

它解决的是“模型不知道你的内部知识”这个根本问题。

经典链路如下:

    1. 文档清洗
    1. 文档切片
    1. 向量化 embedding
    1. 存入向量库
    1. 用户问题向量化
    1. 相似度检索
    1. 重排筛选
    1. 将证据片段注入 prompt
    1. LLM 基于证据生成答案

RAG 的本质不是“让模型更聪明”,而是“让模型回答时有证据”。

2.3 Agent:让模型从“会说”进化到“会做”

如果只有 LLM + RAG,系统通常只能“回答问题”。

但企业场景里,用户经常不是只想知道,而是要系统执行动作,比如:

  • • “帮我查一下年假余额”
  • • “帮我创建一个 VPN 故障工单”
  • • “帮我提交退款申请”

这时候就需要 Agent。

Agent 的核心作用是:

  • • 识别用户意图
  • • 决定是否需要调用外部工具
  • • 决定调用哪个工具
  • • 根据工具结果继续推理
  • • 生成最终答案

工程上常见范式是 ReAct

  • Reasoning:先思考
  • Acting:调用工具
  • Observation:读取工具结果
  • • 再次思考,直到得出最终答案

2.4 Skill:工具的工程化抽象,而不是随手封个 HTTP Client

很多团队在做 Agent 时,会把 Tool 设计成直接调用某个接口的方法。这样早期能跑,但一到生产就会暴露问题:

  • • 权限边界不清晰
  • • 超时、重试、幂等没有治理
  • • 审计记录不完整
  • • 无法独立扩容
  • • 业务逻辑散落在 Agent 层

因此,更好的做法是把工具抽象为 Skill

Skill 可以理解为“可被 Agent 调度的业务能力单元”,它有自己的:

  • • 输入输出契约
  • • 权限规则
  • • 重试熔断
  • • 幂等控制
  • • 审计日志
  • • SLA 指标

一句话总结四者分工:

  • LLM 负责理解与生成
  • RAG 负责提供事实证据
  • Agent 负责决策编排
  • Skill 负责落地执行

三、企业级大模型应用的正确架构,不是“一个 Controller 调 OpenAI”


很多 Demo 的结构是:

Controller -> Prompt -> LLM API -> return

这只能叫“接了大模型”,远远不能叫“系统架构”。

生产级架构至少要同时考虑:

  • • 请求接入与限流
  • • 对话状态管理
  • • 意图识别与路由
  • • 检索增强链路
  • • Agent 工具编排
  • • 业务 Skill 执行
  • • 审计与观测
  • • 缓存与成本控制
  • • 异步削峰与高可用回退

下面给出一套推荐的参考架构。

Client / Web / App

API Gateway

Chat Service

Intent Router

RAG Pipeline

Agent Orchestrator

Embedding Service

Vector DB

Rerank Service

Skill Registry

Order Skill

Refund Skill

IT Ticket Skill

Profile Skill

Redis Session / Cache

Kafka Async Bus

Observability

LLM Gateway

OpenAI / Qwen / Private Model

Prometheus / Grafana / ELK

这套架构有几个非常关键的设计点。

3.1 Chat Service 不直接承载所有复杂逻辑

Chat Service 的责任应该是“编排入口”,而不是“大一统业务中心”。

它主要负责:

  • • 接收请求
  • • 维护上下文
  • • 调用意图路由
  • • 决定走 RAG、Agent 或普通问答
  • • 汇总结果并返回

而真正的知识检索、业务执行、异步任务、模型调用,都应该被下沉到独立组件。

3.2 LLM Gateway 是非常值得单独抽象的一层

不要在业务代码里到处散落 OpenAiChatModel.builder()

LLM Gateway 至少要统一以下能力:

  • • 模型路由
  • • 超时控制
  • • 重试策略
  • • 限流熔断
  • • token 统计
  • • 请求日志脱敏
  • • 提示词模板版本管理

否则,当你从一个模型切换到多模型,或者从公有云切换到私有化推理集群时,改造成本会非常高。

3.3 Skill 要有“服务化思维”

Skill 不应只是 Agent 的注解方法,更应该具备完整工程属性:

  • • 明确的领域边界
  • • 可独立测试
  • • 可单独限流
  • • 可独立扩缩容
  • • 具备审计记录

如果一个 Skill 涉及修改订单、退款、审批这类强业务动作,最好走独立服务或独立执行器,而不是简单的同步内存调用。


四、核心原理深入:为什么很多 RAG 项目上线后效果并不好?


RAG 最容易被低估,也最容易被做坏。

很多团队上线后发现:

  • • 明明接了向量库,还是答非所问
  • • 召回结果不少,但有效证据很少
  • • 模型经常“拿到错误上下文后更加自信地胡说”

原因通常不在模型,而在检索链路设计。

4.1 文档切片决定召回上限

切片太大,会导致:

  • • 一个 chunk 包含多个主题
  • • 检索相似度被噪音稀释

切片太小,会导致:

  • • 单个 chunk 缺乏完整语义
  • • 模型拿到碎片信息后无法正确总结

比较稳妥的策略通常是:

  • • 逻辑切片优先于字符切片
  • • 先按标题、段落、表格、列表等结构切
  • • 再做定长递归切片
  • • 保留一定 overlap,减少上下文断裂

经验值可参考:

  • • 普通制度文档:500-800 tokens
  • • FAQ:200-400 tokens
  • • 技术文档:按小节切,再限制最大 token 数

4.2 召回不是越多越好,关键是“高质量证据”

如果一次检索返回 10 个片段,但其中只有 2 个真正相关,其余 8 个是噪音,那么模型很容易被污染。

因此,生产链路通常不是单阶段检索,而是:

    1. 粗召回:向量检索 TopK
    1. 过滤:按租户、部门、知识域、时间范围做元数据过滤
    1. 精排:Rerank 模型重新排序
    1. 裁剪:控制最终注入模型的证据数量和 token 总量

4.3 强制 RAG 比“让模型自己决定要不要检索”更稳定

很多团队喜欢让 Agent 自由决定是否调用知识库工具,这在开放式问答里没问题,但在制度、政策、规范类场景里,风险很高。

更稳妥的做法是:

  • • 对某些意图类型强制进入 RAG 模式
  • • 先检索,再总结
  • • 明确要求模型“只能基于检索结果回答”

这会明显降低幻觉率,也是很多企业知识问答系统的主流实践。

4.4 RAG 的上线指标,不是只有“像不像”

线上评估不能只看“回答读起来通不通顺”,更要看:

  • Recall@K:目标答案是否被召回
  • MRR / NDCG:相关文档排序质量
  • Citation Accuracy:引用来源是否正确
  • Answer Groundedness:答案是否可由证据支撑
  • Latency P95 / P99
  • Cache Hit Ratio
  • Cost per Request

只有把“效果”和“工程”一起量化,RAG 才能真正落地。


五、Java 落地方案:生产级模块拆分应该怎么做?


推荐按以下方式拆分模块,而不是把所有逻辑堆在一个 service 里:

smart-ai-assistant├── chat-api                 # HTTP/SSE 接口层├── ai-orchestrator          # Agent / RAG / Prompt / 路由编排├── knowledge-service        # 文档摄取、切片、embedding、检索、rerank├── skill-sdk                # Skill 契约、工具注册、审计模型├── skill-order              # 订单相关 Skill├── skill-refund             # 退款相关 Skill├── skill-employee           # 员工/HR 相关 Skill├── session-store            # Redis 会话、摘要压缩├── llm-gateway              # 多模型适配、限流、熔断、token 统计└── ops-observability        # 指标、日志、链路追踪、告警

这样拆分有三个价值:

  • • 代码职责明确,便于团队协作
  • • 单模块可以独立压测、扩容、灰度
  • • 后期从单体演进到微服务时更平滑

六、代码实战一:先搭一个可演进的基础工程


6.1 Maven 依赖建议

下面给出一份更接近生产的依赖组合。你可以使用 LangChain4jAgentScope Java,核心思想相同,本文以 Java 生态通用能力为主。

<dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-webflux</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-validation</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.kafka</groupId>        <artifactId>spring-kafka</artifactId>    </dependency>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j-open-ai</artifactId>        <version>0.35.0</version>    </dependency>    <dependency>        <groupId>dev.langchain4j</groupId>        <artifactId>langchain4j</artifactId>        <version>0.35.0</version>    </dependency>    <dependency>        <groupId>io.milvus</groupId>        <artifactId>milvus-sdk-java</artifactId>        <version>2.5.8</version>    </dependency>    <dependency>        <groupId>io.github.resilience4j</groupId>        <artifactId>resilience4j-spring-boot3</artifactId>    </dependency>    <dependency>        <groupId>io.micrometer</groupId>        <artifactId>micrometer-registry-prometheus</artifactId>    </dependency>    <dependency>        <groupId>com.github.ben-manes.caffeine</groupId>        <artifactId>caffeine</artifactId>    </dependency></dependencies>

6.2 配置文件不要只放 API Key

真正有价值的配置通常包含:

  • • 模型名称与超时
  • • embedding 维度
  • • RAG 检索阈值与 TopK
  • • Agent 最大迭代次数
  • • 限流参数
  • • 会话窗口大小
  • • 强制 RAG 的意图开关
  • • 降级与回退策略
server:  port: 8081spring:  application:    name: smart-ai-assistant  data:    redis:      host: redis      port: 6379  kafka:    bootstrap-servers: kafka:9092management:  endpoints:    web:      exposure:        include: health,info,metrics,prometheusai:  llm:    provider: openai    chat-model: gpt-4.1-mini    embedding-model: text-embedding-3-small    timeout-seconds: 20    max-retries: 2    temperature: 0.0  rag:    top-k: 8    final-top-n: 3    min-score: 0.72    chunk-size: 700    chunk-overlap: 120  agent:    max-iters: 5    force-rag-intents:      - POLICY_RAG      - FAQ_RAG  session:    history-size: 10    summary-threshold: 30  skill:    async-timeout-ms: 3000resilience4j:  ratelimiter:    instances:      llmGateway:        limit-for-period: 120        limit-refresh-period: 1s        timeout-duration: 200ms  circuitbreaker:    instances:      llmGateway:        failure-rate-threshold: 50        sliding-window-size: 20

在这里插入图片描述

七、代码实战二:先做“意图路由”,别一上来就把所有请求都丢给 Agent


很多系统一开始就让 LLM 决定一切,这样很贵,也不稳定。

更合理的策略是先做一层轻量意图路由,把请求分流到不同链路:

  • • 普通寒暄
  • • 强制 RAG
  • • 结构化查询
  • • 动作型执行
  • • 复杂开放问答
package com.example.aiqa.domain.service;import com.example.aiqa.domain.model.ChatCommand;import com.example.aiqa.domain.model.IntentType;import org.springframework.stereotype.Service;@Servicepublic class IntentRouter {    public IntentType route(ChatCommand command) {        String message = command.message().trim().toLowerCase();        if (containsAny(message, "你好", "hello", "hi", "在吗")) {            return IntentType.SMALL_TALK;        }        if (containsAny(message, "年假", "报销", "制度", "流程", "政策", "规范", "激励")) {            return IntentType.POLICY_RAG;        }        if (containsAny(message, "余额", "工号", "员工", "部门", "档案")) {            return IntentType.EMPLOYEE_QUERY;        }        if (containsAny(message, "创建工单", "提工单", "申请", "审批", "报修", "vpn")) {            return IntentType.ACTION_REQUEST;        }        if (message.length() <= 8) {            return IntentType.FAQ;        }        return IntentType.COMPLEX;    }    private boolean containsAny(String message, String... keywords) {        for (String keyword : keywords) {            if (message.contains(keyword)) {                return true;            }        }        return false;    }}

这个设计的价值很大:

  • • 简单请求不必经过完整 Agent 推理链
  • • 制度类问题可以强制走 RAG
  • • 动作型请求可以优先进入工具编排链
  • • 可以显著降低平均 token 消耗和响应时延

八、代码实战三:把 RAG 做成真正可用的检索链路


下面这段代码来自企业知识检索服务的典型实现思路。重点不在“有没有调 Milvus”,而在于它体现了几个生产特征:

  • • 启动时不因向量库不可达而整体崩掉
  • • 检索时支持按部门过滤
  • • 支持文档重建与导入
  • • 能把结果同时作为结构化对象和文本证据输出
@Servicepublic class MilvusKnowledgeService {    private final QaProperties properties;    private final EmbeddingService embeddingService;    private volatile MilvusClientV2 client;    private volatile String unavailableReason;    public List<KnowledgeDocument> search(String question, String department, int topK) {        if (!isAvailable()) {            return List.of();        }        initializeCollection();        List<Float> queryVector = embeddingService.embed(question);        SearchReq searchReq = SearchReq.builder()                .databaseName(properties.milvus().dbName())                .collectionName(properties.milvus().collectionName())                .data(List.of(new FloatVec(queryVector)))                .topK(topK)                .annsField(properties.milvus().vectorField())                .metricType(IndexParam.MetricType.COSINE)                .filter(buildFilter(department))                .outputFields(List.of("*"))                .build();        SearchResp resp = client().search(searchReq);        List<KnowledgeDocument> result = new ArrayList<>();        for (List<SearchResp.SearchResult> group : resp.getSearchResults()) {            for (SearchResp.SearchResult item : group) {                Map<String, Object> entity = item.getEntity();                result.add(new KnowledgeDocument(                        String.valueOf(entity.getOrDefault("documentId", "")),                        String.valueOf(entity.getOrDefault("title", "")),                        String.valueOf(entity.getOrDefault("content", "")),                        String.valueOf(entity.getOrDefault("source", "")),                        String.valueOf(entity.getOrDefault("department", "")),                        null                ));            }        }        return result;    }    public String searchAsText(String question, String department, int topK) {        List<KnowledgeDocument> docs = search(question, department, topK);        if (docs.isEmpty()) {            return "未检索到知识。";        }        return docs.stream()                .map(doc -> "【" + doc.title() + "】" + doc.content() + "(来源:" + doc.source() + ")")                .collect(Collectors.joining("\n"));    }}

8.1 为什么这里一定要有元数据过滤?

因为企业知识不是一个“公共大语料池”,而是强上下文、强权限、强领域隔离的数据集合。

举例:

  • • HR 制度不应该和财务制度混召回
  • • 华东区售后知识不应该给华北区客服用
  • • A 租户的数据不能被 B 租户检索到

因此,向量检索一定要配合 metadata filter。

8.2 文档摄取链路也不能只写个 for 循环

真正上线时,文档摄取建议设计成独立任务流:

    1. 文件上传
    1. 格式解析
    1. 清洗去噪
    1. 结构切片
    1. embedding 批量生成
    1. 向量写入
    1. 索引加载
    1. 版本切换

为了避免线上检索命中半成品数据,推荐使用“双集合切换”策略:

  • • 新知识先导入 knowledge_v2
  • • 建索引、预热完成后切换别名
  • • 老集合 knowledge_v1 延后下线

这比在线直接重建集合稳定得多。


九、代码实战四:Agent 不该是“自由发挥”,而是“受控编排”


这里给出一段非常值得借鉴的 Agent 编排代码。它体现了两个关键思想:

    1. 制度类问题强制先检索知识,再总结。
    1. 非制度类问题才进入 Agent 工具调用链。
@Servicepublic class AgentScopeQaService {    private final QaProperties properties;    private final IntentRouter intentRouter;    private final EnterpriseToolFunctions toolFunctions;    public AgentAnswer answer(ChatContext context) {        String intent = intentRouter.route(context.question());        if ("RAG".equals(intent)) {            return answerWithForcedRag(context);        }        if (properties.dashscopeApiKey() == null || properties.dashscopeApiKey().isBlank()) {            return fallbackAnswer(intent, context);        }        DashScopeChatModel model = DashScopeChatModel.builder()                .apiKey(properties.dashscopeApiKey())                .modelName(properties.chatModel())                .formatter(new DashScopeChatFormatter())                .build();        Toolkit toolkit = new Toolkit();        toolkit.registerTool(new AgentTools(toolFunctions, context.department(), context.userId()));        ReActAgent agent = ReActAgent.builder()                .name("enterprise-qa-agent")                .sysPrompt(buildPrompt(intent, context))                .model(model)                .toolkit(toolkit)                .memory(new InMemoryMemory())                .maxIters(properties.agent().maxIters())                .build();        Msg msg = Msg.builder().name("user").role(MsgRole.USER).textContent(context.question()).build();        Msg response = agent.call(msg).block();        String answer = response == null ? fallbackAnswer(intent, context).answer() : response.getTextContent();        return new AgentAnswer(answer, "AGENTSCOPE_REACT", List.of(), ToolAuditContext.snapshot());    }}

9.1 为什么“强制 RAG + 受控 Agent”比纯 ReAct 更适合企业

因为企业知识问答最大的风险不是“答不上来”,而是“答错了但看起来很像对的”。

把链路改成:

  • • 制度/规范/知识类问题:强制 RAG
  • • 查询/动作类问题:受控 Agent + Skill
  • • 超复杂问题:路由给人工或高级模型

会比“一股脑都交给 Agent 自主决定”稳定得多。

9.2 Agent 的系统提示词必须写出明确边界

真正生产里的 Prompt,不是“你是一个乐于助人的助手”这种空话,而应该明确:

  • • 当前用户是谁
  • • 当前部门是什么
  • • 允许使用哪些工具
  • • 哪些场景必须先检索
  • • 没有依据时必须明确拒答
  • • 工具失败时如何回退

例如:

private String buildPrompt(String intent, ChatContext context) {    String history = context.history() == null || context.history().isEmpty()            ? "无"            : String.join("\n", context.history());    return """            你是企业知识问答助手,负责基于企业知识库和结构化工具提供可信答案。            当前用户:%s            当前部门:%s            当前意图:%s            最近对话历史:            %s            规则:            1. 如果问题涉及余额、员工资料、工单等,调用对应工具。            2. 回答时尽量给出来源或说明数据来自哪个系统。            3. 如果没有把握,不要编造,明确说明缺少依据。            """.formatted(context.userId(), context.department(), intent, history);}

这类 Prompt 不是为了“文采”,而是为了压缩模型的自由度,提升稳定性。


十、代码实战五:Skill 的生产级写法是什么样?


Skill 的关键不是 @Tool 注解本身,而是它背后的治理。

先看一个典型的工具聚合层:

@Componentpublic class EnterpriseToolFunctions {    private final MilvusKnowledgeService milvusKnowledgeService;    public List<KnowledgeDocument> searchKnowledgeDocuments(String question, String department) {        List<KnowledgeDocument> documents = milvusKnowledgeService.search(question, department, 4);        ToolAuditContext.record("searchKnowledge", Map.of(                "question", question,                "department", department == null ? "" : department        ), formatKnowledgeDocuments(documents));        return documents;    }    public String queryLeaveBalance(String employeeId) {        String result = "员工 " + employeeId + " 当前 annual 年假余额为 6.5 天。";        ToolAuditContext.record("queryLeaveBalance", Map.of("employeeId", employeeId), result);        return result;    }    public String createItTicket(String employeeId, String title, String description) {        String result = "已为员工 " + employeeId + " 创建 IT 工单,标题:" + title + ",描述:" + description;        ToolAuditContext.record("createItTicket", Map.of(                "employeeId", employeeId,                "title", title,                "description", description        ), result);        return result;    }}

10.1 为什么工具调用审计是必选项?

因为上线后你一定会遇到这些问题:

  • • 模型为什么调用了这个工具
  • • 工具传参是不是错了
  • • 哪一步超时了
  • • 最终回答基于哪次调用结果
  • • 有没有越权调用敏感接口

因此,Tool Audit 不是锦上添花,而是生产基本盘。

可以像下面这样,通过线程上下文或 Reactor Context 记录每次工具调用:

public final class ToolAuditContext {    private static final ThreadLocal<List<ToolCallRecord>> TOOL_CALLS =            ThreadLocal.withInitial(ArrayList::new);    public static void clear() {        TOOL_CALLS.remove();    }    public static void record(String toolName, Map<String, Object> arguments, String result) {        ToolCallRecord record = new ToolCallRecord(toolName, arguments, result, Instant.now());        TOOL_CALLS.get().add(record);    }    public static List<ToolCallRecord> snapshot() {        return List.copyOf(TOOL_CALLS.get());    }}

10.2 真正的 Skill 至少还要补这四件事

    1. 权限校验

不是所有用户都能查任何数据,更不是所有用户都能发起退款或审批。

    1. 幂等控制

对于创建工单、提交退款、发起审批这类动作,必须有业务幂等键,否则 Agent 重试一次就可能重复执行。

    1. 超时与熔断

不要让一个慢下游把整个问答链路拖死。每个 Skill 都要有自己的超时预算和失败策略。

    1. 结果标准化

Skill 返回结果最好是稳定 JSON,而不是任意文本。因为后续还要被模型或编排器消费。


十一、代码实战六:会话管理、SSE 流式输出与上下文压缩


企业聊天系统不是一次性请求,必须管理上下文。

一个简化但很有代表性的会话存储实现如下:

@Componentpublic class ConversationSessionStore {    private final QaProperties properties;    private final ObjectMapper objectMapper;    public List<String> history(String sessionId) {        try {            Path path = sessionFile(sessionId);            if (!Files.exists(path)) {                return new ArrayList<>();            }            return objectMapper.readValue(Files.readString(path), new TypeReference<>() {});        } catch (Exception ex) {            return new ArrayList<>();        }    }    public void append(String sessionId, String role, String message) {        try {            Path path = sessionFile(sessionId);            Files.createDirectories(path.getParent());            List<String> history = history(sessionId);            history.add(role + ": " + message);            int maxSize = properties.session().historySize() * 2;            if (history.size() > maxSize) {                history = new ArrayList<>(history.subList(history.size() - maxSize, history.size()));            }            Files.writeString(path, objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(history));        } catch (Exception ex) {            throw new IllegalStateException("Failed to persist session history", ex);        }    }}

这段代码适合本地样例或单节点验证,但真正生产环境建议这样升级:

  • • 存储从本地文件切换到 Redis
  • • 长会话增加 摘要压缩
  • • 区分 recent windowlong-term summary
  • • 上下文内容脱敏后再进模型

推荐上下文策略:

    1. 最近 N 轮对话直接保留。
    1. 更早的历史做摘要。
    1. 用户资料、部门、租户等稳定信息单独作为 profile 注入。
    1. 检索证据与会话历史分层拼装,避免互相污染。

流式输出方面,Java 强烈建议使用 WebFlux + SSE

@RestController@RequestMapping("/api/chat")public class ChatController {    @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)    public Flux<String> streamChat(@RequestParam String sessionId,                                   @RequestParam String message) {        return llmStreamingService.stream(sessionId, message);    }}

这样做的价值很直接:

  • • 用户体感更快
  • • 长回答不必阻塞整个请求
  • • 前端可以边生成边渲染

十二、动作型请求不要都同步执行,异步 Skill 才是高并发下的正确姿势


对于“查一下余额”这种读操作,同步执行没问题。

但对下面这些动作:

  • • 提交退款
  • • 创建工单
  • • 发起审批
  • • 调用外部 ERP / CRM / OA

如果还放在同步链路里,会出现几个问题:

  • • 用户等待时间过长
  • • 上游连接占满
  • • 下游慢接口把 Chat Service 拖垮
  • • 峰值流量时请求洪峰直接打爆业务系统

因此,建议把动作型 Skill 设计为异步执行。

12.1 典型异步编排链路

用户请求  -> Chat Controller  -> Agent 识别为动作型请求  -> 生成标准化执行命令  -> 写入 Kafka  -> 立刻返回“任务已受理,处理中”  -> Skill Worker 异步消费执行  -> 执行结果写回状态表 / 推送消息 / WebSocket 通知

12.2 推荐的消息模型

public record SkillCommand(        String requestId,        String sessionId,        String userId,        String skillName,        Map<String, Object> arguments,        Instant createdAt) {}

12.3 为什么要标准化成 SkillCommand?

因为这样你就可以做到:

  • • 消息可重放
  • • 执行可追踪
  • • 失败可补偿
  • • 幂等可校验
  • • 审计可回放

这一点在金融、电商、审批类场景尤其重要。


十三、高并发与可扩展:架构师必须补上的工程化能力


文章讲到这里,核心能力已经通了,但如果没有工程化加固,依然扛不住生产流量。

下面这些是高并发场景里的关键设计。

13.1 多级缓存:把最贵的链路挡在外面

大模型系统里最贵的通常有三段:

  • • embedding 生成
  • • 向量检索
  • • LLM 推理

因此要建立多级缓存:

    1. L1:Caffeine 本地热点缓存
    1. L2:Redis 分布式缓存
    1. L3:向量库 / LLM 实际调用

常见可缓存对象包括:

  • • query -> embedding
  • • normalized question -> answer
  • • question -> retrieved passages
  • • intent routing result

13.2 语义缓存比字符串缓存更有价值

用户会用不同问法表达同一个意思:

  • • “年假还剩多少”
  • • “我还有几天年假”
  • • “帮我查一下年假余额”

如果只做字符串 key 缓存,命中率很低。

更好的做法是:

  • • 对问题做归一化
  • • 或直接做语义 embedding 相似匹配
  • • 相似度高于阈值时返回缓存结果

这类语义缓存通常能显著降低 RAG 和 LLM 请求量。

13.3 LLM 网关必须限流、熔断、超时隔离

LLM 是昂贵且不稳定的外部依赖。

至少要做:

  • • 每秒并发限流
  • • 单请求超时
  • • 错误重试上限
  • • 熔断快速失败
  • • 降级到小模型或本地规则

示意代码如下:

@Servicepublic class LlmGateway {    @RateLimiter(name = "llmGateway")    @CircuitBreaker(name = "llmGateway", fallbackMethod = "fallback")    public Mono<String> chat(ChatRequest request) {        return remoteModelClient.chat(request)                .timeout(Duration.ofSeconds(20));    }    public Mono<String> fallback(ChatRequest request, Throwable ex) {        return Mono.just("当前智能服务繁忙,我先为你返回简化结果,请稍后重试更复杂问题。");    }}

13.4 横向扩展的前提是“真正无状态”

如果会话历史存在本地内存、文件系统,或者 Agent 状态散在单节点里,那么你的 Chat Service 就无法平滑扩容。

无状态设计要求:

  • • 会话放 Redis
  • • 异步任务状态放 DB/Redis
  • • Prompt 配置放配置中心
  • • 文档索引放外部向量库
  • • 实例本身不保存关键业务状态

这样一来,服务就可以通过 HPA 按 CPU、QPS、队列堆积自动扩缩容。

13.5 Skill Registry 要支持“注册即治理”

别把 Skill 注册理解为“丢进一个 Map”。

更好的做法是给每个 Skill 绑定治理元数据:

  • • 名称
  • • 描述
  • • 参数 Schema
  • • 读/写属性
  • • 幂等要求
  • • 超时预算
  • • 权限域
  • • 是否允许 Agent 自动调用

这样后续可以做:

  • • 灰度开放某些 Skill
  • • 针对高风险 Skill 增加人工确认
  • • 对写操作 Skill 强制审计

十四、生产级案例:企业智能助手完整请求链路解析


下面以一个典型请求为例:

“我的 VPN 连不上,顺便帮我查一下年假余额,再给我提一个 IT 工单。”

这不是一个简单问答,而是一个复合任务。合理链路应该是:

14.1 第一步:意图拆解

系统识别出两个子任务:

  • queryLeaveBalance
  • createItTicket

如果模型能力较强,可以在 Agent 内拆解;如果追求稳定性,也可以先做规则 + LLM 混合解析。

14.2 第二步:执行顺序规划

这里其实不需要 RAG,因为它不是制度问答,而是结构化查询 + 动作执行。

因此,Agent 可以生成如下计划:

    1. 查询当前用户年假余额
    1. 基于 VPN 故障描述创建工单
    1. 汇总两步结果,用自然语言回答用户

14.3 第三步:工具调用与审计

每一步调用都要记录:

  • • toolName
  • • arguments
  • • result
  • • timestamp
  • • traceId

这样最终可以返回:

{  "sessionId": "s-001",  "answer": "你的当前年假余额为 6.5 天;已为你创建 IT 工单,标题为 VPN 连接异常。",  "agent": "AGENTSCOPE_REACT",  "toolCalls": [    {      "toolName": "queryLeaveBalance",      "arguments": { "employeeId": "E1024" },      "result": "员工 E1024 当前 annual 年假余额为 6.5 天。"    },    {      "toolName": "createItTicket",      "arguments": {        "title": "VPN 连接异常",        "description": "我的 VPN 连不上"      },      "result": "已为员工 E1024 创建 IT 工单,标题:VPN 连接异常,描述:我的 VPN 连不上"    }  ]}

这类返回对前端、客服后台、审计平台都很友好。

14.4 第四步:失败回退

如果工单系统超时,回答不应该是“创建成功”,而应该变成:

  • • 年假余额正常返回
  • • 工单创建失败明确说明
  • • 如有必要,自动转人工或提示稍后再试

这才是工程系统,而不是“只要模型说得通就行”。


十五、可观测性、治理与安全:这是很多文章完全没讲到的关键层


如果你准备把大模型能力放进企业核心流程,这一层必须补齐。

15.1 指标体系应该看什么?

至少监控以下指标:

  • • 总请求量、成功率、错误率
  • P50/P95/P99 响应时延
  • • LLM 调用次数、失败率、平均耗时
  • • embedding 调用次数与耗时
  • • 向量检索耗时、召回数量、空结果比例
  • • Skill 调用次数、超时数、失败数
  • • 缓存命中率
  • • 每请求平均 token 消耗
  • • 每日模型调用成本

15.2 日志要能回答“这次答案是怎么来的”

一条完整日志链路最好能串起:

  • traceId
  • • 用户请求
  • • 意图类型
  • • 检索证据
  • • prompt 版本
  • • 工具调用记录
  • • 模型返回
  • • 最终答案

只有做到这一点,线上问题才可定位、可复盘。

15.3 Prompt 也应该纳入配置治理

Prompt 不应硬编码在代码里散落管理。

建议做法:

  • • 以模板形式放配置中心
  • • 版本号管理
  • • 支持灰度发布
  • • 支持 A/B 实验

因为 Prompt 在大模型系统里,本质上也是“业务逻辑的一部分”。

15.4 安全边界必须前置

大模型应用比传统接口更容易出现“隐式越权”。

重点防护包括:

  • • Prompt Injection
  • • 数据越权检索
  • • 敏感字段泄露
  • • 高风险 Skill 误触发
  • • 训练/日志中的隐私泄露

因此建议:

  • • 检索前先做租户和权限过滤
  • • Skill 执行前做服务端二次鉴权
  • • 对敏感数据脱敏后再进模型
  • • 对写操作要求显式确认或审批

十六、部署与弹性伸缩:从单机试验到 K8s 生产集群


16.1 Dockerfile 建议

FROM eclipse-temurin:17-jreWORKDIR /appCOPY target/smart-ai-assistant.jar app.jarENTRYPOINT ["java","-Xms1g","-Xmx1g","-XX:+UseG1GC","-XX:MaxGCPauseMillis=200","-jar","app.jar"]

16.2 Kubernetes 部署关键点

  • • Chat Service 无状态化
  • • 使用 HPA 自动扩缩
  • • Redis、Kafka、Milvus 独立部署
  • • 通过 Secret 管理 API Key
  • • 通过 ConfigMap/Nacos 管理 Prompt 和模型路由

示例:

apiVersion: apps/v1kind: Deploymentmetadata:  name: smart-ai-assistantspec:  replicas: 3  selector:    matchLabels:      app: smart-ai-assistant  template:    metadata:      labels:        app: smart-ai-assistant    spec:      containers:        - name: app          image: registry.example.com/smart-ai-assistant:1.0.0          ports:            - containerPort: 8081          env:            - name: OPENAI_API_KEY              valueFrom:                secretKeyRef:                  name: llm-secret                  key: api-key          resources:            requests:              cpu: "500m"              memory: "1Gi"            limits:              cpu: "2"              memory: "3Gi"---apiVersion: autoscaling/v2kind: HorizontalPodAutoscalermetadata:  name: smart-ai-assistant-hpaspec:  scaleTargetRef:    apiVersion: apps/v1    kind: Deployment    name: smart-ai-assistant  minReplicas: 3  maxReplicas: 20  metrics:    - type: Resource      resource:        name: cpu        target:          type: Utilization          averageUtilization: 70

16.3 别忘了“冷启动成本”

很多团队压测时忽略了冷启动问题,结果上线后第一次请求很慢。

建议预热:

  • • 模型连接池
  • • 常用 Prompt 模板
  • • 热门问题缓存
  • • 向量集合加载
  • • 常用 Skill 依赖连接

十七、常见落坑清单:这些问题我建议你上线前逐条对照


17.1 只做向量检索,不做 rerank

容易召回一堆“看起来相关、实际不关键”的片段。

17.2 让 Agent 自由调用所有工具

高风险 Skill 很容易被误触发,尤其是写操作。

17.3 把会话历史原样无限拼接

最后一定会撞 token 上限,也会把噪音带进 prompt。

17.4 工具返回纯文本,没有标准 Schema

后续很难做结构化编排、审计、重放和自动测试。

17.5 没有回退方案

只要模型服务、向量库、外部系统任何一个抖动,整个链路就不可用。

17.6 只测功能,不测成本和延迟

能回答,不代表能上线;能上线,不代表上线后扛得住。


十八、一套可落地的演进路线图


如果你所在团队刚开始做大模型应用,不建议一上来就搞最复杂的多智能体体系。

更推荐下面这条演进路线:

阶段一:LLM API 接入

目标:

  • • 跑通对话
  • • 建立 Prompt 规范
  • • 打通流式输出

阶段二:引入 RAG

目标:

  • • 补足企业知识
  • • 建立文档摄取链路
  • • 建立引用与评估体系

阶段三:引入受控 Agent

目标:

  • • 让系统具备工具调用能力
  • • 打通查询型和动作型请求
  • • 建立 Tool Audit

阶段四:Skill 工程化

目标:

  • • 独立服务化
  • • 做权限、超时、幂等、审计
  • • 支持异步执行与补偿

阶段五:平台化

目标:

  • • 模型网关统一
  • • Prompt 平台化
  • • A/B 实验
  • • 多模型路由
  • • 统一观测治理

这条路线能让团队以最小风险逐步升级,而不是一开始就陷入复杂度泥潭。


十九、总结:Java 工程师做大模型应用,真正的壁垒不在“接模型”,而在“把系统做对”


回到文章开头那句话:

Java 工程师的大模型应用实战,绝不只是“调一个聊天接口”。

真正有工程含量的,是把以下四层能力组合起来:

  • LLM:理解与生成
  • RAG:基于证据回答
  • Agent:进行任务编排与工具决策
  • Skill:承接企业真实业务能力

当这四者被正确组织后,系统才能同时具备:

  • • 可信性:回答有依据,不靠“模型想象”
  • • 可执行性:不仅能答,还能查、能办、能流转
  • • 工程稳定性:限流、熔断、缓存、异步、回退一应俱全
  • • 扩展能力:模型、知识库、工具、业务流程都能演进

如果你是 Java 后端、架构师、技术负责人,我建议你用这样一句话重新定义“大模型项目”:

这不是一个模型接入项目,而是一个“新型企业应用架构项目”。

你要解决的,依然是那些老问题:

  • • 架构分层
  • • 并发处理
  • • 状态管理
  • • 系统治理
  • • 安全审计
  • • 可观测与成本控制

只是这次,LLM 成了新的核心执行单元。

这恰恰意味着,Java 工程师并不会在大模型时代失去价值,反而会因为擅长复杂系统工程,而拥有更大的舞台。


二十、附录:生产落地速查清单


架构设计

  • • 是否区分了 LLM、RAG、Agent、Skill 四层职责
  • • 是否存在统一的 LLM Gateway
  • • 是否实现了意图路由,而不是所有请求都交给 Agent

RAG 质量

  • • 是否有合理切片策略
  • • 是否支持 metadata filter
  • • 是否有 rerank
  • • 是否有引用返回与离线评估

Agent 治理

  • • 是否限制最大迭代次数
  • • 是否对高风险 Skill 做权限与确认
  • • 是否具备工具调用审计

工程稳定性

  • • 是否做了缓存、限流、熔断、回退
  • • 是否做了异步削峰
  • • 是否支持无状态水平扩展

运维治理

  • • 是否采集延迟、成本、token、命中率指标
  • • 是否可以追踪一次回答的完整证据链
  • • 是否支持 Prompt 配置化和灰度

如果这份清单里大部分都已经勾上,那么你的大模型系统就已经不再是一个 Demo,而是一套具备生产价值的企业智能应用。

普通人如何抓住AI大模型的风口?

领取方式在文末

为什么要学习大模型?

目前AI大模型的技术岗位与能力培养随着人工智能技术的迅速发展和应用 , 大模型作为其中的重要组成部分 , 正逐渐成为推动人工智能发展的重要引擎 。大模型以其强大的数据处理和模式识别能力, 广泛应用于自然语言处理 、计算机视觉 、 智能推荐等领域 ,为各行各业带来了革命性的改变和机遇 。

目前,开源人工智能大模型已应用于医疗、政务、法律、汽车、娱乐、金融、互联网、教育、制造业、企业服务等多个场景,其中,应用于金融、企业服务、制造业和法律领域的大模型在本次调研中占比超过 30%。
在这里插入图片描述

随着AI大模型技术的迅速发展,相关岗位的需求也日益增加。大模型产业链催生了一批高薪新职业:
在这里插入图片描述

人工智能大潮已来,不加入就可能被淘汰。如果你是技术人,尤其是互联网从业者,现在就开始学习AI大模型技术,真的是给你的人生一个重要建议!

最后

只要你真心想学习AI大模型技术,这份精心整理的学习资料我愿意无偿分享给你,但是想学技术去乱搞的人别来找我!

在当前这个人工智能高速发展的时代,AI大模型正在深刻改变各行各业。我国对高水平AI人才的需求也日益增长,真正懂技术、能落地的人才依旧紧缺。我也希望通过这份资料,能够帮助更多有志于AI领域的朋友入门并深入学习。

真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发

【附赠一节免费的直播讲座,技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等,欢迎大家~】
在这里插入图片描述

大模型全套学习资料展示

自我们与MoPaaS魔泊云合作以来,我们不断打磨课程体系与技术内容,在细节上精益求精,同时在技术层面也新增了许多前沿且实用的内容,力求为大家带来更系统、更实战、更落地的大模型学习体验。

图片

希望这份系统、实用的大模型学习路径,能够帮助你从零入门,进阶到实战,真正掌握AI时代的核心技能!

01 教学内容

在这里插入图片描述

  • 从零到精通完整闭环:【基础理论 →RAG开发 → Agent设计 → 模型微调与私有化部署调→热门技术】5大模块,内容比传统教材更贴近企业实战!

  • 大量真实项目案例: 带你亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事‌!

02适学人群

应届毕业生‌: 无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。

零基础转型‌: 非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界‌。

业务赋能突破瓶颈: 传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型‌。

image.png

vx扫描下方二维码即可
【附赠一节免费的直播讲座,技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等,欢迎大家~】
在这里插入图片描述

本教程比较珍贵,仅限大家自行学习,不要传播!更严禁商用!

03 入门到进阶学习路线图

大模型学习路线图,整体分为5个大的阶段:
图片

04 视频和书籍PDF合集

图片

从0到掌握主流大模型技术视频教程(涵盖模型训练、微调、RAG、LangChain、Agent开发等实战方向)

图片

新手必备的大模型学习PDF书单来了!全是硬核知识,帮你少走弯路(不吹牛,真有用)
图片

05 行业报告+白皮书合集

收集70+报告与白皮书,了解行业最新动态!
图片

06 90+份面试题/经验

AI大模型岗位面试经验总结(谁学技术不是为了赚$呢,找个好的岗位很重要)图片
在这里插入图片描述

07 deepseek部署包+技巧大全

在这里插入图片描述

由于篇幅有限

只展示部分资料

并且还在持续更新中…

真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发

【附赠一节免费的直播讲座,技术大佬带你学习大模型的相关知识、学习思路、就业前景以及怎么结合当前的工作发展方向等,欢迎大家~】
在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐