别急着回答“打字还是说话”:LLM Agent 输入扰动对比研究的工程启示
开聊之前先说一个场景:你在工位上戴着耳机,对手机里的 Agent 说了一句“帮我查一下下周三下午三点有没有空会议室”,结果语音识别把“下周三”听成了“虾周三”,Agent 直接查了一个不存在的日期。你还来不及解释,又看到旁边同事用键盘给同一个 Agent 发指令,手一快把“会议室”打成了“会议市”,Agent 同样一脸茫然。
这两类问题看似是 ASR(自动语音识别)和输入法的锅,但实际上它们被划到了一个共同的研究范畴里——输入扰动(Input Perturbations)。最近看到一篇研究方向很有意思的文章,标题是Should We Type or Talk to LLM Agents? A Comprehensive Study of Voice and Keyboard Input Perturbations,它把“打字”和“说话”这两条输入链路放进同一套评测框架里做对比,讨论扰动对 LLM Agent 任务完成率的影响。
这篇文章不是纯理论分析,而是希望从工程落地的角度拆解几个问题:
- 语音输入和键盘输入,分别会引入哪些类型的扰动?
- 这些扰动传到 LLM Agent 之后,为什么会导致任务失败?
- 我们在开发 Agent 产品时,应该怎么设计输入链路才能扛住这些扰动?
本文适合正在做 LLM Agent、语音助手、智能客服、自动化工作流产品的开发者阅读。读完你会理解输入扰动对比研究的核心思路,也能拿到一套可复现的扰动模拟评测 Demo,以及工程侧的抗扰动设计建议。
1. 问题背景:为什么“Type or Talk”是一个严肃的技术问题
1.1 输入方式不是简单的交互偏好
以前我们觉得,用户喜欢打字就打字,喜欢说话就说话,这只是交互习惯差异。但放到 LLM Agent 场景里,事情没那么简单。
Agent 和普通聊天机器人最大的区别在于:Agent 需要把用户的自然语言指令,解析成一系列可执行的动作,比如查数据库、调用 API、操作界面、读写文件。这意味着它对输入内容的准确率要求比“闲聊”高得多。
如果你对一个聊天机器人说“帮我写一首关于春天的诗”,即使语音识别把“春天”识别成“蠢天”,机器人大概率还是能顺着语境生成一首标题不准确但看起来合理的诗。但如果你对 Agent 说“删除订单 20240115 的所有记录”,语音识别把“删除”识别成“上传”,或者把“20240115”识别成“20240155”,Agent 就可能执行一个完全错误的操作。
这就是输入扰动研究的起点:同一个意图,经由语音或键盘输入时,会因为环境和用户操作习惯产生不同类型的噪声,而这些噪声对 Agent 的最终行为影响可能是致命的。
1.2 语音输入链路和键盘输入链路的本质差异
两种输入方式在技术链路上完全不同。
语音输入的完整链路是:
用户说话 → 麦克风采集 → 音频信号处理 → ASR 识别 → 文本 → LLM/Agent键盘输入的完整链路是:
用户敲击键盘 → 按键事件 → 输入法(IME)转换 → 文本 → LLM/Agent在语音链路里,扰动可能来自环境噪音、麦克风质量、口音、语速、ASR 模型本身的识别错误,还有同音字混淆。在键盘链路里,扰动可能来自手误、按键粘连、输入法候选词选错、中英文切换失误,甚至自动纠错把正确的内容改坏。
两种链路在扰动模式上几乎没有交集,但它们最终都会落到同一个地方——LLM 拿到的文本指令不准确。因此,对比研究这两类扰动对 LLM Agent 的影响,本质上是研究“什么样的错误会让 Agent 行为崩溃”,而不只是研究语音识别或输入法本身。
1.3 为什么需要一篇“系统性对比”研究
单独看 ASR 错误率、单独看打字错误率,都已经有很多工作了。但在 LLM Agent 场景里,我们更关心的是“错误传递到下游之后的连锁反应”。同一个错误率,放在普通聊天里可能无所谓,放在 Agent 的 API 参数提取里可能直接导致任务失败。所以需要把输入扰动和 Agent 任务结果放在一起评估,而不是只看中间的文本错误率。
这篇文章研究的核心价值就在这里:它提供了一个统一的框架,把语音输入扰动和键盘输入扰动放进同一套评测体系,让开发者能直观地看到“哪种输入方式更容易出问题”“哪种扰动对任务完成率伤害最大”。
2. 核心概念:LLM Agent 与输入扰动
2.1 什么是 LLM Agent
LLM Agent 是“以大语言模型为大脑,能调用工具、规划步骤、执行任务的智能体”。它不只是生成文本,而是能:
- 理解用户的复杂指令;
- 把指令拆解成子任务;
- 调用外部工具(搜索、数据库、API、代码解释器等);
- 根据工具返回结果调整下一步动作。
一个典型的 Agent 工作流可以简化成:
用户输入 → 意图识别 → 参数抽取 → 动作规划 → 工具调用 → 结果汇总 → 回复用户在这个流程里,任何一步如果输入信息不准确,错误都会被放大。例如参数抽取时把日期抽错,动作规划就会基于错误参数进行,最后工具调用自然不对。
2.2 输入扰动的分类
输入扰动是指在用户意图从“大脑中的表达”转变为“LLM 接收到的文本”的过程中,因为中间环节引入的偏差,导致文本与用户真实意图不一致的现象。
按来源可以分成几类:
| 扰动类型 | 来源链路 | 具体示例 |
|---|---|---|
| 环境噪声 | 语音 | 办公室背景音、窗外车流声 |
| 发音偏差 | 语音 | 方言口音、发音含糊、语速过快 |
| ASR 识别错误 | 语音 | 同音字混淆、数字听写错误、标点丢失 |
| 手误 | 键盘 | 字母换位、漏键、多键 |
| 输入法错误 | 键盘 | 候选词选错、拼音切分错误 |
| 自动纠错错误 | 键盘 | 将正确输入“纠正”成错误内容 |
| 截断与缺失 | 两者 | 语音句子被切断、键盘输入被粘贴丢内容 |
在论文和研究语境里,这些扰动通常会用更技术化的方式描述,比如字符替换率、插入率、删除率、同音词替换率等。工程上做评测时,我们一般会人为构造这些扰动,控制扰动强度,再观察 Agent 的表现变化。
2.3 扰动的本质:信息量受损
无论扰动形式怎么变,本质都是同一个问题:用户输入的有效信息量下降了。有些扰动是“局部模糊”(比如一个字错了),有些是“关键信息错误”(比如数字、日期、订单号错了),有些是“语义反转”(比如“不要删除”被识别成“要删除”)。
对 Agent 来说,最危险的不是第一种,而是后面两种——尤其是语义反转和关键参数错误,因为它们不会让 Agent 觉得“我没听懂”,反而会让 Agent 觉得“我懂了,然后执行了一个错的动作”。
3. 研究设计拆解:对比实验怎么做的
做这一类研究,核心是设计一套可对比的实验。虽然不同论文的具体设计会有差异,但通常包括以下几个部分。
3.1 任务类型选择
要评估输入扰动对 LLM Agent 的影响,首先得选一组能体现 Agent 能力的任务。典型任务包括:
- 工具调用类:通过指令调用 API,参数需要从自然语言中精确抽取。
- 信息检索类:从文档或数据库中找到指定记录。
- 操作指令类:执行增删改查操作,操作对象和条件都来自用户指令。
- 多步规划类:用户给出一个模糊目标,Agent 需要自己规划步骤并执行。
选择这些任务的原因是它们对参数准确率敏感,容易体现扰动的影响。如果你只是做一个“写诗”任务,扰动的影响就很难量化。
3.2 语音扰动的构造方式
语音扰动的构造可以分成“真实采集”和“仿真模拟”两类。
真实采集需要真人录音,成本高,但最接近真实场景。仿真模拟则是在已有语音样本上叠加噪音、变速、变调,或者直接使用一个 ASR 模型,把语音转成带有错误倾向的文本。
在文本层面模拟语音扰动时,常见做法是构造同音词替换。例如中文里“周五”与“州五”、“账号”与“张浩”等。通过混淆集合(confusion set)把原句中的词语替换为音近词,再送给 LLM。
3.3 键盘扰动的构造方式
键盘扰动通常基于编辑距离模拟,常见策略包括:
- 字符替换:把某个字符替换为键盘上相邻的字符,比如把“d”替换成“f”。
- 字符删除:随机删除句中的一个字符。
- 字符插入:随机插入相邻字符。
- 字符换位:交换相邻两个字符的位置。
- 输入法候选词替换:在拼音输入时选了错误的候选词。
模拟时通常会控制扰动比例,比如替换 5%、10%、20% 的字符,用来观察扰动强度与 Agent 表现之间的关系。
3.4 评价指标体系
对比研究不能只看“文本是否正确”,还要看“任务是否完成”。常用指标包括:
- 文本准确率:扰动后文本与原始文本的字面相似度。
- 意图识别准确率:Agent 是否正确理解了用户意图。
- 参数抽取准确率:Agent 是否从扰动词中抽取了正确的参数。
- 任务完成率:Agent 是否成功完成任务(最终指标)。
- 错误执行率:Agent 是否正确执行了动作——注意“错误执行”比“不执行”更严重。
有趣的是,文本准确率和任务完成率往往不是线性关系。有时候文本看起来只错了一个字,任务就完全失败了;有时候文本错了一大段,Agent 反而能靠上下文猜出意图。这正是研究扰动影响的价值所在。
4. 典型扰动案例分析
下面用几个具体案例说明扰动如何在链路中传导。
4.1 语音扰动案例
用户原始意图:
帮我预订明天下午3点的会议室 A201经过 ASR 识别后的可能结果:
帮我预订明天下午3点的会议室 A2O1“A201”变成了“A2O1”,字母“O”替换了数字“0”。在语音中,“O”和“0”发音相近,这是非常典型的 ASR 错误。Agent 在抽取会议室编号时拿到“A2O1”,调用会议室系统 API 就会查询失败,或者更糟——查询到一个不存在的编号后“猜”了一个相近的会议室。
另一种情况是数字本身听错:
明天下午3点 → 明天下午3件语气助词“点”被识别成“件”,虽然语义上人类能理解,但参数抽取时可能把“件”混入时间字段。
4.2 键盘扰动案例
用户原始输入:
删除用户ID为1024的所有订单手误后可能变成:
删除用户ID为1204的所有订单“1024”和“1204”在键盘上并不相邻,但手指输入顺序颠倒很常见。这种错误在纯文本上看只是一个数字位换位,但删除操作的目标从用户 1024 变成了用户 1204,结果无法挽回。
中文输入法场景则更特别。如果是拼音输入,用户打“yonghu”想表达“用户”,候选词里可能有“用户”和“拥护”,一旦选错,句子就变成:
删除拥护ID为1024的所有订单这种错误对人和 LLM 来说都很难通过上下文纠正,因为“拥护”和“删除”的组合在语法上不冲突,模型可能直接照做。
4.3 扰动传导到 Agent 后的影响链条
扰动在 Agent 内部的影响不是一步到位的。完整链条是:
- 用户文本被扰动,信息出现偏差。
- Agent 的意图识别模块可能将意图归类为正确类型或错误类型。
- 参数抽取模块从错误的文本中抽取参数,得到错误的槽值。
- 动作规划基于错误的槽值生成计划。
- 工具调用使用错误的参数,返回错误结果或直接报错。
- Agent 基于错误结果生成最终回复。
在实际系统中,第 5 步尤其危险。如果工具调用返回报错,Agent 可能会尝试“猜测”修正,而不是直接向用户确认。一旦猜测方向错误,整个任务就偏移了。
5. 快速搭建一个输入扰动评测 Demo
接下来我们从工程角度,搭建一个最小可用的“输入扰动评测”Python 脚本。这个脚本可以:
- 模拟键盘扰动(字符级编辑);
- 模拟语音扰动(同音词替换);
- 将扰动后的文本发送给 LLM;
- 对比 Agent 在原始输入和扰动输入下的表现。
这个 Demo 的目的是让你理解评测思路,实际使用时可以根据自己的业务语言、任务类型和 LLM 接口进行扩展。
5.1 环境准备
# 建议使用 Python 3.9+ pip install openai这里以 OpenAI 的调用方式为例。如果你的项目使用的是其他模型服务商,只需要替换客户端实例和chat.completions.create这部分即可,整体校验逻辑不变。
5.2 构造键盘扰动模拟器
键盘扰动使用字符级编辑操作来模拟。这是最接近真实手误的模拟方式。
import random import string def simulate_keyboard_perturbation(text, perturb_prob=0.1, seed=42): """ 模拟键盘输入扰动。 扰动类型:字符替换、删除、插入、相邻交换。 实际使用时,可以只保留部分扰动类型,或者调整概率。 """ rng = random.Random(seed) chars = list(text) keyboard_neighbors = { 'a': 'sqwz', 's': 'awedx', 'd': 'serfc', 'f': 'drtvg', 'g': 'ftyhb', 'h': 'gyujn', 'j': 'huikm', 'k': 'jiol', 'l': 'kop', 'q': 'wsa', 'w': 'qesad', 'e': 'wrsdf', 'r': 'etdfg', 't': 'ryfgh', 'y': 'tughj', 'u': 'yihjk', 'i': 'uojkl', 'o': 'ipkl', 'p': 'ol', 'z': 'asx', 'x': 'zsdc', 'c': 'xdfv', 'v': 'cfgb', 'b': 'vghn', 'n': 'bhjm', 'm': 'njk' } result = [] for ch in chars: if ch.lower() in keyboard_neighbors and rng.random() < perturb_prob: neighbors = keyboard_neighbors[ch.lower()] # 只替换为相邻字符,不改变大小写逻辑 new_ch = rng.choice(neighbors) result.append(new_ch if ch.islower() else new_ch.upper()) else: result.append(ch) return ''.join(result) if __name__ == '__main__': text = "删除用户ID为1024的所有订单" print("原始输入:", text) print("键盘扰动:", simulate_keyboard_perturbation(text, 0.15, seed=7))运行一次可能得到类似输出:
原始输入: 删除用户ID为1024的所有订单 键盘扰动: 删除用户ID为1026的所有订羊在这个示例里,“4”被替换为“6”,“单”被替换为“羊”。这类扰动传给 LLM 后,Agent 仍然可能抽取“1026”作为用户 ID,从而查错用户。
5.3 构造语音扰动模拟器
语音扰动在文本层面主要模拟同音字混淆。中文里同音现象非常普遍,这是 ASR 最常见的错误来源。
# 简单同音字混淆表,实际使用时应根据你的业务领域扩充 homophone_map = { '订': '定', '单': '单', '账': '帐', '户': '护', '三': '山', '点': '典', '会': '汇', '议': '义', '室': '市', '用': '拥', '户': '护', '删': '山', '除': '厨', '所': '锁', '有': '又', } def simulate_voice_perturbation(text, perturb_prob=0.15, seed=42): """ 模拟语音识别后的同音字替换。 """ rng = random.Random(seed) chars = list(text) result = [] for ch in chars: if ch in homophone_map and rng.random() < perturb_prob: result.append(homophone_map[ch]) else: result.append(ch) return ''.join(result) if __name__ == '__main__': text = "删除用户ID为1024的所有订单" print("原始输入:", text) print("语音扰动:", simulate_voice_perturbation(text, 0.2, seed=3))注意:这个同音字表只是一个最小示例。真实的语音扰动模拟最好基于发音词典,对音节进行替换,效率更高也更加合理。如果项目中有现成的 ASR 服务,直接对真实语音样本做识别,把识别结果当作扰动文本,效果会更好。
5.4 调用 LLM 对比效果
模拟出扰动文本后,我们需要让 LLM 在“原始输入”和“扰动输入”下分别执行同一个任务,然后对比结果。
from openai import OpenAI client = OpenAI( api_key="YOUR_API_KEY", # 替换为你的 API Key base_url="YOUR_BASE_URL" # 可选,自建代理或兼容服务时配置 ) SYSTEM_PROMPT = """你是一个订单管理 Agent。请从用户指令中提取以下参数: - action: delete(删除)或 query(查询) - user_id: 整数,表示用户ID - scope: all(所有订单)或 specified(指定订单) 如果用户指令中包含无法识别的参数,不要猜测,请输出 UNKNOWN。 只输出 JSON,不要输出其他内容。""" def run_agent(user_input): response = client.chat.completions.create( model="gpt-4o-mini", # 按实际可用模型调整 messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_input} ], temperature=0 ) return response.choices[0].message.content if __name__ == '__main__': original = "删除用户ID为1024的所有订单" keyboard_perturbed = simulate_keyboard_perturbation(original, 0.15, seed=7) voice_perturbed = simulate_voice_perturbation(original, 0.2, seed=3) tests = { "原始输入": original, "键盘扰动": keyboard_perturbed, "语音扰动": voice_perturbed } for label, text in tests.items(): print(f"=== {label} ===") print(f"输入文本: {text}") print(f"Agent 输出: {run_agent(text)}") print()运行后,你大概率会看到类似的结果:原始输入下 Agent 能正确提取参数;键盘扰动下如果 user_id 被改坏,Agent 可能提取出错误的 ID;语音扰动下如果“删除”被替换,Agent 可能无法确定 action。这个评测脚本的核心逻辑就是用一致的任务和一致的评测标准,暴露不同扰动类型的差异。
5.5 评测结果如何记录
工程化评测时,不能只跑几条用例,需要批量数据 + 结果落盘。一个简单的做法是准备一个包含原始指令的 JSONL 文件,然后循环执行扰动、调用 Agent、记录结果、计算指标。
import json def evaluate_from_file(filepath="test_cases.jsonl"): """ 从 JSONL 文件中读取测试用例,逐条执行扰动与评测。 JSONL 每行格式: {"id": "001", "instruction": "删除用户ID为1024的所有订单"} """ results = [] with open(filepath, "r", encoding="utf-8") as f: for line in f: case = json.loads(line) original = case["instruction"] for perturb_type in ["keyboard", "voice"]: if perturb_type == "keyboard": perturbed = simulate_keyboard_perturbation(original, seed=42) else: perturbed = simulate_voice_perturbation(original, seed=42) agent_output = run_agent(perturbed) results.append({ "case_id": case["id"], "perturb_type": perturb_type, "input": perturbed, "agent_output": agent_output }) with open("evaluation_results.json", "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"评测完成,共记录 {len(results)} 条结果")你可以在评测后人工或使用规则判断 Agent 输出是否正确,然后统计不同扰动类型下的参数正确率和任务完成率。
6. 对比结论与应用启示
6.1 输入方式本身会改变 Agent 表现
从研究方向和常见实验现象来看,语音输入和键盘输入在扰动表现上有明显差异。语音输入的问题更偏向“语义层”——同音字、近音词替换会让文本看起来通顺但含义偏移;键盘输入的问题更偏向“字符层”——单字错误、键位偏差虽然局部严重,但上下文往往还能提供纠错线索。理论上,LLM 对字符级错误的抗性比语义级错误更强,因为字符周围有大量上下文可以推测原词;而语义级错误(例如同音词)在中文里往往难以察觉,因为句子本身仍然是“通顺”的。
这也意味着:当任务对关键参数(ID、日期、编号)敏感时,语音输入可能更危险,因为数字和字母的听写错误在语义上无法自我纠正。
6.2 扰动并非均匀地影响所有任务
扰动的影响和任务类型强相关。对于一个“查询天气”的任务,即使城市名识别错误,Agent 也可能根据 IP 或上下文猜出城市;但对于“删除订单”“转账”“修改配置”这类高风险操作,参数错误就是致命的。因此,任何输入扰动研究都应该区分任务的风险等级,避免一概而论。
6.3 对工程化 Agent 的核心启示
- 不要把“文本识别准确率”当作唯一指标,要关注 Agent 的最终动作是否正确。
- 高风险任务要做关键参数确认,这是最简单也最有效的抗扰动手段。
- 输入链路要设计成“可验证”的:语音输入时把 ASR 文本回显给用户;键盘输入时在关键操作前高亮显示解析出的参数。
- 单一输入方式必然有盲区,多模态输入可以互相纠偏。
7. 构建鲁棒 Agent 输入链路的最佳实践
7.1 语音输入的兜底与验证策略
语音输入链路中,ASR 文本是 Agent 的唯一信息来源。要提升抗扰动能力,建议:
- 对关键参数做独立 ASR:不要只依赖整句识别结果,可以单独对数字串、时间、订单号做二次识别。
- 引入置信度分数:ASR 结果一般会附带置信度,低置信度的字段应该触发确认流程。
- 大词汇量纠错:在业务领域内维护常见的易混淆词表,当 ASR 结果命中混淆词时,结合上下文做规则纠偏。
- 提供“修改上一条”机制:语音交互中用户修正错误成本高,Agent 应该支持用户快速指出“刚才的编号不对,是1024不是1026”。
7.2 键盘输入的防错与冲突消解
键盘输入看似更可靠,但 IME 和手误带来的问题同样不可忽视:
- 对数字、字母 ID 使用分段输入和显式校验。比如当用户输入“ID 为 1024”时,Agent 可以提示“你确定用户 ID 是 1024 吗?请确认”。
- 对高风险操作引入二次确认,不能依赖上下文猜测。
- 对输入法候选词错误,可以考虑使用“中文+英文+数字”的混合输入模式,或者在 UI 层提供可点击确认的候选卡片。
7.3 多模态融合:把打字和说话结合
真实产品中,用户不会只用一种输入方式。语音输入快速但容易出错,键盘输入准确但成本高。比较好的设计是:
- 用户先语音输入,Agent 转成文本。
- 文本以可编辑的方式回显(自动填充到输入框)。
- 用户可以直接修改文本或语音发起修改。
- 高危操作启动前再次用视觉方式展示解析参数。
这种“语音起草 + 键盘确认”的融合模式,在 Otter、Notion AI 等产品中已经比较常见。它的本质就是利用键盘的准确性来纠正语音的错误,利用语音的效率来降低打字成本。
7.4 持续评测体系建设
抗扰动能力不是一次模型升级就能解决的,需要建立持续评测机制。
建议按以下方式搭建:
- 建立领域测试集:包含 1000 条以上真实业务指令,覆盖常见参数类型、操作类型和失败模式。
- 分级注入扰动:对每条指令分别做 5%、10%、20% 强度的扰动,观察任务完成率曲线。
- 回归对比:每次 Agent 提示词、模型版本、ASR 模型更新后,重新跑一遍评测,对比基线。
- 线上日志回流:把线上用户纠错行为、失败对话、用户重复输入的数据回流到评测集,持续扩充。
这套评测体系和代码测试、单元测试一样,应该成为 Agent 项目持续集成的一部分。
8. 总结与下一步
回到标题的问题:Should we type or talk to LLM agents?严格来说,这个问题没有统一答案。两种输入方式各有各的扰动模式,语音输入主要难在 ASR 与语义性错误,键盘输入主要难在字符级误操作与 IME 偏差。真正决定 Agent 能不能稳定工作的,不是选哪一种输入方式,而是你为输入噪声做了多少准备。
核心要点总结如下:
- 输入扰动是 LLM Agent 从“实验室可用”走向“生产可用”必须正视的问题。
- 研究输入扰动不能只看文本相似度,要看任务完成率和错误执行率。
- 语音扰动多为语义层错误,隐蔽性强;键盘扰动多为字符层错误,可局部识别。
- 工程上最有效的抗扰动手段是对关键参数做二次确认、建立领域混淆词表、引入多模态输入互补。
- 评测体系要持续建设,把线上失败案例回流到测试集中。
如果你正在做相关项目,下一步可以从三件事开始:一是把本文的扰动模拟器跑通,积累一份自己的评测数据;二是梳理你业务里的高危参数类型,设计确认流程;三是尝试“语音+键盘”混合输入的产品形态。输入扰动研究不是一次性的学术问题,它对 Agent 产品质量的影响会随着业务复杂度的提升越来越明显。希望这篇文章能给你提供一个搭框架、做评测、防踩坑的起点。