作业帮 大模型算法 一面(暑期)
1. 自我介绍
2. 介绍一个你做得比较完整、个人成长最多的项目,背景、目标、方案和效果是什么?
3. 做这个项目时具体用了哪些方案或手段达成目标?
答案:最核心的是把问题拆开,没有直接把所有文档塞给大模型。文档侧先做结构化解析,保留标题层级、版本号、适用部门、发布时间和权限标签;检索侧用了 BM25 和向量召回融合,因为制度类问题经常包含专有名词、系统名和缩写,单纯向量召回容易漏;生成侧强制模型基于证据回答,不允许没有证据时编造。
训练侧做了两类任务,一类是意图分类和工单类型预测,用历史工单标题、正文、处理队列作为弱标签,再人工抽检;另一类是指令微调样本,用高质量问答、证据片段和标准处理建议构造。线上 badcase 会回流到评测集和训练集里,不会直接只改 prompt。这个项目里提升最大的不是换模型,而是把检索、证据约束和数据闭环做扎实。
4. 整个系统的技术链路是怎样的?
答案:系统链路可以分为离线链路和在线链路。离线链路负责知识入库,包括文档抓取、OCR、正文抽取、表格解析、标题树构建、chunk 切分、embedding 计算、倒排索引和向量索引构建。在线链路从用户 query 进来后,先做 query 清洗和意图识别,再根据意图走不同检索策略,比如制度问答走知识库召回,工单问题走历史工单召回,接口问题走接口文档召回。
召回之后会做权限过滤、去重、rerank、上下文压缩,再交给 LLM 生成答案。生成结果还要经过引用校验、敏感信息过滤和结构化字段提取,最后写入日志系统供后续分析。技术上主要用了 Python、FastAPI、Elasticsearch、Milvus、Redis、PyTorch、Transformers 和 vLLM。
用户 query -> query 清洗/改写 -> 意图识别 -> BM25 + 向量混合召回 -> 权限过滤 -> rerank -> 上下文压缩 -> LLM 生成 -> 引用校验/字段抽取 -> 日志与反馈回流
5. 整个系统存储数据用的是什么?存在哪些地方?采用了什么记录方案?
答案:系统里不是所有数据都存在一个库里。原始文档和附件存在对象存储,方便保留版本和回溯;解析后的结构化文本、标题树、元数据存在 PostgreSQL;全文检索索引放在 Elasticsearch;向量索引放在 Milvus;会话状态、热点缓存、prefix cache 的业务侧 key 存在 Redis;线上请求日志和模型中间结果写入日志平台。
记录方案上,每个 chunk 都有唯一 chunk_id,同时关联 doc_id、version_id、title_path、permission_tag、effective_time 和 source_url。这样生成答案时可以追踪到底引用了哪个文档的哪个版本。对于历史工单,会保留脱敏后的问题、处理步骤、最终结论和工单分类,但不会把个人敏感字段直接进入训练集。
chunk_record = {
"chunk_id": "c_202506_00021",
"doc_id": "doc_hr_policy_008",
"version_id": "v3",
"title_path": ["员工服务", "账号权限", "权限申请流程"],
"content": "权限申请需由直属主管审批后提交至 IT 服务台。",
"permission_tag": ["internal", "it"],
"effective_time": "2025-06-01",
"embedding_id": "emb_77291"
}
6. query 做了什么处理?
答案:query 处理不是简单去空格,主要做了清洗、改写、分类和路由。清洗会处理错别字、特殊符号、无意义口语和复制进来的日志片段;改写会把省略表达补全,比如“这个怎么申请”要结合上下文改写成“VPN 权限怎么申请”;分类会判断是制度问答、故障排查、接口查询、闲聊还是无权限问题;路由决定后面走哪套检索和生成策略。
比较重要的是 query decomposition。复杂问题经常同时问多个点,比如“账号锁了怎么解,顺便查一下审批要多久”,这时如果用一个 query 召回,结果会混乱,所以会拆成多个子问题分别召回。对于包含代码、报错日志的 query,会保留关键错误码、接口名、堆栈关键词,不会过度语义改写。
def normalize_query(query, history=None):
q = query.strip()
q = q.replace("\u3000", " ")
q = " ".join(q.split())
if history and any(x in q for x in ["这个", "刚才那个", "它"]):
q = f"结合上下文:{history[-1]}。当前问题:{q}"
return q
7. 你自己做了一些模型微调,训练数据是从哪里来的?
答案:训练数据主要来自三部分。第一部分是历史工单和知识库问答,经过脱敏、去重、规则版本对齐和人工抽检后,用来构造意图分类、工单类型预测和字段抽取样本。第二部分是线上 badcase 回流,比如召回错、分类错、拒答错、格式错,这部分样本价值最高,因为它反映真实错误分布。第三部分是人工构造的边界样本,主要覆盖权限不足、证据冲突、问题不完整、多意图和敏感信息场景。
不会直接把所有历史数据丢进去训练。比如历史工单里有很多处理结论已经过期,或者包含个人信息、临时 workaround,这些都要过滤。用于 SFT 的样本必须保证输入、证据、答案和规则版本一致;用于分类的样本要处理类别不均衡;用于偏好训练的样本会从同一个 prompt 的多个候选答案中筛出 chosen/rejected。
def build_cls_sample(ticket):
return {
"text": ticket["title"] + "\n" + ticket["description"],
"label": ticket["final_category"],
"meta": {
"source": "ticket",
"version": ticket["policy_version"],
"checked": ticket["human_checked"]
}
}
8. 当时微调用的模型是什么,选的是什么尺寸?
答案:分类和字段抽取任务用的是 DeBERTa-v3-base 和 RoBERTa-wwm-ext 这类 encoder 模型,原因是这类任务更需要稳定的判别边界和低延迟,没有必要上大参数生成模型。生成类任务做过 Qwen 系列 7B 级别模型的 LoRA 微调,主要学习企业内部问答风格、证据引用格式和拒答策略。没有直接选择更大的模型,是因为部署成本和延迟压力比较大,7B 级别配合 RAG 已经能覆盖大多数场景。
尺寸选择主要看任务。意图识别、工单分类这类高频链路,base 级别 encoder 更划算;复杂答案生成才走 LLM;如果是强规则判断,会放到规则引擎而不是模型里。微调方式上,生成模型主要用 LoRA/QLoRA,分类模型全参 fine-tune 或者只微调上层都试过,最终按验证集 F1、P95 延迟和线上资源成本综合选择。
lora_config = {
"r": 16,
"lora_alpha": 32,
"target_modules": ["q_proj", "k_proj", "v_proj", "o_proj"],
"lora_dropout": 0.05,
"task_type": "CAUSAL_LM"
}
9. 传统 NLP 做分类场景训练时,最后的损失函数一般是什么?
答案:单标签多分类一般用 Cross Entropy Loss,多标签分类一般用 BCEWithLogitsLoss。如果类别极不均衡,可以用加权交叉熵、Focal Loss 或者重采样。传统 NLP 分类里,模型最后通常输出每个类别的 logits,交叉熵会先做 softmax,再最大化真实类别的概率,本质上是在优化负对数似然。
import torch import torch.nn as nn logits = torch.tensor([[2.0, 0.3, -1.2], [0.1, 1.5, 0.4]]) labels = torch.tensor([0, 1]) loss_fn = nn.CrossEntropyLoss() loss = loss_fn(logits, labels) print(loss.item())
10. 除了交叉熵,还有没有其他可以衡量两个分布之间差异的方式?
答案:可以用 KL 散度、JS 散度、Wasserstein 距离、Total Variation Distance、Hellinger Distance,也可以在蒸馏场景中用 MSE 对齐 logits 或 hidden states。交叉熵更偏训练目标,KL 更常用于分布对齐和蒸馏,JS 是对称且有界的,Wasserstein 在两个分布支撑集不重叠时也能提供更平滑的距离信号。
在大模型里,KL 很常见,比如 RLHF/DPO/GRPO 里会用 KL 控制当前模型不要偏离 reference model 太远。蒸馏里也常用 teacher/student 的 soft label 做 KL,这比只学 hard label 能保留类别间相似性。
import torch import torch.nn.functional as F student_logits = torch.tensor([[2.0, 0.5, -1.0]]) teacher_logits = torch.tensor([[1.5, 1.0, -0.5]]) temperature = 2.0 student_logp = F.log_softmax(student_logits / temperature, dim=-1) teacher_p = F.softmax(tea
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
本专栏聚焦 AI-Agent 面试高频考点,内容来自真实面试与项目实践。系统覆盖大模型基础、Prompt工程、RAG、Agent架构、工具调用、多Agent协作、记忆机制、评测、安全与部署优化等核心模块。以“原理+场景+实战”为主线,提供高频题解析、标准答题思路与工程落地方法,帮助你高效查漏补缺.
