AI 应用开发 — 小白讲解 + 面试题精解

AI 应用开发 — 小白讲解 + 面试题精解

定位:假设你只会调 OpenAI 的 chat 接口,所有概念从零讲起,配代码示例 + 面试题。 覆盖:LangChain4j → RAG 全流程 → Prompt 工程 → 向量数据库 → AI Agent → AI 编程工具 六大模块。 用法:先读讲解理解概念 → 跑代码验证 → 再看面试题自测。 特色:每个知识点都结合简历真实项目案例,面试时能直接用。


目录


第一章:LangChain4j 与 RAG 全流程(简历项目二核心)

简历项目二直接写了“基于 LangChain4j 研发 RAG 企业知识库系统”。 面试官会重点考察:你真的懂 RAG 还是只会调 API


1.1 为什么需要 RAG(而不是直接问 LLM)

小白讲解

直接让 LLM 回答企业知识库问题的三大问题:

1. 知识时效性:LLM 训练数据有截止时间,不知道最新知识
2. 幻觉(Hallucination):LLM 会一本正经地编造不存在的答案
3. 数据隐私:企业文档不能上传给公网 LLM 训练
4. 成本:每次把全部文档塞进 prompt,token 成本爆炸

RAG(Retrieval-Augmented Generation,检索增强生成)的思路:
  不让 LLM 凭空回答,而是先"检索"相关文档,再让 LLM"基于文档"生成答案
  → 检索(Retrieval) + 增强(Augmented) + 生成(Generation)

面试题

Q1:什么是 RAG?为什么不用 LLM 直接回答?(高频 ⭐⭐⭐⭐⭐)

答:

  • RAG = 检索增强生成:先从知识库检索相关文档片段,再把片段拼进 Prompt 让 LLM 基于文档回答
  • 不用 LLM 直接回答的原因:①知识时效性 ②幻觉 ③数据隐私 ④成本

1.2 RAG 全流程(面试必问)

小白讲解

RAG 完整流程(7 步,面试要能背出来):

  离线阶段(建库):
  ① 文档加载(Document Loader):读取 PDF/Word/Excel
  ② 文档分块(Splitter):把长文档切成 500-800 字的小块
  ③ 向量化(Embedding):每块文本转成向量
  ④ 存储(Vector Store):向量存入 pgvector

  在线阶段(查询):
  ⑤ 检索(Retrieve):用户问题向量化 → 相似度检索 Top-K 块
  ⑥ 增强(Augment):把检索到的块 + 用户问题拼进 Prompt
  ⑦ 生成(Generate):LLM 基于拼接的 Prompt 生成答案

口诀:加载 → 分块 → 向量化 → 存储 → 检索 → 增强 → 生成

结合简历案例

简历原文:
  "开发 PDF/Word/Excel 多格式文档上传接口,基于 Apache Tika 提取文本;
   设计文档分块策略(段落语义分割+滑动窗口500-800字);
   集成 LangChain4j 调用 Qwen Embedding API 实现向量化;
   使用 pgvector 设计余弦相似度检索 SQL,Top-5 召回准确率 85%+;
   开发 /chat 对话接口(检索→Prompt拼接→LLM调用→SSE流式返回)"

面试话术(把 RAG 流程和简历对应起来):
  "我负责 RAG 系统的后端核心链路:
   1. 文档加载:用户上传 PDF/Word/Excel,用 Apache Tika 提取文本
   2. 文档分块:按段落语义分割 + 滑动窗口 500-800 字
      (太短上下文不完整,太长向量稀释)
   3. 向量化:调用 Qwen Embedding API 把分块转成 1536 维向量
   4. 存储:向量存入 PostgreSQL + pgvector
   5. 检索:用户问题向量化,用余弦相似度检索 Top-5
   6. 增强:把 Top-5 分块拼进 Prompt
   7. 生成:调用 DeepSeek 生成答案,SSE 流式返回
   
   效果:Top-5 召回准确率 85%+,检索从人工 5 分钟降到 10 秒。"

代码示例

// LangChain4j 实现 RAG 的简化版
@Service
public class RAGService {
    
    @Autowired
    private EmbeddingModel embeddingModel;  // Qwen Embedding
    
    @Autowired
    private ChatLanguageModel chatModel;    // DeepSeek
    
    @Autowired
    private VectorStore vectorStore;        // pgvector
    
