{"slug": "from-user-query-to-execution-building-a-multi-agent-system-with-langgraph", "title": "From User Query to Execution: Building a Multi-Agent System with LangGraph", "summary": "A developer published a walkthrough of a multi-agent system built with LangGraph that chains three agents — a search agent using the DDGS library, a planning agent, and an execution agent — under an orchestrator that routes control between them. The system shares a MultiAgentState object, inheriting from MessagesState, that carries fields including current_agent, context, task_queue, error_count, and metadata, with a custom reducer applied to the task queue. The orchestrator routes work by checking pending tasks and completion flags such as web_search_completed and planning_completed, looping until the queue is empty and all stages finish.", "body_md": "Hi everyone:\n\nRecently i was working on multiagent system with langrapgh and after completing, i though why not publish it as blog post to guide junior developer familarize with multiagent langraph\n\nThe workflow is relatively simple. There are 3 agents\n\nsearch_agent: it's job is to search internet and collect upto 5 related search results with the help of DDGS library.\n\nplanning agent: gets information from search_agent and ask llm to make plaining strategy to implement execution\n\nexecution agent: takes information from planning agent and execute the required action like making startup project folders or installing libraries etc\n\norchestration controls the workflow and route which agent needs to take implement at current timestamp.\n\nThe overall workflow is like, User query--> orchestrator --> search_agent --> orchestrator --> planning agent --> orchestrator --> execution_agent --> orchestrator-- end\n\nBefore deep dive into the project soure code we need to understand what is state in project. In LangGraph, state is the shared data structure that flows through the graph. Agents can read information from the state and return updates that are then available to other nodes.\n\n Here is the state class\n\n`class MultiAgentState(MessagesState):`  MultiAgentState inherits from MessagesState, which already provides the messages field used to store the conversation messages.\n\n    \"\"\"State shared across all agents - inherits messages from MessagesState\"\"\"\n\n    current_agent: str\n\n    current_model: str\n\n    gpu_memory: Dict[str, int]\n\n    context: Dict[str, Any]\n\n    task_queue: Annotated[List[Dict[str, Any]], task_queue_reducer]  # CRITICAL FIX: Use custom reducer\n\n    agent_states: Dict[str, Dict[str, Any]]\n\n    error_count: int\n\n    metadata: Dict[str, Any]\n\ncurrent_agent: identifies which agent or workflow node should run next\n\ncontext: contain additional information like data fetch from search agent\n\ntask_queue: data from the end result of planing agent which need to be executed by execution agent\n\nerror_count: keep track of workflow errors\n\nmetadata: store additional execution information\n\nFurthermore, lets dive into the project source code so we can understand what each part is doing.\n\n**1. orchestrator.py**\n\nThis is the main file which controls which agent needs to run. First of all, i registered the three agents(search,planning and execution agent) with the help of **OrchestratorAgent.register_agent()**. This allows the orchestrator to route the workflow to those agents. \n\n**OrchestratorAgent.process()** This is where the main routing is happening. First it check if the state contains any messaage, if no then workflow terminates. Next it checks if there are any pending task in the  *task_queue*. If pending task exit then the next agent will be executor because the pending work needs to be perform first.if there are no pending task the orchestor will check whether web research is completed.\n\n*state.context.web_search_completed = False* --> Search Agent. After search agent the state will be updated and  *state.context.web_search_completed = True*. The orchestrotor sees the search_agent has done its job and looks whether the planning agent is being called **state.context.planning_completed = False --> Planning Agent**.The planning agent generate the executeable task and put them into *state.task_queue*_. At this point the orchestrator sees pending task and route execute agent next. The cycle repeats until there is no pending task in the state.task_queue. Finally: No pending tasks+ All workflow stages completed--> END.\n\n**2. Seach_agent.py**\n\nThis is the simplest agent and it's work is to get user query, search internet using DDGS python library.\n\n```\nreturn {\n                \"messages\": [\n                    AIMessage(content=response)\n                ],\n                \"current_agent\": \"orchestrator\",\n                \"context\": {\n                    **state.get(\"context\", {}),\n                    \"search_results\": results,\n                    \"web_search_completed\": True,\n                    \"last_search\": {\n                        \"query\": query,\n                        \"results\": results[:5],\n                        \"timestamp\": datetime.now().isoformat()\n                    }\n                }\n            }\n```\n**3. planning_agent.py**\n\nThe Planning Agent takes the user’s request and the web-search results as input.\n\nIt uses an LLM to break the request into clear, step-by-step technical tasks.\n\nThese tasks are then placed into the task queue for the Execution Agent to perform.\n\n`def init_llm():` For the LLM, I used openai/gpt-oss-120b through Groq. I chose Groq because it provides a fast API and has a free usage tier.\n\n                return ChatGroq(\n\n                    model=\"openai/gpt-oss-120b\",\n\n                    temperature=0,\n\n                    max_tokens=1536\n\n                )\n\nIn Planningagent.process() it first check if there is any pending task in the state.task_queue. if yes then it goes back to orchestrator with state.current_agent == \"executor\", returns `\"current_agent\": (` \n\n                    \"executor\" if pending_tasks else \"end\"\n\n                ),\n\nThen it will get user original message and search result from `state.context.get(\"search_results\", []),`. after getting the required data the function call \n\ntask = self._extract_task(state.get(\"messages\", []))\n\n`plan = await self._generate_plan(`. _generate_plain is actually calling the llm with prompt with relevent user message and searched result. The final return of Planningagent.process() is updated with \"current_agent\" ==\"execute\",\n\n            task=task,\n\n            search_results=search_results\n\n        )\n\n`\"context\": {`\n\n                **context,\n\n                \"current_plan\": plan,\n\n                \"planning_completed\": True,\n\n                \"planning_timestamp\": datetime.now().isoformat()\n\n            }\n\n**4. execution_agent.py**\n\nThe Execution Agent receives the tasks generated by the Planning Agent.\n\nIt identifies the type of task and selects the appropriate execution method.\n\nIt performs actions such as installing dependencies, creating folders, and generating project files/code.\n\nAfter completing each task, it updates the task queue and continues until all tasks are completed\n\nit first check the state.task_queue if there is any pending task, so it will assign the given task to a variable current_task.\n\nAgain the function checks is any pending task is available. If not the the function calls `self._handle_direct_execution(state)`\n\nIn one sentence: _handle_direct_execution() is a fallback path that allows the Execution Agent to handle a user's direct execution request without waiting for the Planning Agent to create a task queue.\n\nif there is pending task in the state.task_queue. it will call `result = await self._execute_task(current_task)`. execut_task purpose is to get the task header, description and checks if there is any word in the header or description containing \"install\",\n\n            \"dependencies\",\n\n            \"libraries\",\n\n            \"packages\",\n\n            \"virtual environment\",. if yes return await `self._execute_installation(task)` is called which main purpose os installing.\n\nsecond option is that if word in the header or description contains _ \"initialize repository\",\n\n            \"initialize project\",\n\n            \"starter project\",\n\n            \"project structure\",\n\n            \"project layout\",\n\n            \"scaffold\",\n\n            \"scaffolding\",\n\n            \"development environment\",_ then `await self._execute_project_setup(task)` execute which main purpose is to create folders for the given topic name.\n\nThird option is if words in header or description contains *\"http client\", \"request handling\",\n            \"http requests\",* it will call \n\n`self._execute_http_client(task)` It typically creates the necessary Python file and code for making web requests, handling errors, and respecting things like robots.txt and rate limits.`self._execute_command(task)`,` await self._execute_storage(task)`,` await self._execute_crawler(task)` which has different purposes.```\nupdated_task = {\n            **current_task,\n            \"status\": \"completed\" if result[\"success\"] else \"failed\",\n            \"completed_at\": datetime.now().isoformat(),\n            \"result\": result\n        },\nreturn {\n            \"messages\": [response_message],\n            \"current_agent\": next_agent,\n            \"task_queue\": updated_task_queue,\n            \"context\": {\n                **state.get(\"context\", {}),\n                \"last_execution\": result,\n                \"execution_timestamp\": datetime.now().isoformat()\n            }\n        }\n```\n.\n**5. main.py**\n\nEntry point of the project which imports `MultiAgentOrchestrator`  from orchestrator.py and initailizing all the agents after. Registering all the agents in orchestrator and buidling the graph.\n\nUser need to run the  main file like `python main.py research \"Research the best approach for building a Python news web scraper, identify the required libraries and challenges, create a step-by-step implementation plan, and set up a starter project structure.\"` and all the steps will be printed after that.\n\n[Github link](https://github.com/almovidhussaini/multi-agent-planning-websearch-execution)\n\nPlease read the readme.md file to start the project.\n\nIf you have any question feel free to ask: [shahalmovid@gmail.com](mailto:shahalmovid@gmail.com) \n\nHappy Coding:", "url": "https://wpnews.pro/news/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph", "canonical_source": "https://dev.to/shah_almoveed_22752c60f0/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph-5g2h", "published_at": "2026-09-12 14:32:00+00:00", "updated_at": "2026-09-12 14:44:34.291747+00:00", "lang": "en", "topics": ["ai-agents", "large-language-models", "ai-tools", "developer-tools", "artificial-intelligence"], "entities": ["LangGraph", "DDGS", "MultiAgentState", "MessagesState", "OrchestratorAgent"], "alternates": {"html": "https://wpnews.pro/news/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph", "markdown": "https://wpnews.pro/news/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph.md", "text": "https://wpnews.pro/news/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph.txt", "jsonld": "https://wpnews.pro/news/from-user-query-to-execution-building-a-multi-agent-system-with-langgraph.jsonld"}}