ข้ามไปยังเนื้อหาหลัก

บทช่วยสอน Supermemory: เพิ่มหน่วยความจำถาวรให้ AI Agents

สร้างผู้ฝึกสอนออกกำลังกายด้วย Python ที่บันทึกการออกกำลังกาย จดจำประวัติผู้ใช้ด้วย Supermemory และแนะนำเซสชันเฉพาะบุคคลข้ามการรันเอเจนต์ AI แยกกัน
อัปเดตแล้ว 11 พ.ค. 2569  · 15 นาที อ่าน

เอเจนต์ LLM ส่วนใหญ่เริ่มต้นจากศูนย์ทุกครั้งที่รัน พวกมันลืมชื่อผู้ใช้ บทสนทนาครั้งล่าสุด และไฟล์ที่กำลังทำอยู่ อะไรก็ตามที่เป็นข้อมูลเฉพาะผู้ใช้ต้องถูกยืนยันใหม่ทีละรอบ

บทช่วยสอนนี้จะเพิ่ม หน่วยความจำถาวรให้เอเจนต์ เพื่อให้การรันครั้งถัดไปเริ่มต่อจากจุดที่จบไว้รอบก่อน ชั้นความจำนี้คือ Supermemory ซึ่งเป็น API แบบโฮสต์สำหรับเก็บข้อเท็จจริงรายผู้ใช้และคืนค่าในคำขอเดียว คุณจะสร้างผู้ฝึกสอนออกกำลังกายส่วนบุคคลด้วย Python เอเจนต์จะบันทึกการออกกำลังกาย แนะนำเซสชันถัดไป และจดจำความชอบกับน้ำหนักที่เพิ่งยกไปในแต่ละการรันสคริปต์ที่แยกกัน

สแต็กมีขนาดเล็ก Supermemory จัดเก็บความจำ OpenAI Agents SDK รันลูปของเอเจนต์ ผู้ฝึกสอนประกอบด้วยไฟล์ Python สองไฟล์และไฟล์ pyproject.toml หนึ่งไฟล์

ต้องมี Python 3.10 ขึ้นไป บัญชี OpenAI บัญชี Supermemory และความคุ้นเคยพื้นฐานกับบรรทัดคำสั่ง

Supermemory คืออะไร?

Supermemory อธิบายได้ดีที่สุดว่าเป็น API หน่วยความจำสำหรับเอเจนต์ AI เมื่อคุณส่งสตริงเกี่ยวกับผู้ใช้ของคุณให้ Supermemory ภายหลังมันจะคืนมุมมองแบบกระชับว่า ผู้ใช้นั้นเป็นใครและเพิ่งทำอะไรไปบ้าง การฝังเวกเตอร์ การทำดัชนี และการเรียกคืน ทำงานภายใน Supermemory ทั้งหมด ทำให้โค้ดของเอเจนต์ยังคงเล็ก

เกณฑ์วัด LongMemEval ทดสอบว่าระบบความจำตอบคำถามได้ดีเพียงใดจากประวัติการสนทนายาวๆ Supermemory จำข้อเท็จจริงที่ถูกต้องได้ 81.6% ส่วน Zep ซึ่งเป็นระบบอันดับถัดไปได้ 71.2% ช่องว่าง 10 คะแนนนี้เทียบได้กับคำตอบถูกประมาณ 1 ข้อเพิ่มเติมต่อคำถามผู้ใช้ 10 ข้อ ที่เก็บโอเพ่นซอร์สมีดาว GitHub กว่า 22k เป็นสัญญาณอีกอย่างของการใช้งานจริง

หน่วยความจำ vs RAG

ผู้อ่านส่วนใหญ่ที่มองหาเครื่องมือความจำของเอเจนต์มักเคยใช้ RAG มาก่อน ฉะนั้นการวาง Supermemory ไว้ข้างๆ จะช่วยให้เห็นภาพ RAG และหน่วยความจำแก้ปัญหาคนละแบบ และมักอยู่ร่วมกันในเอเจนต์เดียว

ระบบ RAG ชี้ไปที่คลังเอกสารที่นักพัฒนาจัดเตรียมไว้ครั้งเดียว เช่น คู่มือสินค้า บทความซัพพอร์ต เอกสารภายใน คลังนี้ถูกโหลดตอนดีพลอย คิวรีตอนรันไทม์ และแทบไม่เปลี่ยน เอเจนต์ใช้เพื่อมาตอบคำถามที่ตัวผลิตภัณฑ์รู้อยู่แล้ว

ระบบหน่วยความจำชี้ไปที่ผู้ใช้ Supermemory เขียนข้อเท็จจริงเฉพาะผู้ใช้ขณะเอเจนต์คุยกับผู้ใช้นั้น และสโตร์จะเติบโตขึ้นทุกบทสนทนา เอเจนต์ใช้เพื่อตอบคำถามที่มีแต่ผู้ใช้เท่านั้นที่ตอบได้ เช่น ความชอบ ประวัติ และกิจกรรมล่าสุด

ชื่อ: หน่วยความจำ vs. RAG: แหล่งที่มาของข้อมูล - คำอธิบาย: หน่วยความจำ vs. RAG: แหล่งที่มาของข้อมูล

ในผลิตภัณฑ์จริง ทั้งสองทำงานคู่กัน RAG เหนือคลังความรู้ของบริษัทตอบคำถามว่า "นโยบายการคืนเงินของเราคืออะไร?" ส่วน Supermemory เหนือคำตอบของผู้ใช้ตอบว่า "สัปดาห์ที่แล้วฉันเบนช์เท่าไหร่?" เอเจนต์เดียว กันแต่คนละสโตร์ข้อมูล คนละงาน

โปรไฟล์ผู้ใช้: ข้อเท็จจริงแบบคงที่และแบบพลวัต

แนวคิดหลักของ Supermemory คือโปรไฟล์ผู้ใช้ ทุกบันทึกจะถูกจัดลงสองถัง: ข้อเท็จจริงแบบคงที่ที่แทบไม่เปลี่ยน และข้อเท็จจริงแบบพลวัตเกี่ยวกับกิจกรรมปัจจุบัน รูปแบบที่เกิดซ้ำจะถูกเลื่อนชั้นเป็นคงที่ กิจกรรมล่าสุดอยู่ฝั่งพลวัต

