1. 这篇文章真正要解决的问题你是否遇到过这样的场景你精心设计了一个复杂的对话系统希望大语言模型LLM能根据用户意图精准地调用各种工具如查询天气、搜索数据库、发送邮件却发现模型要么“自作主张”地生成了不存在的工具调用要么在应该调用工具时犹豫不决输出一堆无用的解释性文本这种“幻觉”与“冗余”并存的困境是当前基于LLM的Agent或工具调用应用开发中最令人头疼的可靠性问题之一。今天要探讨的这篇论文《Keep It Real: A Schema-Level Deferral Channel for Concise LLM Dialogue》[PDF]正是为了解决这个核心痛点。它提出的不是一个全新的模型架构而是一个极其精巧的工程化通信协议。简单来说它在大模型和外部系统之间建立了一条“专用热线”让模型可以只说两个字“你来”。这条热线就是Schema-Level Deferral Channel。这篇文章要解决的不是“如何让LLM变得更聪明”而是“如何让已经足够聪明的LLM在需要与外部世界交互时变得更可靠、更简洁、更可控”。对于正在构建生产级AI应用、RAG系统或复杂Agent的开发者而言理解并应用这个思想可能比等待下一个GPT-5的发布更具即时价值。我们将从原理拆解、场景对比、到具体的代码实现完整呈现如何将这一学术思想落地到你的工程实践中。2. 基础概念与核心原理在深入之前我们需要厘清几个关键概念否则很容易迷失在术语中。1. 工具调用与函数调用这是当前LLM与外部世界交互的主流范式。开发者预先定义好一系列“工具”Tool或“函数”Function的规格说明Schema通常以JSON格式描述其名称、参数和用途。LLM在对话中若判断需要调用某个工具则会生成一个符合该Schema的结构化输出如{name: get_weather, arguments: {city: 北京}}。系统捕获此输出后执行真正的函数并将结果返回给LLM由LLM组织成自然语言回复给用户。2. 核心问题幻觉与冗余幻觉调用LLM可能生成一个格式正确但工具名或参数完全虚构的调用导致后端执行失败。冗余解释LLM在决定调用工具时经常附带大量自然语言解释如“用户想查询天气我需要调用get_weather工具参数是...”。这些解释对后端系统是无用的噪音增加了解析的复杂性和出错概率。3. Deferral延迟/移交的核心思想“Deferral”在此处的含义是“移交决策权”。传统模式下LLM需要完整生成工具调用的具体内容。而Deferral模式下LLM只需要做出一个二值决策“这个问题需要调用外部工具吗”如果需要它不再生成具体的调用细节而是发出一个移交信号并将原始用户问题和对话上下文一同“移交”给一个专精于此的、确定性更高的下游模块来处理。这个下游模块被称为“Deferral System”。4. Schema-Level Deferral Channel模式级移交通道这是论文提出的具体实现方案。“Schema-Level”指的是这个通道的“协议”是基于预先定义的工具模式Schema的。它不是让LLM输出“调用A工具”而是输出一个指向某个Schema的引用Reference。你可以把它想象成传统方式LLM说“请执行get_weather(city‘北京’)”。Schema-Level Deferral方式LLM说“这个问题属于‘天气查询’类对应Schema ID:weather_query原始问题是‘北京天气怎么样’请处理。”这个“通道”是逻辑上的在实现上通常就是对话响应中的一个特殊字段或一种特定格式的消息。其最大优势在于极大简化了LLM的生成任务。LLM无需记忆复杂的参数格式只需做分类这个问题对应哪个Schema生成负担和出错率显著降低。所有复杂的参数解析、验证、默认值填充等工作都移交给了后端的确定性系统。3. 为什么需要它与传统方案的对比为了更直观地理解其价值我们通过一个具体场景来对比三种方案。场景用户输入“帮我订一张明天从上海到北京下午出发的机票”。方案一纯自然语言输出早期方案LLM输出“好的我将为您预订明天从上海到北京下午出发的机票。请问您对航空公司、舱位或具体时间有偏好吗”问题输出是自由文本后端系统无法自动解析并执行。需要额外训练一个NLU模型或编写复杂规则来提取意图和实体流程冗长且不通用。方案二标准函数调用当前主流工具Schema:{ name: book_flight, description: 预订航班, parameters: { type: object, properties: { departure_city: {type: string}, arrival_city: {type: string}, date: {type: string, format: date}, time_preference: {type: string, enum: [morning, afternoon, evening]} }, required: [departure_city, arrival_city, date] } }期望的LLM输出:{ name: book_flight, arguments: { departure_city: 上海, arrival_city: 北京, date: 2023-10-28, time_preference: afternoon } }实际可能发生的LLM输出幻觉与冗余:{ name: book_flight_ticket, // 工具名拼写错误或臆造 arguments: { from: 上海, // 参数名与Schema定义不符 to: 北京, departure_date: 明天, // 日期格式不符合date要求 preference: 下午 } }或者LLM可能输出混合内容我需要调用book_flight工具。用户想从上海飞北京时间是明天下午。所以参数是departure_city: 上海, arrival_city: 北京, date: 2023-10-28, time_preference: afternoon。 {name: book_flight, arguments: {...}}后端需要先剥离自然语言再解析JSON容错性差。方案三Schema-Level DeferralLLM输出简化示意:{ deferral_signal: true, schema_reference: book_flight, user_query: 帮我订一张明天从上海到北京下午出发的机票, conversation_context: ... // 可选当前对话历史 }后端处理接收到上述信号。根据schema_reference: “book_flight”定位到对应的工具Schema和专用的参数解析器。解析器可以是规则、小模型或另一个LLM专门处理user_query提取出符合Schema的精确参数。执行book_flight函数。对比优势LLM任务简化从“生成结构化调用”降级为“识别意图分类”显著提高准确率。输出标准化LLM输出格式固定deferral_signal,schema_reference,user_query易于解析无冗余文本。解析专业化参数解析任务交给专门模块可以使用更合适的技术如精准的NER模型、规则引擎不受LLM幻觉影响。系统更健壮即使LLM的schema_reference指错了也只是一个分类错误不会产生畸形的参数组合错误更容易被捕获和处理。4. 环境准备与前置条件要将此理念付诸实践你需要一个基础的LLM应用开发环境。以下是一个基于Python的通用环境设置我们将使用OpenAI API或兼容API作为LLM并构建一个简单的演示系统。核心环境与工具Python 3.9确保你的Python环境已就绪。OpenAI Python SDK用于调用GPT系列模型。pip install openaiPydantic用于数据验证和Schema定义这是实现类型安全的关键。pip install pydanticFastAPI (可选)如果你想构建一个Web服务来演示整个流程。pip install fastapi uvicorn关键概念准备你已经熟悉基本的LLM API调用。你理解JSON Schema的基本结构。你有一个可用的LLM API密钥如OpenAI、Azure OpenAI或本地部署的兼容API。项目结构预览schema_deferral_demo/ ├── schemas/ # 存放工具Schema定义 │ └── weather.py ├── parsers/ # 存放专用参数解析器 │ └── weather_parser.py ├── llm_client.py # LLM交互封装 ├── deferral_system.py # 移交系统核心逻辑 └── main.py # 主程序入口5. 核心流程拆解与系统设计一个完整的Schema-Level Deferral系统包含以下几个核心组件其工作流程如下图所示逻辑描述用户输入 | v [LLM 前端] | (判断是否需要移交移交到哪个Schema) v 生成 Deferral 信号 (含 schema_reference user_query) | v [Deferral 路由] | (根据 schema_reference 分发) v [专用参数解析器] (Parser for Schema X) | (精准解析 user_query) v 结构化参数 (符合Schema X) | v [工具执行器] (Executor for Tool X) | v 执行结果 | v [结果格式化] (可选由LLM或模板生成最终回复) | v 返回给用户步骤详解Schema定义为每个外部工具/能力定义清晰的、机器可读的接口规范。这不仅是给LLM看的更是后端系统的合约。LLM提示工程设计System Prompt和Few-shot Examples引导LLM学会使用Deferral Channel。核心是教会它“当你识别出用户请求属于某个已知工具类别时不要尝试生成具体参数只需告诉我类别编号和原始问题。”Deferral信号解析后端服务需要一个轻量级路由根据LLM返回的schema_reference将任务派发到对应的专用参数解析器。专用参数解析这是系统的“专业工人”。每个解析器只负责从自然语言中提取对应Schema所需的参数。这里可以使用规则正则表达式、小模型如训练一个NER模型甚至另一个专门调优的小型LLM。因为任务范围缩小其精度和可靠性远高于通用LLM。工具执行与返回解析出合规参数后调用真实的工具函数执行并将结果返回。6. 完整示例与代码实现让我们用一个“查询天气”和“创建日历事件”的混合场景来构建一个最小可行系统。6.1 第一步定义工具Schema使用Pydantic我们首先用Pydantic定义两个工具的Schema这能提供强大的类型检查和序列化能力。文件schemas/weather.pyfrom pydantic import BaseModel, Field from typing import Optional from enum import Enum class TimePreference(str, Enum): MORNING morning AFTERNOON afternoon EVENING evening class WeatherQuerySchema(BaseModel): 查询天气的工具Schema schema_reference: str Field(defaultweather_query, descriptionSchema标识符) city: str Field(..., description要查询天气的城市名称如‘北京’、‘New York’) date: Optional[str] Field(None, description查询日期格式YYYY-MM-DD默认为今天) time_preference: Optional[TimePreference] Field(None, description时间偏好如‘上午’、‘下午’) class Config: schema_extra { example: { city: 北京, date: 2023-10-27, time_preference: afternoon } }文件schemas/calendar.pyfrom pydantic import BaseModel, Field from datetime import datetime from typing import List, Optional class CalendarEventSchema(BaseModel): 创建日历事件的工具Schema schema_reference: str Field(defaultcreate_calendar_event, descriptionSchema标识符) title: str Field(..., description事件标题) start_time: datetime Field(..., description事件开始时间) end_time: Optional[datetime] Field(None, description事件结束时间默认为开始时间后1小时) attendees: List[str] Field(default_factorylist, description参与者邮箱列表) location: Optional[str] Field(None, description事件地点) class Config: schema_extra { example: { title: 团队周会, start_time: 2023-10-28T14:00:00, attendees: [alicecompany.com, bobcompany.com] } }6.2 第二步构建专用参数解析器每个Schema对应一个解析器。这里为了演示我们使用规则启发式方法在实际项目中可替换为更强大的模型。文件parsers/weather_parser.pyimport re from datetime import datetime, timedelta from schemas.weather import WeatherQuerySchema, TimePreference from typing import Optional class WeatherQueryParser: 专用于解析天气查询的解析器 # 简单的城市名映射实际应用需更全面的列表 CITY_ALIAS { 帝都: 北京, 魔都: 上海, 深圳: 深圳市, 广州: 广州市 } staticmethod def parse(user_query: str) - WeatherQuerySchema: # 1. 提取城市 city None # 简单关键词匹配生产环境应用需用更精确的NER for possible_city in [北京, 上海, 广州, 深圳, 杭州, 成都]: if possible_city in user_query: city possible_city break if not city: # 尝试别名映射 for alias, real_name in WeatherQueryParser.CITY_ALIAS.items(): if alias in user_query: city real_name break if not city: city 北京 # 默认值实际应返回错误或要求澄清 # 2. 提取日期 date_str None today datetime.now().date() if 明天 in user_query: date_str (today timedelta(days1)).isoformat() elif 后天 in user_query: date_str (today timedelta(days2)).isoformat() elif 今天 in user_query or 现在 in user_query: date_str today.isoformat() else: # 尝试匹配YYYY-MM-DD格式 date_pattern r(\d{4})-(\d{1,2})-(\d{1,2}) match re.search(date_pattern, user_query) if match: date_str match.group(0) # 3. 提取时间偏好 time_pref None if 上午 in user_query or 早上 in user_query or 早晨 in user_query: time_pref TimePreference.MORNING elif 下午 in user_query: time_pref TimePreference.AFTERNOON elif 晚上 in user_query or 傍晚 in user_query: time_pref TimePreference.EVENING # 4. 构建并返回Schema实例 return WeatherQuerySchema( citycity, datedate_str, time_preferencetime_pref ) # 测试解析器 if __name__ __main__: test_query 上海明天下午的天气怎么样 result WeatherQueryParser.parse(test_query) print(result.json(indent2)) # 输出: {schema_reference: weather_query, city: 上海, date: 2023-10-28, time_preference: afternoon}文件parsers/calendar_parser.py# 类似地实现日历事件的解析器可能涉及更复杂的时间表达式解析如“下周一上午十点” # 可以使用 dateparser 或 duckling 库。此处省略详细实现仅展示结构。6.3 第三步设计LLM提示词与Deferral信号格式这是让LLM学会使用移交通道的关键。我们需要在System Prompt中明确指令和格式。文件llm_client.pyimport openai from typing import Dict, Any, Optional import json # 假设已设置 openai.api_key your-api-key class DeferralLLMClient: def __init__(self, model: str gpt-3.5-turbo): self.model model # 定义可移交的Schema列表用于构造提示词 self.available_schemas [ { schema_reference: weather_query, description: 当用户询问某个城市当前或未来的天气情况时使用。 }, { schema_reference: create_calendar_event, description: 当用户请求创建新的日历或日程事件时使用。 }, # ... 可以添加更多 ] def _build_system_prompt(self) - str: schemas_desc \n.join([f- {s[schema_reference]}: {s[description]} for s in self.available_schemas]) prompt f 你是一个专业的对话助手并且可以调用外部工具。为了确保调用准确且高效请遵循以下规则 1. 当用户请求明显属于以下**已知工具类别**之一时你必须使用“移交通道”响应而不是尝试自己生成工具调用的具体参数。 2. 已知工具类别列表 {schemas_desc} 3. 你的响应必须是严格的JSON格式且只包含以下两个字段 - deferral_signal: 布尔值。如果需要移交则为 true如果可以直接用自然语言回答则为 false。 - schema_reference: 字符串。仅当 deferral_signal 为 true 时存在值为上述列表中的某个 schema_reference。 - direct_response: 字符串。仅当 deferral_signal 为 false 时存在包含你的自然语言回答。 - user_query: 字符串。用户原始输入的问题文本。当 deferral_signal 为 true 时必须提供。 4. 示例 用户“北京今天热吗” 你{{ deferral_signal: true, schema_reference: weather_query, user_query: 北京今天热吗 }} 用户“你好请介绍一下你自己。” 你{{ deferral_signal: false, direct_response: 我是AI助手可以帮你查询天气或管理日历。 }} 请严格按此格式响应。不要添加任何其他解释或字段。 return prompt def get_deferral_decision(self, user_message: str, conversation_history: Optional[list] None) - Dict[str, Any]: 调用LLM获取移交决策。 messages [ {role: system, content: self._build_system_prompt()}, ] if conversation_history: messages.extend(conversation_history) messages.append({role: user, content: user_message}) try: response openai.ChatCompletion.create( modelself.model, messagesmessages, temperature0.1, # 低温度确保输出稳定 max_tokens200 ) content response.choices[0].message.content.strip() # 解析JSON响应 return json.loads(content) except json.JSONDecodeError as e: print(fLLM响应不是合法JSON: {content}) # 降级处理返回一个默认的非移交响应 return {deferral_signal: False, direct_response: 我遇到了一个内部错误请重新尝试。} except Exception as e: print(f调用LLM API失败: {e}) return {deferral_signal: False, direct_response: 服务暂时不可用。}6.4 第四步实现Deferral路由与执行系统这是整个系统的中枢负责协调LLM决策、解析器调用和工具执行。文件deferral_system.pyfrom llm_client import DeferralLLMClient from parsers.weather_parser import WeatherQueryParser from schemas.weather import WeatherQuerySchema from typing import Dict, Any, Callable import json # 模拟的工具执行函数 def execute_weather_query(params: WeatherQuerySchema) - str: 模拟执行天气查询 # 这里应该是调用真实天气API return f已查询{params.city}在{params.date or 今天}{params.time_preference or 全天}的天气晴25℃。 def execute_calendar_event(params: Any) - str: 模拟执行创建日历事件 return f已创建事件{params.title} class DeferralSystem: def __init__(self): self.llm_client DeferralLLMClient() # 注册Schema解析器映射 self.parser_registry: Dict[str, Callable] { weather_query: WeatherQueryParser.parse, # create_calendar_event: CalendarEventParser.parse, } # 注册工具执行器映射 self.executor_registry: Dict[str, Callable] { weather_query: execute_weather_query, # create_calendar_event: execute_calendar_event, } def process_query(self, user_query: str) - str: 处理用户查询的主流程 # 1. 咨询LLM是否需要移交移交到哪个Schema llm_decision self.llm_client.get_deferral_decision(user_query) print(fLLM决策: {json.dumps(llm_decision, indent2, ensure_asciiFalse)}) # 2. 根据决策处理 if not llm_decision.get(deferral_signal, False): # 无需移交直接返回LLM的自然语言响应 return llm_decision.get(direct_response, 我不知道如何回答这个问题。) schema_ref llm_decision.get(schema_reference) original_query llm_decision.get(user_query, user_query) if not schema_ref or schema_ref not in self.parser_registry: return f错误LLM指示移交到未知的Schema {schema_ref}。 # 3. 路由到专用解析器 parser_func self.parser_registry[schema_ref] try: parsed_params parser_func(original_query) print(f解析后的参数: {parsed_params}) except Exception as e: return f解析用户请求时出错{e}。请尝试更清晰地表述你的需求。 # 4. 路由到工具执行器 executor_func self.executor_registry.get(schema_ref) if not executor_func: return f错误Schema {schema_ref} 没有注册执行器。 try: result executor_func(parsed_params) return result except Exception as e: return f执行工具时出错{e}。6.5 第五步主程序与交互演示文件main.pyfrom deferral_system import DeferralSystem def main(): system DeferralSystem() test_queries [ 你好你是谁, 上海明天下午的天气怎么样, 帮我看看北京今天热不热。, 下周一上午十点安排一个团队会议。, # 这个例子中LLM应识别为create_calendar_event但我们未实现解析器会走错误分支 巴黎的天气呢, # 城市不在我们的简单列表中解析器会使用默认值 ] for query in test_queries: print(f\n{*50}) print(f用户输入: {query}) print(f{-*50}) response system.process_query(query) print(f系统回复: {response}) print(f{*50}) if __name__ __main__: main()7. 运行结果与效果验证运行python main.py你将会看到类似以下的输出具体内容因LLM响应可能略有不同 用户输入: 你好你是谁 -------------------------------------------------- LLM决策: { deferral_signal: false, direct_response: 我是AI助手可以帮你查询天气或管理日历。 } 系统回复: 我是AI助手可以帮你查询天气或管理日历。 用户输入: 上海明天下午的天气怎么样 -------------------------------------------------- LLM决策: { deferral_signal: true, schema_reference: weather_query, user_query: 上海明天下午的天气怎么样 } 解析后的参数: schema_referenceweather_query city上海 date2023-10-28 time_preferenceafternoon 系统回复: 已查询上海在2023-10-28下午的天气晴25℃。 用户输入: 帮我看看北京今天热不热。 -------------------------------------------------- LLM决策: { deferral_signal: true, schema_reference: weather_query, user_query: 帮我看看北京今天热不热。 } 解析后的参数: schema_referenceweather_query city北京 date2023-10-27 time_preferenceNone 系统回复: 已查询北京在今天全天的天气晴25℃。 用户输入: 下周一上午十点安排一个团队会议。 -------------------------------------------------- LLM决策: { deferral_signal: true, schema_reference: create_calendar_event, user_query: 下周一上午十点安排一个团队会议。 } 系统回复: 错误Schema create_calendar_event 没有注册执行器。 用户输入: 巴黎的天气呢 -------------------------------------------------- LLM决策: { deferral_signal: true, schema_reference: weather_query, user_query: 巴黎的天气呢 } 解析后的参数: schema_referenceweather_query city北京 dateNone time_preferenceNone 系统回复: 已查询巴黎在今天全天的天气晴25℃。 效果验证要点决策准确性LLM成功将天气查询请求识别为weather_querySchema并将寒暄类问题识别为无需移交。输出简洁性LLM的输出是干净、标准的JSON没有冗余的自然语言描述。解析专业性专用解析器成功从“上海明天下午”中提取了城市、日期和时间偏好即使查询表述与Schema参数名不完全一致。错误处理当遇到未实现的Schemacreate_calendar_event或未知城市时系统有明确的错误反馈或降级策略而不是崩溃或产生幻觉输出。流程解耦LLM、解析器、执行器各司其职修改或增强其中任一模块如更换更强大的解析器不会影响其他部分。8. 常见问题与排查思路在实现和应用Schema-Level Deferral模式时你可能会遇到以下典型问题问题现象可能原因排查方式解决方案LLM始终不触发移交 (deferral_signal: false)1. System Prompt定义不清晰或示例不足。2. 用户查询意图模糊LLM自信度低。3. Temperature参数过高输出不稳定。1. 检查并优化System Prompt增加更多、更清晰的few-shot示例。2. 在Prompt中明确“已知工具类别”的边界。3. 将LLM调用的temperature设为0.1或更低。强化Prompt工程使用更强大的模型如GPT-4进行意图分类或在LLM前增加一个轻量级意图分类器。LLM移交到了错误的Schema1. Schema描述相似LLM难以区分。2. 用户查询包含多个潜在意图。1. 分析混淆的Schema对细化它们的描述使其更具区分度。2. 查看对话历史确认上下文是否清晰。1. 合并高度相似的Schema。2. 在Prompt中要求LLM在不确定时优先选择某个Schema或设计一个“澄清”流程。专用解析器提取参数错误1. 解析规则覆盖不全。2. 用户使用了非预期的表达方式。3. 实体识别NER不准确。1. 收集更多真实用户query测试解析器。2. 查看解析失败的具体case补充规则或训练数据。1. 使用更鲁棒的解析库如dateparser处理时间spaCy或FasterWhisper进行NER。2. 对于复杂Schema可以训练一个小的文本分类或序列标注模型。系统整体延迟增加1. 串行流程LLM决策 - 解析 - 执行。2. 解析器本身计算复杂。1. 使用异步调用。2. 对解析器和工具执行进行性能分析。1. 将LLM决策与后续解析/执行并行化在LLM返回移交信号后立即异步启动解析。2. 缓存常见的解析结果。如何处理LLM决策的不确定性LLM可能对边缘case的deferral_signal判断不准。在LLM输出中增加一个confidence字段或让LLM输出多个候选Schema。设置一个置信度阈值。低于阈值时不执行移交而是让LLM以自然语言要求用户澄清。9. 最佳实践与工程建议将论文思想转化为稳定可用的生产系统需要考虑更多工程细节Schema设计原则原子性每个Schema应代表一个最小、最原子的操作。避免设计“多功能”Schema。明确性参数描述要清晰无歧义使用枚举类型约束可选值。向后兼容新增参数时尽量设为可选避免破坏现有解析器。提示词工程优化动态Prompt根据已注册的Schema列表动态生成Prompt中的工具描述部分避免硬编码。多轮对话支持在user_query中附带精简的对话历史帮助LLM和解析器理解上下文。拒绝能力教导LLM识别完全超出系统能力范围的请求并礼貌拒绝而不是强行匹配到一个错误的Schema。解析器的分层设计第一层规则引擎处理格式规整、高确定性的查询如“查询北京天气”。第二层轻量级模型处理稍复杂的查询可使用微调的小型Transformer模型。第三层LLM兜底对于前两层都无法处理的复杂、模糊查询可以回退到使用一个小型LLM如ChatGLM-6B, Qwen-7B专门进行参数解析。此时它的任务范围已被大大缩小。系统的可观测性与监控日志记录详细记录LLM的决策、解析器的输入输出、工具执行结果和耗时。指标收集监控移交成功率、各Schema调用频率、解析错误率、端到端延迟。反馈循环建立机制将解析失败或用户修正的案例收集起来用于持续优化解析器和Prompt。安全与边界考虑输入验证在解析器输出后、执行器调用前必须用Pydantic等工具对参数进行严格的二次验证和清洗。权限控制在工具执行层实施基于用户/角色的权限检查确保“移交”不会越权。沙箱环境对于高风险操作如删除数据、调用外部API应在沙箱或严格受限的环境中执行。Schema-Level Deferral Channel 是一种极具启发性的系统设计模式。它不追求让LLM成为“全能专家”而是承认其长处语义理解、意图识别和短处结构化输出幻觉、冗余通过架构设计扬长避短。对于追求高可靠性、高可控性的AI应用开发者来说这种“让专业的模块做专业的事”的思路远比盲目追求更大、更通用的模型更为务实和有效。建议你在下一个涉及复杂工具调用的Agent项目中尝试引入这一设计思想从小处着手体验其带来的简洁与可靠。