    // 建库:文档入库
    public void ingest(Document document) {
        // 1. 文档分块
        List<TextSegment> segments = document.split(
            DocumentSplitters.recursive(800, 100)  // 800字,100字滑动窗口
        );
        
        // 2. 向量化 + 存储
        for (TextSegment segment : segments) {
            Embedding embedding = embeddingModel.embed(segment.text());
            vectorStore.add(embedding, segment);
        }
    }
    
    // 查询:RAG 问答
    public String ask(String question) {
        // 1. 检索:问题向量化 → 相似度 Top-5
        Embedding questionEmbedding = embeddingModel.embed(question);
        List<TextSegment> relevant = vectorStore.findRelevant(questionEmbedding, 5);
        
        // 2. 增强:拼接 Prompt
        String context = relevant.stream()
            .map(TextSegment::text)
            .collect(Collectors.joining("\n\n"));
        
        String prompt = "请基于以下内容回答问题,如果内容中没有答案,回答'不知道'。\n\n"
            + "【知识库内容】\n" + context + "\n\n"
            + "【用户问题】\n" + question;
        
        // 3. 生成:调用 LLM
        return chatModel.generate(prompt);
    }
}

面试题

Q1:RAG 的完整流程是什么?(超高频 ⭐⭐⭐⭐⭐)

简历直接写了,必须会答!

答:加载 → 分块 → 向量化 → 存储 → 检索 → 增强 → 生成:

  1. 文档加载(Tika 提取 PDF/Word/Excel)
  2. 文档分块(500-800 字 + 滑动窗口)
  3. 向量化(Embedding API 转 1536 维向量)
  4. 存储(pgvector)
  5. 检索(余弦相似度 Top-K)
  6. 增强(检索结果拼进 Prompt)
  7. 生成(LLM 基于 Prompt 生成答案)

Q2:文档分块为什么是 500-800 字 + 滑动窗口?(高频 ⭐⭐⭐⭐)

简历直接写了,必须会答!

答:

  • 太短(如 100 字):上下文不完整,检索到的片段无法理解
  • 太长(如 3000 字):向量被稀释,检索精度下降
  • 滑动窗口(overlap):保证分块边界的内容不丢失上下文(前后块有重叠)
  • 500-800 字是精度和上下文完整性的平衡点

1.3 LangChain4j 核心概念

小白讲解

LangChain4j 是什么?
  Java 版的 LangChain,用于构建 LLM 应用
  (Python 有 LangChain,Java 有 LangChain4j)

核心组件:

  AiServices:声明式接口,把接口方法映射为 LLM 调用
    → 像写 DAO 一样写 AI 调用

  EmbeddingModel:文本 → 向量
  ChatLanguageModel:文本 → 文本(对话)
  VectorStore:向量存储
  DocumentLoader:文档加载
  DocumentSplitter:文档分块
  ChatMemory:对话历史管理

  @Tool 注解:给 AI 注册工具,实现 Function Calling

代码示例

// AiServices 声明式接口(简历"对话接口"的底层)
public interface Assistant {
    String chat(String userMessage);
}

Assistant assistant = AiServices.builder(Assistant.class)
    .chatLanguageModel(chatModel)
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))  // 记忆最近10轮
    .build();

String answer = assistant.chat("帮我查一下项目二的进度");

面试题

Q1:LangChain4j 是什么?核心组件有哪些?(中频 ⭐⭐⭐)

答:Java 版 LLM 应用框架。核心组件:AiServices(声明式接口)、EmbeddingModel(向量化)、ChatLanguageModel(对话)、VectorStore(向量存储)、DocumentLoader/Splitter(文档处理)、ChatMemory(对话记忆)、@Tool(Function Calling)。

Q2:AiServices 接口是什么?(中频 ⭐⭐⭐)

答:声明式接口,像 MyBatis 的 Mapper 一样,只定义方法签名,框架自动实现。方法入参是用户输入,返回值是 LLM 输出。简历的 /chat 对话接口就是基于 AiServices 封装的。


1.4 SSE 流式返回(简历直接写了)

小白讲解

SSE(Server-Sent Events,服务端推送事件):
  服务端持续向客户端推送数据,类似"打字机效果"

为什么 RAG 回答要用 SSE 流式返回?
  LLM 生成一个长答案要几秒到几十秒
  如果等全部生成完再返回,用户要干等
  SSE 流式:生成一个字推一个字,用户看到"打字机"效果,体验好