เมื่อเอเจนต์อ่านโปรไฟล์ การเรียกครั้งเดียวจะคืนทั้งสองถังพร้อมชิ้นความจำที่ตรงกัน

การแยกนี้สำคัญเพราะข้อเท็จจริงแบบคงที่และแบบพลวัตตอบคำถามต่างกันเกี่ยวกับผู้ใช้คนเดียวกัน:

ข้อเท็จจริงแบบคงที่

ข้อเท็จจริงแบบพลวัต

ฝึกที่บ้านด้วยดัมบ์เบลและบาร์โหน

โฟกัสปัจจุบัน: ความแข็งแรงช่วงบน

เข่าซ้ายบาดเจ็บ ไม่สควอตลึก

เบนช์ล่าสุด: 4 เซ็ต เซ็ตละ 5 ครั้ง ที่ 185 ปอนด์

ต้องการเพิ่มเบนช์อีก 20 ปอนด์ภายในสิ้นปี

สัปดาห์นี้ฝึกโหนคางแบบ grease-the-groove

ฝึกตอนเย็นเท่านั้น ไม่เคยฝึกตอนเช้า

วิ่ง 5 กม. เมื่อวานใน 28 นาที

อ่านแถวแรก ด้านคงที่บอกว่าผู้ใช้ฝึกอย่างไร: ที่บ้าน ด้วยอุปกรณ์ที่มี ซึ่งไม่ได้เปลี่ยนทุกสัปดาห์ ด้านพลวัตบอกว่าตอนนี้กำลังเน้นอะไร: ช่วงบน ในไซเคิลนี้

ตัวแนะนำการออกกำลังกายต้องใช้ทั้งสองด้าน ฝั่งคงที่ตัดท่าที่ทำได้เฉพาะในยิมออก ฝั่งพลวัตเลือกเซสชันของวันนี้

เบื้องหลังโปรไฟล์นั้น Supermemory ทำสี่งานที่ปกติคุณต้องสร้างเอง มันเก็บความจำดิบ ฝังเวกเตอร์ทุกชิ้น ค้นหาความคล้ายคลึงตอนอ่าน และสกัดข้อเท็จจริงของโปรไฟล์จากเนื้อหาที่บันทึก ไม่มีสักข้อที่ต้องโผล่ในโค้ดของคุณ

ชื่อ: สิ่งที่ Supermemory จัดการให้ - คำอธิบาย: สิ่งที่ Supermemory จัดการให้

ทุกความจำถูกติดแท็กด้วยสตริงที่นักพัฒนาเลือก การอ่านทุกครั้งจะส่งสตริงเดิมกลับไปเพื่อจำกัดสิ่งที่ออกมา เดโมผู้ฝึกสอนฮาร์ดโค้ดแท็กเดียวเพราะผู้ใช้คนเดียวก็เพียงพอสำหรับสาธิตโปรไฟล์ แอปจริงจะคำนวณแท็กจากผู้ใช้ที่ยืนยันตัวตนแล้ว เช่น JWT ของพวกเขา

ตั้งค่าแวดล้อม Supermemory ของคุณ

ตัวผู้ฝึกสอนต้องใช้คีย์ API สองรายการ (Supermemory และ OpenAI) และโปรเจ็กต์ Python ที่มีเดเพนเดนซีสามตัว สคริปต์ไป-กลับแบบรวดเร็วจะพิสูจน์ว่าทั้งสองคีย์ใช้งานได้ก่อนจะมีโค้ดเอเจนต์เข้าไปเกี่ยวข้อง

ขอรับคีย์ API

คีย์ API ของ Supermemory อยู่ที่ console.supermemory.ai ไม่ใช่ app.supermemory.ai ซับโดเมน app คือผลิตภัณฑ์ความจำสำหรับผู้บริโภค (ไว้บันทึกโน้ตและเรียกดูสเปซของคุณ) ซึ่งไม่มีหน้าคีย์ API ข้ามไปและเข้า console โดยตรง

บน console.supermemory.ai:

  1. ลงชื่อเข้าใช้

  2. คลิก API Keys ที่แถบด้านข้าง

  3. คลิก Create API Key

  4. ตั้งชื่อ (เดโมผู้ฝึกสอนใช้ datacamp-tutorial)

  5. คัดลอกคีย์ที่ได้ จะขึ้นต้นด้วย sm_

ชื่อ: คอนโซลนักพัฒนา Supermemory แสดงหน้า API Keys - คำอธิบาย: คอนโซลนักพัฒนา Supermemory แสดงหน้า API Keys

ยังต้องใช้คีย์ OpenAI สำหรับการเรียก LLM ของเอเจนต์ ขอรับได้ที่ platform.openai.com/api-keys หากยังไม่มี

สร้างไฟล์ .env ที่รูทของโปรเจ็กต์และใส่คีย์ทั้งสอง อย่า commit ไฟล์นี้

SUPERMEMORY_API_KEY=sm_your_key_here
OPENAI_API_KEY=sk-your_key_here

แพ็กเกจฟรีของ Supermemory ครอบคลุมบทช่วยสอนนี้โดยไม่ต้องใส่ข้อมูลการชำระเงิน รายละเอียดขีดจำกัดอยู่ที่ หน้าราคา

ติดตั้งเดเพนเดนซี

บทช่วยสอนนี้ใช้ uv สำหรับตั้งค่าและรันโปรเจ็กต์ หากยังไม่มี uv ให้ติดตั้งครั้งเดียวด้วยคำสั่งหนึ่งบรรทัดจาก astral.sh/uv

เริ่มต้นโปรเจ็กต์:

uv init supermemory-trainer
cd supermemory-trainer

ลบ README.md ที่ถูกสร้างอัตโนมัติจาก uv init ไฟล์ hello.py จะถูกเขียนทับในขั้นตอนถัดไป จึงปล่อยไว้ก่อน

