1. 为什么这道题能"劝退"大半 Java 工程师

面试进行到一半,面试官突然来一句:

“你简历上写了做过 AI 应用,那你把 LLM、RAG、Agent、MCP 这几个概念串起来讲清楚,它们之间是什么关系?”

很多人当场就卡住了。不是完全没听过,而是脑子里是四个孤立的词:LLM 是大模型,RAG 是检索增强,Agent 是智能体,MCP 是……好像是 Anthropic 搞的一个协议?

然后就开始背概念,越背越乱,最后面试官一句"那你实际项目里是怎么用的",直接露馅。

问题不在于你不懂,而在于你没有把它们放进同一条技术演进链路里。这四个词不是并列关系,而是层层递进、互相依赖的关系。一旦你能用一条主线把它们串起来,这道题就从"送命题"变成"加分题"。

本文就用 Java 工程师熟悉的视角,把这条链路讲透,并给出可以直接在面试里复述的表达框架。

2. 一句话主线:从"会说话"到"会干活"

先给结论,这四个概念的关系可以浓缩成一句话:

LLM 提供智能,RAG 补充知识,Agent 赋予行动能力,MCP 统一工具接入标准。

再展开一点:

概念解决的问题类比(Java 视角)
LLM语言理解与生成一个能力很强但没有记忆、没有手脚的"计算核心"
RAG知识时效性与私有知识给核心外挂一个"数据库查询层"
Agent多步骤任务与工具调用给核心加一个"调度器 + 执行引擎"
MCP工具接入的标准化类似 JDBC,统一"驱动"接口,屏蔽各家差异

这条主线背后的逻辑是:模型本身的能力边界是固定的,工程的价值在于围绕模型补齐它缺的东西。缺知识就上 RAG,缺行动就上 Agent,工具太多太乱就上 MCP。

3. LLM:一切的起点,也是天花板

3.1 LLM 到底给了我们什么

大语言模型本质上是一个概率性的文本生成器:给定上下文,预测下一个 token。但工程上我们更关心它带来的三个能力:

  • 语义理解:能把自然语言映射成结构化意图;
  • 知识压缩:训练语料里的通用知识被压缩进参数;
  • 指令遵循:能按格式要求输出 JSON、SQL、代码等。

对 Java 工程师来说,最直观的接入方式就是 HTTP 调用:

public class LlmClient {

    private final HttpClient httpClient = HttpClient.newHttpClient();
    private final String apiKey;
    private final String endpoint;

    public LlmClient(String apiKey, String endpoint) {
        this.apiKey = apiKey;
        this.endpoint = endpoint;
    }

    public String chat(String prompt) throws Exception {
        String body = """
                {
                  "model": "your-model-name",
                  "messages": [{"role": "user", "content": %s}]
                }
                """.formatted(toJsonString(prompt));

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(endpoint))
                .header("Content-Type", "application/json")
                .header("Authorization", "Bearer " + apiKey)
                .POST(HttpRequest.BodyPublishers.ofString(body))
                .build();

        HttpResponse<String> response =
                httpClient.send(request, HttpResponse.BodyHandlers.ofString());
        return response.body();
    }

    private String toJsonString(String raw) {
        return "\"" + raw.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
    }
}

3.2 LLM 的三个硬伤

面试时如果能主动说出 LLM 的局限,比只会夸它强得多:

  1. 知识有截止日期:训练数据之后的事情它不知道;
  2. 不知道你的私有数据:公司内部文档、业务库表它一概不知;
  3. 只会输出文本,不会执行动作:它没法帮你查订单、发消息、改数据库。

这三个硬伤,正好对应后面三个概念要解决的问题。这就是"串起来"的关键钩子——每讲一个概念,都先说它补的是 LLM 的哪个洞。

4. RAG:给 LLM 外挂一个"知识库"

4.1 RAG 的本质

RAG(Retrieval-Augmented Generation,检索增强生成)的核心思想非常朴素:

不要指望模型记住一切,而是在提问时把相关资料"喂"给它。

流程分两段:

  • 离线索引:文档切分 → 向量化(Embedding)→ 存入向量库;
  • 在线检索:用户问题向量化 → 相似度检索 Top-K → 拼进 Prompt → 交给 LLM 生成。

原始文档

文本切分

Embedding 向量化

向量数据库

用户提问

问题向量化

Top-K 相关片段

拼接 Prompt

LLM 生成答案

4.2 Java 侧的实现骨架

public class RagService {

    private final EmbeddingClient embeddingClient;
    private final VectorStore vectorStore;
    private final LlmClient llmClient;

    public RagService(EmbeddingClient embeddingClient,
                      VectorStore vectorStore,
                      LlmClient llmClient) {
        this.embeddingClient = embeddingClient;
        this.vectorStore = vectorStore;
        this.llmClient = llmClient;
    }

    /** 离线:文档入库 */
    public void index(List<String> chunks) {
        for (String chunk : chunks) {
            float[] vector = embeddingClient.embed(chunk);
            vectorStore.upsert(chunk, vector);
        }
    }

