唯品会

NLP 算法工程师一面面经

完整信息

10 条问法 · 10 道题

本篇目录
  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。

  1. 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,模型可能学习“伪造观察结果”,而不是等待环境真正执行工具。

  1. Chat Template、System Prompt 和模型训练阶段写入的行为先验分别是什么关系

三者属于不同层次。

Chat Template 是序列化协议,负责把 system、user、assistant、tool 等结构转换成 token 序列,例如插入角色标记、结束符和工具调用边界。它解决的是“消息如何编码”。

System Prompt 是运行时上下文的一部分,用于声明身份、规则、工具和输出要求。它是显式条件,不是模型结构中天然存在的一段固定文本。

训练形成的行为先验 存在于模型参数中。模型在预训练和后训练过程中学习到:看到某类 system 指令后应当怎样回答、什么时候调用工具、如何结束生成。它不是可直接读取的“隐藏 system prompt”。

可以将最终输入近似表示为:

模型输出则为:

P(Y∣X;θ)

其中 (\theta) 包含训练阶段形成的行为先验。更换模板但不重新适配,可能导致角色边界错乱、工具调用格式失效,甚至使模型无法正确停止。

  1. 将 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。

  1. 工具数量达到数千个时,为什么不能把全部 schema 直接塞进上下文?如何设计工具路由

全部注入会引发三个问题:上下文成本过高、相似工具之间发生注意力干扰、模型选择错误率随候选集合扩大而上升。

更合理的是分层路由:

第一层根据任务选择工具域,例如数据库、代码执行、搜索、办公系统;第二层在域内做向量召回或学习式 rerank;第三层只向大模型暴露 Top-K 工具。

候选召回不能只看用户问题,还应结合对话状态和上一步工具结果:

其中权限不满足的工具应在进入模型上下文之前就被过滤,而不是通过 prompt 告诉模型“不要调用”。

对召回阶段的评测也不能只看工具选择准确率,应重点观察 Recall@K。因为正确工具没有被召回时,后续模型无论多强都无法恢复。

  1. 主流 Agent Harness 的核心差异体现在哪里

Agent Harness 本质上是模型与外部环境之间的运行时。不同框架的核心差异通常不在“能不能调用工具”,而在下面几个方面:

控制循环:固定 ReAct 循环、状态机、事件驱动或图执行;

上下文管理:完整保留、滑动窗口、摘要压缩或分层记忆;

工具执行模型:串行、并行、异步、事务式执行;

状态持久化:是否支持 checkpoint、恢复和跨会话状态;

权限与隔离:文件系统、网络、Shell 和密钥访问是否沙箱化;

可观测性:是否记录 token、工具参数、环境状态和错误链路;

人机协同:高风险操作是否支持审批、回滚和中断;

协议适配:是否兼容 MCP、函数调用协议及不同模型模板。

代码型 Agent 还必须处理仓库索引、补丁生成、测试执行、终端状态和上下文压缩。真正决定上限的是环境建模与状态管理,而不仅是 prompt。

  1. 为什么 Step-level SFT 之后再进行 GRPO,通常比直接从基座模型开始做 GRPO 稳定

GRPO 属于 on-policy 或近似 on-policy 优化。它要求当前策略能够采样出一定比例的有效轨迹,组内奖励才有可比较性。

如果直接从基座模型开始:

大量输出不符合工具协议;

同组样本可能全部奖励为零;

奖励方差过低,归一化优势接近零;

探索空间被无效格式占据;

KL 和熵正则难以同时维持稳定。

GRPO 的组内优势通常写成:

当组内所有轨迹都失败时:

此时几乎没有有效学习信号。Step-level SFT 先让模型学会基本动作空间,例如合法 JSON、工具边界、调用顺序和停止条件,把策略带到奖励非零区域;之后 GRPO 才能优化多种合法策略之间的质量差异。

  1. 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

长期大量出现零方差组,通常说明题目难度、采样策略或奖励函数存在问题,而不只是数值稳定问题。

  1. 长轨迹做 SFT 时,采用截断、切分还是掩码?如何避免破坏工具依赖关系

直接从 token 中间截断最危险,因为可能截掉工具调用参数、工具返回结果或关键依赖节点,导致训练样本语义不闭合。

更合理的方法是按照语义边界切分:

每个 chunk 从完整消息边界开始;

tool call 与对应 tool result 不得分离;

下游调用所依赖的关键观察

本篇目录
  1. 01
  2. 02
  3. 03
  4. 04
  5. 05
  6. 06
  7. 07
  8. 08
  9. 09
  10. 10

同作者面经

展开其余 5 篇收起其余 5 篇

相关公司