SSE vs WebSocket:
  SSE:单向(服务端→客户端),基于 HTTP,简单
  WebSocket:双向,协议升级,复杂
  → LLM 流式输出只需要服务端推,SSE 够用且简单

面试题

Q1:SSE 流式返回是什么?为什么 RAG 要用?(中频 ⭐⭐⭐)

答:

  • SSE 是服务端到客户端的单向推送,基于 HTTP,实现简单
  • RAG 生成答案耗时几秒到几十秒,SSE 边生成边推送,用户体验好(打字机效果)
  • SSE 单向推送够用,不需要 WebSocket 的双向通信

第二章:Prompt 工程(简历直接写了)

简历项目二直接写了“设计 System Prompt 约束回答范围并实现’不知道’保护机制”。


2.1 System Prompt 约束回答范围

小白讲解

Prompt 的两个部分:

  System Prompt(系统提示):
    设定 AI 的角色和行为边界
    → "你是企业知识库助手,只回答知识库内的问题"

  User Prompt(用户提示):
    用户的具体问题

简历的 System Prompt 设计:
  "你是 XX 公司的智能知识库助手。
   请严格基于提供的【知识库内容】回答问题。
   如果知识库中没有相关内容,请直接回答'我不知道',
   不要编造答案。"

结合简历案例

简历原文:
  "设计 System Prompt 约束回答范围并实现'不知道'保护机制"

面试话术:
  "LLM 有个大问题是幻觉——会一本正经地编造答案。
   
   我在 System Prompt 里做了两层约束:
   1. 约束回答范围:'只基于提供的知识库内容回答'
   2. '不知道'保护:'知识库没有相关内容就回答不知道,不要编造'
   
   另外在代码层面加了一道保险:
   检索结果的相似度低于阈值(如 0.75)时,
   直接返回'未找到相关信息',不把低质量检索结果给 LLM,
   避免 LLM 强行编造。"

面试题

Q1:System Prompt 为什么要约束回答范围?(高频 ⭐⭐⭐⭐)

答:防止 LLM 幻觉。如果不约束,LLM 会用训练数据里的知识回答问题,可能和企业的真实情况不符。约束后 LLM 只基于检索到的知识库内容回答,答案可控。

Q2:“不知道”保护机制怎么实现?(高频 ⭐⭐⭐⭐)

简历直接写了,必须会答!

答:两层:

  1. Prompt 层:System Prompt 里写“没有相关内容就回答’不知道’,不要编造”
  2. 代码层:检索结果相似度低于阈值(如 0.75)时,直接返回“未找到相关信息”,不把低质量结果给 LLM

第三章:向量数据库与 Embedding

详见《数据库与缓存-讲解与面试题.md》第四章 pgvector,这里补充 AI 视角的要点。

面试题

Q1:Embedding 是什么?(高频 ⭐⭐⭐⭐)

答:把文本转成高维向量(如 1536 维),语义相近的文本向量距离近。模型通过大量语料训练,让“语义相似”映射为“向量相近”。

Q2:余弦相似度和欧几里得距离的区别?(中频 ⭐⭐⭐)

答:

  • 余弦相似度:只看向量方向(夹角),不看长度,适合文本语义相似度
  • 欧几里得距离:看绝对距离,适合数值特征
  • 文本 Embedding 用余弦相似度(简历 pgvector 检索用的就是 <=> 余弦距离)

Q3:为什么选 pgvector 不用 Milvus?(中频 ⭐⭐⭐)

答:数据量万级时 pgvector 性能足够,且 PostgreSQL 已有基础设施,支持向量+结构化数据混合查询,不需要额外维护专门的向量数据库。Milvus 适合百万/亿级向量场景。


第四章:AI Agent 编排(简历项目二核心升级)

简历已将 Function Calling 升级为 AI Agent 工具编排,面试官会问“Agent 和 Function Calling 有什么区别”。


4.1 Function Calling vs AI Agent

小白讲解

Function Calling(单轮工具调用):
  用户问"查我的 Jira 任务"
  → AI 判断需要调用 Jira 工具
  → 调用 @Tool jiraQuery(userId)
  → 拿到结果 → 生成回答
  → 结束

AI Agent(多步任务规划):
  用户问"查这周的 Jira 任务,顺便看看项目二进度"
  → Agent 规划:
     子任务1:查 Jira 任务
     子任务2:查项目二进度
  → 执行子任务1 → 观察结果
  → 执行子任务2 → 观察结果(失败则重试/降级)
  → 汇总两个子任务 → 生成综合回答

