The dynamic expert agent
Two ideas close this trail, and they combine. Hyper-personalization: the agent knows the user’s preferences and history. The dynamic expert agent: a router classifier identifies the user’s intent, then dynamically assembles the context and tools that intent needs.
Personalization
Personalization is the stateful context from the anatomy of contextAnatomy of contextFive kinds of context, told apart by where they come from and how long they live.Opens in a new tab made visible: facts about this user, kept in a long-term store and pulled in when relevant. LangChain describes semantic memory as the kind used to personalize an application by remembering facts from past interactions, and a profile as one continuously updated record. The catch: a profile that keeps growing becomes error-prone to update. See agent memoryAgent memoryWhat an agent remembers: the thread it is in, and what it keeps across threads.Opens in a new tab.
The expert
The router makes one small decision, which expert. What follows is assembly: that expert’s own prompt, only its tools (see scopingContext scopingShow the model only what it needs for this step, tools included.Opens in a new tab) and the user’s stored preferences. It is the context assemblerThe context assemblerThe step that builds the prompt from scratch on every loop.Opens in a new tab with the choice of ingredients made per question. Experts are plain configuration, so adding one is adding an entry.
from typing import Literalfrom pydantic import BaseModelfrom langchain.agents import create_agentfrom langgraph.graph import MessagesStatefrom langgraph.runtime import Runtime class Intent(BaseModel): name: Literal["billing", "tech"] router = llm.with_structured_output(Intent) EXPERTS = { "billing": {"prompt": "You are a billing specialist.", "tools": [lookup_invoice, issue_refund]}, "tech": {"prompt": "You are a technical support engineer.", "tools": [search_docs, run_diagnostics]},} def expert(state: MessagesState, runtime: Runtime) -> dict: question = state["messages"][-1].content intent = router.invoke(question).name # 1. classify the intent prefs = runtime.store.search((runtime.context.user_id, "preferences"), limit=5) # 2. who is asking agent = create_agent( # 3. build only what this needs llm, tools=EXPERTS[intent]["tools"], system_prompt=f"{EXPERTS[intent]['prompt']}\nUser preferences: {[p.value for p in prefs]}", ) return agent.invoke({"messages": state["messages"]})The catch
Anthropic’s routing workflow exists to separate concerns, so that each kind of input gets a specialised prompt. The cost is the router itself: a wrong classification sends the question to an expert without the tools it needs. The router needs its own test cases (see agent evaluationAgent evaluationTest the flow an agent took and what it left behind, against the goal and the guardrails.Opens in a new tab) and a fallback. When each expert is a full agent in its own right, this becomes the router patternRouter patternA manager that only decides which specialist handles the request.Opens in a new tab.
Sources
- Memory overview, LangChain Docs · accessed October 5, 2026
- Memory, LangGraph Docs · accessed October 5, 2026
- Building effective agents, Anthropic Engineering · accessed October 5, 2026