policy_agent specialistThe policy_agent answers two distinct questions:
OracleVS.AsyncOracleStore, keyed by user_id.Two questions, two tools. One agent.
get_planner_policy (sync, no args)@tool
def get_planner_policy() -> str:
"""Fetch the standing planner-prefs buy-volume policy from OracleVS."""
docs = oracle_vs.similarity_search("planner buy volume policy", k=1)
return docs[0].page_content if docs else "No policy on file."
It’s a targeted similarity search by intent (“policy”), with k=1.
The seed step wrote exactly one policy memo into OracleVS with
metadata {"type": "policy", "name": "planner_buy_volume"}, so this
always returns that single document.
get_user_memory (async, takes user_id)@tool
async def get_user_memory(user_id: str) -> str:
"""Look up long-term saved preferences for a planner by their user_id."""
items = await agent_store.asearch(
("users", user_id, "memories"),
query="preference",
limit=3,
)
if not items:
return f"No saved memories for user_id={user_id}."
return "\n".join(f"- {it.value.get('note', '')}" for it in items)
Two things to notice:
async def. AsyncOracleStore.asearch(...) is awaitable.
create_agent handles async tools transparently — you don’t need a
sync-async wrapper.("users", user_id, "memories") — the namespace pattern.
user_id is interpolated from the function argument, so each
planner sees only their own memories.user_id through the supervisorHow does the policy_agent know which user_id to look up? The
user mentions it in the request:
“I’m planner with user_id=priya. Pull demand intel and draft a buy recommendation that respects my preferences.”
The supervisor delegates to policy_agent and the specialist’s LLM
parses user_id=priya out of the message before deciding to call
get_user_memory(user_id="priya"). We don’t need a structured input
schema — the system prompt tells the agent that user_id arrives in
the supervisor’s request.
policy_agent = create_agent(
agent_model,
tools=[get_planner_policy, get_user_memory],
system_prompt=(
"You are the policy and preference agent. Use `get_planner_policy` for the "
"standing buy-volume policy, and `get_user_memory(user_id=...)` for the active "
"planner's saved preferences (the user_id is mentioned in the supervisor's "
"request). Return both verbatim — do not editorialise."
),
name="policy_agent",
)
The "Return both verbatim" instruction is deliberate: we don’t want
this specialist to interpret the policy or the user preference. That
interpretation is the supervisor’s job (Part 10). Specialists report;
the supervisor decides.
Both tools and the create_agent call. The hard-stop checkpoint:
get_planner_policy.invoke({}) and asserts the result
contains “policy” or “500” (the policy threshold).get_user_memory.ainvoke({"user_id": "priya"}) and asserts
the note contains “conservative” (Priya’s preference).If either fails, you’ve either pointed at the wrong table/namespace or your implementation isn’t returning what the tool’s docstring promises.
Drop this into the TODO 8 cell, replacing both raise NotImplementedError
bodies and the policy_agent = None line:
@tool
def get_planner_policy() -> str:
"""Fetch the standing planner-prefs buy-volume policy from OracleVS."""
docs = oracle_vs.similarity_search("planner buy volume policy", k=1)
return docs[0].page_content if docs else "No policy on file."
@tool
async def get_user_memory(user_id: str) -> str:
"""Look up long-term saved preferences for a planner by their user_id."""
items = await agent_store.asearch(
("users", user_id, "memories"),
query="preference",
limit=3,
)
if not items:
return f"No saved memories for user_id={user_id}."
return "\n".join(f"- {it.value.get('note', '')}" for it in items)
policy_agent = create_agent(
agent_model,
tools=[get_planner_policy, get_user_memory],
system_prompt=(
"You are the policy and preference agent. Use `get_planner_policy` for the "
"standing buy-volume policy, and `get_user_memory(user_id=...)` for the active "
"planner's saved preferences (the user_id is mentioned in the supervisor's "
"request). Return both verbatim — do not editorialise."
),
name="policy_agent",
)
print(f"policy_agent compiled on {LLM_PROVIDER}/{LLM_MODEL}")
→ Part 10 — Supervisor — tie both specialists together with langgraph_supervisor.