
如果你正在构建一个基于大语言模型LLM的智能应用比如一个能联网搜索、处理文档、调用API的智能助手那么你一定遇到过这个核心难题如何让一个“大脑”LLM去灵活、可靠地调用各种“工具”Tools传统的解决方案比如 LangChain 的 Agent 框架其设计哲学很像一个动态路由网络。它让 LLM 在每次思考时都像一个路由器一样根据当前请求实时决定下一步该调用哪个工具。这听起来很智能但实际开发中开发者常常被其复杂性、不稳定的输出和难以调试的特性所困扰。最近一个名为Manifest的 AI 应用开发平台做出了一个看似“倒退”实则极具启发性的决策他们宣布弃用自家基于动态路由的 LLM 路由器架构转而拥抱“单一模型”策略。这个决定背后是对当前 LLM 应用开发范式的深刻反思。简单来说Manifest 认为与其让 LLM 费劲地扮演一个不稳定的“决策路由器”不如让它专心做好“执行者”。把复杂的路由逻辑什么时候、用什么工具、输入什么参数从模型的实时推理中剥离出来交给更确定、更可控的工程化方案去处理。这篇文章我们就来深入拆解这个技术决策。你将了解到动态路由Agent的理想与现实差距究竟在哪里单一模型Single Model策略如何工作它真的更简单有效吗从 Manifest 的实践出发我们该如何重新设计自己的 LLM 应用架构提供可落地的代码示例展示如何用“函数调用Function Calling”这种“单一模型”思路构建一个稳定可靠的智能体。这不是一篇空谈趋势的文章。我们将从具体的技术痛点出发通过对比和实操让你彻底明白为什么在当前的模型能力下“少即是多”的哲学可能才是构建生产级 AI 应用的关键。1. 动态路由 Agent理想很丰满现实很骨感在深入 Manifest 的新策略前我们必须先理解它要替代的“旧方案”——动态路由 Agent。这几乎是过去一年里 LLM 应用开发的主流范式。1.1 什么是动态路由 Agent你可以把它想象成一个由 LLM 驱动的高级任务调度器。其核心工作流程如下接收用户请求例如“帮我查一下北京明天天气然后总结成一份出行建议报告。”LLM 思考与规划模型分析请求将其分解为子任务1. 调用天气API2. 调用文本总结能力。动态选择工具模型从预设的工具箱Toolkit中选择“天气查询”工具。执行与观察系统执行该工具获取结果“北京明天晴15-25°C”并将结果返回给 LLM。循环决策LLM 根据上一步的结果决定下一步是继续调用其他工具如“报告生成”还是直接给出最终答案。最终响应LLM 综合所有中间结果生成最终答案。这个过程中“选择工具”和“决定下一步”这两个关键决策点是在每次循环中由 LLM 实时生成的。这就是“动态路由”——路径不是预设的而是LLM根据上下文实时计算出来的。1.2 动态路由的三大现实痛点尽管这个设计非常符合人类“思考-行动-再思考”的直觉但在工程落地时问题接踵而至痛点一输出不稳定与幻觉LLM 的输出具有随机性。它可能这次正确选择了get_weather工具下次却莫名其妙地选择了一个不相关的send_email工具或者生成不符合工具调用格式的文本。你需要编写复杂的输出解析器Output Parser和重试逻辑来兜底代码复杂度急剧上升。痛点二调试与溯源困难当你的智能体行为异常时你很难定位问题。是提示词Prompt没写对是工具描述不够清晰还是模型本身“抽风”了你需要检查每一步的中间思考Chain-of-Thought这在大规模或并发请求下几乎是灾难。痛点三高昂的成本与延迟每一次“思考-行动”循环都是一次完整的 LLM API 调用。一个复杂任务可能需要循环 5-10 次这意味着成本是简单问答的 5-10 倍总延迟也成倍增加。对于需要快速响应的生产环境这是难以接受的。痛点四有限的可控性作为开发者你很难对智能体的行为进行精确约束和引导。你无法轻易实现“优先使用A工具失败后再尝试B工具”这样的确定性逻辑一切依赖于模型的“临场发挥”。正是这些痛点促使像 Manifest 这样的平台开始寻找更优解。2. Manifest 的转向拥抱“单一模型”与确定性路由Manifest 的核心理念可以概括为将智能Intelligence与路由Routing解耦。让 LLM 专注于它擅长的事理解用户意图、处理自然语言、生成高质量文本、进行逻辑推理。将路由逻辑交给确定性的程序代码用if-else、规则引擎、状态机或者更简单的分类模型来决定工具的使用流程。2.1 “单一模型”策略如何工作这里的“单一模型”并非指只用一个模型而是指在单次 LLM 交互中完成所有必要的“智能”工作工具调用流程则由外部逻辑控制。具体实现通常依赖于函数调用Function Calling或工具调用Tool Calling能力。工作流程对比步骤动态路由 (传统 Agent)单一模型 确定性路由 (新范式)1. 接收请求“查北京天气并写报告”“查北京天气并写报告”2. 首次LLM交互LLM思考需要先调用天气工具。LLM一次性分析识别出需要两个功能get_weather和generate_report。它直接返回一个结构化的调用列表或计划。3. 路由决策由LLM动态决定调用get_weather。由预设逻辑决定程序收到LLM的分析结果根据规则如顺序执行决定先执行get_weather。4. 执行工具执行get_weather(“北京”)。执行get_weather(“北京”)。5. 后续决策LLM再次思考根据天气结果决定调用报告工具。程序控制获取天气结果后程序自动将结果作为输入触发generate_report工具。6. 最终响应LLM综合结果生成报告。LLM可能参与最终报告的润色或由程序直接组装结果。关键区别在右侧的新范式中“何时调用何工具”这个路由决策从 LLM 的循环思考中剥离了出来变成了程序代码可以控制的确定性过程。LLM 更像一个“规划师”或“识别器”在开始时提供一次性的蓝图后续由可靠的“施工队”程序按图施工。2.2 为什么这样做更优稳定性极大提升路由逻辑由代码控制只要LLM的初始识别足够准确后续流程就是100%确定的不存在随机性。调试变得简单你可以清晰地看到LLM输出的“规划”结构化数据如果流程出错你可以快速定位是“规划阶段”的识别问题还是“执行阶段”的工具问题。成本与性能可控理想情况下一次复杂任务只需要1-2次LLM调用一次用于规划一次用于最终合成成本大幅降低延迟也更可预测。架构更清晰智能体的业务逻辑变成了普通的程序代码你可以方便地加入日志、监控、熔断、重试等成熟的工程化组件。3. 实战用“函数调用”构建一个稳定天气助手理论说得再多不如代码有说服力。下面我们用一个完整的例子演示如何用 OpenAI 的“函数调用”功能这是“单一模型”策略的典型实现来构建一个天气查询助手。我们将构建一个流程用户输入自然语言请求。LLM 识别意图并返回符合格式的函数调用请求。我们的程序根据LLM的指示确定性地调用相应的天气API。将API结果返回给LLM让其生成友好的回复。3.1 环境准备你需要准备Python 3.8OpenAI API Key我们将使用 GPT-3.5-turbo 或 GPT-4一个免费的天气API这里用 Open-Meteo安装依赖pip install openai requests3.2 定义工具函数首先我们明确告诉LLM我们有什么工具可用。这通过一个“函数定义”的JSON列表来实现。# tool_definitions.py # 定义可供LLM调用的“工具”函数 WEATHER_FUNCTION [ { type: function, function: { name: get_current_weather, description: 获取指定城市的当前天气情况, parameters: { type: object, properties: { location: { type: string, description: 城市名称例如北京上海San Francisco, }, unit: { type: string, enum: [celsius, fahrenheit], description: 温度单位默认为摄氏度, } }, required: [location], }, } } ]关键点description和parameters的描述必须清晰准确这直接决定了LLM能否正确识别和填充参数。3.3 实现工具的执行函数这是实际执行工作的代码与LLM无关。# weather_tool.py import requests def get_current_weather(location: str, unit: str celsius) - str: 实际调用天气API的函数。 参数: location: 城市名 unit: 温度单位 返回: 天气信息的字符串描述 # 使用 Open-Meteo 免费天气API url https://api.open-meteo.com/v1/forecast params { latitude: 39.9042, # 这里简化处理实际应根据location查询经纬度 longitude: 116.4074, current_weather: True, timezone: auto } try: response requests.get(url, paramsparams) response.raise_for_status() data response.json() current data.get(current_weather, {}) temperature current.get(temperature) weather_code current.get(weathercode) # 简化天气代码转换 weather_map { 0: 晴, 1: 晴间多云, 2: 多云, 3: 阴天, 45: 雾, 61: 小雨 } weather_desc weather_map.get(weather_code, 未知) result f{location}的当前天气{weather_desc}温度 {temperature}°C。 if unit fahrenheit: f_temp temperature * 9/5 32 result f{location}的当前天气{weather_desc}温度 {f_temp:.1f}°F。 return result except Exception as e: return f获取天气信息失败{str(e)}3.4 核心流程LLM识别 确定性执行这是新范式的核心。我们只让LLM做一次“识别和规划”然后由我们的代码接管流程。# main.py import json from openai import OpenAI from tool_definitions import WEATHER_FUNCTION from weather_tool import get_current_weather # 初始化OpenAI客户端 client OpenAI(api_key你的OpenAI API Key) def run_conversation(user_query: str): 主流程处理用户查询。 1. 发送查询和工具定义给LLM。 2. LLM返回是否调用工具及参数。 3. 程序根据LLM的指示确定性地执行工具。 4. 将工具结果返回给LLM生成最终回复。 # 步骤1: 第一次LLM调用让其识别意图 response client.chat.completions.create( modelgpt-3.5-turbo-1106, # 或 gpt-4-turbo-preview支持函数调用 messages[{role: user, content: user_query}], toolsWEATHER_FUNCTION, # 关键告诉LLM可用的工具 tool_choiceauto, # 让LLM自动决定是否调用工具 ) message response.choices[0].message print(fLLM初始回复: {message}) # 步骤2: 检查LLM是否决定调用函数 if message.tool_calls: # 注意这里可能有多个工具调用我们按顺序处理 for tool_call in message.tool_calls: function_name tool_call.function.name function_args json.loads(tool_call.function.arguments) print(fLLM决定调用工具: {function_name}) print(f调用参数: {function_args}) # 步骤3: 确定性路由与执行 # 这里就是我们的“确定性路由逻辑”用简单的if-else或字典映射 if function_name get_current_weather: location function_args.get(location) unit function_args.get(unit, celsius) # 调用实际的功能函数 tool_response get_current_weather(location, unit) print(f工具执行结果: {tool_response}) # 步骤4: 将工具执行结果作为上下文再次发送给LLM second_response client.chat.completions.create( modelgpt-3.5-turbo-1106, messages[ {role: user, content: user_query}, message, # 包含LLM之前决定调用工具的消息 { role: tool, tool_call_id: tool_call.id, content: tool_response, # 工具执行结果 } ], # 这次不需要再传递tools除非需要继续调用 ) final_message second_response.choices[0].message print(f\n最终回复: {final_message.content}) return final_message.content else: # 如果LLM认为不需要调用工具直接回复 print(fLLM直接回复: {message.content}) return message.content if __name__ __main__: # 测试用例 queries [ 今天北京天气怎么样, 帮我写一首关于春天的诗。, # 这个请求不会触发天气工具 上海和北京的天气对比一下。 ] for query in queries: print(f\n{*50}) print(f用户查询: {query}) print(f{*50}) result run_conversation(query) print(f最终答案: {result})3.5 运行结果与解析运行上述main.py你会看到类似以下的输出 用户查询: 今天北京天气怎么样 LLM初始回复: ChatCompletionMessage(contentNone, roleassistant, function_callNone, tool_calls[ChatCompletionMessageToolCall(idcall_abc123, functionFunction(arguments{location: 北京, unit: celsius}, nameget_current_weather), typefunction)]) LLM决定调用工具: get_current_weather 调用参数: {location: 北京, unit: celsius} 工具执行结果: 北京的当前天气晴温度 18.5°C。 最终回复: 北京现在是晴天气温大约18.5摄氏度天气不错哦 最终答案: 北京现在是晴天气温大约18.5摄氏度天气不错哦 用户查询: 帮我写一首关于春天的诗。 LLM初始回复: ChatCompletionMessage(content当然这是一首关于春天的小诗\n\n春风轻拂面..., roleassistant, function_callNone, tool_callsNone) LLM直接回复: 当然这是一首关于春天的小诗... 最终答案: 当然这是一首关于春天的小诗...流程解析对于天气查询LLM在第一次响应时没有直接生成答案而是返回了一个结构化的tool_calls对象指明了要调用的函数名和参数。我们的程序检测到tool_calls后根据function.name进行确定性的路由这里是一个简单的if判断然后执行对应的get_current_weather函数。获取天气结果后程序将结果和原始对话历史一起再次发送给LLM由LLM生成一个自然、友好的最终回复。对于写诗的请求LLM判断无需调用工具直接生成回复流程结束。这就是“单一模型”策略的精髓LLM只负责“识别意图并结构化输出”而“根据结构执行哪个函数”这个路由逻辑完全由开发者编写的、确定性的代码控制。4. 对比与进阶从简单函数调用到复杂工作流上面的例子是单工具调用。对于更复杂的任务如“查天气并写报告”我们可以让LLM在第一次调用时就返回一个完整的计划Plan然后由我们的程序来按序执行这个计划。4.1 多步骤任务规划示例我们可以定义一个create_plan的函数让LLM生成一个任务列表。# 扩展工具定义增加报告生成函数 MULTI_FUNCTIONS [ { type: function, function: { name: create_plan, description: 将复杂的用户请求分解为可执行的任务步骤序列, parameters: { type: object, properties: { tasks: { type: array, items: { type: object, properties: { step: {type: number}, action: {type: string}, tool: {type: string, enum: [get_weather, generate_report, search_web]}, parameters: {type: object} }, required: [step, action, tool] } } }, required: [tasks] } } }, # ... 其他工具定义 (get_weather, generate_report) ] # 在主流程中首先调用 create_plan # 获取计划后程序遍历 tasks 列表根据每个 task.tool 字段确定性地调用对应的函数。 # 这完全脱离了LLM的动态决策循环。在这种架构下LLM 只在最开始扮演一次“规划师”后续所有步骤都是程序按部就班地执行。这带来了无与伦比的可控性和可观测性。5. 常见问题与排查思路在实践这种模式时你可能会遇到以下问题问题现象可能原因排查方式解决方案LLM不返回工具调用1. 用户请求意图不明确。2. 工具描述description不清晰。3. 模型温度temperature参数过高导致输出不稳定。1. 检查LLM的原始回复内容。2. 简化并精确化工具描述。3. 将temperature设为0。1. 优化用户Prompt或先让LLM澄清意图。2. 重写工具描述确保无歧义。3. 使用temperature0确保确定性。工具调用参数错误1. 参数定义parameters的schema不准确。2. LLM对用户输入的理解有偏差。1. 打印出function_args检查。2. 在工具函数内部添加参数验证和日志。1. 严格遵循JSON Schema规范定义参数。2. 在调用实际工具前对参数进行清洗和转换。多工具调用顺序混乱程序逻辑没有正确处理tool_calls列表的顺序。打印整个tool_calls列表观察其结构。按照tool_calls列表的顺序依次执行或根据业务逻辑如依赖关系重新排序。流程无法处理失败工具执行失败如API超时后流程中断。查看异常堆栈信息。在每个工具调用外添加try-catch并设计重试或降级逻辑。将失败信息反馈给LLM或用户。6. 最佳实践与工程建议基于 Manifest 的启示和上述实践以下是在生产环境中应用“单一模型确定性路由”策略的建议清晰定义工具边界每个工具函数应职责单一描述精确。避免让LLM去猜测一个模糊工具的作用。强化输入验证在调用实际工具前务必验证LLM提供的参数。类型、范围、必填项都需要检查防止无效调用。实施结构化日志记录每一次LLM的请求和响应特别是tool_calls、每一次工具调用的输入输出。这是调试和监控的基石。设计降级方案当LLM无法识别意图或工具调用失败时要有备选方案。例如可以回退到直接问答或提示用户重新表述。控制成本与延迟对于复杂任务优先使用一次“规划”调用多次廉价工具执行的模式而不是多次昂贵的LLM调用。考虑对工具结果进行缓存避免重复调用。版本化管理提示词与工具定义将工具的描述Prompt和函数的实现都纳入版本控制。它们的任何改动都可能显著影响智能体行为。区分环境在开发环境使用更便宜的模型如 GPT-3.5-Turbo进行流程测试在上线前再用强模型如 GPT-4进行验证。7. 总结回归工程本质让LLM做它该做的事Manifest 放弃动态路由 LLM 路由器选择“单一模型”策略不是一个技术上的退步而是一次面向工程现实的理性回归。它告诉我们在现阶段LLM 最强大的能力依然是语言理解和生成而不是复杂的实时决策与流程控制。试图让LLM承担后者往往会将系统的不确定性和复杂性放大到难以管理的地步。作为开发者我们的职责是构建可靠、可维护、可预测的系统。将确定性的路由逻辑从LLM中抽离用代码去实现正是这种工程思维的体现。LLM 应该被当作一个强大的“语义理解与规划组件”嵌入到我们的系统中而不是成为整个系统不可控的“大脑”。这种架构转变降低了AI应用开发的门槛提高了系统的稳定性和可观测性使得AI能力能够更可靠地集成到生产流程中。下次当你设计自己的LLM应用时不妨先问自己“这个决策逻辑真的需要LLM来实时判断吗能不能让它一次性告诉我该怎么做然后由我来执行”或许这就是构建下一代可靠AI智能体的关键起点。