เพิ่มเดเพนเดนซีสามตัว:

  • supermemory==3.37.0 คือไคลเอนต์ความจำ ตรึงเวอร์ชันที่ตรวจสอบแล้วกับบทช่วยสอนนี้

  • openai-agents คือ OpenAI Agents SDK ชื่อแพ็กเกจมีขีดกลาง แต่เส้นทาง import คือ agents

  • python-dotenv อ่านไฟล์ .env ที่เพิ่งสร้าง

uv add supermemory==3.37.0 openai-agents python-dotenv

pyproject.toml ที่ได้:

[project]
name = "supermemory-trainer"
version = "0.1.0"
description = "Personal exercise trainer agent built with Supermemory and the OpenAI Agents SDK."
requires-python = ">=3.10"
dependencies = [
	"openai-agents>=0.10.2",
	"python-dotenv>=1.2.1",
	"supermemory==3.37.0",
]

ตรวจสอบการตั้งค่า

ก่อนเขียนโค้ดเอเจนต์ ให้ดู Supermemory ทำงานสักครั้งกับประโยคเดียว สคริปต์ด้านล่างส่งข้อเท็จจริงหนึ่งข้อไปยัง Supermemory รอให้ไปป์ไลน์ทำงาน แล้วอ่านโปรไฟล์กลับออกมา หากรันได้เรียบร้อย แสดงว่าคีย์ใช้ได้ และ SDK เข้าถึงได้ เอาต์พุตยังทำให้เห็นครั้งแรกว่า Supermemory จัดการกับข้อความดิบอย่างไร

เปิด hello.py ที่รูทโปรเจ็กต์และแทนที่เนื้อหาที่สร้างอัตโนมัติด้วยการ import และคำสั่งเขียน:

import time
 
from dotenv import load_dotenv
from supermemory import Supermemory
 
load_dotenv()
 
client = Supermemory()
USER_ID = "demo_warmup"
 
response = client.add(
	content="The user is learning Supermemory by building a personal trainer agent.",
	container_tag=USER_ID,
)
print(f"client.add() -> id={response.id} status={response.status}")

load_dotenv() อ่านคีย์ API จาก .env เข้า environment ก่อนจะสร้าง Supermemory() ไคลเอนต์จะหยิบ SUPERMEMORY_API_KEY อัตโนมัติ ค่าของ container_tag="demo_warmup" จำกัดข้อเท็จจริงเดียวนี้ไว้กับผู้ใช้ชั่วคราว

ตอนนี้เพิ่มการรอและการอ่านไว้ท้ายไฟล์เดียวกัน:

print("Waiting 20 seconds for processing...")
time.sleep(20)
 
prof = client.profile(container_tag=USER_ID, q="learning")
print(f"profile.static  ({len(prof.profile.static)}): {prof.profile.static}")
print(f"profile.dynamic ({len(prof.profile.dynamic)}): {prof.profile.dynamic}")
print(f"search_results.results ({len(prof.search_results.results)}):")
for r in prof.search_results.results[:3]:
	print(f"  - {r['memory']} (similarity={r['similarity']:.3f})")

การพัก 20 วินาทีทำให้ไปป์ไลน์ฝังเวกเตอร์และสกัดข้อมูลของ Supermemory มีเวลาประมวลผลความจำใหม่ หากไม่รอ การอ่านจะไม่คืนข้อมูล และสคริปต์จะดูเหมือนเสียแม้จริงๆ แล้วไม่เสีย

รันไฟล์:

uv run python hello.py

เอาต์พุตที่คาดหวัง:

client.add() -> id=zNLsJBrY1PZupAeZ3Qn6EL status=queued
Waiting 20 seconds for processing...
profile.static  (0): []
profile.dynamic (1): ['Building a personal trainer agent to learn Supermemory.']
search_results.results (1):
  - Building a personal trainer agent to learn Supermemory. (similarity=0.650)

มีสามรายละเอียดที่สำคัญในเอาต์พุตนี้ client.add() คืนค่าทันทีด้วย status="queued" เพราะ Supermemory ประมวลผลเอกสาแบบอะซิงโครนัส ช่วงรอ 20 วินาทีครอบคลุมไปป์ไลน์ฝังเวกเตอร์และสกัดข้อมูล เมื่อถึงตอนอ่าน ประโยคดิบก็กลายเป็นชิ้นความจำที่ค้นหาได้

บรรทัดที่น่าสนใจคือ profile.dynamic อินพุตคือประโยค "The user is learning Supermemory by building a personal trainer agent." เอาต์พุตคือข้อเท็จจริงแบบพลวัต 'Building a personal trainer agent to learn Supermemory.' Supermemory เขียนใหม่จากบุคคลที่สามเป็นข้อเท็จจริงเชิงบุคคลที่หนึ่งเกี่ยวกับผู้ใช้ นั่นคือการทำงานของตัวสกัดโปรไฟล์

profile.static เป็นลิสต์ว่าง ข้อเท็จจริงแบบคงที่จะถูกรวมช้าๆ หลังจากมีบันทึกที่เกี่ยวข้องสะสมหลายรายการ ดังนั้นการเขียนวอร์มอัพครั้งเดียวจึงยังไม่เกิดผล ตัวเครื่องมือแนะนำของผู้ฝึกสอนวางแผนไว้รองรับและถือว่า static เป็นโบนัสมากกว่าคำมั่นใจ

สร้างเอเจนต์ Supermemory: ผู้ฝึกสอนออกกำลังกายส่วนบุคคลด้วย Python

ผู้ฝึกสอนจะห่อ client.add() และ client.profile() เป็นเครื่องมือเอเจนต์สองตัว เพื่อให้การอ่านและเขียนเกิดขึ้นอัตโนมัติขณะผู้ใช้แชต ประวัติการออกกำลังกายเหมาะกับความจำมาก อุปกรณ์ อาการบาดเจ็บ และน้ำหนักล่าสุดไม่ได้อยู่ในข้อมูลฝึกของ LLM และสะสมไปทีละเซสชัน

