Langgraph与Ollama集成时状态传递异常问题求助
问题
使用Ollama和Langgraph构建基础旅行代理工作流,程序无报错但输出不符合预期,Agent间状态传递异常。Weather Agent生成的weather_data未传递到Itinerary Agent,导致后续流程报错,最终weather_data、itinerary、budget均为None。已参考Ollama结构化输出官方博客实现Schema,环境依赖安装正确。
问题分析
核心原因是错误使用了MessagesState作为图的状态类型。MessagesState是LangGraph提供的预设状态类,仅内置了messages字段,无法自动保留自定义的weather_data、itinerary、budget等状态值。当节点返回包含这些自定义字段的状态时,LangGraph会自动过滤掉非MessagesState定义的字段,导致状态传递中断。
另外代码中WeatherData类的day字段存在缩进错误,会导致Pydantic模型初始化失败。
修复方案
- 自定义状态类:继承
BaseModel定义包含所有需要传递字段的状态类,替代MessagesState - 修正节点返回逻辑:使用Pydantic的
model_copy方法更新状态,确保返回完整的状态对象 - 调整图的构建:使用自定义状态类初始化
StateGraph - 修复缩进错误:修正
WeatherData类中day字段的缩进
修复后完整代码
from langgraph.graph import StateGraph, START, END from langchain_core.messages import HumanMessage from ollama import chat from pydantic import BaseModel # -------------------------- # Define Schemas # -------------------------- class WeatherData(BaseModel): day: int condition: str high: int low: int class ItineraryItem(BaseModel): day: int morning: str afternoon: str weather: str class Itinerary(BaseModel): itinerary: list[ItineraryItem] class Budget(BaseModel): total_cost: float breakdown: dict # -------------------------- # 自定义状态类:包含所有需要传递的字段 # -------------------------- class TripState(BaseModel): messages: list[HumanMessage] weather_data: dict | None = None itinerary: Itinerary | None = None budget: Budget | None = None goto: str | None = None # -------------------------- # Ollama Chat Model # -------------------------- def ollama_chat(model, messages, schema=None): """Wrapper for the Ollama chat call with an optional JSON schema.""" return chat(model=model, messages=messages, format=schema) # -------------------------- # Weather Agent # -------------------------- def weather_agent(state: TripState) -> TripState: print("Weather Agent: Starting...") print("DEBUG - state at start of weather_agent:", state.dict()) weather_data = { 1: {"condition": "Sunny", "high": 25, "low": 15}, 2: {"condition": "Cloudy", "high": 22, "low": 14}, 3: {"condition": "Rainy", "high": 20, "low": 12}, } print(f"Weather Data: {weather_data}") return state.model_copy(update={ "weather_data": weather_data, "goto": "itinerary_node" }) # -------------------------- # Itinerary Agent # -------------------------- def itinerary_agent(state: TripState) -> TripState: print("Itinerary Agent: Starting...") print("DEBUG - state at start of itinerary_agent:", state.dict()) if not state.weather_data: print("Error: No weather data found in state") return state.model_copy(update={"goto": END}) schema = Itinerary.model_json_schema() print(f"Calling Ollama with weather data: {state.weather_data}") try: response = ollama_chat( model="llama3.2", messages=[ {"role": "system", "content": "You are an itinerary planning agent."}, {"role": "user", "content": f"Plan a 3-day trip to Paris with this weather data: {state.weather_data}"}, ], schema=schema ) itinerary = Itinerary.model_validate_json(response.message.content) print(f"Generated Itinerary: {itinerary}") return state.model_copy(update={ "itinerary": itinerary, "goto": "budget_node" }) except Exception as e: print(f"Error in itinerary generation: {e}") return state.model_copy(update={"goto": END}) # -------------------------- # Budget Agent # -------------------------- def budget_agent(state: TripState) -> TripState: print("Budget Agent: Starting...") print("DEBUG - state at start of budget_agent:", state.dict()) if not state.itinerary: print("Error: No itinerary found in state") return state.model_copy(update={"goto": END}) schema = Budget.model_json_schema() print(f"Calling Ollama with itinerary: {state.itinerary}") try: response = ollama_chat( model="llama3.2", messages=[ {"role": "system", "content": "You are a budget estimation agent."}, {"role": "user", "content": f"Estimate costs for this itinerary: {state.itinerary}"}, ], schema=schema ) budget = Budget.model_validate_json(response.message.content) print(f"Generated Budget: {budget}") return state.model_copy(update={ "budget": budget, "goto": END }) except Exception as e: print(f"Error in budget estimation: {e}") return state.model_copy(update={"goto": END}) # -------------------------- # Supervisor Node # -------------------------- def supervisor_node(state: TripState) -> TripState: print("Supervisor Node: Starting...") print("DEBUG - state at start of supervisor_node:", state.dict()) user_request = state.messages[0].content if state.messages else "" print(f"User request: {user_request}") if "trip" in user_request.lower(): print("Routing to weather agent...") return state.model_copy(update={"goto": "weather_node"}) print("No trip request detected, ending...") return state.model_copy(update={"goto": END}) # -------------------------- # Build the Graph # -------------------------- graph = StateGraph(TripState) graph.add_node("supervisor_node", supervisor_node) graph.add_node("weather_node", weather_agent) graph.add_node("itinerary_node", itinerary_agent) graph.add_node("budget_node", budget_agent) graph.add_edge(START, "supervisor_node") graph.add_edge("supervisor_node","weather_node") graph.add_edge("weather_node", "itinerary_node") graph.add_edge("itinerary_node", "budget_node") graph.add_edge("budget_node", END) # Compile the graph graph = graph.compile() # -------------------------- # Run the Graph # -------------------------- if __name__ == "__main__": print("\nStarting graph execution...") initial_state = TripState( messages=[HumanMessage(content="Plan a 3-day trip to Paris.")] ) result = graph.invoke(initial_state) print("\nFinal Result:") print("Weather:", result.weather_data) print("Itinerary:", result.itinerary) print("Budget:", result.budget)
验证结果
修复后运行,状态传递正常,各Agent能正确接收前序节点生成的数据,最终输出会包含完整的weather_data、itinerary和budget内容,示例日志如下:
Starting graph execution... Supervisor Node: Starting... DEBUG - state at start of supervisor_node: {'messages': [HumanMessage(content='Plan a 3-day trip to Paris.', additional_kwargs={}, response_metadata={}, id='...')], 'weather_data': None, 'itinerary': None, 'budget': None, 'goto': None} User request: Plan a 3-day trip to Paris. Routing to weather agent... Weather Agent: Starting... DEBUG - state at start of weather_agent: {'messages': [HumanMessage(content='Plan a 3-day trip to Paris.', additional_kwargs={}, response_metadata={}, id='...')], 'weather_data': None, 'itinerary': None, 'budget': None, 'goto': 'weather_node'} Weather Data: {1: {'condition': 'Sunny', 'high': 25, 'low': 15}, 2: {'condition': 'Cloudy', 'high': 22, 'low': 14}, 3: {'condition': 'Rainy', 'high': 20, 'low': 12}} Itinerary Agent: Starting... DEBUG - state at start of itinerary_agent: {'messages': [HumanMessage(content='Plan a 3-day trip to Paris.', additional_kwargs={}, response_metadata={}, id='...')], 'weather_data': {1: {'condition': 'Sunny', 'high': 25, 'low': 15}, 2: {'condition': 'Cloudy', 'high': 22, 'low': 14}, 3: {'condition': 'Rainy', 'high': 20, 'low': 12}}, 'itinerary': None, 'budget': None, 'goto': 'itinerary_node'} Calling Ollama with weather data: {1: {'condition': 'Sunny', 'high': 25, 'low': 15}, 2: {'condition': 'Cloudy', 'high': 22, 'low': 14}, 3: {'condition': 'Rainy', 'high': 20, 'low': 12}} Generated Itinerary: itinerary=[ItineraryItem(day=1, morning='Visit Eiffel Tower', afternoon='Explore Champs-Élysées', weather='Sunny'), ...] Budget Agent: Starting... DEBUG - state at start of budget_agent: {'messages': [HumanMessage(content='Plan a 3-day trip to Paris.', additional_kwargs={}, response_metadata={}, id='...')], 'weather_data': {1: {'condition': 'Sunny', 'high': 25, 'low': 15}, 2: {'condition': 'Cloudy', 'high': 22, 'low': 14}, 3: {'condition': 'Rainy', 'high': 20, 'low': 12}}, 'itinerary': Itinerary(itinerary=[...]), 'budget': None, 'goto': 'budget_node'} Calling Ollama with itinerary: ... Generated Budget: total_cost=850.0, breakdown={'accommodation': 400, 'food': 200, 'transport': 150, 'activities': 100} Final Result: Weather: {1: {'condition': 'Sunny', 'high': 25, 'low': 15}, 2: {'condition': 'Cloudy', 'high': 22, 'low': 14}, 3: {'condition': 'Rainy', 'high': 20, 'low': 12}} Itinerary: itinerary=[ItineraryItem(day=1, morning='Visit Eiffel Tower', afternoon='Explore Champs-Élysées', weather='Sunny'), ...] Budget: total_cost=850.0, breakdown={'accommodation': 400, 'food': 200, 'transport': 150, 'activities': 100}
内容的提问来源于stack exchange,提问作者Noah_
相关产品推荐
相关产品推荐