    /** 在线:检索 + 生成 */
    public String answer(String question) throws Exception {
        float[] queryVector = embeddingClient.embed(question);
        List<String> contexts = vectorStore.search(queryVector, 5);

        String prompt = """
                请仅根据以下资料回答问题,资料中没有的信息请明确说明"不知道"。

                【资料】
                %s

                【问题】
                %s
                """.formatted(String.join("\n---\n", contexts), question);

        return llmClient.chat(prompt);
    }
}

4.3 面试常被追问的点

  • 切分策略:按固定长度切还是按语义切?重叠多少?这直接影响召回质量。
  • 为什么用向量检索而不是关键词:向量能捕捉语义相似,关键词只能字面匹配,实践中常常混合检索。
  • 幻觉怎么控制:Prompt 里强约束"只依据资料回答",并给出引用来源。
  • 效果怎么评估:召回率、答案忠实度,需要构建评测集,不能靠感觉。

能答出这些,说明你不是"调了个 API",而是真的踩过坑。

5. Agent:让 LLM 从"说"到"做"

5.1 Agent 的核心循环

RAG 解决了"知识"问题,但 LLM 依然只会输出文本。Agent 的本质是把 LLM 变成一个决策器,让它自己决定"下一步做什么"。

经典循环是 ReAct(Reasoning + Acting):

是

否

用户目标

LLM 推理:下一步做什么

需要调用工具?

执行工具,拿到结果

输出最终答案

关键点在于:LLM 的输出不再是给人看的文本,而是给程序解析的"动作指令",比如"调用 queryOrder,参数是 orderId=123"。程序执行完把结果再喂回去,循环直到任务完成。

5.2 一个极简的 Agent 实现

public class SimpleAgent {

    private final LlmClient llmClient;
    private final Map<String, Tool> tools;

    public SimpleAgent(LlmClient llmClient, Map<String, Tool> tools) {
        this.llmClient = llmClient;
        this.tools = tools;
    }

    public String run(String goal, int maxSteps) throws Exception {
        String scratchpad = "";
        for (int step = 0; step < maxSteps; step++) {
            String prompt = buildPrompt(goal, scratchpad);
            String decision = llmClient.chat(prompt);

            if (decision.startsWith("FINAL:")) {
                return decision.substring("FINAL:".length()).trim();
            }

            // 期望格式:ACTION: toolName | args
            String action = decision.substring("ACTION:".length()).trim();
            String[] parts = action.split("\\|", 2);
            String toolName = parts[0].trim();
            String args = parts.length > 1 ? parts[1].trim() : "";

            Tool tool = tools.get(toolName);
            String observation = tool == null
                    ? "工具不存在: " + toolName
                    : tool.execute(args);

            scratchpad += "\n思考: " + decision + "\n观察: " + observation;
        }
        return "达到最大步数,任务未完成";
    }

    private String buildPrompt(String goal, String scratchpad) {
        StringBuilder sb = new StringBuilder();
        sb.append("你可以使用以下工具:\n");
        tools.forEach((name, tool) ->
                sb.append("- ").append(name).append(": ").append(tool.description()).append("\n"));
        sb.append("\n目标:").append(goal);
        sb.append("\n历史:").append(scratchpad);
        sb.append("\n请输出 ACTION: 工具名 | 参数,或 FINAL: 最终答案");
        return sb.toString();
    }
}

5.3 Agent 的工程难点

面试官很爱问"Agent 落地有什么坑",可以这样答:

  • 不可控性:LLM 可能选错工具、传错参数,需要参数校验和兜底;
  • 循环失控:必须设最大步数、超时、成本上限;
  • 状态管理:多轮对话的上下文、中间结果怎么存,本质是个状态机问题;
  • 可观测性:每一步的思考、工具调用、返回都要能追踪,否则线上出问题无从排查。

6. MCP:工具接入的"JDBC 时刻"

6.1 为什么需要 MCP

Agent 要调用工具,问题来了:每个工具都要单独写适配代码。查数据库一套、调内部 API 一套、读文件一套,换个模型或换个 Agent 框架,全部重写。

MCP(Model Context Protocol)就是来解决这个问题的:它定义了一套标准协议,让"工具提供方"和"模型使用方"解耦。

用 Java 工程师最熟悉的类比:

MCP 之于 AI 工具,就像 JDBC 之于数据库。

JDBC 之前,每种数据库一套 API;JDBC 之后,面向 Connection、Statement 编程,换数据库只换驱动。MCP 干的是同一件事:工具按协议暴露能力,Agent 按协议调用,双方都不关心对方是谁。

6.2 MCP 的核心抽象

MCP 里主要三类能力:

  • Tools(工具):可被模型调用的函数,有明确的输入 schema;
  • Resources(资源):可被读取的数据,类似文件或文档;
  • Prompts(提示模板):预置的提示词模板,供复用。

传输层通常有两种:本地进程用 stdio,远程用 HTTP/SSE。对 Java 侧来说,就是实现一个 MCP Server,把内部能力按协议暴露出去。

