LLMCompiler:大语言模型并行函数调用框架,降低延迟与成本
1. 项目概述一个为LLM设计的“并行函数调用编译器”如果你正在构建基于大语言模型的智能体应用并且被工具调用的延迟和成本问题所困扰那么LLMCompiler这个框架值得你花时间深入了解。简单来说它就像是一个为LLM的“函数调用”行为设计的智能调度器或编译器。传统上当LLM需要调用多个外部工具或API来完成一个复杂任务时比如先搜索天气、再查询航班、最后计算预算模型通常会以串行的方式“思考一步执行一步”。这种模式不仅导致总响应时间等于所有步骤的耗时之和还因为LLM需要在每个步骤都进行上下文推理可能产生不必要的Token消耗和更高的API成本。LLMCompiler的核心创新在于它通过静态分析和规划让LLM在推理初期就识别出一个复杂任务中所有可并行执行的子任务。想象一下你作为项目经理拿到一个项目需求不是立刻动手做第一件事而是先花点时间画出完整的工作分解结构和依赖关系图然后把所有能同时开工的任务分派给不同的成员。LLMCompiler做的正是这件事它引导LLM扮演“规划者”的角色先输出一个包含并行任务流的执行计划然后再由一个独立的“执行单元”并发地调用所有无依赖关系的函数。最终带来的收益是直观的更低的端到端延迟、更少的LLM调用次数从而降低成本以及因减少了长链推理中的错误累积而可能带来的准确性提升。这个框架的实用性很强它不绑定于某个特定的模型。无论是OpenAI的GPT系列还是开源的LLaMA、Llama 2等模型都可以集成进来。这意味着你可以在追求极致性能的闭源模型和需要数据隐私、可控成本的开源模型之间灵活选择。接下来我将结合自己的实验和项目经验深入拆解LLMCompiler的设计思路、实操细节以及如何将它应用到你的自定义场景中。2. 核心架构与工作原理解析要理解LLMCompiler为何有效我们需要深入到它的三层架构设计中去。这不仅仅是阅读论文更是理解如何将一个学术思想工程化为稳定可用的系统。2.1 三层流水线规划、分发、执行LLMCompiler将一次复杂的工具调用过程解耦为三个清晰阶段形成了高效的流水线。第一层规划器这是整个系统的“大脑”。它的输入是用户的原始查询例如“帮我规划一个去纽约的周末旅行预算控制在5000元以内”以及一套预定义的工具函数描述。规划器本身由一个LLM驱动其核心任务是进行任务分解和依赖关系分析。它不会立即执行任何操作而是输出一个结构化的执行计划。这个计划在论文和代码中体现为一个有向无环图其中节点代表需要调用的具体函数如search_flights(destination, date)、get_hotel_prices(city, check_in_date)边代表任务间的依赖关系例如“预订酒店”可能依赖于“查询航班日期”的结果。关键在于规划器会识别出哪些任务是完全独立的可以并行执行。例如“查询纽约天气”和“查询近期机票价格”这两个任务通常没有依赖关系可以被标记为并行。注意规划器的提示词工程至关重要。代码库中configs/目录下的gpt_prompts.py文件提供了不同场景的示例。你需要精心设计这些“少样本示例”明确教导LLM如何将自然语言问题分解为带依赖关系的任务列表。这是整个系统能否正确工作的基础。第二层任务获取单元你可以将它理解为“调度中心”。它接收来自规划器的结构化计划并对其进行解析。其核心职责是进行依赖关系解析和任务排序。它会找出图中所有入度为0的节点即没有前置依赖的任务并将这些“就绪任务”打包分发给下一层。一旦某个任务执行完成任务获取单元会更新依赖图将被阻塞的后续任务其前置依赖已全部满足激活送入就绪队列。这种设计实现了执行阶段的流水线化只要有计划中的独立任务执行器就不会空闲。第三层执行器这是系统的“手脚”负责实际调用外部函数。执行器接收来自任务获取单元的一批就绪任务并发地执行它们。这里的“并发”是关键的性能提升点。例如如果计划中有三个独立的网络API调用执行器会通过异步IO的方式同时发起这三个请求而不是一个一个地等待。执行完成后它将结果返回给任务获取单元用于更新任务状态和解析后续依赖。执行器本身不包含复杂的逻辑它严格遵循规划器生成的指令和参数调用对应的工具函数。2.2 并行化带来的性能红利这种架构带来的优势是多维度的我通过对比实验能明显感受到差异。延迟优化这是最直接的收益。假设一个任务需要调用5个外部API每个平均耗时200毫秒。在传统的串行ReAct模式下总网络延迟至少是1秒5 * 200ms这还不算每个步骤中LLM生成和思考的时间。在LLMCompiler的并行模式下如果这5个调用中有3个是独立的那么这3个调用可以同时进行这部分的总延迟就从600ms压缩到了200ms左右。在实际的hotpotqa多跳问答基准测试中论文报告了平均1.6倍到2.1倍的延迟降低我在使用开源模型本地测试时对于IO密集型的任务加速比甚至更明显。成本降低成本与LLM的调用次数和输入的Token数量直接相关。在串行模式中LLM每决定调用一个工具都需要将历史对话、工具结果和当前问题重新作为上下文输入这会产生大量的重复Token。LLMCompiler的规划阶段虽然也可能消耗不少Token来生成完整的计划但这是一次性的。后续的执行阶段不再需要LLM反复参与推理只需按计划执行即可。对于长链条任务这种“一次规划多次执行”的模式能显著减少总Token消耗从而降低使用闭源API的成本。准确性潜在提升这一点初看反直觉但有其道理。在长链串行推理中早期步骤的一个小错误比如错误地解析了一个日期会随着上下文传递到后续步骤导致错误累积最终答案偏离正轨。LLMCompiler的规划器在开始时拥有完整的全局视角它生成的计划理论上更可能保证数据流逻辑的正确性。同时由于并行执行减少了步骤数也降低了中间步骤出错污染后续步骤的概率。在movie电影推荐基准测试中这种基于完整上下文的并行规划显示出比逐步推理更高的准确性。3. 从零开始环境配置与基础运行理解了原理后我们动手把它跑起来。这里我会详细说明每一步的操作意图和可能遇到的坑确保你能顺利复现。3.1 系统环境与依赖安装首先项目基于Python 3.10使用Conda管理环境是最清晰的方式能避免与系统其他Python包的冲突。# 创建并激活一个干净的Python 3.10环境 conda create --name llmcompiler python3.10 -y conda activate llmcompiler接下来克隆仓库并安装依赖。这里有一个细节需要注意requirements.txt文件里通常包含了像openai,langchain等包的特定版本。直接安装可能因为网络问题或版本冲突失败。git clone https://github.com/SqueezeAILab/LLMCompiler cd LLMCompiler # 建议先升级pip并使用国内镜像源加速 pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple实操心得如果安装过程中某个包比如torch安装失败或版本不匹配不要死磕requirements.txt。可以先注释掉该行手动安装一个兼容的版本。例如对于PyTorch去官网根据你的CUDA版本选择命令安装往往更可靠。核心是保证openai如果你用GPT、vllm如果你用本地模型等关键库能正确导入。3.2 运行官方基准测试项目提供了三个基准测试来验证效果hotpotqa多跳问答、movie电影推荐和parallelqa并行问答。我们以hotpotqa为例使用OpenAI的GPT模型运行。第一步设置API密钥。安全起见不建议将密钥硬编码在脚本中而是通过环境变量传入。# 在终端中设置环境变量临时关闭终端后失效 export OPENAI_API_KEY你的实际API密钥 # 或者在 ~/.bashrc 或 ~/.zshrc 中永久设置第二步运行测试脚本。核心脚本是run_llm_compiler.py。python run_llm_compiler.py --benchmark hotpotqa --store ./results/hotpotqa_result.json --stream让我逐一解释这些参数--benchmark hotpotqa指定要运行的基准测试名称。--store ./results/hotpotqa_result.json指定结果保存路径。脚本会生成一个JSON文件包含每个问题、真实答案、模型预测和耗时。--stream强烈建议启用。这是LLMCompiler的一个关键优化选项。启用后规划器一旦生成一个独立的“任务节点”就会立刻流式地发送给任务获取单元和执行器而不是等整个计划图都生成完毕再开始执行。这实现了“规划”和“执行”的阶段重叠进一步减少了端到端延迟是生产环境中必备的选项。运行后控制台会输出执行过程。你会看到类似“Planner generating graph...”、“Executing parallel tasks: [task_a, task_b]...”的日志直观展示了并行调度的过程。第三步查看评估结果。运行结束后使用评估脚本生成总结报告。python evaluate_results.py --file ./results/hotpotqa_result.json这个脚本会计算并打印出整个测试集的准确率、平均延迟、总成本等关键指标方便你与论文中的数据进行对比。3.3 使用开源模型进行本地推理对于希望本地部署、关注数据隐私或需要控制成本的开发者LLMCompiler支持通过vLLM框架集成开源模型。第一步使用vLLM部署模型服务。vLLM是一个高性能的LLM推理和服务引擎特别擅长Attention算子的优化和连续批处理。# 假设你已安装vLLM启动一个Llama-2-7b-chat模型的服务 python -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-2-7b-chat-hf \ --served-model-name llama-2-7b \ --port 8000 \ --trust-remote-code这个命令会在本地的8000端口启动一个兼容OpenAI API格式的服务。--trust-remote-code参数对于某些需要自定义代码的模型是必须的。第二步配置并运行LLMCompiler。你需要告诉LLMCompiler去连接这个本地服务而不是OpenAI。export VLLM_PORT8000 # 设置vLLM服务端口 python run_llm_compiler.py \ --model_type vllm \ --benchmark hotpotqa \ --store ./results/hotpotqa_vllm.json \ --model_name llama-2-7b \ --vllm_port $VLLM_PORT关键参数解析--model_type vllm指定后端为vLLM服务。--model_name llama-2-7b这个名称需要与启动vLLM服务时--served-model-name参数指定的名称一致。--vllm_port指定vLLM服务监听的端口。重要提示代码库中预置的提示词模板在configs/目录下是针对LLaMA-2 70B的非对话模型llama-2-70b进行优化的。如果你使用其他模型尤其是Chat版本或更小/更大的模型这些提示词可能不是最优的。你可能会观察到规划器生成的计划质量下降。这时你需要根据你的模型微调gpt_prompts.py中的示例这是一个必要的调优步骤。4. 深度定制打造你自己的智能体应用LLMCompiler的真正威力在于其可扩展性。官方基准测试只是一个起点将其适配到你的业务场景才是价值所在。这主要涉及两个核心文件的配置tools.py和gpt_prompts.py。4.1 定义工具函数所有可以被LLM调用的外部能力都需要在tools.py中定义为Python函数并使用tool装饰器进行注册。这个装饰器会捕获函数的名称、描述和参数schema这些信息会自动提供给LLM作为工具调用的依据。假设我们要构建一个“智能旅行助手”需要定义以下工具# 在你的 custom_benchmark/configs/ 目录下的 tools.py 文件中 from typing import Dict, List from llmcompiler.tool import tool tool def search_flights(departure_city: str, arrival_city: str, date: str) - List[Dict]: 根据出发城市、到达城市和日期搜索航班信息。 Args: departure_city: 出发城市例如“北京”。 arrival_city: 到达城市例如“上海”。 date: 出发日期格式为‘YYYY-MM-DD’例如‘2024-10-01’。 Returns: 一个字典列表每个字典包含航班号、航空公司、起飞时间、到达时间、价格等信息。 示例[{“flight_no”: “CA1501”, “airline”: “中国国际航空”, “price”: 1200}, ...] # 这里应该是实际的API调用逻辑例如请求携程、飞猪的接口 # 为演示我们返回模拟数据 print(f[模拟调用] 搜索航班: {departure_city} - {arrival_city} on {date}) return [ {flight_no: CA1501, airline: 中国国际航空, departure_time: 08:00, price: 1200}, {flight_no: MU5101, airline: 中国东方航空, departure_time: 10:30, price: 1100}, ] tool def get_attractions(city: str, days: int) - List[str]: 获取某个城市的旅游景点推荐列表并根据旅行天数进行筛选。 Args: city: 城市名称。 days: 计划游览的天数。 Returns: 一个推荐的景点名称列表。 print(f[模拟调用] 获取{city}的{days}日游景点推荐) # 模拟根据城市返回景点 attraction_pool { 北京: [天安门广场, 故宫, 长城, 颐和园], 上海: [外滩, 东方明珠, 迪士尼乐园, 豫园], } base_attractions attraction_pool.get(city, [城市中心, 历史博物馆]) # 简单模拟天数越多推荐的景点越多 return base_attractions[:min(days, len(base_attractions))] tool def calculate_budget(flight_price: float, hotel_price_per_night: float, days: int, daily_spending: float) - Dict[str, float]: 计算旅行总预算细分。 Args: flight_price: 往返机票总价。 hotel_price_per_night: 每晚酒店价格。 days: 旅行总天数。 daily_spending: 每日餐饮交通等杂费预算。 Returns: 一个包含机票、酒店、杂费及总预算的字典。 print(f[模拟调用] 计算预算: 机票{flight_price}, 酒店{hotel_price_per_night}/晚, {days}天, 每日消费{daily_spending}) hotel_total hotel_price_per_night * (days - 1) # 假设住 nights-1 晚 misc_total daily_spending * days total flight_price hotel_total misc_total return { flight: flight_price, hotel: hotel_total, miscellaneous: misc_total, total: total }定义工具的要点类型注解至关重要str,int,float,List,Dict等类型注解不仅让代码更清晰更重要的是LLMCompiler会利用这些信息来构建更准确的工具调用schema帮助LLM理解应该传入什么格式的参数。文档字符串是LLM的说明书函数的docstring会被直接用作工具描述。必须清晰说明功能、每个参数的含义和格式、返回值的结构。提供示例返回值能极大提升LLM调用工具的准确性。模拟与真实调用在开发调试阶段函数体内可以先用print和模拟数据。等逻辑验证通过后再替换为真实的HTTP请求到你的内部API或第三方服务。4.2 设计提示词示例定义了工具之后你需要教导LLM如何针对你的任务类型进行规划。这是通过gpt_prompts.py中的“少样本示例”来实现的。每个示例都是一个字典包含user_query和对应的plan。继续以旅行助手为例我们需要设计几个典型的用户查询和理想的规划输出# 在你的 custom_benchmark/configs/ 目录下的 gpt_prompts.py 文件中 PLANNER_EXAMPLES [ { user_query: 我想下周五从北京去上海下周日晚上回来预算不超过3000元请帮我规划一下。, plan: [ { task: search_flights, args: {departure_city: 北京, arrival_city: 上海, date: 2024-10-11}, id: 1, dependencies: [] }, { task: search_flights, args: {departure_city: 上海, arrival_city: 北京, date: 2024-10-13}, id: 2, dependencies: [] }, { task: get_attractions, args: {city: 上海, days: 3}, id: 3, dependencies: [] # 这个任务与查询航班可以并行 }, { task: calculate_budget, args: { flight_price: 1:price 2:price, # 注意这里演示了如何引用之前任务的结果 hotel_price_per_night: 500, # 假设一个默认值实际中可能需要另一个工具查询 days: 3, daily_spending: 300 }, id: 4, dependencies: [1, 2] # 计算预算依赖于两个航班查询的结果 } ] }, # ... 可以添加更多不同场景的示例例如只查询景点、复杂多城市旅行等。 ]设计提示词的技巧覆盖多样性示例应覆盖你预期中用户可能提出的主要问题类型。对于旅行助手可以包括单程查询、往返查询、多城市联程、纯景点咨询、带预算约束的规划等。明确依赖关系在plan列表中dependencies字段是定义任务图的关键。空列表[]表示该任务可以立即执行。[1, 2]表示该任务必须等待ID为1和2的任务完成。这直观地体现了数据流。结果引用语法注意在calculate_budget的args中1:price是一种占位符语法具体语法需参考LLMCompiler的实际实现这里是概念示意表示要使用ID为1的任务返回结果中的price字段。这教会了LLM如何将上游任务的输出作为下游任务的输入。任务ID的连续性每个任务都有一个唯一的id依赖关系通过id来引用。保持id的连续和清晰有助于LLM理解和生成正确的图结构。4.3 创建自定义配置并运行配置好工具和提示词后你需要创建一个与hotpotqa、movie同级的配置目录例如configs/travel_assistant/里面包含你的tools.py和gpt_prompts.py。然后你需要修改或创建一个新的运行脚本或配置文件指向你的自定义工具集和提示词。查看run_llm_compiler.py的源码你会发现它通过--benchmark参数来加载对应configs/下的配置。最直接的方式是复制一份run_llm_compiler.py并修改其加载配置的路径指向你的travel_assistant目录。一个更工程化的做法是仿照现有基准测试的结构在代码中注册你的新场景。这可能需要你稍微深入代码但复用性更好。核心是确保LLMCompiler初始化时加载的是你定义的工具和提示词。5. 生产环境部署与性能调优指南将LLMCompiler从实验环境迁移到生产环境需要考虑稳定性、性能和可观测性。以下是我在实际部署中总结的几个关键点。5.1 模型选择与提示词调优闭源 vs. 开源模型OpenAI GPT-4/GPT-3.5-Turbo在规划能力上通常表现更稳定、更准确能生成更复杂的依赖图。缺点是API调用有成本且存在延迟和速率限制。适合对规划准确性要求高、任务复杂度高、且预算充足的场景。开源模型如LLaMA 2 70B, Mixtral, Qwen通过vLLM本地部署无数据出境风险长期成本可控。挑战在于提示词工程需要更精细。70B级别的模型规划能力尚可但更小的模型7B、13B可能难以生成可靠的多步骤并行计划需要进行大量测试和提示词迭代。提示词调优实战分解粒度教导LLM将问题分解到合适的粒度。粒度过粗如“规划整个旅行”会导致单个工具函数过于复杂粒度过细如“查天气”、“查温度”、“查湿度”会产生大量不必要的串行依赖丧失并行优势。你需要通过示例来展示“刚刚好”的分解。输出格式约束在给规划器的系统提示词中必须严格要求其以指定的JSON格式输出计划。可以使用类似“你必须严格按照以下JSON格式输出不要有任何额外解释”的指令并在示例中展示完美格式。这对于开源模型尤其重要。错误处理引导在示例中可以加入一些处理边界情况的示范。例如当用户查询“去一个不存在的城市”时规划器可以安排一个validate_city的工具调用先行如果验证失败则直接返回错误避免后续无意义的调用。5.2 执行引擎的异步优化与错误处理LLMCompiler的执行器并发调用工具这依赖于Python的异步编程。确保工具函数的异步性如果你的工具函数涉及网络I/O调用外部API务必将其定义为async函数并在内部使用aiohttp等异步HTTP客户端而不是requests库。这样可以避免阻塞事件循环实现真正的并发。import aiohttp from llmcompiler.tool import tool tool async def search_flights_async(departure_city: str, arrival_city: str, date: str) - List[Dict]: 异步版本的航班搜索。 url https://your-flight-api.com/search params {from: departure_city, to: arrival_city, date: date} async with aiohttp.ClientSession() as session: async with session.get(url, paramsparams) as response: if response.status 200: return await response.json() else: # 明确抛出异常便于执行器捕获 raise Exception(f航班API调用失败: {response.status})实现健壮的错误处理在并行执行中某个工具调用失败不应该导致整个任务链崩溃。你需要在执行器层面添加重试和降级逻辑。重试机制对于网络超时等临时性错误可以配置自动重试如最多3次指数退避。任务级联取消如果一个关键任务失败例如查询核心数据的任务所有依赖于它的后续任务应该被取消或标记为失败而不是继续执行无意义的调用。优雅降级当某个非核心工具不可用时例如某个特定的酒店推荐API挂了执行器应能捕获异常并可能用一个更简单的默认值或备用工具来替代保证主流程能继续。5.3 监控、日志与调试在生产环境中你需要清晰地知道每个请求的执行路径、耗时和状态。结构化日志启用--logging参数是一个开始但你可能需要集成更强大的日志系统如structlog或直接输出到JSON格式的文件方便后续用ELK栈进行分析。关键日志点包括规划器生成的完整计划图、每个任务的开始/结束时间及状态成功/失败、任务间的依赖关系、最终聚合答案的生成过程。性能指标收集除了总延迟还应监控规划阶段耗时LLM生成计划图的时间。关键路径耗时从第一个任务开始到最后一个依赖任务结束的时间这反映了并行化后的实际执行时间。并行度平均每个请求中最大并行执行的任务数量。这反映了你的任务分解和规划的有效性。工具调用成功率每个外部API的健康状态。调试技巧当遇到规划结果不理想时首先单独测试规划器。可以写一个简单的脚本只调用规划器LLM输入你的查询和工具列表查看其输出的原始计划。这能帮你判断是提示词问题还是模型能力问题。对于执行错误仔细查看失败任务的错误信息和输入参数检查工具函数对参数的处理逻辑和异常捕获是否完善。6. 常见问题排查与实战心得在实际集成和使用LLMCompiler的过程中你肯定会遇到各种问题。下面是我踩过的一些坑以及解决方案希望能帮你节省时间。6.1 规划器生成错误或非结构化输出问题现象LLM没有输出预期的JSON格式计划而是输出了一段自然语言描述或者JSON格式错误无法解析。排查步骤检查提示词格式首先确认你的gpt_prompts.py中的示例格式完全正确且与代码中PlanParser期望的格式严格匹配。一个多余的逗号或少一个括号都可能导致解析失败。验证模型兼容性如果你使用的是开源模型确认该模型是否经过足够的指令微调具备遵循复杂格式指令的能力。未经微调的基础模型很难完成此任务。尝试换用Chat版本如Llama-2-7b-chat-hf。简化任务如果问题复杂尝试先给规划器一个极其简单的、几乎必然能并行化的任务例如“同时获取北京和上海的天气”看它是否能生成正确计划。如果不能说明提示词或模型基础能力有问题。增加格式约束在系统提示词中强化输出格式要求。可以使用类似“你必须输出一个有效的JSON数组每个元素是一个任务对象包含task,args,id,dependencies四个键。”的强硬指令并在示例中反复强调。6.2 并行度不理想加速比低问题现象虽然使用了LLMCompiler但整体执行时间相比串行方式没有明显改善。排查步骤分析任务依赖图打印或记录规划器生成的计划。检查是否大部分任务都被设置了不必要的依赖关系导致它们被强制串行执行。这通常是提示词示例中依赖关系设计过紧导致的。审视工具设计检查你的工具函数是否设计得过于“粗粒度”。如果一个工具函数内部做了多件本可独立的事例如一个get_travel_info函数内部既查航班又查酒店那它自然无法并行。遵循单一职责原则将工具拆细。检查IO阻塞确认你的工具函数是否是真正的异步非阻塞。如果在异步函数中混用了同步的requests.get()或者有耗时的CPU计算会阻塞整个事件循环导致“假并行”。使用asyncio.to_thread将CPU密集型任务放到线程池中执行。外部API瓶颈即使内部并发完美如果所有并行任务都调用同一个外部API且该API有速率限制那么并发请求会被排队或拒绝实际延迟取决于API的响应能力。考虑为关键服务设置客户端限流或使用连接池。6.3 工具调用结果传递错误问题现象下游任务无法正确获取上游任务的结果导致参数错误或执行失败。排查步骤确认结果引用语法仔细阅读代码中关于如何在前置任务args中引用后置任务结果的语法。是使用id:field这样的模板还是通过变量名确保你的提示词示例中的引用方式与执行引擎的解析逻辑一致。检查返回数据结构上游工具函数返回的字典结构必须与下游任务参数中期望引用的字段名完全一致。例如如果下游任务期望1:price那么ID为1的任务返回的字典中必须有一个名为price的键。查看执行日志启用详细日志查看每个任务执行后的实际返回值是什么以及传递给依赖任务的参数是什么。对比一下就能发现是返回值不对还是参数组装逻辑有误。6.4 与LangChain/LlamaIndex集成时的注意事项LLMCompiler已集成到LangChain和LlamaIndex中这降低了使用门槛但也引入了一些抽象层。在LangGraphLangChain中使用优势可以直接利用LangChain丰富的工具生态和Chain的编排能力。LLMCompiler作为一个特殊的“Graph”来运行与其他LangChain组件结合更方便。注意点LangChain的集成可能不是最新版本。关注GitHub上langgraph仓库示例中的LLMCompiler.ipynb那里的代码和用法是最权威的。注意工具定义需要遵循LangChain的BaseTool格式。在LlamaIndex中使用优势如果你的应用重度依赖RAG检索增强生成LlamaIndex的集成让你能轻松地将并行工具调用与向量检索结合起来。注意点同样需要关注LlamaIndex Packs中llm_compiler的版本和文档。工具的定义方式可能与原生LLMCompiler稍有不同。通用建议无论是哪种集成都先从最简单的例子跑通确保基础的工具调用和规划功能正常再逐步加入你自定义的复杂逻辑。集成的抽象有时会隐藏底层错误调试时可能需要深入到集成的包装器代码中去看。