本质区别:
  Function Calling:AI 判断"要不要调工具",调一次
  AI Agent:AI 自主"规划任务 + 选工具 + 多轮执行 + 汇总"

结合简历案例

简历原文:
  "基于 LangChain4j @Tool 注解与 Function Calling 实现 AI Agent 工具编排
   (Jira 分工查询、版本查询、指令进度追踪),
   Agent 自主规划任务并决策调用内部 API,支持多轮对话动态工具选择,
   系统上线后日均 50+ 次智能问答,信息获取效率提升 30 倍"

面试话术:
  "项目二的智能助手不只是问答,还要能查内部系统的数据。
   我用 @Tool 注解注册了三个内部工具:
   - Jira 分工查询
   - 版本查询
   - 指令进度追踪
   
   用户问'这周有哪些任务?项目二进展如何?'时,
   Agent 会自主规划:
   1. 先调 Jira 工具查本周任务
   2. 再调进度追踪工具查项目二状态
   3. 汇总两个结果生成综合回答
   
   这就是 Agent 和普通 Function Calling 的区别:
   Function Calling 是单次工具调用,Agent 是多步任务规划。
   上线后日均 50+ 次智能问答,信息获取效率提升 30 倍。"

代码示例

// 用 @Tool 注解注册工具,实现 Agent 工具编排
public class InternalTools {
    
    @Tool("查询某个用户的 Jira 任务分工")
    public List<String> queryJiraTasks(String userId) {
        return jiraService.queryTasks(userId);
    }
    
    @Tool("查询指定项目的版本信息")
    public String queryVersion(String projectId) {
        return versionService.query(projectId);
    }
    
    @Tool("查询指令处理的进度")
    public String queryInstructionProgress(String instructionNo) {
        return progressService.query(instructionNo);
    }
}

// Agent 对话接口(自动编排工具)
Assistant assistant = AiServices.builder(Assistant.class)
    .chatLanguageModel(chatModel)
    .tools(new InternalTools())  // 注册工具
    .chatMemory(MessageWindowChatMemory.withMaxMessages(20))  // 会话状态
    .build();

// 用户问"查我的Jira任务,顺便看项目二进度"
// Agent 自主规划 → 依次调用 queryJiraTasks + queryInstructionProgress → 汇总
String answer = assistant.chat("查我的Jira任务,顺便看下项目二进度");

面试题

Q1:AI Agent 和 Function Calling 的区别?(超高频 ⭐⭐⭐⭐⭐)

简历直接写了,必须会答!

答:

  • Function Calling:单次工具调用。AI 判断“要不要调工具”,调一次拿结果继续生成
  • AI Agent:多步任务规划。AI 自主拆解任务、动态选工具、多轮执行、汇总结果
  • Agent 底层可以用 Function Calling 实现,但多了“规划-执行-观察”的循环
  • 简历项目二把 Function Calling 升级为 Agent 工具编排,支持多步任务和多轮动态工具选择

Q2:Agent 多轮对话怎么保持状态?(高频 ⭐⭐⭐⭐)

答:Redis 存对话历史(TTL 30 分钟),每轮把上一步工具调用结果作为上下文传给 LLM,Agent 根据历史上下文决定下一步动作。

Q3:工具调用失败怎么办?(中频 ⭐⭐⭐)

答:重试 N 次 → 降级返回默认提示 → 连续失败转人工。简历写了“工具调用失败重试/降级策略”,面试要能说清。

Q4:什么是 ReAct 模式?(中频 ⭐⭐⭐)

答:ReAct = Reason + Act,思考→行动→观察→再思考的循环。Agent 先推理(该做什么),再行动(调工具),观察结果,再推理(下一步),直到完成任务。


第五章:AI 编程工具与 Skills 规则工程(简历新增亮点)

简历新增了 Cursor、Claude Code、DeepSeek 的使用经验,以及“编写 Skills 规则文件约束 AI 输出质量”。 面试官会问“你怎么保证 AI 生成的代码质量”“Skills 规则写了什么”。


5.1 Skills 规则文件是什么

小白讲解

Skills 规则文件 = 给 AI 编程助手设定的"编码规范 + 安全红线"

类似团队的 Code Review check list,但写成 AI 能理解的规则文件,
让 AI 生成代码时自动遵守。

