NLP 算法工程师一面面经
完整信息
本篇目录
1. 请做一下自我介绍,并重点说明你在大模型后训练、Agent 或工具调用方向上的技术积累
2. 轨迹数据的最长长度、最短长度与任务复杂度之间是什么关系?训练时如何调用这类数据
轨迹长度本身不是任务复杂度的充分条件。一个任务可能调用十几个工具,却只是重复翻页;另一个任务只有两次调用,但需要精确处理跨工具依赖。因此更合理的复杂度指标应同时考虑:
有效工具调用数量;
依赖图深度与分支数;
参数约束数量;
环境状态变化次数;
错误恢复次数;
最终答案对中间结果的依赖程度。
可以定义一个近似复杂度分数:
最短轨迹一般用于学习基本格式、工具选择和参数填充;中等长度轨迹负责学习多步规划;最长轨迹则用于训练状态维护、错误恢复和长程 credit assignment。
训练时不能简单按照数据原始分布随机采样,否则短轨迹会占据大多数 batch。通常采用长度桶和复杂度桶联合采样:
import random
from collections import defaultdict
buckets = defaultdict(list)
for sample in dataset:
length_bin = min(sample["num_calls"] // 3, 5)
complexity_bin = min(int(sample["complexity_score"] // 2), 5)
buckets[(length_bin, complexity_bin)].append(sample)
def sample_batch(batch_size):
keys = list(buckets.keys())
batch = []
for _ in range(batch_size):
key = random.choice(keys)
batch.append(random.choice(buckets[key]))
return batch
还可以在训练前期提高短轨迹比例,保证结构化输出稳定;中后期逐步增加长轨迹和失败恢复轨迹,形成 curriculum learning。
3. Tool-use SFT 的训练目标是什么?基座模型已经具备工具调用能力时,SFT 还需要学习什么
Tool-use SFT 的目标不是让模型“知道存在工具”,而是把通用能力约束到特定工具协议和业务环境中。即使基座模型已经具备较强的工具调用能力,也不代表它能直接完成下面这些事情:
从大量候选工具中选择正确工具;
遵循私有 schema 和字段约束;
按照正确顺序完成多工具调用;
将上一步工具结果准确传入下一步;
在工具报错、超时或返回空结果时恢复;
判断何时停止调用并输出最终答案。
训练数据通常写成:
其中T是工具定义,ai是工具调用,oi是环境返回,z 是最终答案。
需要特别注意 loss mask。工具返回结果来自外部环境,通常不应该作为模型预测目标;真正需要计算 loss 的部分是 assistant 的推理、工具选择、参数和最终回答。
labels = input_ids.clone()
for span in environment_output_spans:
labels[span.start:span.end] = -100
for span in system_prompt_spans:
labels[span.start:span.end] = -100
loss = model(
input_ids=input_ids,
attention_mask=attention_mask,
labels=labels
).loss
如果把工具返回也纳入 loss,模型可能学习“伪造观察结果”,而不是等待环境真正执行工具。
4. Chat Template、System Prompt 和模型训练阶段写入的行为先验分别是什么关系
三者属于不同层次。
Chat Template 是序列化协议,负责把 system、user、assistant、tool 等结构转换成 token 序列,例如插入角色标记、结束符和工具调用边界。它解决的是“消息如何编码”。
System Prompt 是运行时上下文的一部分,用于声明身份、规则、工具和输出要求。它是显式条件,不是模型结构中天然存在的一段固定文本。
训练形成的行为先验 存在于模型参数中。模型在预训练和后训练过程中学习到:看到某类 system 指令后应当怎样回答、什么时候调用工具、如何结束生成。它不是可直接读取的“隐藏 system prompt”。
可以将最终输入近似表示为:
模型输出则为:
P(Y∣X;θ)
其中 (\theta) 包含训练阶段形成的行为先验。更换模板但不重新适配,可能导致角色边界错乱、工具调用格式失效,甚至使模型无法正确停止。
5. 将 MCP 工具的 JSON Schema 注册给模型时,模型究竟是如何获得结构化工具信息的
MCP 解决的是客户端与工具服务之间的发现、描述和调用协议,它不会把工具“写进模型参数”。一次完整流程通常是:
客户端连接 MCP Server;
通过 tools/list 获取工具名称、描述和输入 schema;
将工具 schema 转换成模型 API 或模板支持的格式;
把工具定义注入当前上下文;
模型生成结构化 tool call;
客户端解析并校验参数;
调用 MCP Server;
将工具结果作为 tool message 回填上下文;
模型继续生成。
工具定义可能被序列化为类似下面的内容:
{
"type": "function",
"function": {
"name": "query_order",
"description": "根据订单编号查询订单状态",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string"
}
},
"required": ["order_id"],
"additionalProperties": false
}
}
}
模型并不真正“执行函数”,它只是生成一个满足协议的调用意图:
{
"name": "query_order",
"arguments": {
"order_id": "A1024"
}
}
真正的参数校验、权限控制、函数执行、重试和结果回填都由 Agent Runtime 完成。生产环境不能仅依赖模型生成合法 JSON,还需要在执行前进行 schema validation。
6. 工具数量达到数千个时,为什么不能把全部 schema 直接塞进上下文?如何设计工具路由
全部注入会引发三个问题:上下文成本过高、相似工具之间发生注意力干扰、模型选择错误率随候选集合扩大而上升。
更合理的是分层路由:
第一层根据任务选择工具域,例如数据库、代码执行、搜索、办公系统;第二层在域内做向量召回或学习式 rerank;第三层只向大模型暴露 Top-K 工具。
候选召回不能只看用户问题,还应结合对话状态和上一步工具结果:
其中权限不满足的工具应在进入模型上下文之前就被过滤,而不是通过 prompt 告诉模型“不要调用”。
对召回阶段的评测也不能只看工具选择准确率,应重点观察 Recall@K。因为正确工具没有被召回时,后续模型无论多强都无法恢复。
7. 主流 Agent Harness 的核心差异体现在哪里
Agent Harness 本质上是模型与外部环境之间的运行时。不同框架的核心差异通常不在“能不能调用工具”,而在下面几个方面:
控制循环:固定 ReAct 循环、状态机、事件驱动或图执行;
上下文管理:完整保留、滑动窗口、摘要压缩或分层记忆;
工具执行模型:串行、并行、异步、事务式执行;
状态持久化:是否支持 checkpoint、恢复和跨会话状态;
权限与隔离:文件系统、网络、Shell 和密钥访问是否沙箱化;
可观测性:是否记录 token、工具参数、环境状态和错误链路;
人机协同:高风险操作是否支持审批、回滚和中断;
协议适配:是否兼容 MCP、函数调用协议及不同模型模板。
代码型 Agent 还必须处理仓库索引、补丁生成、测试执行、终端状态和上下文压缩。真正决定上限的是环境建模与状态管理,而不仅是 prompt。
8. 为什么 Step-level SFT 之后再进行 GRPO,通常比直接从基座模型开始做 GRPO 稳定
GRPO 属于 on-policy 或近似 on-policy 优化。它要求当前策略能够采样出一定比例的有效轨迹,组内奖励才有可比较性。
如果直接从基座模型开始:
大量输出不符合工具协议;
同组样本可能全部奖励为零;
奖励方差过低,归一化优势接近零;
探索空间被无效格式占据;
KL 和熵正则难以同时维持稳定。
GRPO 的组内优势通常写成:
当组内所有轨迹都失败时:
此时几乎没有有效学习信号。Step-level SFT 先让模型学会基本动作空间,例如合法 JSON、工具边界、调用顺序和停止条件,把策略带到奖励非零区域;之后 GRPO 才能优化多种合法策略之间的质量差异。
9. GRPO 中相对奖励是如何计算的?同一组奖励方差接近零时如何处理
GRPO 对同一问题采样 (G) 条回答,并使用组内相对表现估计优势:
优化目标可写为:
其中:
当组内方差接近零时,强行除以很小的标准差会放大噪声。可采用以下处理:
跳过该组更新;
使用标准差下限;
混合全局 advantage statistics;
增大采样组大小或采样温度;
改善任务难度分布,减少全对或全错样本;
增加过程奖励,提高组内区分度。
def group_advantage(rewards, eps=1e-6, min_std=0.05):
mean = rewards.mean()
std = rewards.std(unbiased=False)
if std < eps:
return None # 跳过无区分度的 group
std = std.clamp_min(min_std)
return (rewards - mean) / std
长期大量出现零方差组,通常说明题目难度、采样策略或奖励函数存在问题,而不只是数值稳定问题。
10. 长轨迹做 SFT 时,采用截断、切分还是掩码?如何避免破坏工具依赖关系
直接从 token 中间截断最危险,因为可能截掉工具调用参数、工具返回结果或关键依赖节点,导致训练样本语义不闭合。
更合理的方法是按照语义边界切分:
每个 chunk 从完整消息边界开始;
tool call 与对应 tool result 不得分离;
下游调用所依赖的关键观察
本篇目录
- 01
请做一下自我介绍,并重点说明你在大模型后训练、Agent 或工具调用方向上的技术积累
- 02
轨迹数据的最长长度、最短长度与任务复杂度之间是什么关系?训练时如何调用这类数据
- 03
Tool-use SFT 的训练目标是什么?基座模型已经具备工具调用能力时,SFT 还需要学习什么
- 04
Chat Template、System Prompt 和模型训练阶段写入的行为先验分别是什么关系
- 05
将 MCP 工具的 JSON Schema 注册给模型时,模型究竟是如何获得结构化工具信息的
- 06
工具数量达到数千个时,为什么不能把全部 schema 直接塞进上下文?如何设计工具路由
- 07
主流 Agent Harness 的核心差异体现在哪里
- 08
为什么 Step-level SFT 之后再进行 GRPO,通常比直接从基座模型开始做 GRPO 稳定
- 09
GRPO 中相对奖励是如何计算的?同一组奖励方差接近零时如何处理
- 10
长轨迹做 SFT 时,采用截断、切分还是掩码?如何避免破坏工具依赖关系
