Function Calling 是让 LLM 应用从对话工具变成实际执行系统的关键能力。但在实际工程中,「调用函数」这件事远比 API 文档里的示例复杂:单次调用、链式执行、并行规划各有适用边界,选错模式会让系统既慢又脆弱。
单次调用:确定性任务的最优解
最简单的模式是用户意图明确、只需调用一个函数的场景。假设一个客服系统需要查询订单状态,用户问「我的订单 #12345 发货了吗」,LLM 解析出 query_order_status(order_id="12345") 后直接执行并返回结果。
这种模式的优势是延迟可控(一次 LLM 推理 + 一次函数执行)、成本固定、易于调试。关键实现要点:
- 函数定义要严格:参数类型、枚举值、必填项都要在 schema 里明确,避免 LLM 生成无效调用
- 立即执行不等待:拿到函数调用就执行,不要再让 LLM 二次确认(那会多一轮推理成本)
- 结果直接返回:除非业务要求二次加工(如翻译成更友好的话术),否则函数返回值就是最终答案
适用场景:查询类工具(天气、汇率、库存)、单步操作(发邮件、创建工单)、确定性强的业务流程。
顺序链式:依赖关系明确的多步任务
当任务需要多个函数且存在数据依赖时,就需要链式调用。假设一个报表生成场景:先调用 get_sales_data(region="华东") 获取原始数据,再调用 generate_chart(data=..., type="bar") 生成图表,最后调用 send_email(attachment=...) 发送。
工程上有两种实现路径:
LLM 驱动链式:每次函数执行后把结果返回给 LLM,让它决定下一步。优点是灵活(LLM 可根据中间结果调整策略),缺点是多轮推理累积延迟和成本。
预定义工作流:如果步骤固定,可以在应用层写死 DAG(有向无环图),LLM 只负责填参数。假设「生成周报并发送」是高频需求,直接把三个函数编排成流程,LLM 调用 generate_weekly_report(region=...) 这一个入口函数即可。
选择标准:步骤越固定越倾向预定义工作流,中间分支越多越需要 LLM 动态决策。
并行规划:复杂目标的分解与协同
最复杂的模式是用户给出模糊目标,需要 LLM 自主拆解成多个可并行的子任务。假设用户说「帮我准备明天去上海的行程」,系统需要同时查询航班、酒店、天气、会议日程,这些调用之间没有依赖关系,可以并发执行。
实现要点:
- 规划阶段与执行阶段分离:先让 LLM 输出完整的函数调用列表(可以是 JSON 数组),再批量执行,避免边规划边执行导致的上下文混乱
- 并行执行要有超时与降级:某个函数卡住不应阻塞整体,设置合理的超时(如 5 秒),失败的调用返回
{"error": "timeout"}让 LLM 基于部分结果继续 - 结果聚合交给 LLM:多个函数返回后一次性喂给 LLM,让它综合成用户友好的回答,而不是简单拼接
这种模式的成本高(规划推理 + 结果聚合推理),但对复杂任务体验提升明显。判断是否需要它的标准:用户意图是否需要跨多个数据源、子任务是否可独立完成、并行执行能否显著减少总耗时。
工程实践中的通用建议
无论哪种模式,都要处理好这些共性问题:
- 函数执行失败的处理:返回结构化错误(错误码 + 可读消息),让 LLM 有机会重试或换策略,而不是直接抛异常中断流程
- 上下文窗口管理:多轮调用会快速消耗 token,对历史消息做截断(保留系统提示词 + 最近 N 轮对话),或者用摘要压缩早期轮次
- 可观测性:记录每次函数调用的参数、返回值、耗时,出问题时能快速定位是 LLM 规划错了还是函数执行失败
如果你的团队正在搭建复杂的 LLM 应用,需要在架构选型、成本优化、可靠性保障上获得支持,欢迎联系 Taylent Labs 讨论具体方案。