الذاكرة

ذاكرة دلالية طويلة المدى لوكلاء الذكاء الاصطناعي باستخدام adk-memory.

نظرة عامة

يوفر نظام الذاكرة تخزينًا دائمًا وقابلاً للبحث لمحادثات الوكلاء. على عكس حالة الجلسة (التي تكون مؤقتة)، تستمر الذاكرة عبر الجلسات وتمكن الوكلاء من استدعاء السياق ذي الصلة من التفاعلات السابقة.

التثبيت

[dependencies]
adk-memory = "2.0.0"

المفاهيم الأساسية

MemoryEntry

سجل ذاكرة واحد يحتوي على المحتوى والمؤلف والطابع الزمني:

use adk_memory::MemoryEntry;
use adk_core::Content;
use chrono::Utc;

let entry = MemoryEntry {
    content: Content::new("user").with_text("I prefer dark mode"),
    author: "user".to_string(),
    timestamp: Utc::now(),
};

MemoryService Trait

السمة الأساسية لخلفيات الذاكرة:

#[async_trait]
pub trait MemoryService: Send + Sync {
    /// Store session memories for a user
    async fn add_session(
        &self,
        app_name: &str,
        user_id: &str,
        session_id: &str,
        entries: Vec<MemoryEntry>,
    ) -> Result<()>;

    /// Search memories by query
    async fn search(&self, req: SearchRequest) -> Result<SearchResponse>;
}

SearchRequest

معلمات الاستعلام للبحث في الذاكرة:

use adk_memory::SearchRequest;

let request = SearchRequest {
    query: "user preferences".to_string(),
    user_id: "user-123".to_string(),
    app_name: "my_app".to_string(),
    limit: None,
    min_score: None,
    project_id: None, // None = global only, Some("id") = global + project
};

InMemoryMemoryService

تطبيق بسيط في الذاكرة للتطوير والاختبار:

use adk_memory::{InMemoryMemoryService, MemoryService, MemoryEntry, SearchRequest};
use adk_core::Content;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let memory = InMemoryMemoryService::new();

    // Store memories from a session
    let entries = vec![
        MemoryEntry {
            content: Content::new("user").with_text("I like Rust programming"),
            author: "user".to_string(),
            timestamp: Utc::now(),
        },
        MemoryEntry {
            content: Content::new("assistant").with_text("Rust is great for systems programming"),
            author: "assistant".to_string(),
            timestamp: Utc::now(),
        },
    ];

    memory.add_session("my_app", "user-123", "session-1", entries).await?;

    // Search memories
    let request = SearchRequest {
        query: "Rust".to_string(),
        user_id: "user-123".to_string(),
        app_name: "my_app".to_string(),
        limit: None,
        min_score: None,
        project_id: None,
    };

    let response = memory.search(request).await?;
    println!("Found {} memories", response.memories.len());

    Ok(())
}

عزل الذاكرة

يتم عزل الذكريات بواسطة:

  • app_name: التطبيقات المختلفة لها مساحات ذاكرة منفصلة
  • user_id: ذكريات كل مستخدم خاصة
  • project_id (اختياري): يمكن تحديد نطاق الإدخالات لمشروع داخل مستخدم
// User A's memories
memory.add_session("app", "user-a", "sess-1", entries_a).await?;

// User B's memories (separate)
memory.add_session("app", "user-b", "sess-1", entries_b).await?;

// Search only returns user-a's memories
let request = SearchRequest {
    query: "topic".to_string(),
    user_id: "user-a".to_string(),
    app_name: "app".to_string(),
    limit: None,
    min_score: None,
    project_id: None, // None = global entries only
};

الذاكرة ذات النطاق المحدد للمشروع

يمكن تحديد نطاق الذكريات لمشروع داخل مستخدم. يصبح مفتاح العزل (app_name, user_id, project_id?):

  • الإدخالات العامة (project_id = None): مرئية في جميع سياقات المشروع وفي عمليات البحث العالمية فقط.
  • إدخالات المشروع (project_id = Some(id)): مرئية فقط عند البحث داخل هذا المشروع المحدد.
  • بحث المشروع (project_id = Some(id)): يعيد الإدخالات العامة + إدخالات لهذا المشروع.
  • البحث العام (project_id = None): يعيد الإدخالات العامة فقط.

