智能体面经分享

222 阅读1小时+

智能体面经分享

目录


RAG(检索增强生成)部分

RAG的具体流程是怎样的?底层用什么进行检索?检索的策略优化细节

数据准备
  1. 解析提取:清洗多格式文档(PDF、Word、HTML等),提取纯文本。
  2. 文本分块 (Chunking):将长文本切分为适合模型上下文长度的小块。
  3. 向量化 (Embedding):使用 Embedding 模型将文本块转化为高维向量。
    • 要清楚自己用到的embedding模型是啥,这个模型优点是啥。
  4. 建库 (Indexing):将向量和元数据(Metadata)存入底层数据库。
    • 用的向量数据库是啥,Pgvector还是Milvus这种专业向量数据库
    • 建议说是用的Pgvector,要不然面试官可能会问Milvus的一些细节
    • 可以准备一下常见的向量数据库之间的区别
检索阶段 (Retrieval)
  • 接收用户的Query
  • 使用同一个Embedding模型将Query向量化
  • 在数据库中计算Query向量与文档块向量的相似度(如余弦相似度),召回Top-K个最相关的Chunk
生成阶段 (Generation)
  • 将召回的Top-K文本块与用户的原始Query拼接进预设的Prompt模板中
  • 将组装好的Prompt提交给LLM,LLM基于提供的上下文生成最终回答
检索细节

问题:100万条数据怎么检索的,肯定不能一个个计算余弦相似度。

ANN近似最近邻算法

对于大规模向量检索,暴力计算所有向量与查询向量的相似度(如余弦相似度)时间复杂度是O(N),当数据量达到百万级时计算开销太大。因此需要使用近似最近邻(Approximate Nearest Neighbor, ANN)算法,在检索精度和计算效率之间做平衡。

ANN算法的核心思想:

  • 不要求找到完全准确的最近邻
  • 而是找到"足够接近"的邻居
  • 大大降低计算量

HNSW(Hierarchical Navigable Small World)

HNSW是ANN算法的一种高效实现,全称是"分层导航小世界"。它的灵感来源于现实生活中"六度分隔理论"——只要认识6个人,就能认识世界上任何人。

HNSW把向量索引构建成分层图结构:

  • 上层图稀疏
  • 下层图密集

具体来说,假设有100万条向量数据,HNSW可能构建三层结构:

  • 第0层(最底层):100万个节点,节点之间连接密集,包含所有数据
  • 第1层:10万个节点,从下层按一定概率采样得到
  • 第2层(最高层):1万个节点,再次采样

检索过程详解

  1. 检索从最高层(第2层)开始,在1万个节点中贪婪搜索,找到距离查询向量最近的节点A
  2. 然后进入下一层(第1层),在节点A的邻居节点中继续搜索,找到更近的节点B
  3. 接着进入最底层(第0层),在节点B的邻居节点中继续搜索,直到找到距离查询向量最近的节点

什么时候检索结束?

检索结束的条件有两个:

  1. 达到预设的Top-K数量:比如要召回Top-10个最相关的文档,当找到10个符合条件的节点后,检索就可以结束
  2. 达到最大搜索深度:如果还没找到足够的Top-K节点,就会遍历到最底层(第0层),确保不会漏掉任何可能的结果

为什么比暴力匹配更快?

方式时间复杂度计算量(N=100万)特点
暴力匹配O(N)100万次向量点积运算耗时可能达到几百毫秒甚至几秒
HNSWO(logN)几百到几千次速度快几十倍甚至上百倍,精度损失很小(1%-5%)

面试话术建议

"当面试官问百万级数据怎么检索时,我会这样回答:首先暴力计算不现实,必须用ANN近似算法。我项目中用的是HNSW算法,它通过构建分层图结构来加速检索。具体来说,底层包含所有数据节点,上层按概率采样形成金字塔结构。检索时从顶层开始贪婪搜索,逐层向下精化,类似'先在大范围找区域,再在小范围找目标'。这样把检索复杂度从O(N)降到O(logN),百万级数据也能毫秒级返回。如果面试官继续问,我可以补充HNSW的参数调优,比如efConstruction控制构建精度,ef控制检索精度,这些都是tradeoff。"


检索策略优化细节

检索前 - 改造Query

查询重写:用LLM将用户的口语化、指代不清的提问重写为规范的检索词。

检索中 - 混合检索

结合向量检索(懂语义)和BM25(抓关键词,如专有名词、型号),通过Alpha参数调节权重,再进行结果合并。

检索后 (Post-retrieval) - 重排与过滤

Reranking (重排序)

  • 召回的Top-K(比如20个)可能顺序不够准
  • 引入专门的Reranker模型(如BGE-Reranker-large)
  • 计算Query和这20个Chunk的交叉注意力,给出更精确的排序
  • 最后只保留Top-3或Top-5喂给LLM

Chunk(文本切块)的方案中,除了简单按大小切分,还有哪些策略?