ชื่อ: จุดที่ Supermemory เสียบเข้าสู่ลูปของเอเจนต์ - คำอธิบาย: จุดที่ Supermemory เสียบเข้าสู่ลูปของเอเจนต์

โครงสร้างโปรเจ็กต์และเอเจนต์

ผู้ฝึกสอนมีขนาดเล็กพอที่ทั้งโปรเจ็กต์อยู่ในไฟล์ Python สองไฟล์บวก pyproject.toml ที่คุณมีอยู่แล้ว:

supermemory-trainer/
├── .env            	# your real keys (gitignored)
├── .env.example    	# placeholders, committed
├── .gitignore
├── .python-version
├── main.py         	# agent definition, system prompt, REPL loop
├── pyproject.toml
└── tools.py        	# log_workout and suggest_next_session

tools.py เก็บเครื่องมือสองตัวที่พึ่งหน่วยความจำซึ่งคุณจะเขียนต่อไป log_workout เขียนการออกกำลังกายไปยัง Supermemory ผ่าน client.add() ส่วน suggest_next_session อ่านโปรไฟล์ผู้ใช้ผ่าน client.profile() และ main.py จะ import ทั้งสองและเชื่อมเอเจนต์

ส่วนใหญ่ของ main.py เป็นโค้ดโครงสำหรับ OpenAI Agents SDK ประโยคเดียวใน system prompt ทำงานกับ Supermemory: ทุกข้อเท็จจริงเกี่ยวกับผู้ใช้ต้องกลับมาผ่านการเรียกเครื่องมือ เอเจนต์ถูกบอกว่าไม่มีความจำของตัวเอง กฎเดียวนี้ทำให้ผู้ฝึกสอนมีหน่วยความจำรองรับ

เปิด main.py และเริ่มด้วยการ import และ system prompt:

import asyncio
 
from agents import Agent, Runner, SQLiteSession
 
from tools import log_workout, suggest_next_session
 
SYSTEM_PROMPT = """You are a personal exercise trainer who logs the user's
workouts and recommends what to do next.
 
You have no memory of the user's history on your own. Every fact about the
user lives in Supermemory and reaches you only through tool calls.
 
Two rules, no exceptions:
 
1. Whenever the user reports completing a workout, call log_workout immediately, before responding. Extract the exercise, sets, reps, weight, and any notes from what they said. If a value is missing, ask one short follow-up question instead of guessing. After logging, confirm in one short sentence and stop. Do NOT recommend the next session unless the user asks for one.
 
2. When the user explicitly asks what to do next (or asks for a recommendation, suggestion, or plan), call suggest_next_session first. Never recommend from your own training data. The tool returns the user's
 recent activity, stable preferences, and matching past sessions. Reference those facts directly in your reply.
 
Keep replies concise (2-4 sentences). Be specific: name the exercise, sets, reps, and weight. Honor any injuries or equipment constraints the tool surfaces.
"""

ทั้งสองกฎใน system prompt บังคับให้โมเดลผ่าน Supermemory

กฎ 1 บังคับเรียก log_workout ทุกครั้งเมื่อผู้ใช้รายงานการออกกำลังกาย เพื่อให้ทุกเซสชันไปถึงสโตร์ความจำ กฎ 2 บังคับเรียก suggest_next_session ก่อนการแนะนำใดๆ เพื่อให้ทุกคำแนะนำยึดตามสิ่งที่ Supermemory รู้

หากข้ามกฎเหล่านี้ เอเจนต์จะตอบจากข้อมูลฝึกของมันเอง ซึ่งขัดกับจุดประสงค์ของชั้นความจำ

ตอนนี้กำหนดเอเจนต์และลูปแชตในไฟล์เดียวกัน:

def build_agent() -> Agent:
	return Agent(
    	name="Trainer",
    	instructions=SYSTEM_PROMPT,
    	tools=[log_workout, suggest_next_session],
    	model="gpt-5",
	)
 
 
async def chat() -> None:
	agent = build_agent()
	session = SQLiteSession(session_id="trainer-cli")
 
	print("Trainer ready. Type a message, or 'exit' to quit.\n")
	while True:
    	try:
        	message = input("You: ").strip()
    	except (EOFError, KeyboardInterrupt):
        	print()
        	break
    	if not message:
        	continue
    	if message.lower() in {"exit", "quit"}:
        	break
 
    	result = await Runner.run(agent, message, session=session)
    	print(f"\nTrainer: {result.final_output}\n")
 
 
if __name__ == "__main__":
	asyncio.run(chat())

มีสองบรรทัดที่ควรกล่าวถึง tools=[log_workout, suggest_next_session] ลงทะเบียนเครื่องมือสองตัวที่พึ่งหน่วยความจำ ตัวตกแต่ง @function_tool บนแต่ละฟังก์ชัน (ใน tools.py) บอก SDK ว่าสามารถเรียกได้ หากไม่มีตัวตกแต่ง เอเจนต์จะไม่มีเครื่องมือขณะรันไทม์ แม้คำสั่งสร้างจะสำเร็จก็ตาม

SQLiteSession(session_id="trainer-cli") เก็บประวัติการโต้ตอบระยะสั้นภายในกระบวนการ Python ที่กำลังรัน Supermemory เก็บข้อเท็จจริงระยะยาวของผู้ใช้ข้ามกระบวนการ ปิดกระบวนการ Python จะทิ้งเซสชัน SQLite แต่ข้อมูลใน Supermemory ยังคงอยู่

สำคัญ: รัน main.py เป็นสคริปต์ ไม่ใช่ในเซลล์ Jupyter เพราะอีเวนต์ลูปของ Jupyter ขัดกับ asyncio.run() ไคลเอนต์ Supermemory() แบบซิงก์ทำงานได้ภายในฟังก์ชันเครื่องมือแบบอะซิงก์ เพราะ Agents SDK รันเครื่องมือในเธรดพูล สำหรับข้อมูล SDK เพิ่มเติม ดู บทช่วยสอน OpenAI Agents SDK

เขียนเครื่องมือบันทึกการออกกำลังกาย