تخزين الإدخالات ذات النطاق المحدد للمشروع

use adk_memory::{InMemoryMemoryService, MemoryService, MemoryEntry};
use adk_core::Content;
use chrono::Utc;

let service = InMemoryMemoryService::new();

let entry = MemoryEntry {
    content: Content::new("user").with_text("Project uses microservices"),
    author: "user".to_string(),
    timestamp: Utc::now(),
};

// Global entry (no project scope)
service.add_session("app", "user-1", "sess-1", vec![entry.clone()]).await?;

// Project-scoped entry
service.add_session_to_project("app", "user-1", "sess-2", "my-project", vec![entry.clone()]).await?;

// Single entry to a project
service.add_entry_to_project("app", "user-1", "my-project", entry).await?;

البحث بنطاق المشروع

use adk_memory::SearchRequest;

// Global-only search — returns only global entries
let global = service.search(SearchRequest {
    query: "microservices".into(),
    user_id: "user-1".into(),
    app_name: "app".into(),
    limit: None,
    min_score: None,
    project_id: None,
}).await?;

// Project search — returns global + project entries
let project = service.search(SearchRequest {
    query: "microservices".into(),
    user_id: "user-1".into(),
    app_name: "app".into(),
    limit: None,
    min_score: None,
    project_id: Some("my-project".into()),
}).await?;

حذف بنطاق المشروع

// Delete entries matching a query within a project only
service.delete_entries_in_project("app", "user-1", "my-project", "microservices").await?;

// Delete ALL entries for a project
service.delete_project("app", "user-1", "my-project").await?;

// Global delete — only removes global entries, project entries are unaffected
service.delete_entries("app", "user-1", "microservices").await?;

// GDPR delete_user — removes everything (global + all projects)
service.delete_user("app", "user-1").await?;

MemoryServiceAdapter بنطاق المشروع

يربط MemoryServiceAdapter بين MemoryService و adk_core::Memory. استخدم with_project_id() لتحديد نطاق جميع العمليات:

use adk_memory::{InMemoryMemoryService, MemoryServiceAdapter};
use adk_core::Memory;
use std::sync::Arc;

let service = Arc::new(InMemoryMemoryService::new());

// Adapter without project — operates on global entries
let global_adapter = MemoryServiceAdapter::new(service.clone(), "app", "user-1");

// Adapter with project — all search/add/delete operations scoped to the project
let project_adapter = MemoryServiceAdapter::new(service.clone(), "app", "user-1")
    .with_project_id("my-project");

// Core Memory trait also supports ad-hoc project access
global_adapter.search_in_project("query", "other-project").await?;
global_adapter.add_to_project(entry, "other-project").await?;

التحقق من معرف المشروع

يتم التحقق من معرفات المشروع في جميع عمليات الكتابة:

  • يجب ألا تكون فارغة
  • يجب ألا تتجاوز 256 حرفًا
use adk_memory::validate_project_id;

validate_project_id("my-project")?;       // Ok
validate_project_id("")?;                  // Err: must not be empty
validate_project_id(&"x".repeat(257))?;   // Err: exceeds 256 chars

مصفوفة دلالات البحث

SearchRequest.project_idإرجاع الإدخالات العامةإرجاع إدخالات المشروع
None✅ مطابقة الاستعلام❌ لا شيء
Some("A")✅ مطابقة الاستعلام✅ إدخالات المشروع "A" فقط المطابقة للاستعلام

مصفوفة دلالات الحذف

العمليةالنطاق
delete_entries (لا يوجد مشروع)الإدخالات العامة المطابقة للاستعلام فقط
delete_entries_in_project("A")إدخالات المشروع "A" المطابقة للاستعلام فقط
delete_project("A")جميع إدخالات المشروع "A"
delete_userجميع الإدخالات (العامة + جميع المشاريع)

سلوك البحث

تستخدم InMemoryMemoryService المطابقة القائمة على الكلمات:

  1. يتم تقسيم الاستعلام إلى كلمات (أحرف صغيرة)
  2. يتم تقسيم محتوى كل ذاكرة
  3. يتم إرجاع الذكريات التي تحتوي على أي كلمات متطابقة
