Skip to content

Commit e42a8b6

Browse files
author
AgentPatterns
committed
feat(examples/python): add agent-memory runnable example
1 parent 0cfd907 commit e42a8b6

File tree

6 files changed

+239
-0
lines changed

6 files changed

+239
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Agent Memory - Python Implementation
2+
3+
Runnable learning example that highlights short-term vs long-term memory in agents.
4+
5+
---
6+
7+
## Quick start
8+
9+
```bash
10+
# (optional) create venv
11+
python -m venv .venv && source .venv/bin/activate
12+
13+
# install dependencies (none required, command kept for consistency)
14+
pip install -r requirements.txt
15+
16+
# run the demo
17+
python main.py
18+
```
19+
20+
## What it demonstrates
21+
22+
- Short-term memory can lose early instructions when context is small
23+
- Long-term memory persists user preferences across tasks
24+
- Same report request can produce different output depending on memory strategy
25+
26+
## Project layout
27+
28+
```text
29+
examples/
30+
foundations/
31+
agent-memory/
32+
python/
33+
README.md
34+
main.py
35+
agent.py
36+
memory.py
37+
tools.py
38+
requirements.txt
39+
```
40+
41+
## Notes
42+
43+
- This code is intentionally simple for learning.
44+
- The website contains multilingual explanation and context.
45+
46+
## License
47+
48+
MIT
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from memory import LongMemoryStore, ShortMemory
2+
from tools import get_orders_count, get_sales_total, render_report
3+
4+
DEFAULT_PREFS = {
5+
"report_format": "default",
6+
"currency": "USD",
7+
}
8+
9+
10+
def save_user_preferences(
11+
*,
12+
user_key: str,
13+
prefs: dict[str, str],
14+
short_memory: ShortMemory,
15+
long_memory: LongMemoryStore,
16+
) -> None:
17+
short_memory.add("user", f"Save prefs: {prefs}")
18+
long_memory.save_prefs(user_key, prefs)
19+
short_memory.add("assistant", "Preferences saved to long-term memory")
20+
21+
22+
def parse_prefs_from_short_memory(short_memory: ShortMemory) -> dict[str, str]:
23+
# Simplified parser for learning: looks for lines like "pref:key=value".
24+
parsed: dict[str, str] = {}
25+
for item in short_memory.snapshot():
26+
content = item["content"]
27+
if "pref:" not in content:
28+
continue
29+
payload = content.split("pref:", 1)[1]
30+
if "=" not in payload:
31+
continue
32+
key, value = payload.split("=", 1)
33+
parsed[key.strip()] = value.strip()
34+
return parsed
35+
36+
37+
def build_weekly_report(
38+
*,
39+
user_id: int,
40+
user_key: str,
41+
request: str,
42+
short_memory: ShortMemory,
43+
long_memory: LongMemoryStore,
44+
use_long_memory: bool,
45+
) -> dict:
46+
trace: list[str] = []
47+
48+
short_memory.add("user", request)
49+
trace.append(f"request={request}")
50+
51+
short_prefs = parse_prefs_from_short_memory(short_memory)
52+
trace.append(f"short_prefs={short_prefs}")
53+
54+
long_prefs = long_memory.load_prefs(user_key) if use_long_memory else {}
55+
trace.append(f"long_prefs={long_prefs}")
56+
57+
prefs = {**DEFAULT_PREFS, **short_prefs, **long_prefs}
58+
trace.append(f"resolved_prefs={prefs}")
59+
60+
total = get_sales_total(user_id)
61+
orders = get_orders_count(user_id)
62+
63+
report = render_report(
64+
total=total,
65+
orders=orders,
66+
currency=prefs["currency"],
67+
report_format=prefs["report_format"],
68+
)
69+
70+
short_memory.add("assistant", f"Report generated with prefs={prefs}")
71+
72+
return {
73+
"prefs": prefs,
74+
"report": report,
75+
"trace": trace,
76+
"short_memory_snapshot": short_memory.snapshot(),
77+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
from agent import build_weekly_report, save_user_preferences
2+
from memory import LongMemoryStore, ShortMemory
3+
4+
USER_ID = 42
5+
USER_KEY = "user:anna"
6+
7+
8+
def print_result(title: str, result: dict) -> None:
9+
print(f"\n=== {title} ===")
10+
print("Resolved prefs:", result["prefs"])
11+
print("\nReport:")
12+
print(result["report"])
13+
print("\nTrace:")
14+
for line in result["trace"]:
15+
print(" ", line)
16+
17+
18+
def main() -> None:
19+
long_memory = LongMemoryStore()
20+
21+
# Scenario 1: only short-term memory, early instruction falls out of context.
22+
short_memory_1 = ShortMemory(max_items=4)
23+
short_memory_1.add("user", "pref:report_format=short-bullets")
24+
short_memory_1.add("user", "pref:currency=EUR")
25+
short_memory_1.add("assistant", "working...")
26+
short_memory_1.add("assistant", "still working...")
27+
short_memory_1.add("assistant", "collecting data...") # pushes out old prefs
28+
29+
result_short_only = build_weekly_report(
30+
user_id=USER_ID,
31+
user_key=USER_KEY,
32+
request="Build weekly sales report",
33+
short_memory=short_memory_1,
34+
long_memory=long_memory,
35+
use_long_memory=False,
36+
)
37+
print_result("SCENARIO 1: SHORT MEMORY ONLY", result_short_only)
38+
39+
# Scenario 2: persist prefs in long-term memory, then start a new task.
40+
short_memory_2 = ShortMemory(max_items=4)
41+
save_user_preferences(
42+
user_key=USER_KEY,
43+
prefs={"report_format": "short-bullets", "currency": "EUR"},
44+
short_memory=short_memory_2,
45+
long_memory=long_memory,
46+
)
47+
short_memory_2.clear() # new task, short memory resets
48+
49+
result_with_long = build_weekly_report(
50+
user_id=USER_ID,
51+
user_key=USER_KEY,
52+
request="Build weekly sales report like last time",
53+
short_memory=short_memory_2,
54+
long_memory=long_memory,
55+
use_long_memory=True,
56+
)
57+
print_result("SCENARIO 2: WITH LONG MEMORY", result_with_long)
58+
59+
60+
if __name__ == "__main__":
61+
main()
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from dataclasses import dataclass, field
2+
from typing import Any
3+
4+
5+
@dataclass
6+
class ShortMemory:
7+
max_items: int = 6
8+
items: list[dict[str, Any]] = field(default_factory=list)
9+
10+
def add(self, role: str, content: str) -> None:
11+
self.items.append({"role": role, "content": content})
12+
if len(self.items) > self.max_items:
13+
self.items = self.items[-self.max_items :]
14+
15+
def snapshot(self) -> list[dict[str, Any]]:
16+
return list(self.items)
17+
18+
def clear(self) -> None:
19+
self.items.clear()
20+
21+
22+
@dataclass
23+
class LongMemoryStore:
24+
_prefs: dict[str, dict[str, str]] = field(default_factory=dict)
25+
26+
def save_prefs(self, user_key: str, prefs: dict[str, str]) -> None:
27+
self._prefs[user_key] = dict(prefs)
28+
29+
def load_prefs(self, user_key: str) -> dict[str, str]:
30+
return dict(self._prefs.get(user_key, {}))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# No external dependencies for this learning example.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
def get_sales_total(user_id: int) -> float:
2+
_ = user_id
3+
return 12400.0
4+
5+
6+
def get_orders_count(user_id: int) -> int:
7+
_ = user_id
8+
return 31
9+
10+
11+
def render_report(*, total: float, orders: int, currency: str, report_format: str) -> str:
12+
if report_format == "short-bullets":
13+
return (
14+
f"- Total sales: {total:.2f} {currency}\n"
15+
f"- Orders: {orders}\n"
16+
"- Status: stable"
17+
)
18+
19+
return (
20+
f"Sales report: total={total:.2f} {currency}, "
21+
f"orders={orders}, status=stable"
22+
)

0 commit comments

Comments
 (0)