log_workout เป็นด้านเขียนของหน่วยความจำเอเจนต์ ฟังก์ชันรับอาร์กิวเมนต์แบบโครงสร้างจากเอเจนต์: ชื่อท่า จำนวนเซ็ต จำนวนครั้ง น้ำหนัก และโน้ตเสริม จากนั้นแปลงเป็นประโยคภาษาอังกฤษสั้นๆ และส่งให้ Supermemory ผ่าน client.add() ไปป์ไลน์ฝังเวกเตอร์และสกัดข้อมูลทำงานใน Supermemory หลังจากนั้นและไม่ต้องการสิ่งใดจากผู้ฝึกสอน

เปิด tools.py และเริ่มด้วยการ import และไคลเอนต์ที่ใช้ร่วมกันหนึ่งตัว:

from agents import function_tool
from dotenv import load_dotenv
from supermemory import Supermemory
 
load_dotenv()
 
USER_ID = "demo_user"
 
client = Supermemory()

load_dotenv() รันตอน import เพื่อให้ SUPERMEMORY_API_KEY อยู่ใน environment ก่อนสร้าง Supermemory() หากสร้างไคลเอนต์ก่อนโหลด env คุณจะได้ไคลเอนต์ที่ยังไม่ได้ยืนยันตัวตน คำเรียกแรกจะคืน 401 ที่ชวนสับสน ฟังก์ชันเครื่องมือทั้งสองในไฟล์นี้ใช้ไคลเอนต์เดียวและค่าคงที่ USER_ID เดียวร่วมกัน

เพิ่มเครื่องมือบันทึกด้านล่างไคลเอนต์:

@function_tool
def log_workout(
	exercise: str,
	sets: int,
	reps: int,
	weight: float,
	notes: str = "",
) -> str:
	"""Log a completed workout to the user's memory.
 
	Args:
    	exercise: Name of the exercise.
    	sets: Number of sets performed.
    	reps: Number of reps per set.
    	weight: Weight in pounds. Pass 0 for bodyweight or cardio.
    	notes: Optional notes about the session.
	"""
	print(f"[log_workout] {exercise=} {sets=} {reps=} {weight=} {notes=}")
 
	content = f"Performed {exercise}: {sets} sets of {reps} reps at {weight} lbs."
	if notes:
    	content += f" Notes: {notes}"
 
	response = client.add(content=content, container_tag=USER_ID)
	print(f"[log_workout] -> id={response.id} status={response.status}")
	return f"Logged {exercise} ({sets}x{reps} @ {weight} lb)."

สตริงเอกสารของ @function_tool คือสิ่งที่ LLM เห็นเมื่อจะตัดสินใจเรียกเครื่องมือ บล็อก Args: จับคู่กับคำอธิบายรายพารามิเตอร์ ทั้งสองเป็นส่วนหนึ่งของสัญญาระหว่างเอเจนต์กับฟังก์ชัน

เครื่องมือส่งประโยคธรรมดาไปที่ client.add() ไม่ใช่ JSON ตัวสกัดโปรไฟล์ของ Supermemory อ่านภาษาธรรมชาติและอนุมานข้อเท็จจริงจากนั้น JSON ใช้ได้ในเชิงเทคนิค แต่คุณภาพการสกัดจะลดลงเพราะโมเดลไม่มีเรื่องเล่าให้สรุป "Performed bench press: 4 sets of 5 reps at 185.0 lbs" ให้ประโยคสะอาดๆ ที่ตัวสกัดทำงานได้ดี

คำสั่ง print() ทั้งสองจะเขียนการเรียกเครื่องมือแต่ละครั้งลงเทอร์มินัล: ครั้งแรกคืออาร์กิวเมนต์ที่แยกได้ แล้วตามด้วยการตอบกลับ

[log_workout] exercise='bench press' sets=4 reps=5 weight=185.0 notes=''
[log_workout] -> id=xY7AK3qLzBPx5Vd2HnRf1M status=queued

ค่า status="queued" ตรงกับที่สคริปต์วอร์มอัพคืนมา ก่อนหน้านี้ บันทึกดิบถูกเก็บแล้ว แต่ client.profile() จะยังไม่คืนเป็นผลการค้นหาจนกว่าไปป์ไลน์จะเสร็จ คุณจะเพิ่มขั้นตอนยืนยันในภายหลังเพื่อรอให้สิ่งนี้นิ่ง

เขียนเครื่องมือแนะนำเซสชัน

suggest_next_session เป็นด้านอ่าน และนี่คือจุดที่การแยกคงที่-พลวัตให้ประโยชน์ การเรียก client.profile(container_tag=USER_ID, q=focus) ครั้งเดียวคืนมุมมองของผู้ใช้สามแบบในรอบการเรียกเดียว

ความชอบที่คงที่คืนใน profile.static กิจกรรมปัจจุบันใน profile.dynamic และความทรงจำในอดีตที่ใกล้เคียงที่สุดใน search_results.results งานของเครื่องมือคือทำให้ทั้งสามมุมมองแบนลงเป็นบล็อกบริบทเดียวที่เอเจนต์อ้างอิงได้

หลังจากมีบันทึกไม่กี่ครั้ง เครื่องมือจะสร้างเอาต์พุตประมาณนี้:

Recent activity:
- Trains at home instead of a gym
- Performed deadlift: 3 sets of 5 reps at 225.0 lbs
- Performed 5k run in 26 minutes
- Reports no knee pain during bench press
- Performed bench press: 4 sets of 5 reps at 185.0 lbs
Closest matching past entries:
- Trains at home instead of a gym
- Performed deadlift: 3 sets of 5 reps at 225.0 lbs
- Performed bench press: 4 sets of 5 reps at 185.0 lbs
- Performed 5k run in 26 minutes
- Reports no knee pain during bench press

