View a markdown version of this page

Memori jangka panjang semantik untuk LangGraph agen - Amazon DynamoDB

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Memori jangka panjang semantik untuk LangGraph agen

LangGraph memisahkan dua jenis status agen. Short-term state adalah utas percakapan itu sendiri, yang dipertahankan checkpointer sehingga utas dapat melanjutkan, memutar ulang, dan memulihkan (lihatMenggunakan DynamoDB sebagai penyimpanan pos pemeriksaan untuk agen LangGraph). Long-term memori adalah apa yang diketahui agen di seluruh utas, dan LangGraph memodelkannya sebagai penyimpanan: permukaan nilai kunci dengan spasi nama yang ditulis agen dengan sengaja dan dibaca kembali nanti.

Kel DynamoDBStore as, dalam paket langgraph-checkpoint-aws, adalah implementasi DynamoDB dari penyimpanan itu. Ini menangani sisi nilai kunci dengan ruang nama hierarkis, Time to Live untuk memori basi yang kedaluwarsa, dan pemfilteran dasar. Dengan indeks vektor yang dikonfigurasi (lihatMenggunakan indeks vektor di DynamoDB), search() metodenya melakukan pencarian semantik: memori disematkan pada penulisan, diindeks secara asinkron, dan diingat berdasarkan makna, diberi peringkat berdasarkan kesamaan, dari tabel yang sama yang menahannya. Tidak ada database vektor terpisah untuk disediakan dan tidak ada saluran menyalin data ke dalamnya.

Prasyarat

  • Akun AWS Dengan izin untuk membuat tabel DynamoDB dan memanggil model Amazon Bedrock

  • Akses ke model embeddings di Amazon Bedrock di Wilayah Anda. Contoh-contoh ini menggunakan Amazon Titan Text Embeddings V2

  • Python 3.10 atau yang lebih baru, dengan langgraph-checkpoint-aws 1.2.2 atau yang lebih baru dan boto3 1.43.64 atau lebih baru (boto3versi sebelumnya tidak memiliki operasi) SearchVectors

Instal pustaka menggunakan pip:

pip install langgraph langgraph-checkpoint-aws langchain-aws

Siapkan toko

Konfigurasikan toko dengan index blok dan panggilsetup():

