1. 从“我知道”到“我知道我不知道”智能体可靠性的新挑战最近在折腾大语言模型的应用开发特别是围绕Function Calling函数调用这个核心能力构建一些自动化工作流。相信很多同行都踩过类似的坑你精心设计了一套工具函数满怀期待地交给模型去调用结果它信心满满地给你返回了一个完全错误的参数或者调用了一个风马牛不相及的函数。这种“一本正经地胡说八道”不仅让人哭笑不得更严重的是它会直接导致整个自动化流程的崩溃让所谓的“智能体”变得极不可靠。问题的根源恰恰在于当前大多数语言模型在Function Calling时都表现得像一个“无所不知”的专家。它们被训练成对于任何输入都要给出一个确定的、结构化的输出。然而现实世界充满了模糊性、信息缺失和模型知识边界。当用户的问题超出模型的知识范围或者指令描述存在歧义时一个可靠的系统不应该强行给出一个可能错误的答案而应该有能力说“对不起这个问题我目前无法处理需要更多信息或人工介入。”这就是“The ‘I Don‘t Know’ Filter”“我不知道”过滤器这个概念的核心价值。它不是一个具体的算法或库而是一种设计范式与实现策略的集合。其目标是为基于大语言模型的智能体Agent注入一种关键能力不确定性感知与可靠决策。简单来说就是教会AI在没把握的时候“闭嘴”或“求助”而不是硬着头皮乱来。这对于构建真正能在生产环境中稳定运行的、负责任的AI应用至关重要。无论是客服机器人、数据分析助手还是代码生成工具这种能力都是其从“玩具”迈向“工具”的关键一步。2. 为什么Function Calling特别需要“不确定性”管理要理解“我不知道”过滤器的重要性我们得先拆解一下标准Function Calling的工作流程及其脆弱性。一个典型的流程包括用户输入自然语言指令 - 大语言模型理解意图并选择匹配的工具函数 - 模型生成符合该函数要求的参数通常是JSON- 系统执行函数并返回结果。这个链条的每个环节都可能引入不确定性2.1 意图理解的模糊地带用户的指令可能不精确。例如用户对股票分析助手说“帮我看看苹果的表现。”这里的“苹果”是指Apple Inc. (AAPL)这只股票还是指水果市场的行情又或者是对设计助手说“把这个背景调亮一点。”“亮一点”是多少是增加10%的亮度还是50%模型如果选择一个它认为“最可能”的解读并自信地调用get_stock_price(symbol“AAPL”)或adjust_brightness(value30)一旦猜错结果就是无用的甚至有害的。2.2 知识边界与实时性缺失模型的知识有截止日期且不包含非公开信息。当用户问“特斯拉今天下午的股价是多少”时如果模型的知识截止到2023年7月它可能根据记忆中的历史数据编造一个价格或者调用一个需要实时数据但参数错误的函数。它“不知道”自己不知道今天的具体数据。2.3 工具函数描述的匹配误差我们为模型提供工具列表时会包含名称和描述。模型需要判断用户请求是否与某个工具的描述匹配。这种匹配基于语义相似度本身就有概率性。一个请求可能同时与两个工具的描述部分匹配例如“总结这篇文章”可能匹配summarize_text()和extract_key_points()模型可能会选择置信度略高的那个但未必是用户真正想要的。2.4 参数生成的幻觉与格式错误这是最常见的坑。即使模型正确选择了函数在生成结构化参数如JSON时也可能产生“幻觉”编造出不存在的参数项或者提供格式错误、类型不符的值例如需要整数却给了字符串需要特定枚举值却给了无效值。传统的、没有“我不知道”过滤器的流程会像一台没有故障报警灯的机器无论内部计算多么不确定都会强制输出一个结果。而引入“我不知道”过滤器就是在流程中增加了多个“质量检查点”和“熔断机制”当不确定性超过某个阈值时流程会暂停转而向用户请求澄清、提供备选方案或者安全地降级处理。3. 构建“我不知道”过滤器的核心组件与策略实现一个有效的“我不知道”过滤器不是简单地让模型在输出前加一句“我不确定”而是一个系统工程。它需要结合提示工程、置信度评估、后处理逻辑以及备选流程设计。下面我们来拆解几个核心的构建策略。3.1 提示工程引导模型表达不确定性最直接的方法是在系统提示词System Prompt中明确要求模型识别并表达不确定性。这比单纯禁止它猜测更有效。基础提示示例“你是一个谨慎可靠的助手。当用户请求涉及以下情况时你必须主动承认知识或能力限制并请求澄清而不是猜测信息模糊存在多种可能解释例如‘苹果’可能指公司或水果。请求的信息超出你的知识截止日期或范围。你无法确定哪个工具函数最符合用户意图。你无法从指令中提取出工具函数所需的全部必要参数。 如果你不确定请输出一个特定的JSON结构例如{“need_clarification”: true, “reason”: “参数X缺失或模糊”, “question_to_user”: “您指的‘苹果’是苹果公司(AAPL)还是水果”}”实操心得仅仅在提示词中要求是不够的。模型特别是较小规模的模型可能会“过度谨慎”或“过度自信”。因此提示词需要与后续的置信度评分结合使用。一个有效的技巧是使用“思维链”Chain-of-Thought提示要求模型在输出最终决定前先输出其推理过程我们在后处理阶段可以解析这个推理过程来评估其确定性。3.2 置信度评分量化“不知道”的程度这是过滤器的技术核心。我们需要一个可量化的指标来判断模型输出的可靠性。有几种常见方法生成概率Logprobs许多API如OpenAI可以提供模型在生成每个token词元时的对数概率。对于函数调用我们可以计算整个function_call部分包括函数名和参数的平均生成概率或最低概率。一个低概率通常意味着模型在“勉强”生成这些内容不确定性高。计算示例概念性假设模型生成了function_call: {“name”: “get_weather”, “arguments”: “{\”city\“: \”Paris\“}”}。API返回了arguments中每个token的logprob。如果”Paris”的logprob很低可能意味着模型不太确定城市是不是巴黎。备选方案分析N-best Lists要求模型或通过API参数如logit_bias、top_logprobs不仅给出最佳答案还给出几个备选答案。如果最佳答案的置信度只比第二、第三名高一点点说明模型自己也很纠结这时就应该触发“我不知道”流程。场景举例用户说“订一张去纽约的票”。模型可能同时以相近的概率建议调用book_flight(destination”NYC”)和book_train(destination”New York”)。这时过滤器应该捕获这种竞争关系并反问用户“您是想预订航班还是火车票”专用不确定性评估模型可以训练或微调一个小的分类器模型专门评估主模型输出的不确定性。这个评估模型的输入可以是原始用户查询、主模型的完整输出包括思维链、以及生成概率等特征输出是一个0到1的置信度分数。3.3 后处理与验证层在模型输出之后、执行函数之前插入一个验证层。这个层根据置信度评分和预定义的规则来决定下一步行动。# 伪代码示例 def uncertainty_filter(model_response, confidence_score, tools_definition): 处理模型响应根据置信度决定行动。 if confidence_score UNCERTAINTY_THRESHOLD: # 置信度过低启动澄清流程 clarification_needed True reason “模型对函数选择或参数生成的置信度过低。” # 可以尝试从模型的思维链或备选方案中提取具体问题 question construct_clarification_question(model_response) return {“action”: “ask_user”, “question”: question, “reason”: reason} # 置信度达标继续验证参数 func_name model_response.function_call.name func_args model_response.function_call.arguments # 1. 验证函数是否存在 if func_name not in [t[“name”] for t in tools_definition]: return {“action”: “ask_user”, “question”: f“工具‘{func_name}’不存在。请重新描述您的需求。”, “reason”: “未知工具调用”} # 2. 验证参数格式和必填项 validation_result validate_arguments(func_name, func_args, tools_definition) if not validation_result[“is_valid”]: return {“action”: “ask_user”, “question”: validation_result[“message”], “reason”: “参数验证失败”} # 3. 验证参数值合理性业务逻辑层 sanity_result sanity_check_arguments(func_name, func_args) if not sanity_result[“is_sane”]: return {“action”: “ask_user”, “question”: sanity_result[“message”], “reason”: “参数合理性检查失败”} # 所有检查通过执行函数 return {“action”: “execute”, “function_call”: model_response.function_call} # 使用 filtered_action uncertainty_filter(llm_output, confidence_score0.65, tools_definitions) if filtered_action[“action”] “ask_user”: # 向用户发送 filtered_action[“question”] send_to_user(filtered_action[“question”]) elif filtered_action[“action”] “execute”: result call_function(filtered_action[“function_call”])注意事项阈值UNCERTAINTY_THRESHOLD的设置需要根据实际场景通过测试来调整。设置过高会导致模型频繁“弃权”用户体验差设置过低则过滤器形同虚设。一个可行的办法是进行A/B测试根据任务完成率和用户满意度来动态调整。3.4 安全降级与备选流程当过滤器触发后系统不能只是简单地说“我不知道”然后挂起。必须设计友好的备选流程主动澄清如上例所示基于不确定性的具体原因模糊指令、缺失参数等生成一个具体、清晰的问题反问用户。提供选择如果是不确定哪个工具可以将2-3个最可能的选项及其解释呈现给用户选择。例如“您是想‘总结全文’生成一段概述还是‘提取要点’列出几个关键句子”默认安全操作在某些高风险场景如金融交易、设备控制当不确定性高时系统应自动执行风险最低的操作或直接转为人工处理。记录与学习将所有触发“我不知道”的案例记录下来包括用户原始输入、模型输出、置信度分数和最终的人工处理结果。这些数据极其宝贵可以用于后续分析模糊模式优化工具描述甚至微调模型。4. 实战在LangChain中为Agent添加简易置信度检查理论讲完了我们来看一个在流行框架LangChain中落地的简化示例。假设我们有一个查询天气的Agent。首先我们定义一个自定义的LLM包装器用于获取模型输出的原始logprobs这里以OpenAI为例需要能访问相关API。from langchain.llms.base import BaseLLM from langchain.schema import LLMResult from typing import Any, List, Optional, Dict import openai class OpenAIWithLogprobs(BaseLLM): model_name: str “gpt-3.5-turbo” def _generate(self, prompts: List[str], stop: Optional[List[str]] None) - LLMResult: responses [] for prompt in prompts: # 调用OpenAI API请求返回logprobs response openai.ChatCompletion.create( modelself.model_name, messages[{“role”: “user”, “content”: prompt}], max_tokens150, logprobsTrue, # 关键参数 top_logprobs5, # 获取top 5的备选token及其概率 stopstop ) # 简化处理我们这里只关心最终输出的文本和logprobs message response.choices[0].message logprobs response.choices[0].logprobs responses.append(LLMResult(generations[[{ “text”: message.content, “generation_info”: {“logprobs”: logprobs} }]])) return LLMResult(generations[[gen] for gen in responses[0].generations[0]] if responses else []) property def _llm_type(self) - str: return “openai-with-logprobs”接下来我们创建一个自定义的Agent执行器它在调用工具前插入置信度检查。from langchain.agents import AgentExecutor, BaseSingleActionAgent from langchain.schema import AgentAction, AgentFinish import json import math class UncertaintyAwareAgentExecutor(AgentExecutor): confidence_threshold: float 0.5 # 置信度阈值 def _take_next_step(self, name_to_tool_map: Dict[str, Any], inputs: Dict[str, str]) - List[AgentAction]: # 调用父类方法获取模型决定的下一步动作 intermediate_steps self._plan(inputs, self.intermediate_steps) # 假设 intermediate_steps 返回的是一个 AgentAction 或 AgentFinish # 这里我们需要从LLM结果中提取logprobs实际情况更复杂需要自定义Agent类来传递logprobs # 以下为概念性代码 llm_output self.agent.llm_output # 假设这里存储了带logprobs的输出 if llm_output and “logprobs” in llm_output.get(“generation_info”, {}): logprobs llm_output[“generation_info”][“logprobs”] # 计算一个简单的平均置信度实际应用需要更精细的计算如只计算function_call部分 avg_logprob self._calculate_avg_logprob(logprobs) confidence math.exp(avg_logprob) # 将对数概率转换回概率近似 if confidence self.confidence_threshold: # 置信度过低不执行动作返回一个要求澄清的AgentFinish clarification “我对您请求的理解置信度较低。能否请您更具体地描述一下您的需求” return [AgentFinish({“output”: clarification}, log“”)] # 置信度达标继续正常流程 return super()._take_next_step(name_to_tool_map, inputs) def _calculate_avg_logprob(self, logprobs): # 简化计算平均所有token的logprob # 实际中需要过滤掉无关token如提示词部分 total 0 count 0 if logprobs and “content” in logprobs: for item in logprobs[“content”]: total item[“logprob”] count 1 return total / count if count 0 else -float(‘inf’)踩坑实录在实际集成中最大的挑战是从LangChain的标准流程中“拦截”并解析模型的原始输出包括logprobs。LangChain的高级抽象在方便使用的同时也隐藏了底层细节。你可能需要自定义BaseSingleActionAgent类重写plan方法确保能将LLM的完整响应包括元数据传递到执行器。此外计算函数调用部分的置信度需要精确地定位输出文本中对应function_call的token范围这需要对tokenization和API响应结构有较深的理解。5. 超越二值判断不确定性作为连续信号一个更高级的思路是不把“知道”和“不知道”看作非黑即白的判断而是将“不确定性”作为一个连续的信号贯穿整个智能体的决策流程。5.1 基于不确定性的动态工具选择智能体可以同时评估多个工具的适用性并给出一个概率分布。例如对于请求“分析这份销售数据”工具calculate_summary_statistics的匹配置信度为0.7generate_trend_chart为0.6identify_outliers为0.4。系统可以设置一个规则如果最高置信度工具的概率低于0.8且第二高置信度工具的概率与之相差小于0.2则自动进入“多工具建议”模式向用户展示前2-3个可能相关的工具及其简要说明让用户选择。5.2 参数生成中的模糊处理与集合返回当模型对某个参数不确定时可以不生成一个确定值而是生成一个可能值的集合或一个范围。例如用户说“找一些价格适中的餐厅”。模型可以调用search_restaurants(price_range[“$$”, “$$$”])其中price_range是一个包含了“中等”和“中等偏上”的集合而不是武断地选择其中一个。后端函数需要能处理这种多值参数返回更广泛的结果让用户筛选。5.3 不确定性在链式调用中的传播在复杂的多步工作流中前一步的不确定性会传播到后续步骤。一个健壮的系统应该能跟踪和累积这种不确定性。例如第一步数据提取的置信度是0.8第二步基于此数据分析的置信度是0.9那么最终结论的综合置信度可能只有0.72。系统可以在输出结果时附带一个“置信度分数”的说明例如“根据现有信息该结论的置信度约为70%建议您核对原始数据X和Y。”5.4 与外部知识源和验证器的联动“我不知道”过滤器不应该是一个终点而应该是一个触发器触发系统去寻求外部帮助。这可以包括知识库检索当模型对某个事实不确定时自动触发向量数据库检索用检索到的证据来补充或修正模型的输出。代码执行器验证对于涉及计算或逻辑判断的请求让模型生成代码然后在沙箱中执行代码来验证结果。如果代码执行出错或结果异常则触发不确定性流程。人类在环Human-in-the-loop对于置信度低于某个严格阈值或涉及高风险操作的请求直接放入人工审核队列等待人工确认后再执行。6. 评估“我不知道”过滤器的效果不仅仅是准确率引入过滤器后如何评估其效果传统的准确率Accuracy指标可能不再适用因为系统现在会主动拒绝一些它没把握的任务。需要建立一套新的评估体系可靠性Reliability系统在承诺给出答案时答案的正确率。这个指标应该比引入过滤器前有显著提升因为低置信度的错误答案被过滤掉了。覆盖率Coverage系统成功处理包括直接回答和通过澄清后回答的查询占总查询的比例。过滤器不能过于保守导致覆盖率暴跌。澄清效率系统提出的澄清问题是否精准、有效用户需要多少轮交互才能得到最终答案平均澄清轮次越少越好。用户满意度通过调查或隐式反馈如任务完成率、后续使用频率来衡量。用户是更喜欢一个偶尔犯错但高效的助手还是一个非常谨慎但步骤繁琐的助手这需要权衡。失败模式分析仔细分析那些被过滤器错误放行假阴性和错误拦截假阳性的案例。假阴性导致错误假阳性导致用户体验下降。通过分析这些案例可以持续优化置信度阈值和验证规则。个人体会在实际项目中我们引入了一个基于规则和简单概率检查的初级过滤器后生产环境中的严重错误如调用错误API、传递荒谬参数减少了约60%但平均对话轮次增加了1.2轮。经过几轮迭代优化提示词和澄清话术后平均对话轮次增加降到了0.8轮同时错误率继续下降。这个权衡是值得的尤其是在错误成本高的领域如金融、医疗咨询。最终一个可靠的智能体其价值不在于它永远正确而在于它知道自己能力的边界并能以一种安全、可控的方式与用户协作。