常见规则维度:
  1. 代码规范:命名风格、注释要求、包结构
  2. 安全红线:资金操作必须加锁、金额必须校验、SQL 必须参数化
  3. 异常处理:不允许吞异常、必须记日志、统一错误码
  4. 事务边界:哪些方法必须 @Transactional
  5. 禁止事项:不允许 Executors 创建线程池、不允许 new Date()

结合简历案例

简历原文:
  "编写 AI Skills 规则文件约束代码风格与安全边界
   (如资金类操作强制加锁、金额校验不可省略),人工评审后合入"

面试话术:
  "用 Cursor + DeepSeek 生成状态机、交收匹配这类核心代码时,
   如果无脑让 AI 生成,可能忽略资金操作的安全要求。
   
   我写了 Skills 规则文件,把团队的安全红线固化进去:
   1. 资金类操作(扣款/入账/清算)必须加 Redisson 分布式锁
   2. 金额字段用 BigDecimal,禁止 double/float
   3. SQL 必须参数化(#{}),禁止字符串拼接(${})
   4. 状态机变更走统一入口,禁止直接 update 状态字段
   
   AI 生成代码时自动遵守这些规则,
   再经过人工评审后才合入,保证代码质量。"

Skills 规则文件示例

# .cursor/rules/custody-system.md

## 安全红线(必须遵守)
- 资金类操作(扣款/入账/清算)必须加分布式锁(Redisson)
- 金额校验不可省略:金额必须 > 0,精度不超过 2 位小数
- SQL 必须使用参数化查询(MyBatis #{}),禁止字符串拼接 ${}
- 状态机变更必须走统一入口,禁止直接 update 状态字段

## 编码规范
- Service 方法名使用动词开头:create/settle/cancel/query
- DTO 命名:XxxCreateDTO / XxxQueryDTO / XxxVO
- 所有金额字段使用 BigDecimal,禁止使用 double/float

## 异常处理
- 不允许 catch Exception 后不处理(吞异常)
- 业务异常抛 BusinessException,系统异常抛 RuntimeException
- 所有异常必须记录日志(包含指令ID、操作人、时间戳)

面试题

Q1:为什么要写 Skills 规则文件?(高频 ⭐⭐⭐⭐)

简历直接写了,必须会答!

答:AI 生成的代码如果不加约束,可能:①不符合编码规范 ②忽略安全红线(资金操作没加锁、SQL 拼接)③异常处理不完整。Skills 规则把团队 Code Review 标准固化成 AI 能理解的规则,让 AI 生成代码时自动遵守,减少人工 review 成本。

Q2:Skills 规则和 System Prompt 的区别?(高频 ⭐⭐⭐⭐)

答:

  • System Prompt 约束 LLM 的“回答范围”(如“只基于文档回答”)
  • Skills 规则约束 AI 的“编码行为”(如“资金操作必须加锁”)
  • 互补:System Prompt 管“说什么”,Skills 规则管“怎么写代码”

Q3:AI 生成的代码直接合入吗?(中频 ⭐⭐⭐)

答:不直接合入。流程是:AI 生成 → 人工评审 → 修改 → 测试 → 合入。Skills 规则减少但不消除人工审查的必要性。简历写了“人工评审后合入”,面试要强调这一点。

Q4:Cursor Rules 和 Claude Code Skills 有什么区别?(中频 ⭐⭐⭐)

答:本质相同,都是约束 AI 编码行为的规则文件,只是语法和存放位置不同:

  • Cursor Rules:.cursor/rules/*.md
  • Claude Code Skills:.claude/skills/*.md
  • 内容维度一致:代码规范、安全红线、异常处理、事务边界

面试速查表(AI 应用开发)

考点 一句话答案
RAG 流程 加载→分块→向量化→存储→检索→增强→生成
为什么不用 LLM 直接回答 时效性+幻觉+隐私+成本
文档分块 500-800字+滑动窗口,平衡精度和上下文
余弦相似度 看方向不看长度,文本语义用余弦
System Prompt 约束回答范围,防幻觉
“不知道”保护 Prompt 约束 + 相似度阈值拦截
Function Calling 单次工具调用
AI Agent 多步任务规划+动态选工具+多轮执行
ReAct 模式 思考→行动→观察→再思考循环
会话状态管理 Redis TTL 30分钟,多轮上下文
工具调用失败 重试→降级→转人工
Skills 规则 约束 AI 编码行为(规范/安全/异常)
Skills vs System Prompt 一个管写代码,一个管说什么
AI 代码合入 AI生成→人工评审→测试→合入

最后更新:2026-08-19