// Query: "rust programming"
// Matches memories containing "rust" OR "programming"

واجهة خلفية ذاكرة مخصصة

قم بتطبيق MemoryService للتخزين المخصص (مثل قاعدة بيانات المتجهات):

use adk_memory::{MemoryService, MemoryEntry, SearchRequest, SearchResponse};
use adk_core::Result;
use async_trait::async_trait;

pub struct VectorMemoryService {
    // Your vector DB client
}

#[async_trait]
impl MemoryService for VectorMemoryService {
    async fn add_session(
        &self,
        app_name: &str,
        user_id: &str,
        session_id: &str,
        entries: Vec<MemoryEntry>,
    ) -> Result<()> {
        // 1. Generate embeddings for each entry
        // 2. Store in vector database with metadata
        Ok(())
    }

    async fn search(&self, req: SearchRequest) -> Result<SearchResponse> {
        // 1. Generate embedding for query
        // 2. Perform similarity search
        // 3. Return top-k results
        Ok(SearchResponse { memories: vec![] })
    }
}

التكامل مع Agents

تتكامل الذاكرة مع LlmAgentBuilder:

use adk_agent::LlmAgentBuilder;
use adk_memory::InMemoryMemoryService;
use std::sync::Arc;

let memory = Arc::new(InMemoryMemoryService::new());

let agent = LlmAgentBuilder::new("assistant")
    .model(model)
    .instruction("You are a helpful assistant with memory.")
    .memory(memory)
    .build()?;

عندما يتم تكوين الذاكرة:

  1. قبل كل دور، يتم البحث عن الذكريات ذات الصلة
  2. يتم حقن الذكريات المتطابقة في السياق
  3. بعد كل جلسة، يتم تخزين المحادثة كذكريات

البنية

┌─────────────────────────────────────────────────────────────┐
│                      Agent Request                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Memory Search                            │
│                                                             │
│   SearchRequest { query, user_id, app_name, project_id }   │
│                         │                                   │
│                         ▼                                   │
│   ┌─────────────────────────────────────────────────────┐  │
│   │              MemoryService                          │  │
│   │  ┌─────────┐ ┌──────────┐ ┌────────┐ ┌──────────┐  │  │
│   │  │InMemory │ │ SQLite   │ │Postgres│ │  Redis   │  │  │
│   │  │(dev)    │ │          │ │pgvector│ │          │  │  │
│   │  └─────────┘ └──────────┘ └────────┘ └──────────┘  │  │
│   │  ┌─────────┐ ┌──────────┐                           │  │
│   │  │MongoDB  │ │  Neo4j   │                           │  │
│   │  └─────────┘ └──────────┘                           │  │
│   └─────────────────────────────────────────────────────┘  │
│                         │                                   │
│                         ▼                                   │
│   SearchResponse { memories: Vec<MemoryEntry> }            │
│   (filtered by project scope)                              │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              Context Injection                              │
│                                                             │
│   Relevant memories added to agent context                 │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    Agent Execution                          │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                   Memory Storage                            │
│                                                             │
│   Session conversation stored for future recall            │
│   (global or project-scoped)                               │
└─────────────────────────────────────────────────────────────┘

أفضل الممارسات

الممارسةالوصف
استخدام قاعدة بيانات المتجهات في الإنتاجInMemory مخصص للتطوير/الاختبار فقط
التحديد حسب المستخدمقم دائمًا بتضمين user_id للخصوصية
تحديد النتائجحدد الذكريات المعادة لتجنب تجاوز سعة السياق
تنظيف الذكريات القديمةنفّذ TTL أو أرشفة للبيانات القديمة
التضمين بشكل استراتيجيخزّن الملخصات، وليس المحادثات الخام

مقارنة مع Sessions

الميزةحالة الجلسةالذاكرة
الاستمراريةمدة الجلسةدائم
النطاقجلسة واحدةعبر الجلسات
البحثالبحث بالقيمة المفتاحيةالبحث الدلالي
حالة الاستخدامالسياق الحاليالاستدعاء طويل الأمد

السابق: ← Guardrails | التالي: Studio →

الذاكرة - وثائق ADK-Rust | ADK-Rust