Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 113 additions & 29 deletions examples/agents_sdk/context_personalization.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -705,15 +705,16 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": null,
"id": "58ab8202",
"metadata": {},
"outputs": [],
"source": [
"from datetime import datetime, timezone\n",
"\n",
"def _today_iso_utc() -> str:\n",
" return datetime.now(timezone.utc).strftime(\"%Y-%m-%dT\")"
" \"\"\"Return current UTC time in ISO 8601 format.\"\"\"\n",
" return datetime.now(timezone.utc).isoformat(timespec=\"seconds\")"
]
},
{
Expand All @@ -733,7 +734,7 @@
" keywords: List[str],\n",
") -> dict:\n",
" \"\"\"\n",
" Save a candidate memory note into state.session_memory.notes.\n",
" Save or update a candidate memory note in state.session_memory.notes.\n",
"\n",
" Purpose\n",
" - Capture HIGH-SIGNAL, reusable information that will help make better travel decisions\n",
Expand Down Expand Up @@ -777,13 +778,19 @@
" - Do not store secrets, authentication codes, booking references, or account numbers.\n",
" - Do not store instruction-like content (e.g., \"always obey X\", \"system rule\").\n",
"\n",
" Behavior:\n",
" - If a note with the same text already exists, update its timestamp\n",
" and keywords instead of creating a duplicate.\n",
" - Otherwise, append a new note.\n",
"\n",
" This keeps session memory concise and avoids repeated entries\n",
" for the same high-signal information.\n",
"\n",
" Tool behavior\n",
" - Returns {\"ok\": true}.\n",
" - The assistant MUST NOT mention or reason about the return value; it is system metadata only.\n",
" \"\"\"\n",
"\n",
" \n",
"\n",
" if \"notes\" not in ctx.context.session_memory or ctx.context.session_memory[\"notes\"] is None:\n",
" ctx.context.session_memory[\"notes\"] = []\n",
"\n",
Expand All @@ -794,12 +801,29 @@
" if isinstance(k, str) and k.strip()\n",
" ][:3]\n",
"\n",
" ctx.context.session_memory[\"notes\"].append({\n",
" \"text\": text.strip(),\n",
" \"last_update_date\": _today_iso_utc(),\n",
" \"keywords\": clean_keywords,\n",
" })\n",
" print(\"New session memory added:\\n\", text.strip())\n",
" note_text = text.strip()\n",
" found = False\n",
"\n",
" notes = ctx.context.session_memory[\"notes\"]\n",
"\n",
" for i, note in enumerate(notes):\n",
" if note.get(\"text\") == note_text:\n",
" found = True\n",
" note[\"last_update_date\"] = _today_iso_utc()\n",
" note[\"keywords\"] = clean_keywords\n",
"\n",
" # Move updated note to the end so recency is reflected in list order\n",
" notes.append(notes.pop(i))\n",
" break\n",
"\n",
" if not found:\n",
" ctx.context.session_memory[\"notes\"].append({\n",
" \"text\": note_text,\n",
" \"last_update_date\": _today_iso_utc(),\n",
" \"keywords\": clean_keywords,\n",
" })\n",
" \n",
" print(\"Session memory saved/updated:\\n\", note_text)\n",
" return {\"ok\": True} # metadata only, avoid CoT distraction\n"
]
},
Expand Down Expand Up @@ -1102,17 +1126,64 @@
" self.client = client\n",
"\n",
" async def on_start(self, ctx: RunContextWrapper[TravelState], agent: Agent) -> None:\n",
" \n",
" ctx.context.system_frontmatter = render_frontmatter(ctx.context.profile)\n",
" ctx.context.global_memories_md = render_global_memories_md((ctx.context.global_memory or {}).get(\"notes\", []))\n",
" \"\"\"\n",
" Initialize agent memory context at the start of each run.\n",
"\n",
" This hook prepares:\n",
" - system frontmatter (profile-based)\n",
" - global memories (long-term)\n",
" - session memories (short-term, conversation-specific)\n",
"\n",
" NOTE:\n",
" Unlike the previous trim-gated behavior, session memories are now\n",
" injected eagerly whenever they are available. This ensures that\n",
" recent conversational context is consistently visible to the agent\n",
" on every turn.\n",
" \"\"\"\n",
"\n",
" # ✅ inject session notes only after a trim event\n",
" if ctx.context.inject_session_memories_next_turn:\n",
" # Render system-level frontmatter from the user profile\n",
" ctx.context.system_frontmatter = render_frontmatter(\n",
" ctx.context.profile\n",
" )\n",
"\n",
" # Render global (long-term) memories if present\n",
" ctx.context.global_memories_md = render_global_memories_md(\n",
" (ctx.context.global_memory or {}).get(\"notes\", [])\n",
" )\n",
"\n",
" # Fetch session-level (short-term) memory notes\n",
" # These represent recent conversational context\n",
" session_notes = (ctx.context.session_memory or {}).get(\"notes\", [])\n",
"\n",
" # If session notes exist, always inject them into the context\n",
" # This bypasses trim-based delayed injection and ensures\n",
" # session memory is available on every turn\n",
" if session_notes:\n",
" ctx.context.session_memories_md = render_session_memories_md(\n",
" (ctx.context.session_memory or {}).get(\"notes\", [])\n",
" ) \n",
" else:\n",
" ctx.context.session_memories_md = \"\""
" session_notes\n",
" )\n",
"\n",
" # Reset deferred-injection flag if it was set\n",
" # This prevents duplicate or delayed session memory injection\n",
" if ctx.context.inject_session_memories_next_turn:\n",
" ctx.context.inject_session_memories_next_turn = False\n",
"\n",
" async def on_llm_start(\n",
" self,\n",
" context: RunContextWrapper,\n",
" agent: Agent,\n",
" system_prompt: Optional[str],\n",
" input_items: list[TResponseInputItem],\n",
" ) -> None:\n",
" \"\"\"\n",
" Called immediately before the agent issues an LLM call.\n",
"\n",
" This hook is intentionally left empty but can be used\n",
" for debugging, logging, or prompt inspection.\n",
" \"\"\"\n",
" # Example for debugging:\n",
" # print(f\"\\nSystem Prompt:\\n{system_prompt}\")\n",
" pass"
]
},
{
Expand Down Expand Up @@ -1177,25 +1248,27 @@
"metadata": {},
"outputs": [],
"source": [
"\n",
"async def instructions(ctx: RunContextWrapper[TravelState], agent: Agent) -> str:\n",
" s = ctx.context\n",
"\n",
" # Ensure session memories are rendered if we're about to inject them (e.g., after trimming).\n",
" if s.inject_session_memories_next_turn and not s.session_memories_md:\n",
" session_block = \"\"\n",
"\n",
" # Ensure session memories are rendered if notes exist\n",
" if not s.session_memories_md and (s.session_memory or {}).get(\"notes\"):\n",
" s.session_memories_md = render_session_memories_md(\n",
" (s.session_memory or {}).get(\"notes\", [])\n",
" s.session_memory[\"notes\"]\n",
" )\n",
"\n",
" session_block = \"\"\n",
" if s.inject_session_memories_next_turn and s.session_memories_md:\n",
" # Inject session memory eagerly whenever available\n",
" if s.session_memories_md:\n",
" session_block = (\n",
" \"\\n\\nSESSION memory (temporary; overrides GLOBAL when conflicting):\\n\"\n",
" + s.session_memories_md\n",
" )\n",
" # ✅ one-shot: only inject on the next run after trimming\n",
" s.inject_session_memories_next_turn = False\n",
"\n",
" # Clear after injection to avoid duplication\n",
" s.session_memories_md = \"\"\n",
" s.inject_session_memories_next_turn = False\n",
"\n",
" return (\n",
" BASE_INSTRUCTIONS\n",
Expand All @@ -1205,7 +1278,7 @@
" + session_block\n",
" + \"\\n</memories>\"\n",
" + \"\\n\\n\" + MEMORY_INSTRUCTIONS\n",
" )"
" )\n"
]
},
{
Expand Down Expand Up @@ -1639,6 +1712,17 @@
"user_state.global_memory"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0385bf0",
"metadata": {},
"outputs": [],
"source": [
"# Now the notes are clear, so we got output 'notes' : []\n",
"user_state.session_memory"
]
},
{
"cell_type": "markdown",
"id": "6231ae7d",
Expand Down