Task Planner Agent
Source notebook
Repo path:
gemini/agents/genai-experience-concierge/agent-design-patterns/task-planner.ipynb· Open on GitHub · advanced
Builds a LangGraph task-planner agent that plans, searches with Gemini, reflects, and responds.
Summary
The notebook teaches a multi-agent task planner architecture with Planner, Executor, Reflector, and Post-Process nodes. It shows how to generate a structured plan or direct response, execute plan tasks with Gemini and Google Search grounding, reflect on results, and either add more tasks or return a final response. It also demonstrates LangGraph state management, in-memory checkpointing, retries, and notebook streaming for a conversational session.
Key code patterns
Structured plan or response
client = genai.Client(vertexai=True, project=project, location=region)
content_response = await client.aio.models.generate_content(
model=model_name,
contents=contents,
config=genai_types.GenerateContentConfig(
response_mime_type="application/json",
response_schema=PlanOrRespond,
system_instruction=system_instruction,
),
)
return PlanOrRespond.model_validate_json(content_response.text)Constrains Gemini output to a Pydantic schema so routing can choose between a plan and a response.
Grounded task execution
search_tool = genai_types.Tool(google_search=genai_types.GoogleSearch())
for idx, task in enumerate(executed_plan.tasks):
if task.result is not None:
continue
content_response = await client.aio.models.generate_content(
model=model_name,
contents=contents,
config=genai_types.GenerateContentConfig(tools=[search_tool]),
)
task.result = content_response.text
yield idx, taskUses the Google Search tool only in the executor to fill task results with live research findings.
LangGraph agent loop
state_graph = graph.StateGraph(GraphSession)
state_graph.add_node(PLANNER_NODE_NAME, planner_node)
state_graph.add_node(EXECUTOR_NODE_NAME, executor_node)
state_graph.add_node(REFLECTOR_NODE_NAME, reflector_node)
state_graph.add_node(POST_PROCESS_NODE_NAME, post_process_node)
state_graph.set_entry_point(PLANNER_NODE_NAME)
compiled_graph = state_graph.compile(checkpointer=memory.MemorySaver())Defines the planner, executor, reflector, and post-processing flow with session checkpointing.
Custom stream updates
async for stream_mode, chunk in compiled_graph.astream(
input={"current_turn": {"user_input": user_input}},
config={"configurable": {"thread_id": thread_id, "agent_config": agent_config}},
stream_mode=["custom"],
):
if "plan" in chunk:
text = stringify_plan(Plan.model_validate(chunk["plan"]))
elif "executed_task" in chunk:
text = stringify_task(Task.model_validate(chunk["executed_task"]))Streams generated plans, executed tasks, and responses into the notebook as the graph runs.
Retry transient failures
@retry(
retry=retry_if_exception(is_retryable_error),
wait=wait_exponential(min=1, max=10),
stop=stop_after_attempt(3),
reraise=True,
)
async def generate_plan(...):
...Retries selected API and connection failures without hiding final errors.
Models & APIs used
- Models: gemini-3.5-flash
- APIs / services: Vertex AI, Google Search Grounding
- SDKs / libraries:
google-genai,langgraph,langgraph-checkpoint,pydantic,tenacity,requests,IPython,langchain_core
When to use this
Use this pattern when a user request needs multi-step research, live search, and iterative planning instead of a single model response.
Gotchas & caveats
- Colab users must authenticate with google.colab.auth.authenticate_user().
- The runtime must be restarted after installing the notebook dependencies.
- PROJECT_ID must be provided or resolved from GOOGLE_CLOUD_PROJECT, and REGION defaults to us-central1.
- The notebook states this multi-agent design can be much slower than a single-agent design because one turn can include many LLM calls and tool uses.
- The executor iterates through a linear task list and skips only tasks whose result is already set.
- Retry handling is limited to APIError codes 429, 502, 503, 504 and requests ConnectionError, with three attempts.
- Reflector execution asserts that every plan task has a non-empty result before reflection.
Best practices
- Use Pydantic models for Task, Plan, Response, and PlanOrRespond to keep agent decisions structured.
- Reset task results to None before executing newly generated plan tasks.
- Separate planning, execution, reflection, and post-processing into explicit LangGraph nodes.
- Use Google Search grounding in the executor for research tasks requiring live information.
- Use a MemorySaver checkpointer and thread_id to preserve conversation state across turns.
- Stream plan, executed_task, and response events so users can observe long-running agent progress.
- Apply exponential backoff retries to transient GenAI API and connection errors.
Related
- Concepts: Agents & ADK · Function Calling & Tools · RAG & Grounding
- Entities: Vertex AI · Google GenAI SDK · LangGraph · Grounding · Function Calling · Gemini
- Area: Gemini Notebooks
- Best practices: Agents & ADK - Best Practices · Function Calling & Tools - Best Practices · RAG & Grounding - Best Practices