เอเจนต์อ่านบล็อกนั้นและเขียนคำแนะนำที่มีฐานจากประวัติจริงของผู้ใช้ หากไม่มีโปรไฟล์ของ Supermemory คุณจะต้องสร้างบริบทเดียวกันเอง ซึ่งแปลว่าต้องมีการค้นหาเชิงความหมายแยกต่างหาก สโตร์โปรไฟล์ของคุณเอง และรวมผลลัพธ์เข้าด้วยกัน client.profile() ครั้งเดียวแทนที่ทั้งสามอย่าง

เพิ่มสิ่งนี้ลงใน tools.py ใต้ log_workout:

@function_tool
def suggest_next_session(focus: str) -> str:
	"""Fetch the user's training history and preferences for a given focus.
 
	Returns a context string the agent can use to recommend the next session.
	The agent is responsible for the actual recommendation. This tool only
	surfaces what Supermemory knows about the user.
 
	Args:
    	focus: What the user wants to train next (e.g. "upper body", "legs",
        	"cardio", "today"). Drives semantic search against past logs.
	"""
	print(f"[suggest_next_session] focus={focus!r}")
	profile = client.profile(container_tag=USER_ID, q=focus)
 
	static_facts = profile.profile.static
	dynamic_facts = profile.profile.dynamic
	matches = profile.search_results.results
 
	print(
    	f"[suggest_next_session] static={len(static_facts)} "
    	f"dynamic={len(dynamic_facts)} matches={len(matches)}"
	)
 
	sections = []
	if static_facts:
    	sections.append("Stable preferences and constraints:")
    	sections.extend(f"- {fact}" for fact in static_facts)
	if dynamic_facts:
    	sections.append("Recent activity:")
    	sections.extend(f"- {fact}" for fact in dynamic_facts)
	if matches:
    	sections.append("Closest matching past entries:")
    	for r in matches[:5]:
        	sections.append(f"- {r['memory']}")
 
	if not sections:
    	return (
        	"No prior training history found for this user. "
        	"Ask the user about their goals, equipment, and recent training."
    	)
	return "\n".join(sections)

client.profile(container_tag=USER_ID, q=focus) คืนอ็อบเจ็กต์ ProfileResponse หลังมีบันทึกสั้นๆ 5 รายการ ฟิลด์ทั้งสามที่เครื่องมืออ่านจะมีหน้าตาประมาณนี้:

profile.profile.static        	# [] (list[str])
profile.profile.dynamic       	# ["Performed bench press: 4 sets of 5 reps at 185.0 lbs", ...]
profile.search_results.results	# [{"memory": "...", "similarity": 0.631, ...}, ...] (list[dict])

ผลการค้นหาแต่ละรายการเป็น dict ของ Python ไม่ใช่อ็อบเจ็กต์ Pydantic ใช้ r["memory"] สำหรับข้อความและ r["similarity"] สำหรับคะแนน dict เต็มมีคีย์ดังต่อไปนี้:

  • id

  • memory

  • rootMemoryId

  • metadata

  • updatedAt

  • version

  • similarity

  • filepath

  • documents

สไนเป็ต r.memory or r.chunk จากหน้าการผสาน Supermemory กับ OpenAI Agents SDK ทำให้เกิด AttributeError กับ supermemory==3.37.0 ให้ใช้การเข้าถึงด้วยวงเล็บ

static ว่างในที่นี้ จึงมีการแตกแขนงด้วย if static_facts: ส่วน dynamic และ search_results ทำงานหลักในช่วงบันทึกโหลแรกๆ

Supermemory ยังมีค่า threshold ความคล้ายคลึงเริ่มต้น ข้อเท็จจริงที่พูดเพียงครั้งเดียวอาจไม่กลับมาสำหรับทุกคำค้น บันทึก 5 รายการข้างต้นทั้งหมดคืนผลสำหรับ q="today" แต่คำถามที่เจาะจงมากขึ้นอาจคืนมาน้อยกว่า การการันตีด้วย if matches: จัดการสิ่งนี้โดยไม่ล้ม

รันเซสชันที่ 1: บันทึกการออกกำลังกาย

เริ่มเซสชันแรกและบันทึกการออกกำลังกายสองสามครั้งเพื่อเติม Supermemory ให้มีสิ่งให้อ่านในภายหลัง รันสคริปต์:

uv run python main.py

บันทึกเบนช์เพรส จากนั้นวิ่ง 5k แล้วต่อด้วยเดดลิฟต์ พร้อมข้อความความชอบหนึ่งข้อ: "ฉันฝึกที่บ้านเท่านั้น ไม่เข้ายิม" เอเจนต์จะเรียก log_workout หนึ่งครั้งต่อการออกกำลังกาย และบรรทัด print() ของเครื่องมือทำให้เห็นการเรียกทุกครั้งในเทอร์มินัล

ชื่อ: เทอร์มินัลเซสชัน 1 แสดงการเรียก log_workout สามครั้ง - คำอธิบาย: เทอร์มินัลเซสชัน 1 แสดงการเรียก log_workout สามครั้ง

ตัวอย่างเอาต์พุต วาทกรรมของเอเจนต์คุณอาจต่างออกไปเพราะโมเดลไม่กำหนดค่าแน่นอน

สามบรรทัด status=queued คือช่วงที่ Supermemory เข้ามารับช่วงต่อ แต่ละบรรทัดตรงกับเอกสารที่กำลังผ่านไปป์ไลน์ฝังเวกเตอร์และสกัดข้อมูลฝั่ง Supermemory สำหรับบันทึกข้อความสั้นๆ เช่นนี้ เอกสารจะค้นหาได้ผ่าน client.profile() ภายในราว ~12 วินาที

ไม่มีอะไรในโค้ดของผู้ฝึกสอนที่รอสิ่งนี้ เอเจนต์จะไปต่อ และ Supermemory ทำงานให้เสร็จเบื้องหลัง

ทุกบันทึกเรียก log_workout เพียงครั้งเดียว แล้วเอเจนต์หยุด ไม่มีคำแนะนำเชิงรุก ไม่มีการเรียกเครื่องมือเพิ่มเติม ไม่มีคำแนะนำติดตามผล กฎข้อแรกของ system prompt เป็นตัวกำหนด หากไม่มี จะมีการแนะนำเซสชันถัดไปหลังจากแต่ละบันทึก ทำให้จำนวนการเรียกเครื่องมือเพิ่มเป็นสองเท่า