按规则/结构切分
  • Markdown/HTML分割:利用文档原有的层级结构(如#,##,<li>)进行切分,保证同一标题下的内容或同一个表格的完整性。大模型最喜欢的就是markdown文本
  • 自然语言边界:基于段落(\n\n)、句子(句号、问号)进行递归切分,优先保证句意完整
  • 重叠切换:每个块之间有10%的重叠
语义切分

使用一些小模型比如GPT4-mini动态计算相邻两个句子的Embedding向量相似度,如果相似度突然出现"断崖式下跌",说明话题变了,就在这里切一刀。这种方法能产生长短不一但语义高度内聚的Chunk。

父子块策略
  • 建库时:切得很碎(比如一句话一个Chunk),用于精准匹配Query,提高检索准确率
  • 喂给模型时:一旦检索到这句话,不只把这句话给LLM,而是把包含这句话的整个段落(父块)甚至前后文提取出来给LLM。这样既保证了检索的精度,又提供了充足的上下文
摘要/总结切分

对大段落或整篇文档先让LLM生成一个浓缩摘要。将"摘要"进行向量化建库。检索时匹配摘要,匹配中后再把原文档调出来给模型。非常适合长文档阅读理解。


如果模型回答的内容跟检索到的文档不相关,通常是哪个环节出了问题?

1. Prompt工程环节

问题:约束力不够

Prompt中没有强硬地限制LLM必须且只能基于参考资料回答。

解决方法

在Prompt中加入强烈指令,例如:

"你必须严格基于以下提供的文档回答问题。如果文档中没有相关信息,请直接回答'根据提供的资料无法回答',绝不能动用你自己的先验知识捏造答案。"

问题:丢失中间信息 (Lost in the Middle)

给LLM喂了太多检索到的文本(比如10个长文档),导致模型注意力机制忽略了中间的关键信息。

解决方法

  • 减少Top-K数量
  • 使用Reranker将最相关的文档放在Prompt的最开头或最末尾
2. 检索数据质量环节

问题:召回了"假相关"的噪音

虽然计算出向量距离很近,但实际上包含的答案不完整,或者有大量干扰信息。

解决方法

  • 优化Chunk策略,增加Chunk的重叠部分
  • 使用Metadata注入(在每个Chunk前自动加上标题或实体信息,上传文档阶段打标签)

Agent架构与设计

Agent和用户之间的多轮对话是怎么串联起来的?用户的两次独立HTTP请求,系统怎么识别它们属于同一个对话并拼接历史上下文?

多轮对话的核心是通过会话标识(conversation_id)+ 上下文存储机制来实现的。

会话标识机制

  • 系统会在用户第一次请求时生成一个唯一的会话id
  • 后续每次请求都会携带这个ID
  • 从而将多次独立的HTTP请求串联为同一个对话

存储方式

  • Redis:常用于在线会话
  • MySQL:用来持久化会话记录
  • 向量数据库(如pgvector):存储长期记忆、摘要这种

上下文拼接策略

  • 每次请求拼接历史对话 + 当前问题
  • 实际上也会有提示词缓存(prompt cache)
  • 一般来说,基本不变的内容,比如长期记忆这种适合作为提示词缓存,利用KV cache优化

Token控制

  • 使用滑动窗口,只保留最近N轮
  • 超过设置的token数就进行历史摘要压缩
  • 长短期记忆分层

大模型"重新规划"任务的具体含义是什么?底层状态机的状态是如何流转的?

Replanning: 执行失败 → 重新规划

常见的执行策略
策略流程
直接执行输入 → LLM → 输出
ReAct(推理+行动)Thought → Action → Observation → ...
Plan-and-Execute先生成完整计划 → 再逐步执行
Replanning执行失败 → 重新规划
Multi-Agent(多智能体)Planner Agent任务分析规划 + Executor Agent执行 + Judge Agent打分 → 不达标则重新执行

Multi-Agent详解

  • Planner Agent:进行任务分析规划
  • Executor Agent:去进行执行
  • Judge Agent:用更高级的模型对执行结果进行打分
  • 分数不达标,带着系统错误上下文回到分析阶段重新执行
  • 从而实现一个自我纠错的闭环执行逻辑

系统运行状态下动态修改大模型配置时,具体支持修改哪些配置参数?

模型本身的参数
  • temperature:低的话输出稳定性高
  • top_p
  • max_tokens
模型本身的配置
  • url
  • api_key
Prompt配置
  • system prompt
  • 业务提示词模板
工具能力(Agent能力)
  • Function Calling函数调用
  • MCP工具列表
  • Skill配置

长期记忆短期记忆

在Agent的记忆设计里:

短期记忆

  • 主要维护当前对话连续性
  • 一般通过保留最近N轮消息或滑动窗口实现

长期记忆

  • 主要保存跨轮次仍有价值的信息
  • 通常通过摘要压缩、向量检索或结构化存储实现
  • 向量检索就是把之前的对话记录存储到向量数据库中,做RAG

核心权衡

由于上下文窗口有限,记忆管理一定伴随信息压缩,所以不能追求完全无损,而是要在token成本和信息保真之间做权衡。

工程方案

  • 最近消息原样保留
  • 历史消息异步摘要
  • 原始记录持久化

这样可以尽量减少信息丢失对回答质量的影响。


工具调用

Function Calling真的是由大模型自己直接在调用的吗?

不是。Function Calling不是大模型"自己直接调用函数",而是大模型在输出中生成结构化调用意图

流程示例

比如问北京天气咋样,大模型只是输出结构化结果:

{
  "tool_call": {
    "name": "get_weather",
    "arguments": {
      "city": "北京"
    }
  }
}

由本地宿主程序负责真正执行,再把执行结果反馈给模型:

{
  "temperature": "22C",
  "condition": "sunny"
}

模型最终组织成人类可读的话输出:"北京今天晴,22摄氏度。"


MCP Server到底部署在哪里,特别是针对本地命令行工具,它是怎么启动的?

MCP协议的核心价值:解耦工具实现与Agent逻辑,避免大量胶水代码。

传统方式的问题

  • 每接入一个工具,Agent代码里都要写适配逻辑
  • 比如要调用天气API、操作数据库、读写文件
  • 每个都要单独写调用代码、处理返回格式、做异常处理
  • 时间久了,Agent代码就变成了一个巨大的"胶水层",难以维护

MCP协议的解决方案

  • 把工具使用服务做成独立进程
  • Agent只需要对接MCP协议这个统一的"传话筒"
  • 工具的增删改都不影响Agent核心代码,实现了真正的插件化
MCP Server的三种部署方式
方式适用场景特点
Stdio本地工具和命令行程序Agent启动时拉起MCP Server子进程,通过标准输入输出通信。简单、安全,不需要网络通信
SSE远程服务且需要服务端推送MCP Server以网络服务形式运行,客户端通过HTTP长连接接入。适合监控告警、实时数据流等
HTTP远程服务且无状态调用客户端通过HTTP短连接调用,每次请求-响应后连接断开。适合简单的查询类工具

Stdio方式详解

比如在Claude Code的配置文件中,可以配置一个filesystem的MCP Server,启动命令是"npx -y @modelcontextprotocol/server-filesystem"

当Claude Code启动时:

  1. 执行这个命令拉起一个子进程
  2. 通过stdin把大模型的工具调用请求发给这个子进程
  3. 子进程执行完毕后通过stdout返回结果

原生的Function Calling、代码里硬编码调API、MCP协议以及Skill,这几个概念之间有什么关系?在具体场景下应该怎么做选择?

这四个概念是不同层级的抽象,从底层到高层依次是:

层级概念特点适用场景
最底层硬编码API调用最灵活,但耦合度最高,每接入一个工具都要写适配代码一次性需求或快速验证
第二层Function Calling调用时机由模型决定,更智能。但工具描述和调用逻辑还是要写在Agent代码里需要模型决策调用时机
第三层MCP协议标准化的工具调用协议,解决跨平台复用问题。同一个MCP Server可以被多个Agent复用多Agent共享工具、跨平台复用
最高层Skill把Prompt模板、工具定义、执行流程、约束规则打包成一个完整的"能力包"标准化任务、团队协作

Skill示例

比如一个"数据分析Skill"可能包含:

  • 数据读取工具
  • 清洗工具
  • 可视化工具
  • 以及一套完整的执行流程

用户只需要说"帮我分析这份数据",Skill就会自动完成整个流程。


高并发情况下,结合SSE流式交互,由于大模型响应RT很长,如何防止后端线程被大量阻塞拖死?

问题分析

  • 线程资源是有限的
  • 但大模型响应时间是固定的
  • 用同步模型会导致线程长时间被占用,无法服务新请求

解决方案的核心思路

让Tomcat线程只负责接收请求,快速释放,把耗时的调用大模型操作交给独立的异步线程池或响应式框架处理,结果通过SSE推送给客户端。

方案一:异步Servlet

Spring Boot支持异步Servlet,核心是使用AsyncContext。

流程

  1. 当请求进来时,Tomcat线程调用request.startAsync()开启异步上下文
  2. 然后把任务提交到独立的线程池
  3. Tomcat线程立即释放去处理其他请求
  4. 异步线程池负责调用大模型API,通过SSE把结果推送给客户端
  5. 最后调用asyncContext.complete()结束请求

优缺点:兼容Servlet规范,但还需要自己管理异步线程池。

方案二:Spring WebFlux响应式

WebFlux基于Reactor和Netty,采用事件驱动模型,完全非阻塞。

优点

  • Netty用少量的事件循环线程就能支撑大量并发连接
  • 不会因为等待大模型响应而阻塞
  • 代码简洁,只需要返回一个Flux流
  • 框架自动处理背压和流控

缺点:学习曲线陡峭,需要理解响应式编程范式。

方案三:消息队列解耦

把请求发送到消息队列,由独立的消费者服务调用大模型API,结果通过WebSocket或轮询返回给客户端。

优缺点:彻底解耦了请求接收和模型调用,但架构复杂度较高,适合超大规模场景。

实际项目选择

在实际项目中,我使用Spring WebFlux实现完全非阻塞的流式交互。Netty的事件驱动模型可以用少量线程支撑大量并发连接,即使大模型响应时间很长,也不会阻塞线程。同时,WebFlux的背压机制可以控制数据流速度,避免客户端处理不过来。


Agent评测与迭代(言之有理即可,能说出一部分就行)

在自己的Agent智能体工作平台项目中,用什么思路去对Agent效果做评测的?

核心概括为:"分层评测、多维指标、自动化为主"。

分层评测
层级评测内容
基础能力层评测原子能力(如工具调用准确率、参数提取规范度)
业务流程层评测多步编排任务(关注中间链路流转是否正确,最终目标是否达成)
端到端效果层评测最终用户体验(如响应时间、回答质量)
多维指标体系
  • 正确性:任务完成率、工具命中率
  • 效率:平均耗时(RT)、Token消耗量
  • 稳定性:错误率、超时率、重试率
  • 用户体验:信息完整度、格式规范度
自动化为主
  • 构建覆盖典型场景与边界情况的测试用例库
  • 结合自动化评测脚本接入CI/CD流水线
  • 实现代码变更后的低成本快速回归,替代人工验证

Agent升级迭代(新老版本替换)时,怎么保证新版本不会比旧版本更差?评测的具体方案怎么做?

建立可量化的评测基准 (Baseline)
  • 在旧版本发布时,跑通测试用例库形成基线数据
  • 引入LLM-as-a-Judge机制,从正确性、完整性等维度对新老版本的输出进行自动化打分比对(可辅以统计学t检验)
  • 确保新版核心指标不退化
多阶段验证发布
阶段内容
离线自动化卡点跑赢基线数据是进入发布流程的硬性门槛
A/B测试双版本同时服务小部分流量,对比真实用户行为(采纳率、追问频次、差评率)
灰度发布逐步扩大放量比例(如5% → 20% → 全量),期间密切监控大盘错误率
应急回滚机制
  • 始终保留旧版本镜像与配置
  • 一旦灰度期间监控告警触发阈值,支持分钟级切流回滚
  • 并捞取Bad Case作为下一轮优化的输入

使用更强的模型做裁判(LLM-as-a-Judge)、使用规则校验、以及人工评测,这三者各存在什么优缺点?怎么进行组合选择?

LLM-as-a-Judge

用更强的模型作为裁判,对被评测模型的输出打分。

优点缺点
成本低,不需要人工参与,可以大规模自动化评测可能存在偏见,裁判模型可能对某些类型的回答有偏好
速度快需要选择足够强的模型做裁判,否则裁判本身的能力会成为瓶颈
覆盖广,可以评测主观性较强的维度(如流畅度、逻辑性、有帮助程度)难以评测事实准确性,裁判模型也可能不知道正确答案
规则校验

用预定义的规则检查输出是否符合预期。比如检查输出是否包含必要字段、格式是否正确、是否包含敏感词、工具调用参数是否合法。

优点缺点
确定性强,规则写死了就不会有歧义覆盖有限,只能检查规则覆盖到的情况
可解释性好,不通过的原因一目了然维护成本高,新问题出现就要加新规则
执行速度快,几乎不需要额外成本容易绕过,模型可能学会"钻空子"
人工评测

由人类专家对模型输出进行评估。

优点缺点
最准确,人类可以理解语义、判断事实、评估有帮助程度成本最高,需要投入人力
可以发现新问题,人工评测过程中可能发现之前没预料到的case速度最慢,难以支撑快速迭代
可以作为其他评测方式的校准基准可能存在主观性,不同评测人员标准可能不一致
常见组合做法
  1. 首先规则校验拦截一部分基础问题
  2. 然后LLM-as-a-Judge进一步评判
  3. 输出内容给用户时,可以设置点赞和点踩标识
  4. 将用户点踩不满意的内容进行进一步人工处理

AI工程化与研发流程

从需求到最终交付的整个研发流程中,你在每个环节具体是怎么使用AI的?

在我的研发流程中,AI贯穿了从需求分析到最终交付的每一个环节,但每个环节的使用方式和侧重点不同。

需求分析阶段

我会和AI进行对话式探讨:

  • 拿到一个需求后,我先把需求描述给AI
  • 让AI帮我分析需求的完整性、边界情况、潜在风险
  • AI会提出一些我没想到的问题,比如"如果用户中途取消订单,库存怎么处理"、"并发情况下会不会超卖"
  • 这种对话式的需求澄清帮我发现了很多隐藏的问题
  • 然后我会让AI把复杂需求拆分成一个个小的功能点,每个功能点都有明确的输入输出和验收标准
设计阶段

我会让AI帮我设计技术方案:

  • 比如设计一个秒杀系统,我会让AI给出架构选型建议、数据流设计、关键接口定义
  • AI会基于它的知识库给出业界最佳实践,比如"用Redis预扣库存、用MQ异步下单、用本地消息表保证最终一致性"
  • 我会和AI讨论方案的优缺点,最终确定技术选型
  • 这个阶段AI的作用是提供思路和参考,但最终的决策权在我手里
编码阶段

我会让AI生成具体功能的代码:

  • 对于每个拆分好的功能点,我会给AI明确的上下文:当前项目的架构风格、相关的已有代码、期望的接口定义
  • AI生成代码后,我不会直接使用,而是先审查一遍
  • 检查是否符合项目规范、是否有明显的Bug、是否处理了边界情况
  • 对于复杂的逻辑,我会让AI解释代码的实现思路,确保我完全理解
测试阶段

我会让AI生成测试用例和测试代码:

  • 我会告诉AI要测试的功能点、边界条件、异常场景
  • AI会生成对应的单元测试代码
  • 对于关键业务逻辑,我会要求AI生成覆盖各种分支的测试用例,包括正常流程、异常流程、边界值
代码审查阶段

我会让AI检查代码质量:

  • 我会把写好的代码发给AI,让它检查是否有安全漏洞、性能问题、代码异味
  • AI会指出一些潜在问题,比如"这里没有做参数校验,可能导致NPE"、"这个循环里每次都查数据库,可以批量查询优化"
文档编写阶段

我会让AI生成技术文档:

  • 包括接口文档、设计文档、部署文档
  • 我会提供关键信息,AI会组织成结构化的文档
  • 这比我自己从头写要快很多,而且AI生成的文档结构清晰、表述规范

总结

整个流程下来,AI在每个环节都扮演了"助手"的角色,但核心的决策和判断还是由我来做。AI的作用是提高效率、提供思路、发现盲点,而不是替代我思考。


在研发流程中,有没有设定一些规则、Skill或策略来对AI的生成进行约束?

有的。我在研发流程中使用了一套Skills体系来约束AI的生成行为,核心是让AI在特定场景下遵循特定的规范和流程,而不是自由发挥。

Superpowers的Skills体系

Superpowers是一个AI开发技能框架,它的核心逻辑是把AI的开发行为"纪律化"。

Superpowers把AI解决问题分为四个阶段:

阶段内容
构思与规划阶段输出设计文档,将工作拆成极小任务
开发与执行阶段Git工作区隔离,TDD测试驱动开发
调试阶段四阶段根因分析法,系统性定位问题
审查与收尾阶段代码审查,运行所有测试,清理工作区

TDD约束示例

Superpowers强制要求AI遵循TDD(测试驱动开发)原则:

  • 先写测试
  • 看着测试失败
  • 写最少代码让测试通过
  • 重构

如果AI想跳过测试直接写业务代码,这个Skill会阻止它。

ui-ux-pro-max Skill

这个Skill的作用是让AI在前端开发时遵循UI/UX设计规范。

它会约束AI生成的:

  • 界面布局
  • 配色方案
  • 交互逻辑

确保前端代码不仅功能正确,而且用户体验良好。

比如我让AI设计一个表单页面,ui-ux-pro-max会要求AI考虑:

  • 表单验证提示的位置
  • 按钮的点击状态
  • 移动端的适配等细节

而不是只生成一个"能用但难看"的界面。

架构图绘制Skill

这个Skill让AI能够生成标准化的架构图,包括:

  • 分层架构
  • 组件关系
  • 数据流向

我会让AI在项目初期生成架构图,作为团队沟通的基准文档。

Skill的核心价值

把"经验性的最佳实践"固化成"可执行的规则":

  • 比如TDD大家都知道好,但实际开发时很容易偷懒跳过
  • 有了Skill的约束,AI会强制遵循这些实践
  • 相当于有一个"不会偷懒的助手"
  • 同时,Skill也让AI的输出更加可预测、可控制,减少了"AI自由发挥导致翻车"的情况

让AI先生成代码,再让AI自己去检查这段代码,这种做法在实际应用中有意义吗?

这种做法的核心价值在于"视角切换"。

角色关注点
AI生成代码时(实现者)"怎么把功能做出来"
AI检查代码时(审查者)"这段代码有什么问题"

角色的切换会带来视角的变化,AI在审查模式下会关注一些生成时忽略的问题:

  • 边界情况处理
  • 异常场景覆盖
  • 代码规范遵守

在Java语言中结合AI去实践TDD(测试驱动开发)是否好落地?

TDD是什么?

TDD(测试驱动开发)是一种开发方法论,核心流程是"红-绿-重构"三步循环:

  1. 先写一个会失败的测试用例(红灯)
  2. 然后写最少量的代码让测试通过(绿灯)
  3. 最后重构代码优化结构(重构)

TDD的好处

  • 测试先行保证了代码的可测试性
  • 小步迭代降低了出错概率
  • 重构步骤保证了代码质量
在Java中结合AI实践TDD是比较好落地的

原因一:Java生态的测试基础设施非常成熟

框架作用
JUnit标准测试框架
MockitoMock框架
Spring Boot Test集成测试框架

AI生成测试代码时,只需要遵循这些框架的API规范,就能生成可运行的测试用例。

原因二:Java的静态类型系统让AI更容易生成正确的代码

  • Java的类型系统在编译期就能发现很多错误
  • AI生成的测试代码如果有类型问题,编译器会直接报错
  • 开发者可以立即修正
  • 这降低了AI生成错误代码的风险

原因三:AI可以很好地遵循TDD的流程约束

在我的实践中:

  • 我会让AI先分析需求,生成测试用例列表
  • 然后逐个实现
  • AI会按照"先写测试、再写实现"的顺序来,不会偷懒跳过测试
  • 同时,AI生成的测试用例往往比我想到的更全面,覆盖了各种边界情况和异常场景

日常用AI辅助写代码时,如何去规避AI生成代码带来的隐藏Bug和难以测试排查的问题?

第一:永远不要盲目信任AI生成的代码

把AI生成的代码当作"一个初级同事写的代码"来审查:

  • 逐行检查逻辑是否正确
  • 边界情况是否处理
  • 异常是否捕获
  • 特别是涉及业务逻辑的部分,我会对照需求文档验证代码实现是否正确
第二:要求AI生成测试用例,用测试来验证代码

我会让AI在生成代码的同时生成对应的测试用例:

  • 正常流程
  • 异常流程
  • 边界值

测试用例是代码正确性的"试金石",如果测试覆盖充分且全部通过,代码的可信度就大大提高。

第三:让AI解释关键逻辑的实现思路

对于复杂的代码片段,我会让AI解释:

  • "为什么这样实现"
  • "有没有考虑其他方案"
  • "这个边界条件为什么这样处理"

通过解释,我可以判断AI是否真的理解了问题,还是在"蒙"一个答案。如果AI解释不清楚或者逻辑有漏洞,说明代码可能有问题。

第四:分步生成,逐步验证

对于复杂功能,我不会让AI一次性生成全部代码,而是拆分成多个小步骤:

  • 每一步生成后立即验证
  • 比如先让AI生成数据模型,验证正确后再生成数据访问层,再生成业务逻辑层
  • 这样每一步的问题都能在小范围内定位,不会累积到最后才发现
第五:建立代码审查清单

我总结了一份AI代码审查清单,每次审查AI生成的代码时逐项检查:

检查项说明
空指针处理是否有NPE风险
参数校验入参是否做了校验
异常捕获异常是否被正确处理
事务边界事务是否正确划分
并发安全是否有线程安全问题
SQL注入是否有SQL注入风险
敏感信息泄露是否泄露敏感信息

这个清单帮我系统性地发现问题,避免遗漏。


Prompt工程 vs Context工程 vs Harness工程

Prompt工程、Context工程和Harness工程,这三个的侧重点和区别是什么?

建议参考这个视频:www.bilibili.com/video/BV1Zk…

这三个工程代表了AI应用开发的三个不同层次,从描述任务到补充背景知识,再到构建执行闭环,是层层递进的关系。

Prompt工程

核心:向大模型描述"怎么完成这个任务"

本质:通过自然语言指令,把任务目标、执行方式、输出格式、约束条件告诉模型

技术手段

手段说明
零样本直接通过指令描述任务,不提供示例
少样本提供几个输入输出的示例,让模型模仿
举反例告诉模型"不要这样做",通过负面案例约束模型行为

适用场景:通用任务、模型已有相关知识储备的任务

核心局限性:大模型的知识是有限的,缺乏专门领域的垂直背景知识

Context工程

核心:为大模型补充"完成任务所需的背景知识"

本质:解决Prompt工程的知识局限性,通过外部信息供给让模型具备领域知识

技术手段

手段说明
RAG(检索增强生成)通过检索外部文档库来补充知识
长期记忆管理把历史对话、用户偏好、项目背景等信息存储起来,需要时召回
动态上下文注入根据当前任务阶段动态加载相关的工具文档、API说明、业务规则
上下文压缩与摘要当信息量超过上下文窗口时,把历史信息压缩成摘要,保留关键信息
多源信息融合把数据库查询结果、API返回数据、文档检索内容融合到上下文中

适用场景:垂直领域应用、需要外部知识的任务、多轮对话场景

解决的问题:"信息供给"问题,但仍然无法解决模型在执行过程中偏离目标的问题

Harness工程

核心:构建"任务执行的确定性闭环"

本质:在模型外围构建一套机制,保证复杂任务能够按计划执行,一旦出现偏差能够及时纠正

要解决的问题:当模型需要一步步执行复杂任务时,某一步出现偏差,后续步骤会基于错误的前提继续执行,导致偏差越来越大

典型架构(六层)

层级作用
循环层建立"观察→决策→行动→验证"的循环引擎
工具层提供文件读写、API调用等原子能力
上下文层作为信息防火墙控制输入质量
持久化层管理跨会话的状态和记忆
验证层在执行后自我验收,失败则重试
约束层设定权限边界防止失控

适用场景:复杂任务编排、自动化工作流、需要多步执行的Agent

三者的关系
工程解决的问题
Prompt工程"怎么说清楚任务"
Context工程"怎么补充知识"
Harness工程"怎么保证执行"

在实际项目中,三者是配合使用的:

  • Prompt工程把任务描述清楚
  • Context工程补充必要的背景知识
  • Harness工程保证执行过程不偏离目标

在你自己的项目中,具体用到了哪些Harness工程的设计?

我的项目没有直接使用Anthropic的Harness框架,但实现了一套类似的自我纠错闭环机制,核心理念是一致的:在复杂任务执行过程中及时检测偏差并纠正,防止"一步错、步步错"。

具体实现:"规划→执行→验证→纠错"的闭环流程

规划阶段

  • 当用户提交一个复杂任务时,大模型分析任务目标
  • 拆解成多个子步骤,生成执行计划

执行阶段

  • 系统按照计划依次执行每个子步骤
  • 每一步都会记录执行结果和中间状态

验证阶段

  • 执行完一个阶段后,对执行结果进行打分评估
  • 打分的维度包括:
    • 任务完成度(是否达成了当前步骤的目标)
    • 结果正确性(输出是否符合预期格式和约束)
    • 资源消耗(Token和工具调用是否合理)

纠错机制

  • 如果分数达标,系统继续执行下一步
  • 如果分数不达标,系统不会继续往下走,而是带着错误上下文回到规划阶段重新分析
  • 具体来说,系统会把当前步骤的执行结果、错误信息、评分反馈给大模型
  • 让大模型分析失败原因,调整执行计划,然后重新执行

核心价值:一旦发现偏差就立即纠正,而不是带着错误继续往下走,避免错误累积放大。

防止死循环的机制
机制说明
最大执行步数限制比如一个任务最多允许执行50步,如果50步内没有完成,系统会判定任务失败
单步重试次数限制比如每个步骤最多重试3次,如果3次都失败,就跳过这个步骤或回退到上一步重新规划

Transformer与深度学习基础

Attention 注意力公式肯定得清楚吧,从公式可以看出来推理的时候,上下文一长,矩阵维度就会很大,并且也正是因为这个公式,所以会有大模型的丢失中间问题,并且推理的时候,是单向注意力的,根据这个公式,生成第N个字的时候需要和前N-1个字的K还有V矩阵做计算,所以我们可以进行一个KV缓存,把之前的字的KV矩阵缓存起来,这个就是KV cache优化。后面优化的时候也就有了提示词缓存。

建议阅读下面的内容了解:

先从整体架构说起

Transformer由编码器和解码器两部分组成:

  • 编码器:负责理解输入序列
  • 解码器:负责生成输出序列

在机器翻译任务中:

  • 编码器把源语言句子编码成一系列向量表示
  • 解码器根据这些表示逐个生成目标语言的词

编码器中的Self-Attention:双向的,每个位置可以关注所有位置,包括前面和后面的词

解码器中的Self-Attention:单向的,每个位置只能关注它之前的位置,不能看到后面的内容,这叫做因果掩码

GPT vs BERT
模型使用的部分注意力类型适用任务
GPT系列只使用了解码器部分单向注意力生成式任务,逐字生成文本
BERT只使用了编码器部分双向注意力理解任务,如文本分类、命名实体识别

GPT只使用解码器的原因:GPT是生成式模型,它的任务是逐字生成文本,不需要编码器来理解输入。GPT的解码器是单向注意力,生成第N个词时只能看到前N-1个词。

BERT只使用编码器的原因:BERT是双向注意力,每个词可以同时看到前面和后面的词,所以BERT更适合理解任务。

对比LSTM

Transformer最大的优势是并行计算

模型计算方式序列长度1000时的计算
LSTM循环神经网络,必须按顺序逐个处理串行执行1000次前向计算
TransformerSelf-Attention可以一次性计算所有位置之间的关系只需要一次矩阵运算就能完成所有位置的注意力计算

这使得Transformer在GPU上的训练速度比LSTM快很多,也是大模型能够规模化训练的基础。

Self-Attention的计算过程示例

假设输入句子是"我希望我成为一名优秀的程序员",一共10个字。在编码器的Self-Attention中,每个字都会和这句话所有的字计算注意力分数。

以"为"这个字为例:

  1. 它会和"我、希、望、我、成、为、一、名、优、秀、的、程、序、员"这14个字分别计算注意力分数
  2. 计算过程:
    • 先把"为"字通过线性变换得到Query向量
    • 把其他每个字通过线性变换得到Key向量
    • 然后用Query和每个Key做点积,得到14个注意力分数
  3. 这些分数经过Softmax归一化后,变成14个0到1之间的权重值,加起来等于1
  4. 权重值越大,说明"为"字越关注那个字
  5. 比如为"字可能更关注"成"和"程序员",因为语义上"成为程序员"是一个完整的搭配
  6. 然后用这14个权重值,对14个Value向量(每个字通过线性变换得到)做加权求和,得到"为"字经过注意力机制后的输出向量
  7. 这个输出向量融合了整句话的信息,但融合的程度不同,关注度高的字贡献更大

每个字都会经历同样的过程,一次性并行计算出来。这就是为什么Transformer能够并行处理整个序列,而LSTM必须逐个处理。

推理时的过程

在推理生成阶段,模型是逐字生成的,每次只能看到已经生成的词。

假设我们要让模型续写"我希望我成为一名优秀的程序员"后面的内容:

步骤输入注意力计算预测
第一步"我""我"的Self-Attention(只有一个词,注意力就是自己)预测"希望"
第二步"我希望"单向注意力,"我"可以看"我"和"希望",但"希望"只能看"我"和"希望"预测"我"
第三步"我希望我"同样的过程,每个词只能看自己和前面的词预测"成为"

依此类推,每生成一个新词,都要把之前所有词重新计算一遍Self-Attention。

计算量:假设生成了N个词,计算量是1+2+3+...+N,也就是O(N²)。当N很大时,计算量会急剧增加。

KV Cache优化

KV Cache优化就是针对这个问题。

观察:每次生成新词时,之前词的Key和Value向量其实已经计算过了,只是被丢弃了。

KV Cache的做法

  • 把每个词的K和V向量缓存起来
  • 生成第N个词时,只需要计算第N个词的Q、K、V
  • 然后把新的K、V追加到缓存中
  • 用缓存的K、V和新词的Q做注意力计算

效果

  • 每次生成的计算量从O(N)降到了O(1)
  • 整个序列的计算量从O(N²)降到了O(N)
从注意力公式还可以看出几个问题

公式是:Attention(Q,K,V) = softmax(QK^T / √d) V

  • QK^T的结果是一个L×L的矩阵,L是序列长度
  • 当L很大时,这个矩阵的内存占用和计算量都会急剧增加
  • 这就是大模型有上下文长度限制的原因

丢失中间问题

  • 注意力分数经过Softmax归一化后,每个位置分配给其他位置的注意力分数都很小
  • 中间位置的信息容易被"稀释"
  • 这就是大模型"丢失中间"问题的根源
  • 模型倾向于关注开头和结尾的内容,中间部分的信息难以被充分关注

除了 KV cache 以外,还有Flash Attention是如何优化的?

Flash Attention解决的核心问题:显存带宽瓶颈

在标准的Self-Attention实现中,计算过程需要频繁访问GPU的高带宽内存(HBM):

  1. QK^T点积的结果是一个L×L的注意力矩阵
  2. 这个矩阵需要写入HBM
  3. 然后从HBM读取出来做Softmax
  4. 再把结果写入HBM
  5. 又从HBM读取出来和V做矩阵乘法

当序列长度L很大时:

  • 这个L×L的矩阵非常占内存
  • 而且频繁的HBM读写成为性能瓶颈
  • GPU的计算速度很快,但大部分时间都在等待数据从HBM搬运过来
Flash Attention的核心思想:分块计算和减少HBM访问

它把Q、K、V矩阵分成小块:

  • 每块大小刚好能放进GPU的SRAM(片上高速缓存)里
  • 然后在SRAM里完成所有计算
  • 最后只把最终结果写回HBM

这样就避免了中间结果(L×L的注意力矩阵)在HBM和SRAM之间来回搬运。

具体例子

假设序列长度L=4096,头维度d=64:

  • 标准实现需要计算一个4096×4096的注意力矩阵,大约128MB的显存
  • 而且这个矩阵需要多次读写HBM
  • Flash Attention把Q、K、V分成小块,比如每块128个token
  • 每次只把128×128的小块注意力矩阵放进SRAM计算
  • 算完一块就输出结果,不需要把整个4096×4096的大矩阵存下来

效果:显存占用从O(L²)降到了O(L),大大降低了显存需求。

Flash Attention还采用了在线Softmax技术

标准Softmax需要:

  • 先算出所有分数的最大值
  • 然后对每个分数减去最大值再做指数运算
  • 这意味着必须等所有分数都算出来才能做Softmax

Flash Attention的在线Softmax:

  • 可以在分块计算的过程中逐步更新最大值和归一化因子
  • 不需要等所有分数都算出来
  • 每处理一块新的注意力分数,就更新当前的最大值和累加和
  • 最终结果和标准Softmax完全一致
Flash Attention 2:进一步优化并行性

第一代Flash Attention主要优化了显存访问,但并行度不够高。

Flash Attention 2的优化:

  • 调整了线程块和工作分配
  • 让不同的GPU线程块并行处理不同的查询块
  • 提高了GPU利用率
  • 同时减少了非矩阵乘法的计算
  • 因为现代GPU有专门的矩阵乘法加速单元,非矩阵乘法操作效率较低

效果:Flash Attention 2比第一代快约2倍。

Flash Attention 3:针对H100 GPU的新特性优化

H100 GPU支持FP8低精度计算和异步操作。

Flash Attention 3的优化:

  • 利用这些特性,在计算注意力矩阵的同时异步加载下一块数据
  • 进一步隐藏了内存延迟
  • 同时使用FP8精度计算,在保持数值稳定性的前提下提高了计算吞吐量

效果:Flash Attention 3比Flash Attention 2又快了约1.5到2倍。

总结Flash Attention的价值
优化点效果
显存复杂度从O(L²)降到了O(L),使得大模型能够处理更长的上下文
上下文长度比如原来只能处理4K上下文,用了Flash Attention后可以处理32K甚至更长
推理速度减少了HBM访问次数,推理速度大幅提升

现在Flash Attention已经成为大模型推理的标准优化手段,几乎所有主流框架都集成了这个技术。


还有可能问到的强化学习的相关知识

DPO GRPO PPO SFT 微调 RLHF

只需要简单了解有个印象就行:

SFT(监督微调)

SFT是后训练的第一步,核心是用高质量的标注数据教模型"怎么说话"。

具体做法

  • 开发者会人工编写大量"一问一答"的对话范例
  • 比如用户问"请帮我写一封请假信",标注人员写出高质量的回答
  • 这些数据喂给模型后,模型学会了从"文字接龙机器"变成"问答助手"

特点

  • SFT改变了模型的回答方式
  • 但没有教模型"什么是好话、什么是坏话"

RLHF(基于人类反馈的强化学习)

RLHF的核心是让模型学会人类的偏好。

具体流程

  1. 首先训练一个奖励模型,这个模型能够给回答打分,判断哪个回答更好
  2. 然后用强化学习算法(如PPO)优化大模型,让模型生成的回答能够获得更高的奖励分数

好处:能够捕捉人类偏好中难以用规则描述的标准,比如回答是否有帮助、是否安全、是否符合价值观

缺点:实现复杂、训练不稳定、需要大量人工标注偏好数据

PPO(近端策略优化)

PPO是RLHF中常用的强化学习算法。

核心思想:限制策略更新的幅度,避免模型在一次更新中变化太大导致训练崩溃

PPO需要同时在显存中加载四个模型

模型作用
Actor模型要训练的大模型
Reference模型初始状态的快照,防止模型偏离太远
Reward模型给回答打分
Value模型预测当前状态的预期得分

问题:内存开销大、实现复杂、超参数多

DPO(直接偏好优化)

DPO是对RLHF的简化。

核心发现:证明了可以直接用偏好数据优化策略,而不需要显式训练奖励模型

具体做法

  • DPO的核心是把"人类偏好A比B好"这种数据直接用于训练
  • 通过对比学习让模型提高生成A的概率、降低生成B的概率

相比PPO

  • 只需要两个模型(Actor和Reference)
  • 实现简单、训练稳定、不需要复杂的强化学习流程

缺点:对于需要精细控制的场景效果不如PPO

GRPO(组相对策略优化)

GRPO是DeepSeek提出的创新算法,核心是去掉了PPO中的Value模型。

具体做法

  1. 让模型对同一个问题生成N个不同的回答
  2. 用奖励模型给这N个回答打分
  3. 计算平均分作为基线
  4. 得分高于平均的回答获得正向奖励,低于平均的回答获得负向惩罚

这种组内相对评分的方式

  • 替代了Value模型的预期得分预测
  • 减少了约25%的内存开销

适用场景:特别适合数学、代码等答案可验证的领域,因为这类领域的奖励信号明确,组内比较更稳定

四者的关系和选择

方法定位特点
SFT基础教会模型说话的方式
RLHF进阶让模型学会人类偏好
PPORLHF的经典实现效果好但复杂
DPOPPO的简化版适合资源有限的场景
GRPOPPO的优化版特别适合可验证领域

实际应用顺序

  1. 先用SFT让模型具备基本能力
  2. 再根据场景选择合适的偏好优化方法:
    • 如果资源充足且需要精细控制,用PPO
    • 如果追求简单稳定,用DPO
    • 如果是数学代码等可验证领域,用GRPO

大模型微调与训练

垂直领域客服Agent,仅通过Prompt+RAG准确率只有70%,如何进一步提升?

第一:优化RAG的检索质量

70%的准确率可能是因为检索环节出了问题,召回的文档不够相关或不完整。

可以尝试

  • 更换更好的Embedding模型,比如从通用模型换成领域专用的Embedding模型
  • 引入重排序机制,先用向量检索召回Top-K文档,再用Cross-Encoder精排,把最相关的文档排在前面
  • 优化Chunk切分策略,避免关键信息被切断,可以尝试语义切分或重叠分块
  • 增加元数据过滤,根据用户问题类型、业务模块等维度过滤文档,缩小检索范围
第二:优化Prompt的指令设计

可以尝试

  • 更明确的角色定义,让模型清楚自己是某个垂直领域的专家
  • 给出Few-shot示例,展示高质量问答的输入输出模式
  • 设置约束条件,比如"只根据检索到的文档回答,不要编造信息"
  • 引入思维链,让模型先分析问题类型、再定位关键信息、最后组织回答

这些优化可能把准确率从70%提升到80%左右。

第三:构建领域知识图谱

RAG是基于相似度检索,但有些领域知识需要结构化的关系推理。

比如医疗领域,症状和疾病之间有复杂的关联关系,单纯靠向量检索可能找不到。知识图谱可以把这些关系显式存储,模型回答时可以沿着关系链推理。

知识图谱和RAG可以结合使用:

  • 检索到的文档作为背景知识
  • 知识图谱提供结构化推理
第四:引入多轮澄清机制

很多时候准确率低是因为用户问题本身不清晰或缺少上下文。

可以让Agent在回答前先判断问题是否明确,如果不明确就追问澄清。

比如用户问"这个药怎么吃",Agent可以追问"请问是哪种药?您的年龄和体重是多少?"这样获得更多信息后再回答,准确率会高很多。

第五:考虑SFT微调

如果以上方法都尝试过还是遇到瓶颈,就考虑SFT微调。

用真实的客服对话数据微调模型,让模型学会这个领域的回答风格、专业术语、常见问题的标准答案。

微调后效果

  • 模型对这个领域的理解会更深入
  • 准确率可以提升到90%以上

注意事项

  • 微调需要高质量的标注数据,成本较高
  • 而且需要定期更新以适应新的业务变化

有没有了解过SFT(监督微调)?在什么情况下选择继续优化RAG/Prompt,什么情况下选择做SFT?

选择RAG/Prompt优化的情况
  1. 数据量不足:没有足够的高质量标注数据来做微调
  2. 领域知识更新频繁:比如新闻、政策、产品信息,这些内容每天都在变,微调后模型很快就会过时,用RAG可以实时检索最新信息
  3. 需要可解释性:RAG可以告诉用户"我是根据这些文档回答的",用户可以验证信息来源,微调后的模型则是"黑盒"
  4. 资源有限:微调需要GPU算力和技术积累,RAG只需要一个向量数据库和检索服务
  5. 快速迭代验证:Prompt改了立刻生效,微调需要重新训练,周期较长
选择SFT微调的情况
  1. 有充足的高质量标注数据:比如积累了大量真实的客服对话记录,且标注质量可靠
  2. 需要模型学习特定的回答风格或格式:比如医疗领域要求回答严谨、法律领域要求引用法条,这些风格很难通过Prompt完全约束
  3. RAG/Prompt优化已经遇到瓶颈:准确率卡在某个水平上不去
  4. 需要降低推理成本:微调后可以用较小的模型达到较好的效果,减少对大模型的依赖
  5. 领域知识相对稳定:不需要频繁更新,比如医学基础知识、法律条文
实际决策顺序
  1. 先用Prompt工程快速验证,看能否达到基本要求
  2. 如果不够,加入RAG补充领域知识
  3. 如果RAG检索质量有问题,优化检索策略
  4. 如果这些都做了还是不够,且手头有高质量数据,就考虑SFT微调
  5. 如果微调后还需要更精细的控制,再考虑RLHF

这是一个渐进的过程,不要一上来就做微调,成本高且不一定有效。


SFT和RLHF(基于人类反馈的强化学习)有什么区别?分别适用于什么场景?

SFT(监督微调)

核心:用标注数据教模型"怎么说话"

具体做法

  • 开发者准备大量"问题-回答"对
  • 用标准的监督学习方式训练模型
  • 模型看到问题,输出回答,计算和标准回答的差异,反向传播更新参数

目标:让模型学会特定的输出格式、回答风格、领域知识

示例:用医疗问答数据微调,模型就学会用专业术语回答医疗问题

RLHF(基于人类反馈的强化学习)

核心:让模型学会"什么是好的回答"

具体做法

  • 它不是告诉模型"应该这样回答"
  • 而是让模型自己探索,人类给出反馈"这个回答比那个好"
  • 模型根据反馈调整策略

RLHF分三个阶段

  1. 训练奖励模型,让人类对多个回答排序,训练一个能预测人类偏好的模型
  2. 用强化学习优化策略模型,让模型生成的回答能获得更高的奖励分数
  3. 可能还有拒绝采样、安全对齐等步骤
两者的核心区别
对比维度SFTRLHF
学习方式"模仿学习",模型学习标注数据的模式"偏好学习",模型学习人类的评价标准
目标输出和标注尽可能接近生成人类喜欢的回答,但具体怎么回答由模型自己探索
数据类型"正确答案""偏好排序"
训练特点训练稳定、实现简单,但上限受限于标注数据质量能捕捉人类偏好中难以标注的标准,但训练复杂、容易不稳定
适用场景

SFT适合

  • 需要注入领域知识,比如让模型学会医学、法律知识
  • 需要学习特定格式,比如让模型输出JSON、SQL
  • 需要改变回答风格,比如让模型更简洁或更详细

RLHF适合

  • 需要优化主观质量,比如让回答更有帮助、更安全、更符合价值观
  • SFT后需要进一步对齐,SFT教会模型说话,RLHF教会模型说好话
  • 需要减少有害输出,比如让模型拒绝回答危险问题
实际应用顺序

通常先SFT再RLHF:

  1. 先用SFT让模型具备基本能力,学会领域知识和回答格式
  2. 再用RLHF优化输出质量,让回答更符合人类期望

直接做RLHF效果通常不好,因为模型还没有基本的回答能力,探索空间太大。


本地部署的模型是否有必要进行微调?为什么模型经过微调之后输出会更加稳定?

是否有必要微调?

不需要微调的情况

如果只是做通用任务,比如日常对话、文本摘要、翻译,直接用开源模型(如Qwen、Llama、ChatGLM)通常就够了。这些模型已经在大规模数据上训练过,通用能力不错。

需要微调的情况

  1. 垂直领域应用:比如医疗问答、法律咨询、金融分析。通用模型在这些领域的表现通常不好,因为训练数据中领域知识占比很小,模型对专业术语、业务规则都不熟悉。微调可以让模型学会领域知识,提高回答的准确性和专业性。

  2. 需要特定的输出格式或风格:比如让模型输出结构化的JSON、按照特定模板生成报告、用特定的语气和用户对话。这些需求通过Prompt也可以部分实现,但微调的效果更稳定、更可控。

  3. 对推理成本敏感:可以考虑微调小模型。用领域数据微调一个7B模型,可能在特定任务上接近未微调的70B模型效果,但推理成本低很多。

为什么微调后输出更稳定?

这涉及到模型学习的本质。

预训练模型的问题

  • 预训练模型是在海量通用数据上训练的,学到了各种领域的知识
  • 但也意味着对任何特定领域都不够专精
  • 当用户问一个垂直领域问题时,模型的参数空间里有很多可能的回答路径
  • 模型不确定该走哪条,所以输出会有波动

微调的作用

  • 微调相当于在模型的参数空间里"聚焦"
  • 用领域数据微调后,模型参数会朝着有利于该领域的方向调整
  • 原本分散的注意力被收拢到领域相关的模式上
  • 模型对领域问题的回答路径变得更确定

类比:就好比一个人什么都懂一点但都不精,回答问题时会犹豫;经过专业训练后,对专业问题的回答就变得果断和一致。

另外

  • 微调数据通常是高质量的、经过清洗的,格式统一、风格一致
  • 模型学习这些数据后,输出也会继承这种一致性
  • 而预训练数据来源复杂、质量参差不齐,模型学到的输出模式也是混杂的

训练过程中,训练集Loss在下降但验证集Loss反而上升,这说明了什么原因?如何解决?

(面试官问我研究生有没有发过深度学习相关的论文,然后给我出的题目,这个题目不想看可以不看)

这是典型的过拟合现象

现象解释

  • 训练集Loss下降说明模型在训练数据上表现越来越好
  • 验证集Loss上升说明模型在未见过的数据上表现变差
  • 这意味着模型开始"死记硬背"训练数据,而不是学习真正的规律
  • 模型记住了训练样本的特征,但这些特征在新数据上不适用,泛化能力下降
过拟合的原因
  1. 训练数据太少:模型容量相对过大,模型有足够的能力记住每个训练样本
  2. 模型太复杂:参数量相对于数据量过大,模型学习到了训练数据中的噪声而不是真实模式
  3. 训练时间太长:模型在训练集上优化过度,把训练数据的噪声也学进去了
  4. 训练数据和验证数据分布不一致:模型学到的是训练集特有的分布,在新分布上不适用
解决方法

第一:增加训练数据

更多的数据可以让模型学到更普遍的规律,而不是记住个别样本。如果没有更多数据,可以用数据增强,比如同义词替换、回译、改写等方式生成新样本。

第二:减少模型复杂度

  • 可以用更小的模型,减少层数或隐藏维度
  • 或者用正则化技术:
    • Dropout:在训练时随机丢弃一部分神经元,防止模型过度依赖某些特征
    • 权重衰减:在损失函数中加入权重的L2范数,限制参数的大小

第三:早停

在训练过程中监控验证集Loss,当验证集Loss开始上升时就停止训练,使用验证集Loss最低时的模型参数。这是最简单有效的防止过拟合的方法。

第四:交叉验证

把数据分成多份,轮流用其中一份做验证集,其他做训练集,最后取平均效果。这样可以更准确地评估模型泛化能力,避免因数据划分偶然性导致的误判。

第五:检查数据质量

如果训练数据有标注错误或噪声,模型会学习这些错误模式,在新数据上表现差。需要清洗数据,去除明显错误的样本。

第六:学习率调整

如果学习率太大,模型可能在最优解附近震荡,无法收敛到好的解。可以尝试减小学习率,或用学习率调度器,训练后期逐渐减小学习率。

实际训练时的做法

我会同时用多种方法:

  • 设置早停机制
  • 加入Dropout和权重衰减
  • 监控训练集和验证集的Loss曲线
  • 如果发现过拟合趋势就提前停止或调整超参数

补充内容

DeepSeek-R1 的四阶段流水线:如何练出顶级推理能力?

也可以应对最近有没有阅读过一些技术报告、论文,都可以用这个回答:

DeepSeek-R1之所以在逻辑推理上极其惊艳,并不是一蹴而就的,而是走了一条非常精密的四阶段强化学习流水线:

第一阶段:冷启动 SFT
  • 先用少量但极其高质量的"思维链 (CoT)"数据给模型打个样
  • 让模型知道"哦,原来遇到复杂问题我要先这么拆解"
  • 这给后续的强化学习提供一个稳固的起点
第二阶段:可验证领域的强化学习 (RL)

这是最关键的一步。

  • 在数学和代码这两个领域,答案是对是错非常明确(代码跑得通就是对,跑不通就是错)
  • DeepSeek使用了GRPO算法(相比传统的PPO算法,它大大减轻了系统开销)
  • 疯狂让模型在这些领域试错
  • 只要做对了就给奖励,模型为了拿到奖励,慢慢就"顿悟"出了强大的逻辑推理能力
第三阶段:拒绝采样微调
  • 模型在第二步里产生了海量的推理过程(有好的也有坏的)
  • 开发者把里面最优秀的轨迹挑选出来
  • 重新作为SFT数据喂给它,进一步巩固它的好习惯
第四阶段:偏好反馈对齐
  • 最后一步,融入安全性和有益性的反馈
  • 确保模型虽然绝顶聪明,但不会去教人做炸弹,也不会满嘴脏话
  • 完成最终的行为收敛

OpenClaw

OpenClaw 是一个开源的个人 AI Agent 框架。和普通聊天机器人不一样,它不是只负责回答问题,而是把"大模型推理能力"和"本地执行能力"结合起来,让 AI 能够在本地环境中完成一整套任务闭环。

核心能力

  • 可以常驻后台运行
  • 通过Gateway接入飞书、Telegram、Discord等渠道接收指令
  • 用模型做任务理解和规划
  • 再由执行器去操作文件系统、终端、浏览器
  • 同时通过Skills机制扩展能力
  • 并把用户偏好、路径、历史任务等信息持久化保存下来

核心价值:把AI从"对话工具"推进到"可执行任务的代理系统"。

如果面试官追问"它和普通 AI 助手有什么区别",你可以答:

OpenClaw的差异点主要有四个:

  1. 本地优先:任务调度、文件处理和工具执行都尽量在本地完成,不像传统AI那样每一步都依赖网页对话和云端上传

  2. 常驻式Agent:可以24×7挂在后台,支持定时任务和事件触发,而不是"打开网页问一句、关掉就结束"

  3. 可扩展:通过Skill包扩展能力,Skill以SKILL.md为描述入口,还可以附带脚本或二进制工具

  4. 多渠道接入:用户可以在飞书、Telegram、Discord等已有沟通平台里直接下指令,而不必切换到特定App

Open claw 的实现原理

OpenClaw是一个开源的个人AI Agent框架,它不是传统的一问一答聊天机器人,而是一个能常驻后台、接收自然语言指令并在本地执行任务的代理系统。

核心架构(四部分)
部分职责
模型层理解任务、拆解步骤和生成行动计划
执行器真正操作文件系统、终端和浏览器,把模型的计划落地
Gateway和接入适配器把飞书、Discord、Telegram等渠道的消息接进来再把结果发回去
持久化记忆层存储用户偏好、路径和历史任务状态,支持跨会话延续
扩展方式:Skill机制
  • Skill一般用SKILL.md来描述能力
  • 也可以附带脚本或工具
  • 因此比较适合做文件整理、自动化办公、监控提醒这类规则明确的任务
从原理上看

OpenClaw本质上是"大模型决策 + 本地工具执行 + 状态持久化 + 多渠道接入"的闭环Agent Runtime。

优势:本地优先、常驻运行、扩展性强

问题:部署成本、Token消耗和高权限带来的安全风险

OpenClaw 的单次任务流程可以概括为六步

第一步:接收指令

用户通过飞书、Discord之类的渠道输入一句自然语言指令,Gateway把它转成内部任务。

第二步:构造上下文

系统会把当前用户请求、历史偏好、路径信息、可能的技能能力说明等拼成上下文,一起送给模型。持久化记忆在这里发挥作用。

第三步:模型做规划

模型负责理解"用户要我做什么",并把任务分解成若干步骤,比如先读目录,再分类文件,再生成结果说明。

第四步:执行器执行动作

模型给出的动作并不会直接生效,而是由执行器调用本地能力去真正完成,比如操作浏览器、运行终端命令、处理文件。

第五步:必要时调用Skill

如果基础执行器能力不够,系统还可以调用Skill包。文档里说Skill是用Markdown作为接口定义的扩展包,包含SKILL.md描述文件,以及可选的脚本、工具或二进制程序。

第六步:返回结果并更新记忆

任务完成后,把结果发回原消息渠道,同时把本轮任务中的重要状态写入本地记忆,为下一轮服务。