Semantic Router Agent

Source notebook

Repo path: gemini/agents/genai-experience-concierge/agent-design-patterns/semantic-router.ipynb · Open on GitHub · intermediate

Builds a LangGraph semantic router that sends retail queries to mocked Gemini expert assistants.

Summary

The notebook teaches a semantic router agent pattern for dynamically selecting one expert assistant based on user intent. It uses Gemini through the Google GenAI SDK on Vertex AI to classify each query into retail search, customer support, or unsupported, then routes through LangGraph nodes. The workflow defines typed state, structured router output, streaming expert responses, post-processing, graph compilation, and a test conversation with shared session history.

Key code patterns

Vertex GenAI client

client = genai.Client(
    vertexai=True,
    project=agent_config.project,
    location=agent_config.region,
)

Initializes Gemini access through Vertex AI with project and region from agent config.

Structured router classification

response = await client.aio.models.generate_content(
    model=agent_config.router_model_name,
    contents=contents,
    config=genai_types.GenerateContentConfig(
        response_mime_type="application/json",
        response_schema=RouterClassification,
    ),
)
router_classification = RouterClassification.model_validate_json(response.text)

Forces the router to return a typed JSON classification before selecting the next node.

LangGraph routing command

match router_classification.target:
    case RouterTarget.retail_search:
        next_node = RETAIL_NODE_NAME
    case RouterTarget.customer_service:
        next_node = CUSTOMER_SERVICE_NODE_NAME
    case RouterTarget.unsupported:
        next_node = POST_PROCESS_NODE_NAME
return lg_types.Command(update=GraphSession(current_turn=current_turn), goto=next_node)

Maps classifier output to the graph transition for retail, customer service, or fallback handling.

Streaming expert response

response = await client.aio.models.generate_content_stream(
    model=agent_config.chat_model_name,
    contents=contents,
    config=genai_types.GenerateContentConfig(
        temperature=0.2,
        seed=0,
        system_instruction=RETAIL_SYSTEM_PROMPT,
    ),
)
async for chunk in response:
    stream_writer({"text": chunk.text})

Streams Gemini response chunks from a selected mocked sub-agent into LangGraph custom output.

Memory-backed graph

state_graph = graph.StateGraph(state_schema=GraphSession)
state_graph.add_node(ROUTER_NODE_NAME, ainvoke_router)
state_graph.add_node(RETAIL_NODE_NAME, ainvoke_retail_search)
state_graph.add_node(CUSTOMER_SERVICE_NODE_NAME, ainvoke_customer_service)
state_graph.set_entry_point(ROUTER_NODE_NAME)
compiled_graph = state_graph.compile(memory_checkpoint.MemorySaver())

Builds the stateful agent graph and keeps thread-scoped conversation state in memory.

Models & APIs used

  • Models: gemini-3.5-flash
  • APIs / services: Vertex AI
  • SDKs / libraries: google-genai, langgraph, langgraph-checkpoint, langchain_core, pydantic, IPython

When to use this

Use this pattern when one conversational interface must route user turns to different specialized agent backends by intent.

Gotchas & caveats

  • Requires Google Cloud project ID and a Vertex AI region; defaults to GOOGLE_CLOUD_PROJECT when unset.
  • Colab users must authenticate with google.colab.auth.authenticate_user().
  • The notebook says to restart the runtime after installing langgraph, langgraph-checkpoint, and google-genai.
  • The retail and customer service assistants are mocked Gemini calls and are allowed to make up Cymbal information for demo purposes.
  • Router context is limited by MAX_TURN_HISTORY, set to 3 in the notebook.

Best practices

  • Use a structured Pydantic schema for router output instead of parsing free-form text.
  • Keep router temperature low and seed fixed for more deterministic classification.
  • Store user and model messages per turn so later calls can include conversation history.
  • Route unsupported inputs to a fallback response instead of an expert node.
  • Use LangGraph MemorySaver with thread_id to preserve session-specific state.