/** 一个简化的 MCP 工具定义示意 */
public record McpTool(
        String name,
        String description,
        Map<String, String> inputSchema,
        Function<Map<String, Object>, String> handler
) {
    public String invoke(Map<String, Object> arguments) {
        return handler.apply(arguments);
    }
}

public class McpToolRegistry {

    private final Map<String, McpTool> tools = new ConcurrentHashMap<>();

    public void register(McpTool tool) {
        tools.put(tool.name(), tool);
    }

    public String call(String name, Map<String, Object> args) {
        McpTool tool = tools.get(name);
        if (tool == null) {
            throw new IllegalArgumentException("未注册的工具: " + name);
        }
        return tool.invoke(args);
    }

    public List<McpTool> list() {
        return List.copyOf(tools.values());
    }
}

6.3 MCP 带来的变化

  • 一次实现,多处复用:同一个 MCP Server 可以被不同 Agent、不同模型使用;
  • 权限边界清晰:工具在服务端统一管控,谁能调什么一目了然;
  • 生态可组合:社区工具即插即用,不用为每个工具写胶水代码。

7. 把四个概念串成一条完整链路

现在回到面试现场。你可以这样组织回答:

第一层,LLM 是底座。它提供语义理解和生成能力,但知识有截止日期、不懂私有数据、只会输出文本。

第二层,RAG 补知识。把私有文档向量化存起来,提问时检索相关片段拼进 Prompt,解决"不知道"的问题。

第三层,Agent 补行动。让 LLM 输出结构化的动作指令,程序执行工具再把结果喂回去,循环直到任务完成,解决"不会做"的问题。

第四层,MCP 补标准。Agent 要接的工具越来越多,每个都单独适配成本太高,MCP 用统一协议把工具接入标准化,就像 JDBC 统一了数据库访问。

所以它们不是四个并列的技术,而是一条演进链:模型能力固定 → 补知识 → 补行动 → 补工具生态。我做的项目里,就是 LLM 打底、RAG 保证答案准确、Agent 编排多步任务、MCP 统一接入内部系统。

配一张图更直观:

知识不足

只会说不会做

工具接入碎片化

LLM 大模型
语义理解与生成

RAG 检索增强
补充私有与实时知识

Agent 智能体
多步推理与工具调用

MCP 协议
工具接入标准化

可落地的 AI 应用

8. 面试加分项:主动暴露工程细节

光讲清概念只能算及格。真正拉开差距的是你有没有工程视角。以下几个点,主动抛出来效果很好:

关于 RAG

  • 切分粒度怎么定?我一般按语义段落切,重叠 10%~20%,再用小规模评测集验证召回。
  • 纯向量检索不够,关键词和向量混合召回效果更稳。
  • 一定要做引用溯源,让用户能点开原文核对。

关于 Agent

  • 工具描述写得好不好,直接决定模型选得对不对,description 要写清"什么时候用"。
  • 必须有最大步数和超时,否则一次死循环能把成本打穿。
  • 每一步的 trace 都要落库,线上排查全靠它。

关于 MCP

  • 工具暴露要遵循最小权限原则,别把整个数据库的写权限都开出去。
  • 输入 schema 要严格,参数校验放在服务端,不能信任模型输出。

关于整体架构

  • 成本控制:简单问题走小模型,复杂任务才上大模型。
  • 降级策略:检索失败、模型超时都要有兜底回复。
  • 可观测性:链路追踪、Token 消耗、延迟指标一个都不能少。

9. 常见追问与参考回答

Q:RAG 和微调怎么选?
A:知识频繁变化、需要溯源,选 RAG;风格、格式、领域术语需要固化,选微调。两者不冲突,实践中常常 RAG 打底、微调优化表达。

Q:Agent 会不会取代 RAG?
A:不会。Agent 里经常把 RAG 当成一个工具来调用。它们是包含关系,不是替代关系。

Q:MCP 是不是必须用?
A:不是。工具少的时候直接写适配代码更简单。工具多、跨团队、跨模型复用时,MCP 的价值才体现出来。

Q:这些在 Java 生态里怎么落地?
A:LLM 走 HTTP 客户端调用,向量库可以用 Milvus、Qdrant 的 Java 客户端,Agent 编排可以自己写状态机也可以接框架,MCP Server 用 Java 实现后通过 stdio 或 HTTP 暴露即可。核心还是那套东西:接口抽象、依赖注入、异常兜底。

10. 总结

把这道题答好,关键不在于背熟四个定义,而在于建立一条问题驱动的技术演进主线:

  • LLM 有能力,但缺知识、缺行动、缺工具生态;
  • RAG 补知识,Agent 补行动,MCP 补标准;
  • 四者层层递进,共同构成一个可落地的 AI 应用架构。

下次面试官再问,别急着背概念,先说一句:“这四个不是并列的,是一条演进链,我从 LLM 的局限讲起。”——这一句,就已经赢了一半。

Logo

更多推荐