
本文深入探讨了 langchain csv/pandas dataframe agent 在处理 数据分析 任务时,可能遇到的一个 常见问题 :代理返回json 格式的函数调用而非直接执行。核心原因在于所选 llm 与openai 函数调用格式不兼容。文章提供了两种有效的解决方案:一是切换至支持 openai函数调用的模型,二是改用如 `zero_shot_react_description` 等不依赖特定函数调用格式的代理类型,确保代理能够正确执行 工具 并返回自然语言结果。
理解 langchain 代理与函数调用机制
LangChain 的 csv Agent 和 Pandas Dataframe Agent 是强大的 工具,它们允许开发者通过自然语言与结构化数据(如 CSV 文件)进行交互式分析。这些代理通常利用大型语言模型(LLM)的推理能力,结合特定的工具(如python REPL)来执行数据操作。
当使用 AgentType.openai_FUNCTIONS 类型的代理时,其工作流程依赖于 LLM 对 OpenAI 函数调用格式的理解和生成。理想情况下,当用户提出一个数据分析问题(例如“有多少行?”),代理会:
- 将用户请求和数据上下文发送给 LLM。
- LLM 根据其训练,生成一个符合 OpenAI 函数调用规范的 jsON 对象,指定要使用的工具(如python_repl_ast)和要执行的代码(如 df.shape[0])。
- 代理接收到这个 json 后,会自动解析并调用相应的工具执行代码。
- 工具执行结果返回给 LLM。
- LLM 根据工具结果生成一个自然语言的答案,返回给用户。
这个流畅的交互过程是实现高效数据分析的关键。
问题阐述:代理未执行函数调用
然而,在实际应用中,我们可能会遇到一个问题:代理接收到 LLM 生成的 JSON 函数调用后,并未按照预期执行工具,而是直接将该 JSON 对象输出给用户,导致整个链条中断。
示例问题代码与输出:
import os from langchain.chat_models import ChatOpenAI from langchain.agents.agent_types import AgentType from langchain_experimental.agents.agent_toolkits import create_csv_agent # 假设 环境变量 已配置 api_key = os.environ.get("OPENROUTER_API_KEY") api_base = "https://openrouter.ai/api/v1" model = "mistralai/mixtral-8x7b-instruct" # 问题模型 chat_model = ChatOpenAI(api_key=api_key, base_url=api_base, model=model, temperature=0.0,) def main(): filepath = "your_data.csv" # 替换为你的 CSV 文件路径 agent = create_csv_agent( chat_model, filepath, verbose=True, openai_model=chat_model, # 注意这里传入的也是 chat_model agent_type=AgentType.OPENAI_FUNCTIONS, # 问题代理类型) while True: user_message = input("You: ") if user_message.lower() in ["goodbye", "goodbye!"]: break response = agent.run(user_message) print(response) if __name__ == "__main__": main()
当运行上述代码并输入“How many rows are there?”时,预期的输出应该是执行 df.shape[0]并返回“There are X rows in the dataframe.”,但实际输出却是:
> Entering new AgentExecutor chain…… {"function": "python_repl_ast", "parameters": { "query": "len(df)" } } > Finished chain.
这种现象表明,LLM 成功生成了函数调用指令,但代理执行器未能识别并调用相应的工具。
根本原因:LLM 与 OpenAI 函数调用格式不兼容
经过排查,导致此问题的核心原因在于所选的 LLM(例如上述代码中的 mistralai/mixtral-8x7b-instruct)并未针对 OpenAI 的函数调用格式进行适当的微调。AgentType.OPENAI_FUNCTIONS 代理类型高度依赖于 LLM 能够准确生成和理解这种特定格式的 JSON 响应,并期望代理执行器能无缝地将其映射到工具调用。如果 LLM 未能完全遵循此规范或代理执行器无法解析非标准响应,就会出现上述问题。
解决方案
针对 LLM 与 OpenAI 函数调用格式不兼容的问题,有两种主要的解决方案可以确保 LangChain 代理正常工作。
方案一:切换至兼容 OpenAI 函数调用的 LLM
最直接的解决方案是选择一个明确支持 OpenAI 函数调用功能的 LLM。OpenAI 官方提供的模型(如 GPT-3.5 Turbo、GPT- 4 等)都经过了专门的微调,能够很好地与 AgentType.OPENAI_FUNCTIONS 配合。
实现方式: 将 ChatOpenAI 实例中的 model 参数更改为兼容的模型。
import os from langchain.chat_models import ChatOpenAI from langchain.agents.agent_types import AgentType from langchain_experimental.agents.agent_toolkits import create_csv_agent api_key = os.environ.get("OPENAI_API_KEY") # 确保使用正确的 API 密钥 # api_base = "https://api.openai.com/v1" # OpenAI 官方接口通常不需要显式设置 base_url model = "gpt-3.5-turbo" # 更改为 OpenAI 官方支持函数调用的模型 chat_model = ChatOpenAI(api_key=api_key, # base_url=api_base, # 默认即可 model=model, temperature=0.0,) def main(): filepath = "your_data.csv" agent = create_csv_agent( chat_model, filepath, verbose=True, # openai_model 参数在 create_csv_agent 中已废弃,直接使用第一个参数即可 agent_type=AgentType.OPENAI_FUNCTIONS,) while True: user_message = input("You: ") if user_message.lower() in ["goodbye", "goodbye!"]: break response = agent.run(user_message) print(response) if __name__ == "__main__": main()
注意事项: 使用 OpenAI 官方模型需要相应的 API 密钥和配额。此外,OpenAI 官方文档会列出所有支持函数调用的模型。
方案二:采用其他代理类型(如 ZERO_SHOT_react_DESCRIPTION)
如果由于成本、性能或其他原因,必须使用不支持 OpenAI 函数调用的 LLM,那么可以切换到其他代理类型。AgentType.ZERO_SHOT_REACT_DESCRIPTION 是一个常用的替代方案,它基于 ReAct(Reasoning and Acting)框架,通过“思考 - 行动 - 观察”循环 来驱动代理决策,不依赖于 LLM 生成特定的 JSON 函数调用格式。
实现方式: 将 create_csv_agent 中的 agent_type 参数更改为 AgentType.ZERO_SHOT_REACT_DESCRIPTION。
import os from langchain.chat_models import ChatOpenAI from langchain.agents.agent_types import AgentType from langchain_experimental.agents.agent_toolkits import create_csv_agent api_key = os.environ.get("OPENROUTER_API_KEY") # 沿用原有的 OpenRouter 配置 api_base = "https://openrouter.ai/api/v1" model = "mistralai/mixtral-8x7b-instruct" # 沿用原有的模型 chat_model = ChatOpenAI(api_key=api_key, base_url=api_base, model=model, temperature=0.0,) def main(): filepath = "your_data.csv" agent = create_csv_agent( chat_model, filepath, verbose=True, # openai_model 参数在 create_csv_agent 中已废弃,直接使用第一个参数即可 agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION, # 更改代理类型) while True: user_message = input("You: ") if user_message.lower() in ["goodbye", "goodbye!"]: break response = agent.run(user_message) print(response) if __name__ == "__main__": main()
注意事项: ZERO_SHOT_REACT_DESCRIPTION 代理类型通过提示词引导 LLM 生成思考过程和行动指令,因此其性能可能受到 LLM 的推理能力和提示词工程的影响。不同的代理类型有不同的优点和缺点,选择时需根据具体应用场景进行权衡。
总结
当 LangChain CSV/Pandas Dataframe Agent 未能按预期执行 LLM 生成的函数调用时,这通常是由于所选 LLM 与 OpenAI 函数调用格式不兼容所致。解决此问题的关键在于:
- 更换 LLM:优先选用 OpenAI 官方支持函数调用的模型,以确保无缝集成。
- 更换代理类型:若无法更换 LLM,则选择如 ZERO_SHOT_REACT_DESCRIPTION 等不依赖特定函数调用格式的代理类型,通过不同的推理机制来驱动工具执行。
通过理解代理的工作原理和 LLM 的兼容性要求,开发者可以更有效地调试和配置 LangChain 应用,从而构建稳定可靠的智能数据分析工具。在开发过程中,保持 verbose=True 的设置对于观察代理的内部决策过程和排查问题非常有帮助。