from langchain_aws import BedrockEmbeddings from langgraph_checkpoint_aws import DynamoDBStore store = DynamoDBStore( table_name="support-agent-memory", region_name="us-east-1", index={ "embed": BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0"), "dims": 1024, "fields": ["text"], "distance_function": "COSINE", }, ) store.setup()

Empat pengaturan di index blok patut dipahami:

  • embedmengambil objek LangChain embeddings apa pun, atau callable biasa yang memetakan daftar string ke daftar vektor.

  • dimsharus sesuai dengan ukuran output model Anda. Titan Text Embeddings V2 mengembalikan 1.024 dimensi secara default. Jika ini tidak setuju, toko menimbulkan kesalahan ketidakcocokan dimensi yang menamai kedua angka pada penulisan pertama, daripada menulis vektor yang tidak dapat digunakan indeks.

  • fieldsmemilih bagian mana dari nilai yang disematkan. Di sini hanya text bidang yang tertanam. Default,["$"], membuat serial seluruh nilai ke JSON dan menyematkannya, yang nyaman tetapi juga menyematkan atribut pembukuan Anda.

  • distance_functiondefault keCOSINE, dan juga menerima EUCLIDEAN danDOT_PRODUCT.

setup()membuat tabel jika tidak ada, melampirkan indeks vektor, dan mengaktifkan Time to Live jika Anda mengonfigurasinya. Pada tabel yang ada, itu hanya menambahkan apa yang hilang, sehingga penerapan yang ada DynamoDBStore dapat mengadopsi pencarian semantik tanpa membuat ulang tabel atau memigrasikan item. Perlu diingat bahwa setup() tidak kembali sampai indeks melaporkan ACTIVE dan tidak lagi diisi ulang: indeks yang melaporkan ACTIVE sementara masih mengisi ulang menolak SearchVectors panggilan. Di meja kosong baru penantiannya singkat. Pemasangan ulang ke meja dengan barang-barang yang ada membutuhkan waktu selama pengisian ulang.

Tulis kenangan

Menulis adalah hal yang biasaput(). Penyematan terjadi untuk Anda:

namespace = ("memories", "acme-corp", "user-8812") store.put(namespace, "mem-1", { "text": "Account runs a proxy that closes idle sockets after 60 seconds", "source": "ticket-4471", }) store.put(namespace, "mem-2", { "text": "Customer prefers email, asked not to be called by phone", "source": "ticket-4471", }) store.put(namespace, "mem-3", { "text": "Uses a custom build of the SDK pinned to version 2.14", "source": "ticket-4502", })

Ingat dengan makna

Pelanggan membuka percakapan baru dan mengatakan koneksi mereka terus putus setelah sekitar satu menit:

results = store.search( namespace, query="connection drops after a minute of inactivity", limit=3, ) for item in results: print(round(item.score, 3), item.value["text"])

Output:

0.324 Account runs a proxy that closes idle sockets after 60 seconds 0.039 Customer prefers email, asked not to be called by phone 0.031 Uses a custom build of the SDK pinned to version 2.14

Memori yang relevan menempati urutan pertama, dan tidak ada kunci dalam kueri yang cocok dengan apa pun. SearchItem.scoremengikuti LangGraph konvensi di mana yang lebih tinggi lebih relevan. DynamoDB mengembalikan jarak, di mana yang lebih rendah lebih dekat, sehingga toko mengonversi: untuk COSINE skor adalah1 - distance, karena EUCLIDEAN itu1 / (1 + distance), dan DOT_PRODUCT skor melewati tidak berubah. Jika Anda memasukkan skor absolut untuk memutuskan apakah memori cukup relevan untuk disuntikkan ke dalam prompt, kalibrasi ambang batas itu terhadap data Anda sendiri dan fungsi jarak yang Anda pilih.

Memori lingkup dengan skema pencarian

Ketika DynamoDBStore membuat indeks vektor, ia mendeklarasikan kunci partisi tabel sebagai HASH elemen dalam skema pencarian indeks. Karena elemen itu ada, setiap pencarian harus menyediakan kondisi untuknya, dan toko menyediakan namespace: tuple namespace bergabung untuk membentuk nilai kunci partisi, sehingga setiap pencarian semantik disematkan tepat ke satu namespace. Tiga konsekuensi berikut:

  • Isolasi bersifat struktural, bukan filter yang Anda ingat untuk menulis. Pencarian tidak dapat mencapai memori di namespace yang berbeda, sehingga toko yang melayani banyak penyewa tidak memiliki bentuk kueri yang mengembalikan memori penyewa lain. Ini adalah pelingkupan kueri daripada otorisasi: kepala sekolah dengan dynamodb:SearchVectors izin di tabel dapat mencari nilai namespace apa pun secara langsung, jadi memutuskan ruang nama mana yang dapat dibaca pemanggil masih milik IAM dan lapisan otorisasi aplikasi Anda.

  • Biaya mengingat melacak memori satu pengguna, bukan seluruh tabel Anda. Pekerjaan pencarian dibatasi oleh seberapa banyak yang dimiliki satu namespace, bukan oleh berapa banyak memori yang dimiliki seluruh produk Anda, yang membuat latensi dan biaya pencarian vektor tetap kecil dan stabil saat Anda tumbuh.

  • Pencarian semantik adalah namespace yang tepat, bukan awalan. Pencarian mencapai persis namespace yang Anda lewati dan tidak ada di bawahnya. Pencarian ("memories", "acme-corp") tidak mencapai("memories", "acme-corp", "user-8812"), karena itu adalah nilai kunci partisi yang berbeda. Namespace Anda adalah cakupan penarikan Anda, jadi pilihlah agar sesuai dengan cakupan yang Anda ingin melihat satu pencarian.

Tabel berikut merangkum cara memilih bentuk namespace.

Bentuk namespace

Satu search() mencapai

Pilihlah kapan

("memories", user_id)

Kenangan pengguna itu

Single-tenant produk dengan penarikan per pengguna

("memories", tenant_id, user_id)

Pengguna itu, di penyewa itu

Multi-tenant, kasus umum

("memories", tenant_id, user_id, agent_name)

Catatan satu agen tentang pengguna itu

Beberapa agen khusus yang seharusnya tidak membaca catatan satu sama lain

("account_facts", tenant_id)

Tenant-wide pengetahuan

Fakta yang berlaku untuk setiap pengguna di akun

Hindari menempatkan setiap memori dalam satu namespace dan memfilter metadata: filter diterapkan setelah DynamoDB telah memilih kecocokan terdekat di seluruh namespace, dan satu pencarian mengembalikan paling banyak 100 kecocokan teratas, jadi setelah satu penyewa sibuk mengisi 100 teratas untuk kueri umum, pencarian penyewa yang lebih tenang mengembalikan hasil yang lebih sedikit daripada yang diminta meskipun memori mereka ada. Lebih suka pelingkupan namespace daripada filter untuk apa pun yang menentukan kebenaran. Lebih baik daripada batas ingatan Anda juga merupakan desain yang sah: agen yang membutuhkan konteks khusus pengguna dan seluruh akun mencari ruang nama dan menggabungkan hasilnya.

Pertimbangan-pertimbangan

  • Indeks akhirnya konsisten, model yang sama dengan indeks sekunder global. A yang search() dikeluarkan segera setelah a put() mungkin belum menyertakan memori baru. Untuk agen yang menulis memori dan kemudian mengingatnya dalam giliran yang sama, bacalah kembali dengan kunci sebagai gantinya.

  • Satu pencarian mengembalikan paling banyak 100 pertandingan teratas. Permintaan yang nil limit ainya offset melebihi itu ditolak dengan kesalahan yang jelas daripada diam-diam tidak mengembalikan apa pun di luar batas.

  • Jika Anda menggunakan Time to Live untuk mengakhiri memori basi dan mengaktifkan penyegaran saat dibaca, memori yang sebenarnya diingat agen akan mengalami kedaluwarsa didorong keluar, sehingga memori yang digunakan secara aktif bukanlah yang menghilang secara diam-diam.

  • Kegagalan pencarian vektor meningkat sehingga aplikasi Anda dapat bereaksi. Throttle, masalah izin, dan hasil yang benar-benar kosong akan terlihat identik jika toko mengembalikan daftar kosong saat gagal.

  • Operasi vektor menagih dalam unitnya sendiri, diukur secara terpisah dari unit permintaan baca dan tulis tabel dasar, dan keduanya menskalakan dengan jumlah dimensi. Dimensi embedding yang lebih kecil lebih murah pada setiap penulisan dan setiap pencarian, jadi gunakan dimensi terkecil yang mempertahankan kualitas ingatan Anda.

  • Memori yang ditulis tanpa teks dalam konfigurasi fields disimpan tanpa penyematan dan tidak akan muncul dalam hasil semantik. Toko mencatat peringatan ketika ini terjadi.

Sumber daya tambahan