พิมพ์ exit เพื่อปิดเซสชัน 1 กระบวนการ Python จะสิ้นสุด และ SQLiteSession ถูกลบทิ้งพร้อมกัน บันทึกการออกกำลังกายและข้อความความชอบจะอยู่ใน Supermemory ภายใต้ container_tag="demo_user" แยกจากสคริปต์ที่เขียนมัน

ยืนยันการเรียกคืนและรันเซสชันที่ 2

ก่อนเซสชันที่ 2 ยืนยันว่าข้อเท็จจริงจากเซสชันแรกถูกคิวรีได้ เปิด REPL ของ Python ใหม่หรือบันทึกเป็นสคริปต์สั้นๆ:

from dotenv import load_dotenv
from supermemory import Supermemory
 
load_dotenv()
client = Supermemory()
 
prof = client.profile(container_tag="demo_user", q="training")
print(f"static  ({len(prof.profile.static)}): {prof.profile.static}")
print(f"dynamic ({len(prof.profile.dynamic)}):")
for fact in prof.profile.dynamic:
	print(f"  - {fact}")
print(f"matches ({len(prof.search_results.results)}):")
for r in prof.search_results.results[:5]:
	print(f"  - {r['memory']} (similarity={r['similarity']:.3f})")

เอาต์พุตจริงที่จับระหว่างสองเซสชัน:

static  (0): []
dynamic (5):
  - Trains at home instead of a gym
  - Performed deadlift: 3 sets of 5 reps at 225.0 lbs
  - Performed 5k run in 26 minutes
  - Reports no knee pain during bench press
  - Performed bench press: 4 sets of 5 reps at 185.0 lbs
matches (5):
  - Trains at home instead of a gym (similarity=0.682)
  - Performed deadlift: 3 sets of 5 reps at 225.0 lbs (similarity=0.643)
  - Performed bench press: 4 sets of 5 reps at 185.0 lbs (similarity=0.631)
  - Performed 5k run in 26 minutes (similarity=0.585)
  - Reports no knee pain during bench press (similarity=0.585)

ดูสิ่งที่ตัวสกัดของ Supermemory สร้างขึ้น ผู้ใช้พูดว่า "ฉันฝึกที่บ้านเท่านั้น ไม่เข้ายิม" เพียงครั้งเดียว ตัวสกัดแปลงเป็นข้อเท็จจริงแบบพลวัต "Trains at home instead of a gym"

บันทึกเบนช์เพรสมีช่องโน้ตเกี่ยวกับไม่มีอาการปวดเข่า ตัวสกัดแยกบันทึกเดียวเป็นสองข้อเท็จจริงแบบพลวัต: หนึ่งสำหรับการออกกำลังกาย หนึ่งสำหรับการไม่มีอาการปวด

บันทึกสี่รายการกลายเป็นข้อเท็จจริงแบบพลวัตที่ทำให้เป็นมาตรฐานห้ารายการบวกชิ้นความจำที่ตรงกันห้ารายการพร้อมคะแนนความคล้ายคลึงระหว่าง 0.585 ถึง 0.682 ไม่มีการแยก ทำมาตรฐาน หรือการจับคู่ใดๆ ที่เกิดขึ้นในโค้ดของผู้ฝึกสอน หาก dynamic ว่างสำหรับคุณ ให้รออีก 10 วินาทีและรันสไนเป็ตซ้ำ คิวประมวลผลอาจพุ่งเป็นช่วงๆ

ตอนนี้เริ่มเซสชันที่ 2 ในกระบวนการใหม่ทั้งหมด:

uv run python main.py

นี่คืออินเทอร์พรีเตอร์ Python ใหม่ ไม่มีหน่วยความจำร่วมกับเซสชันแรก ไม่มีแคชอุ่น อะไรก็ตามที่เอเจนต์เรียกคืนมาจาก Supermemory

ส่งข้อความเดียว: "วันนี้ฉันควรออกกำลังกายอะไรดี?"

ชื่อ: เทอร์มินัลเซสชัน 2 ในกระบวนการใหม่ที่เรียกคืนการออกกำลังกายก่อนหน้า - คำอธิบาย: เทอร์มินัลเซสชัน 2 ในกระบวนการใหม่ที่เรียกคืนการออกกำลังกายก่อนหน้า

ตัวอย่างเอาต์พุต ใช้สโตร์ความจำเดียวกัน แต่เป็นกระบวนการ Python ใหม่

เอเจนต์เรียก suggest_next_session("today") เครื่องมือพิมพ์ static=0 dynamic=5 matches=5 การรันที่จับไว้ตอบด้วยเซสชันช่วงล่างที่บ้าน (สควอต ลันจ์ สเต็ปอัป)

คำแนะนำสอดคล้องกับบันทึกก่อนหน้าเพราะโปรไฟล์ของ Supermemory บอกเอเจนต์ว่าเป็นอะไร เบนช์ เดดลิฟต์ และ 5k เป็นช่วงบนหรือคาร์ดิโอ และผู้ใช้ฝึกที่บ้านเท่านั้น ทั้งสองข้อเท็จจริงกลับมาจากการเรียก client.profile() เดียวกัน การรันของคุณอาจใช้ถ้อยคำต่างกันเพราะโมเดลไม่กำหนดค่าแน่นอน แต่เส้นทางการเรียกคืนเหมือนกัน

ก้าวถัดไปสำหรับเอเจนต์ Supermemory ของคุณ

เดโมนี้มีผู้ใช้หนึ่งคน สองเครื่องมือ และ CLI เวอร์ชันจริงของผู้ฝึกสอนจะขยายไปในสามทิศทางที่สอดรูปกับ Supermemory ก่อนจะแตะลูปเอเจนต์

