Long-Term Memory in AI Agents: A Structured Approach with LangMem
🌟 Introduction
In today’s fast-paced digital world, email management has become a critical productivity challenge. This article explores an AI-powered email assistant that combines three types of memory systems to intelligently handle communications:
- Semantic Memory: Stores factual knowledge (contacts, company info)
- Episodic Memory: Remembers email conversation history
- Procedural Memory: Learns user preferences for email handling
What is LangMem?
Long-term memory allows agents to remember important information across conversations. LangMem provides ways to extract meaningful details from chats, store them, and use them to improve future interactions. At its core, each memory operation in LangMem follows the same pattern:
- Accept conversation(s) and current memory state
- Prompt an LLM to determine how to expand or consolidate the memory state
- Respond with the updated memory state
LangMem helps agents learn and adapt from their interactions over time.
It provides tooling to extract important information from conversations, optimize agent behavior through prompt refinement, and maintain long-term memory.
It offers both functional primitives you can use with any storage system and native integration with LangGraph’s storage layer.
This lets your agents continuously improve, personalize their responses, and maintain consistent behavior across sessions.
Key features
- 🧩 Core memory API that works with any storage system
- 🧠 Memory management tools that agents can use to record and search information during active conversations “in the hot path”
- ⚙️ Background memory manager that automatically extracts, consolidates, and updates agent knowledge
- ⚡ Native integration with LangGraph’s Long-term Memory Store, available by default in all LangGraph Platform deployments
Types of Memory
The Tech Stack 🛠️
- LangGraph for workflow orchestration 🔄
- Groq’s llama-3.3–70b-versatile for AI processing 🧠
- Streamlit for the user interface 💻
- LangSmith for monitoring and debugging 📊
The Architecture: A Three-Memory System 🏗️
1. Semantic Memory 📚
Stores and retrieves conceptual information:
from langmem import create_manage_memory_tool, create_search_memory_tool
manage_memory_tool = create_manage_memory_tool(
namespace=("email_assistant", "{user_id}", "collection")
)
search_memory_tool = create_search_memory_tool(
namespace=("email_assistant", "{user_id}", "collection")
)
2. Episodic Memory 📝
Maintains conversation history and past interactions:
class CodeReviewState(TypedDict):
email_input: dict
messages: Annotated[list, add_messages]
profile: dict
3. Procedural Memory ⚙️
Handles email processing rules and workflows:
prompt_instructions = {
"triage_rules": {
"ignore": "Marketing newsletters, spam emails…",
"notify": "Team member out sick, build notifications…",
"respond": "Direct questions, meeting requests…"
}
}
The Core Components 🔧
1. Email Triage System 📧
```python
def triage_router(state: State, config, store) -> Command:
# Analyzes incoming emails and routes them to:
# — IGNORE 🚫
# — NOTIFY 🔔
# — RESPOND ✍️
```
### 2. In-Memory Store 💾
store = InMemoryStore(index={"embed": embeddings})
3. LangGraph Workflow Engine 🔄
def create_workflow():
workflow = StateGraph(State)
workflow.add_node(triage_router)
workflow.add_node("response_agent", response_agent)
workflow.add_edge(START, "triage_router")
Smart Features 🌟
1. Multi-Prompt Optimizer📈
Create a multi-prompt optimizer that improves prompt effectiveness. This function creates an optimizer that can analyze and improve multiple prompts simultaneously using the same optimization strategy. Each prompt is optimized using the selected strategy
from langmem import create_multi_prompt_optimizer
optimizer = create_multi_prompt_optimizer(
"groq:llama-3.3-70b-versatile",
kind="prompt_memory",
)
# Example conversation with feedback
conversation = [
{"role": "user", "content": "Tell me about the solar system"},
{"role": "assistant", "content": "The solar system consists of..."},
]
feedback = {"clarity": "needs more structure"}
# Use conversation history to improve the prompts
trajectories = [(conversation, feedback)]
prompts = [
{"name": "research", "prompt": "Research the given topic thoroughly"},
{"name": "summarize", "prompt": "Summarize the research findings"},
]
better_prompts = await optimizer.ainvoke(
{"trajectories": trajectories, "prompts": prompts}
)
print(better_prompts)
Prompt Memory Optimizer
Learns from conversation history:
- Extracts successful patterns from past interactions
- Identifies improvement areas from feedback
- Applies learned patterns to new prompts
2. Context-Aware Responses ✨
@traceable(name="create_summary")
def create_summary(state: CodeReviewState):
summary_prompt = ChatPromptTemplate.from_messages([
SystemMessage(content="""Create a concise summary…"""),
HumanMessage(content="Review: {review}\nSeverity: {severity}")
])
3. Semantic Search 🔍
store.get(("userid,), "namespace").value['prompt']
High Level Implementation
1. Email Management 📨
- Automatic email categorization
- Priority-based handling
- Smart response generation
2. Meeting Scheduling 📅
@tool
def schedule_meeting(
attendees: list[str],
subject: str,
duration_minutes: int,
preferred_day: str
) -> str:
return f"Meeting scheduled for {preferred_day}"
3. Task Tracking 📋
- Monitors action items
- Follows up on pending tasks
- Updates task status
Performance Monitoring 📊
LangSmith Integration
from uuid import uuid4
import os
from langsmith import Client
unique_id = uuid4().hex[0:8]
#
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = f"Code_Review - {unique_id}"
os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGSMITH_API_KEY"] = os.getenv("LANGSMITH_API_KEY")
Code Implementation
1. Installation
pip install langchain langchain-groq langsmith langgraph
2. Environment Configuration
from langsmith import Client
from langsmith.run_helpers import traceable
from uuid import uuid4
import os
# Initialize LangSmith
unique_id = uuid4().hex[0:8]
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = f"Email_Assitant - {unique_id}"
os.environ["LANGSMITH_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGSMITH_API_KEY"] = os.getenv("LANGSMITH_API_KEY")
client = Client()
3. Setup Profile
profile = {
"name": "Plaban",
"full_name": "Plaban Nayak",
"user_profile_background": "Senior software engineer leading a team of 5 developers",
}
4. Prompt Instructions
prompt_instructions = {
"triage_rules": {
"ignore": "Marketing newsletters, spam emails, mass company announcements",
"notify": "Team member out sick, build system notifications, project status updates",
"respond": "Direct questions from team members, meeting requests, critical bug reports",
},
"agent_instructions": "Use these tools when appropriate to help manage Plaban's tasks efficiently."
}
5. Sample Email Input
email = {
"from": "Alice Smith ",
"to": "Plaban Nayak",
"subject": "Quick question about API documentation",
"body": """
Hi John,
I was reviewing the API documentation for the new authentication service and noticed a few endpoints seem to be missing from the specs. Could you help clarify if this was intentional or if we should update the docs?
Specifically, I'm looking at:
- /auth/refresh
- /auth/validate
Thanks!
Alice""",
}
6. Instantiate the Memory Store
from langgraph.store.memory import InMemoryStore
from langchain_huggingface import HuggingFaceEmbeddings
model_name = "BAAI/bge-small-en"
model_kwargs = {"device": "cpu"}
encode_kwargs = {"normalize_embeddings": True}
embeddings = HuggingFaceEmbeddings(
model_name=model_name, model_kwargs=model_kwargs, encode_kwargs=encode_kwargs
)
#
store = InMemoryStore(index={"embed": embeddings})
7. Instantiate LLMs
from langchain_groq import ChatGroq
llm = ChatGroq(model="llama-3.3-70b-versatile", api_key=os.getenv("GROQ_API_KEY"))
8. Different Prompts
# Agent prompt baseline
agent_system_prompt = """
< Role >
You are {full_name}'s executive assistant. You are a top-notch executive assistant who cares about {name} performing as well as possible.
Role >
< Tools >
You have access to the following tools to help manage {name}'s communications and schedule:
1. write_email(to, subject, content) - Send emails to specified recipients
2. schedule_meeting(attendees, subject, duration_minutes, preferred_day) - Schedule calendar meetings
3. check_calendar_availability(day) - Check available time slots for a given day
Tools >
< Instructions >
{instructions}
Instructions >
"""
# Agent prompt semantic memory
agent_system_prompt_memory = """
< Role >
You are {full_name}'s executive assistant. You are a top-notch executive assistant who cares about {name} performing as well as possible.
Role >
< Tools >
You have access to the following tools to help manage {name}'s communications and schedule:
1. write_email(to, subject, content) - Send emails to specified recipients
2. schedule_meeting(attendees, subject, duration_minutes, preferred_day) - Schedule calendar meetings
3. check_calendar_availability(day) - Check available time slots for a given day
4. manage_memory("email_assistant", user, "collection") - Store any relevant information about contacts, actions, discussion, etc. in memory for future reference
5. manage_memory("email_assistant", user, "user_profile") - Store any relevant information about the recipient, {name}, in the user profile for future reference the current user profile is shown below
6. search_memory("email_assistant", user, "collection") - Search memory for detail from previous emails
7. manage_memory("email_assistant", user, "instructions") - Update the instructions for agent tool usage based upon the user feedback
Tools >
< User profile >
{profile}
User profile >
< Instructions >
{instructions}
Instructions >
"""
# Triage prompt
triage_system_prompt = """
< Role >
You are {full_name}'s executive assistant. You are a top-notch executive assistant who cares about {name} performing as well as possible.
Role >
< Background >
{user_profile_background}.
Background >
< Instructions >
{name} gets lots of emails. Your job is to categorize each email into one of three categories:
1. IGNORE - Emails that are not worth responding to or tracking
2. NOTIFY - Important information that {name} should know about but doesn't require a response
3. RESPOND - Emails that need a direct response from {name}
Classify the below email into one of these categories.
Instructions >
< Rules >
Emails that are not worth responding to:
{triage_no}
There are also other things that {name} should know about, but don't require an email response. For these, you should notify {name} (using the `notify` response). Examples of this include:
{triage_notify}
Emails that are worth responding to:
{triage_email}
Rules >
< Few shot examples >
{examples}
Few shot examples >
"""
triage_user_prompt = """
Please determine how to handle the below email thread:
From: {author}
To: {to}
Subject: {subject}
{email_thread}"""
9. Format example input in the format of the prompt
template = """Email Subject: {subject}
Email From: {from_email}
Email To: {to_email}
Email Content:
```
{content}
```
> Triage Result: {result}"""
# Format list of few shots
def format_few_shot_examples(examples):
strs = ["Here are some previous examples:"]
for eg in examples:
strs.append(
template.format(
subject=eg.value["email"]["subject"],
to_email=eg.value["email"]["to"],
from_email=eg.value["email"]["author"],
content=eg.value["email"]["email_thread"][:400],
result=eg.value["label"],
)
)
return "\n\n------------\n\n".join(strs)
10. Define Response Format
from pydantic import BaseModel, Field
from typing_extensions import TypedDict, Literal, Annotated
#
class Router(BaseModel):
"""Analyze the unread email and route it according to its content."""
reasoning: str = Field(
description="Step-by-step reasoning behind the classification."
)
classification: Literal["ignore", "respond", "notify"] = Field(
description="The classification of an email: 'ignore' for irrelevant emails, "
"'notify' for important information that doesn't need a response, "
"'respond' for emails that need a reply",
)
#
llm_router = llm.with_structured_output(Router)
11. Define the state
from langgraph.graph import add_messages
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command
from typing import Literal
from IPython.display import Image, display
class State(TypedDict):
email_input: dict
messages: Annotated[list, add_messages]
profile: dict
12. Updated triage_router gets ignore, notify and respond rule from store
@traceable(name="triage_router")
def triage_router(state: State, config, store) -> Command[
Literal["response_agent", "__end__"]
]:
author = state['email_input']['author']
to = state['email_input']['to']
subject = state['email_input']['subject']
email_thread = state['email_input']['email_thread']
namespace = (
"email_assistant",
config['configurable']['langgraph_user_id'],
"examples"
)
examples = store.search(
namespace,
query=str({"email": state['email_input']})
)
examples=format_few_shot_examples(examples)
langgraph_user_id = config['configurable']['langgraph_user_id']
namespace = (langgraph_user_id, )
result = store.get(namespace, "triage_ignore")
if result is None:
store.put(
namespace,
"triage_ignore",
{"prompt": prompt_instructions["triage_rules"]["ignore"]}
)
ignore_prompt = prompt_instructions["triage_rules"]["ignore"]
else:
ignore_prompt = result.value['prompt']
result = store.get(namespace, "triage_notify")
if result is None:
store.put(
namespace,
"triage_notify",
{"prompt": prompt_instructions["triage_rules"]["notify"]}
)
notify_prompt = prompt_instructions["triage_rules"]["notify"]
else:
notify_prompt = result.value['prompt']
result = store.get(namespace, "triage_respond")
if result is None:
store.put(
namespace,
"triage_respond",
{"prompt": prompt_instructions["triage_rules"]["respond"]}
)
respond_prompt = prompt_instructions["triage_rules"]["respond"]
else:
respond_prompt = result.value['prompt']
system_prompt = triage_system_prompt.format(
full_name=profile["full_name"],
name=profile["name"],
user_profile_background=profile["user_profile_background"],
triage_no=ignore_prompt,
triage_notify=notify_prompt,
triage_email=respond_prompt,
examples=examples
)
user_prompt = triage_user_prompt.format(
author=author,
to=to,
subject=subject,
email_thread=email_thread
)
result = llm_router.invoke(
[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt},
]
)
if result.classification == "respond":
print("📧 Classification: RESPOND - This email requires a response")
goto = "response_agent"
update = {
"messages": [
{
"role": "user",
"content": f"Respond to the email {state['email_input']}",
}
]
}
elif result.classification == "ignore":
print("🚫 Classification: IGNORE - This email can be safely ignored")
update = None
goto = END
elif result.classification == "notify":
# If real life, this would do something else
print("🔔 Classification: NOTIFY - This email contains important information")
update = None
goto = END
else:
raise ValueError(f"Invalid classification: {result.classification}")
return Command(goto=goto, update=update)
13.Build Tools
from langchain_core.tools import tool
@tool
def write_email(to: str, subject: str, content: str) -> str:
"""Write and send an email."""
# Placeholder response - in real app would send email
return f"Email sent to {to} with subject '{subject}'"
#
@tool
def schedule_meeting(
attendees: list[str],
subject: str,
duration_minutes: int,
preferred_day: str
) -> str:
"""Schedule a calendar meeting."""
# Placeholder response - in real app would check calendar and schedule
return f"Meeting '{subject}' scheduled for {preferred_day} with {len(attendees)} attendees"
#
@tool
def check_calendar_availability(day: str) -> str:
"""Check calendar availability for a given day."""
# Placeholder response - in real app would check actual calendar
return f"Available times on {day}: 9:00 AM, 2:00 PM, 4:00 PM"
14. Use Semantic memory
from langmem import create_manage_memory_tool, create_search_memory_tool
manage_memory_tool = create_manage_memory_tool(
namespace=(
"email_assistant",
"{langgraph_user_id}",
"collection"
)
)
search_memory_tool = create_search_memory_tool(
namespace=(
"email_assistant",
"{langgraph_user_id}",
"collection"
)
)
15. System prompt for semantic memory
agent_system_prompt_memory = """
< Role >
You are {full_name}'s executive assistant. You are a top-notch executive assistant who cares about {name} performing as well as possible.
Role >
< Tools >
You have access to the following tools to help manage {name}'s communications and schedule:
1. write_email(to, subject, content) - Send emails to specified recipients
2. schedule_meeting(attendees, subject, duration_minutes, preferred_day) - Schedule calendar meetings
3. check_calendar_availability(day) - Check available time slots for a given day
4. manage_memory - Store any relevant information about contacts, actions, discussion, etc. in memory for future reference
5. search_memory - Search for any relevant information that may have been stored in memory
Tools >
< Instructions >
{instructions}
Instructions >
"""
16. Updated prompts in the store
def create_prompt(state, config, store):
langgraph_user_id = config['configurable']['langgraph_user_id']
namespace = (langgraph_user_id, )
result = store.get(namespace, "agent_instructions")
if result is None:
store.put(
namespace,
"agent_instructions",
{"prompt": prompt_instructions["agent_instructions"]}
)
prompt = prompt_instructions["agent_instructions"]
else:
prompt = result.value['prompt']
return [
{
"role": "system",
"content": agent_system_prompt_memory.format(
instructions=prompt,
**profile
)
}
] + state['messages']
17. Create Email Agent
from langgraph.prebuilt import create_react_agent
#
tools= [
write_email,
schedule_meeting,
check_calendar_availability,
manage_memory_tool,
search_memory_tool
]
response_agent = create_react_agent(
llm,
tools=tools,
prompt=create_prompt,
# Use this to ensure the store is passed to the agent
store=store
)
response_agent
18. Setup config parameters to access Semantic Store
config = {"configurable": {"langgraph_user_id": "Plaban"}}
response_agent.invoke({"messages":[{"role":"user","content":"What is the availability for Tuesday"}]},config=config)
###############RESPONSE###############################################
{'messages': [HumanMessage(content='What is the availability for Tuesday', additional_kwargs={}, response_metadata={}, id='7005b579-a019-46e0-9e6c-d142b075298d'),
AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_12vn', 'function': {'arguments': '{"day": "Tuesday"}', 'name': 'check_calendar_availability'}, 'type': 'function'}]}, response_metadata={'token_usage': {'completion_tokens': 17, 'prompt_tokens': 933, 'total_tokens': 950, 'completion_time': 0.061818182, 'prompt_time': 0.059918984, 'queue_time': 0.255777121, 'total_time': 0.121737166}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_41c250edc7', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run-0ee36a6c-62ad-4596-a56b-50826ec19877-0', tool_calls=[{'name': 'check_calendar_availability', 'args': {'day': 'Tuesday'}, 'id': 'call_12vn', 'type': 'tool_call'}], usage_metadata={'input_tokens': 933, 'output_tokens': 17, 'total_tokens': 950}),
ToolMessage(content='Available times on Tuesday: 9:00 AM, 2:00 PM, 4:00 PM', name='check_calendar_availability', id='4de197b1-87f0-48b8-96d7-096b3d2fe91c', tool_call_id='call_12vn'),
AIMessage(content='Tuesday has available time slots at 9:00 AM, 2:00 PM, and 4:00 PM.', additional_kwargs={}, response_metadata={'token_usage': {'completion_tokens': 26, 'prompt_tokens': 982, 'total_tokens': 1008, 'completion_time': 0.094545455, 'prompt_time': 0.063813211, 'queue_time': 0.253129614, 'total_time': 0.158358666}, 'model_name': 'llama-3.3-70b-versatile', 'system_fingerprint': 'fp_c4760ee73b', 'finish_reason': 'stop', 'logprobs': None}, id='run-0f1a2012-b773-48e8-83ee-174ffee406b2-0', usage_metadata={'input_tokens': 982, 'output_tokens': 26, 'total_tokens': 1008})]}
19. Build the workflow
email_agent = StateGraph(State)
email_agent.add_node(triage_router)
email_agent.add_node("response_agent", response_agent)
email_agent.add_edge(START, "triage_router")
email_agent_workflow = email_agent.compile(store=store)
email_agent_workflow
20. Check Current Behavior of the agent
email_input = {
"author": "Alice Jones ",
"to": "Plaban Nayak ",
"subject": "Quick question about API documentation",
"email_thread": """Hi Plaban,
Urgent issue - your service is down. Is there a reason why""",
}
#
config = {"configurable": {"langgraph_user_id": "Plaban"}}
#
response = email_agent_workflow.invoke(
{"email_input": email_input},
config=config
)
for m in response["messages"]:
m.pretty_print()
###############################RESPONSE##############################
📧 Classification: RESPOND - This email requires a response
================================ Human Message =================================
Respond to the email {'author': 'Alice Jones <alice.jones@bar.com>', 'to': 'Plaban Nayak <plaban.nayak@company.com>', 'subject': 'Quick question about API documentation', 'email_thread': 'Hi Plaban,\n\nUrgent issue - your service is down. Is there a reason why'}
================================== Ai Message ==================================
Tool Calls:
write_email (call_37w0)
Call ID: call_37w0
Args:
to: alice.jones@bar.com
subject: Re: Quick question about API documentation
content: Hi Alice,
Thanks for reaching out. Sorry to hear that our service is down. I'll look into this immediately and get back to you with an update.
Best,
Plaban Nayak
================================= Tool Message =================================
Name: write_email
Email sent to alice.jones@bar.com with subject 'Re: Quick question about API documentation'
================================== Ai Message ==================================
Tool Calls:
manage_memory (call_n6ph)
Call ID: call_n6ph
Args:
action: create
content: Alice Jones from Bar.com contacted Plaban regarding urgent issue with service being down.
================================= Tool Message =================================
Name: manage_memory
created memory fc6cecf6-d266-4ecb-aa49-7ef92ed49834
================================== Ai Message ==================================
<function=search_memory({"query": "service down", "limit": 1})</function>
response['messages'][-4].content
##########################################
"Email sent to alice.jones@bar.com with subject 'Re: Quick question about API documentation'"
response['messages'][1].additional_kwargs['tool_calls']
##################################################################
[{'id': 'call_37w0',
'function': {'arguments': '{"to": "alice.jones@bar.com", "subject": "Re: Quick question about API documentation", "content": "Hi Alice, \\n\\nThanks for reaching out. Sorry to hear that our service is down. I\'ll look into this immediately and get back to you with an update. \\n\\nBest, \\nPlaban Nayak"}',
'name': 'write_email'},
'type': 'function'}]
21. Use an LLM to update instructions.
- Instruct the agent to always sign the email with the sender name
from langmem import create_multi_prompt_optimizer
conversations = [
(
response['messages'],
"Always sign your email `Plaban Nayak`"
)
]
### Update the Instructions again
prompts = [
{
"name": "main_agent",
"prompt": store.get(("Plaban",), "agent_instructions").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on how the agent should write emails or schedule events"
},
{
"name": "triage-ignore",
"prompt": store.get(("Plaban",), "triage_ignore").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be ignored"
},
{
"name": "triage-notify",
"prompt": store.get(("Plaban",), "triage_notify").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails the user should be notified of"
},
{
"name": "triage-respond",
"prompt": store.get(("Plaban",), "triage_respond").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be responded to"
},
]
22. Instantiate the Multi-Prompt Orchestrator
optimizer = create_multi_prompt_optimizer(
"groq:llama-3.3-70b-versatile",
kind="prompt_memory",
)
#
updated = optimizer.invoke(
{"trajectories": conversations, "prompts": prompts}
)
updated
#############################RESPONSE###################################
[{'name': 'main_agent',
'prompt': "Use these tools when appropriate to help manage Plaban's tasks efficiently. Always sign your email as 'Plaban Nayak'.",
'update_instructions': 'keep the instructions short and to the point',
'when_to_update': 'Update this prompt whenever there is feedback on how the agent should write emails or schedule events'},
{'name': 'triage-ignore',
'prompt': 'Marketing newsletters, spam emails, mass company announcements',
'update_instructions': 'keep the instructions short and to the point',
'when_to_update': 'Update this prompt whenever there is feedback on which emails should be ignored'},
{'name': 'triage-notify',
'prompt': 'Team member out sick, build system notifications, project status updates',
'update_instructions': 'keep the instructions short and to the point',
'when_to_update': 'Update this prompt whenever there is feedback on which emails the user should be notified of'},
{'name': 'triage-respond',
'prompt': 'Direct questions from team members, meeting requests, critical bug reports. Always sign your email as Plaban Nayak',
'update_instructions': 'keep the instructions short and to the point',
'when_to_update': 'Update this prompt whenever there is feedback on which emails should be responded to'}]
#json dumps is a bit easier to read
import json
print(json.dumps(updated, indent=4))
################################RESPONSE################################
[
{
"name": "main_agent",
"prompt": "Use these tools when appropriate to help manage Plaban's tasks efficiently. Always sign your email as 'Plaban Nayak'.",
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on how the agent should write emails or schedule events"
},
{
"name": "triage-ignore",
"prompt": "Marketing newsletters, spam emails, mass company announcements",
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be ignored"
},
{
"name": "triage-notify",
"prompt": "Team member out sick, build system notifications, project status updates",
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails the user should be notified of"
},
{
"name": "triage-respond",
"prompt": "Direct questions from team members, meeting requests, critical bug reports. Always sign your email as Plaban Nayak",
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be responded to"
}
]
23. Update the store with modified instructions
for i, updated_prompt in enumerate(updated):
old_prompt = prompts[i]
if updated_prompt['prompt'] != old_prompt['prompt']:
name = old_prompt['name']
print(f"updated {name}")
if name == "main_agent":
store.put(
("lance",),
"agent_instructions",
{"prompt":updated_prompt['prompt']}
)
else:
store.put(
("Plaban",),
name,
{"prompt":updated_prompt['prompt']}
)
print(f"{name} prompt updated in the store Successfully")
else:
print(f"{name} prompt not updated in the store")
#########################RESPONSE##############################
updated main_agent
main_agent prompt updated in the store Successfully
main_agent prompt not updated in the store
main_agent prompt not updated in the store
updated triage-respond
triage-respond prompt updated in the store Successfully
check the store
store.get(("Plaban",), "agent_instructions").value['prompt']
################RESPONSE#############################
"Use these tools when appropriate to help manage Plaban's tasks efficiently."
24. Invoke the email assistant
response = email_agent_workflow.invoke(
{"email_input": email_input},
config=config
)
#
for m in response["messages"]:
m.pretty_print()
#########################RESPONSE#####################################
================================ Human Message =================================
Respond to the email {'author': 'Alice Jones <alice.jones@bar.com>', 'to': 'Plaban Nayak <plaban.nayak@company.com>', 'subject': 'Quick question about API documentation', 'email_thread': 'Hi Plaban,\n\nUrgent issue - your service is down. Is there a reason why'}
================================== Ai Message ==================================
Tool Calls:
write_email (call_tsk5)
Call ID: call_tsk5
Args:
to: alice.jones@bar.com
subject: Re: Quick question about API documentation
content: Hi Alice,
I'm so sorry to hear that our service is down. I'll look into this immediately and get back to you with an update.
Best,
Plaban Nayak
================================= Tool Message =================================
Name: write_email
Email sent to alice.jones@bar.com with subject 'Re: Quick question about API documentation'
================================== Ai Message ==================================
Tool Calls:
manage_memory (call_n4q8)
Call ID: call_n4q8
Args:
action: create
content: Alice Jones from bar.com reached out about an urgent issue with the service being down.
================================= Tool Message =================================
Name: manage_memory
created memory 3b9605de-13b7-4573-8e61-a443d2eba6e0
================================== Ai Message ==================================
Tool Calls:
write_email (call_f1dp)
Call ID: call_f1dp
Args:
to: dev.team@company.com
subject: Service Down - Urgent Attention Required
content: Hi Team,
We have an urgent issue - our service is currently down. Please look into this and get back to me with an update as soon as possible.
Best,
Plaban Nayak
================================= Tool Message =================================
Name: write_email
Email sent to dev.team@company.com with subject 'Service Down - Urgent Attention Required'
================================== Ai Message ==================================
Tool Calls:
manage_memory (call_2zfd)
Call ID: call_2zfd
Args:
action: create
content: Notified dev team about the service being down.
================================= Tool Message =================================
Name: manage_memory
created memory 4c1b700d-0ebe-4222-9cfa-c56cc739f7b1
================================== Ai Message ==================================
Tool Calls:
check_calendar_availability (call_tx9c)
Call ID: call_tx9c
Args:
day: today
================================= Tool Message =================================
Name: check_calendar_availability
Available times on today: 9:00 AM, 2:00 PM, 4:00 PM
================================== Ai Message ==================================
Tool Calls:
schedule_meeting (call_p7v3)
Call ID: call_p7v3
Args:
attendees: ['dev.team@company.com']
subject: Service Down Discussion
duration_minutes: 60
preferred_day: today
================================= Tool Message =================================
Name: schedule_meeting
Meeting 'Service Down Discussion' scheduled for today with 1 attendees
================================== Ai Message ==================================
Tool Calls:
write_email (call_64wx)
Call ID: call_64wx
Args:
to: alice.jones@bar.com
subject: Re: Quick question about API documentation
content: Hi Alice,
Just wanted to follow up on the service being down. We're currently investigating the issue and I'll get back to you with an update soon.
Best,
Plaban Nayak
================================= Tool Message =================================
Name: write_email
Email sent to alice.jones@bar.com with subject 'Re: Quick question about API documentation'
================================== Ai Message ==================================
Tool Calls:
manage_memory (call_w2a1)
Call ID: call_w2a1
Args:
action: update
id: 3b9605de-13b7-4573-8e61-a443d2eba6e0
content: Investigating the service down issue reported by Alice Jones from bar.com.
================================= Tool Message =================================
Name: manage_memory
updated memory 3b9605de-13b7-4573-8e61-a443d2eba6e0
================================== Ai Message ==================================
Tool Calls:
manage_memory (call_4enf)
Call ID: call_4enf
Args:
action: create
content: Followed up with Alice Jones about the service being down.
================================= Tool Message =================================
Name: manage_memory
created memory fa92ee10-dd8a-486b-9cb4-3c35c2f24264
================================== Ai Message ==================================
Tool Calls:
search_memory (call_s7k8)
Call ID: call_s7k8
Args:
query: service down
limit: 10
================================= Tool Message =================================
Name: search_memory
[{"namespace": ["email_assistant", "Plaban", "collection"], "key": "4c1b700d-0ebe-4222-9cfa-c56cc739f7b1", "value": {"content": "Notified dev team about the service being down."}, "created_at": "2025-03-22T13:16:39.804441+00:00", "updated_at": "2025-03-22T13:16:39.804448+00:00", "score": 0.9049934631618353}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "567d9400-d17b-495b-9da6-7fad26bd631b", "value": {"content": "Urgent issue: service is down. Need to investigate and get back to Alice Jones - meeting scheduled for today."}, "created_at": "2025-03-22T13:02:55.239699+00:00", "updated_at": "2025-03-22T13:02:55.239706+00:00", "score": 0.8996743795426038}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "991d9f2a-852a-4262-958d-a672eb0455c7", "value": {"content": "Urgent issue with John Doe's service being down"}, "created_at": "2025-03-22T13:07:12.111179+00:00", "updated_at": "2025-03-22T13:07:12.111186+00:00", "score": 0.8969359015062597}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "fa92ee10-dd8a-486b-9cb4-3c35c2f24264", "value": {"content": "Followed up with Alice Jones about the service being down."}, "created_at": "2025-03-22T13:16:53.194690+00:00", "updated_at": "2025-03-22T13:16:53.194695+00:00", "score": 0.8946786919837046}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "6750ce0b-1407-48b8-917f-f3046d3bd5de", "value": {"content": "Alice Jones: alice.jones@bar.com, reported urgent issue with service being down - meeting scheduled for today."}, "created_at": "2025-03-22T13:02:54.451615+00:00", "updated_at": "2025-03-22T13:02:54.451622+00:00", "score": 0.8935506392916787}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "3b9605de-13b7-4573-8e61-a443d2eba6e0", "value": {"content": "Investigating the service down issue reported by Alice Jones from bar.com."}, "created_at": "2025-03-22T13:16:52.496635+00:00", "updated_at": "2025-03-22T13:16:52.496641+00:00", "score": 0.8876080529253129}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "fc6cecf6-d266-4ecb-aa49-7ef92ed49834", "value": {"content": "Alice Jones from Bar.com contacted Plaban regarding urgent issue with service being down."}, "created_at": "2025-03-22T13:08:01.510731+00:00", "updated_at": "2025-03-22T13:08:01.510737+00:00", "score": 0.8807318085615965}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "c725f7e5-0e3e-4a63-9b4c-945fcb1f0c97", "value": {"content": "Follow up with Plaban on urgent issue reported by Alice Jones - meeting scheduled for today."}, "created_at": "2025-03-22T13:02:35.356561+00:00", "updated_at": "2025-03-22T13:02:35.356568+00:00", "score": 0.8338445419678701}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "da9f9242-e3b1-4de6-86eb-bdc180237b89", "value": {"content": "Email thread mix-up between Alice Jones and John Doe"}, "created_at": "2025-03-22T13:07:11.440381+00:00", "updated_at": "2025-03-22T13:07:11.440386+00:00", "score": 0.8185530436387874}, {"namespace": ["email_assistant", "Plaban", "collection"], "key": "7e149eca-5cad-4eec-b15f-878597a09b39", "value": {"content": "Alice Jones' question about API documentation"}, "created_at": "2025-03-22T13:07:12.757896+00:00", "updated_at": "2025-03-22T13:07:12.757902+00:00", "score": 0.7965957309412914}]
================================== Ai Message ==================================
The search results show that there are several memories related to the service being down, including notifications to the dev team, meetings scheduled, and follow-ups with Alice Jones. The most relevant memories are:
* "Investigating the service down issue reported by Alice Jones from bar.com." (score: 0.8876080529253129)
* "Notified dev team about the service being down." (score: 0.9049934631618353)
* "Followed up with Alice Jones about the service being down." (score: 0.8946786919837046)
These memories suggest that the service being down is a current and ongoing issue that is being investigated and addressed by the dev team and Plaban.
- we can see that the email has been signed with the name of the sender in this case ‘Plaban Nayak’
25. Follow up email
email_input = {
"author": "Alice Jones ",
"to": "Plaban Nayak ",
"subject": "Quick question about API documentation",
"email_thread": """Hi Plaban,
Urgent issue - your service is down. Is there a reason why""",
}
#
response = email_agent_workflow.invoke(
{"email_input": email_input},
config=config
)
#
for m in response["messages"]:
m.pretty_print()
#
##################RESPONSE####################
================================ Human Message =================================
Respond to the email {'author': 'Alice Jones <alice.jones@bar.com>', 'to': 'Plaban Nayak <plaban.nayak@company.com>', 'subject': 'Quick question about API documentation', 'email_thread': 'Hi Plaban,\n\nUrgent issue - your service is down. Is there a reason why'}
================================== Ai Message ==================================
Tool Calls:
write_email (call_4b5b)
Call ID: call_4b5b
Args:
to: alice.jones@bar.com
subject: Re: Quick question about API documentation
content: Hi Alice,
Thanks for reaching out. I'm looking into the issue with our service being down. Can you please provide more details about the error you're experiencing? I'll get back to you as soon as possible.
Best, Plaban
================================= Tool Message =================================
Name: write_email
Email sent to alice.jones@bar.com with subject 'Re: Quick question about API documentation'
================================== Ai Message ==================================
<function=manage_memory {"action": "create", "content": "Alice Jones from Bar.com reached out about an urgent issue with our service being down."}</function>
26. Update Feedback instructions to ignore mails from Alice
conversations = [
(
response['messages'],
"Always Ignore any emails from Alice Jones"
)
]
27.Define List of prompts because the old list of prompts has been outdated for the main agent
prompts = [
{
"name": "main_agent",
"prompt": store.get(("Plaban",), "agent_instructions").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on how the agent should write emails or schedule events"
},
{
"name": "triage-ignore",
"prompt": store.get(("Plaban",), "triage_ignore").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be ignored"
},
{
"name": "triage-notify",
"prompt": store.get(("Plaban",), "triage_notify").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails the user should be notified of"
},
{
"name": "triage-respond",
"prompt": store.get(("Plaban",), "triage_respond").value['prompt'],
"update_instructions": "keep the instructions short and to the point",
"when_to_update": "Update this prompt whenever there is feedback on which emails should be responded to"
},
]
updated = optimizer.invoke(
{"trajectories": conversations, "prompts": prompts}
)
#
store.get(("Plaban",), "triage_ignore").value['prompt']
################RESPONSE########################
'Marketing newsletters, spam emails, mass company announcements'
Here the prompts have been not updated hence triage_ignore instructions remains unchanged.
28.Update the prompts in long term memory
for i, updated_prompt in enumerate(updated):
old_prompt = prompts[i]
if updated_prompt['prompt'] != old_prompt['prompt']:
name = old_prompt['name']
print(f"updated {name}")
if name == "main_agent":
store.put(
("Plaban",),
"agent_instructions",
{"prompt":updated_prompt['prompt']}
)
if name == "triage-ignore":
store.put(
("Plaban",),
"triage_ignore",
{"prompt":updated_prompt['prompt']}
)
else:
#raise ValueError
print(f"Encountered {name}, implement the remaining stores!")
#
store.get(("Plaban",), "triage_ignore").value['prompt']
#
###################RESPONSE####################################
'<updated_prompt>\nMarketing newsletters, spam emails, mass company announcements\n\nAdditionally, always ignore any emails from Alice Jones.\n</updated_prompt>'
29. Provide sample email input to test if the instructions have been updated
response = email_agent_workflow.invoke(
{"email_input": email_input},
config=config
)
####################RESPONSE###########################
🚫 Classification: IGNORE - This email can be safely ignored
30.Observablity in Langsmith
Future Enhancements 🔮
1. 🤖 Integration with other AI models
2. 📱 Mobile app interface
3. 🔄 Improved learning algorithms
4. 🌐 Multi-language support
5. 🤝 Team collaboration features
Conclusion 🎉
This email assistant demonstrates the power of combining different types of memory with modern AI capabilities. It’s not just about handling emails; it’s about creating an intelligent system that learns and adapts to user needs.