กำหนดขอบเขตความจำต่อผู้ใช้จริง ค่าคงที่ USER_ID = "demo_user" ใช้ได้กับคนเดียว แอปโปรดักชันคำนวณแท็กจากไอดีผู้ใช้ที่ยืนยันตัวตนแล้ว เช่น container_tag="user_sarah" หรือ container_tag=customer_id ความจำระหว่างผู้ใช้ยังคงแยกกันเพราะการอ่านทุกครั้งส่งแท็กกลับไป เปลี่ยนแค่จุดเดียวใน tools.py โค้ดอื่นไม่ต้องขยับ

เพิ่มเครื่องมือที่พึ่งหน่วยความจำมากขึ้น สัปดาห์ผ่อนแรง การติดตามสถิติสูงสุด และพรอมต์โมบิลิตีรายสัปดาห์ แต่ละอย่างคือฟังก์ชัน @function_tool อีกตัวที่เรียก client.add() สำหรับเขียนและ client.profile() สำหรับอ่านโดยอิงแท็ก container_tag เดิม รูปแบบเครื่องมือคงเดิม เปลี่ยนแค่ว่าเอเจนต์บันทึกอะไรและขออะไร

จัดการกรณี Supermemory ล้มเหลว ห่อ client.add() และ client.profile() ด้วย try/except supermemory.APIError เพื่อป้องกันความล้มเหลวชั่วคราวจาก Supermemory ทำให้เอเจนต์ล่ม ตั้งค่า timeout รายคำขอหากเอเจนต์รันในสภาพแวดล้อมที่จำกัด

ส่วนลูปเอเจนต์เป็นอิสระจาก Supermemory และเปลี่ยนได้ภายหลัง วาง CLI ไว้หน้าบริการอย่าง Telegram, Discord, หรือ Slack เพื่อให้ผู้ใช้ส่งข้อความการออกกำลังกายและบอทเรียก Runner.run() หรือเปลี่ยนเฟรมเเวิร์ก Supermemory มี การผสานกับ LangChain หากสแต็กของคุณใช้งาน เอเจนต์ LangChain อยู่แล้ว และโค้ดความจำก็ไม่ต้องเปลี่ยน

การแยกคงที่-พลวัตยังเข้ากับโดเมนอื่นๆ

  • เอเจนต์ซัพพอร์ตลูกค้า: คงที่ = ปัญหาที่ทราบและความชอบในบัญชี พลวัต = ทิกเก็ตที่เปิดอยู่และการติดต่อเมื่อไม่นานนี้
  • เอเจนต์โค้ดดิ้ง: คงที่ = ภาษาและเฟรมเวิร์กที่ชอบ พลวัต = งานปัจจุบันและไฟล์ที่เพิ่งแตะ

การแยกนี้ใช้ได้เสมอเมื่อผู้ใช้คือแหล่งความจริง

สรุป

คุณเพิ่งสร้างผู้ฝึกสอน Python ที่มีสองเครื่องมือและหน่วยความจำถาวรข้ามกระบวนการ client.add() เขียนบันทึกการออกกำลังกาย client.profile() อ่านผู้ใช้กลับมาเป็นข้อเท็จจริงคงที่ ข้อเท็จจริงพลวัต และการจับคู่เชิงความหมายในการเรียกเดียว ทั้งหมดจำกัดขอบเขตด้วย container_tag Supermemory จัดการการแบ่งชิ้น ฝังเวกเตอร์ ค้นหา และสกัดโปรไฟล์ที่เดโมไม่ต้องเขียนเอง

จับคู่กับ RAG แล้วเอเจนต์เดียวกันจะตอบคำถามทั้งเกี่ยวกับผู้ใช้และตัวผลิตภัณฑ์ อธิบาย LLM Agents ครอบคลุมรูปแบบเอเจนต์ที่กว้างขึ้น และเส้นทาง Associate AI Engineer for Developers ยังลงลึกต่อในเอเจนต์ที่พึ่งหน่วยความจำ

Supermemory FAQs

Supermemory คืออะไร?

Supermemory คือ API หน่วยความจำแบบโฮสต์ที่จัดเก็บ ทำดัชนี และเรียกคืนบริบทระยะยาวให้เอเจนต์ AI เพื่อให้จดจำผู้ใช้ข้ามเซสชันได้

Supermemory ต่างจากฐานข้อมูลเวกเตอร์ทั่วไปอย่างไร?

Supermemory เพิ่มการฝังเวกเตอร์ การทำดัชนี การค้นหาเชิงความหมาย และการสกัดข้อเท็จจริงแบบโปรไฟล์บนพื้นฐานของการจัดเก็บ ทำให้คุณได้ความจำที่ใช้งานได้ในการเรียก API ครั้งเดียว

Supermemory จัดการทั้ง RAG และหน่วยความจำผู้ใช้ได้หรือไม่?

ได้ รองรับ RAG แบบเอกสารเหนือไฟล์และ URL ควบคู่กับความจำเชิงผู้ใช้ ช่วยให้ API เดียวกันขับเคลื่อนทั้งความรู้ของผลิตภัณฑ์และประวัติส่วนบุคคล

ต้องจัดการโมเดลการฝังเวกเตอร์เองเมื่อใช้ Supermemory ไหม?

ไม่จำเป็น Supermemory รันไปป์ไลน์ฝังเวกเตอร์และการเรียกคืนให้ คุณส่งข้อความดิบหรือคอนเทนต์แล้วคิวรีภายหลังได้โดยไม่ต้องจัดการโมเดลหรือดัชนีเอง

มีแพ็กเกจฟรีให้ลองใช้ Supermemory หรือไม่?

มี แผนฟรีสำหรับการทดสอบและโปรเจ็กต์ขนาดเล็ก พร้อมโควตาโทเค็นและจำนวนคิวรีรายเดือน เพื่อให้ผสานและทดลองได้ก่อนอัปเกรด

หัวข้อ

เรียน AI Engineering กับ DataCamp!

Tracks

Associate AI Engineer for Developers

26 ชม.
Learn how to integrate AI into software applications using APIs and open-source libraries. Start your journey to becoming an AI Engineer today!
ดูรายละเอียดRight Arrow
เริ่มหลักสูตร
ดูเพิ่มเติมRight Arrow