مفاهيم الذاكرة

الجانب الخاص بـ semantic-store من الذاكرة مبني من عدد قليل من الأنواع الصغيرة. تعرّف على هذه الأربعة، وباقي القسم سيتبع.

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(),
};

تُعدّ الإدخالات وحدة التخزين ووحدة الاسترجاع — search يعيد الإدخالات الأكثر صلةً باستعلام ما.

صفة MemoryService

كل backend يطبّق صفة واحدة. طريقتان مطلوبتان؛ أما الباقي فله تنفيذات افتراضية يمكن لـ backend أن يتجاوزها:

#[async_trait]
pub trait MemoryService: Send + Sync {
    // Required
    async fn add_session(&self, app: &str, user: &str, session: &str,
                         entries: Vec<MemoryEntry>) -> Result<()>;
    async fn search(&self, req: SearchRequest) -> Result<SearchResponse>;

    // Optional (default: "not implemented")
    async fn delete_user(&self, app: &str, user: &str) -> Result<()>;          // GDPR
    async fn delete_session(&self, app: &str, user: &str, session: &str) -> Result<()>;
    async fn add_entry(&self, app: &str, user: &str, entry: MemoryEntry) -> Result<()>;
    async fn delete_entries(&self, app: &str, user: &str, query: &str) -> Result<u64>;
    // …plus a health check
}
  • add_session تستوعب مجموعة إدخالات محادثة مكتملة. يستدعي Runner هذا لك عندما تكون الذاكرة مرفقة.
  • search هو الاسترجاع. تفسّر backend الاستعلام بشكل مختلف — مطابقة الكلمات المفتاحية، أو تشابه embedding، أو scoring للرموز في graph — لكن العقدة نفسها هي نفسها.

SearchRequest / SearchResponse

use adk_memory::SearchRequest;

let req = SearchRequest {
    app_name: "support".into(),
    user_id:  "alice".into(),
    query:    "contact preference".into(),
    ..Default::default()      // top_k, project scoping, etc.
};

let resp = memory.search(req).await?;   // resp.entries: Vec<MemoryEntry>

حالة الجلسة مقابل الذاكرة

هذان أداتان مختلفتان — لا تخلط بينهما:

حالة الجلسةالذاكرة
المدةواحدة محادثةعبر المحادثات
APIadk-session (SessionService، state map)adk-memory (MemoryService)
يحتفظ بـالنص الحي + حالة المسودةحقائق دائمة تستحق التذكر لاحقًا
يقرأدائمًا في السياقعند الطلب، عبر search_memory

تدفق نموذجي: تعيش المحادثة في حالة الجلسة؛ وعندما تنتهي (أو في كل دورة)، تُكتب الأجزاء الأبرز إلى الذاكرة؛ وتقوم الجلسة التالية بالبحث في الذاكرة لإعادة تهيئة السياق. راجع Sessions & State.

العزل: التطبيق، المستخدم، والمشروع

تكون الذاكرة دائمًا مرتبطة بـ (app_name, user_id)، لذلك لا يرى المستخدمون ذاكرات بعضهم البعض. يمكنك تحديد مستوى ثالث — المشروع — داخل المستخدم:

  • المدخلات العامة (project_id = None) — مرئية في كل سياق.
  • مدخلات المشروع (project_id = Some(id)) — مرئية فقط داخل ذلك المشروع.
  • بحث المشروع يعرض المدخلات العامة + مدخلات المشروع المطابقة؛ أما البحث العام فيعرض فقط المدخلات العامة.
use adk_memory::{MemoryServiceAdapter, InMemoryMemoryService};
use std::sync::Arc;

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

// store within a project
service.add_session_to_project("app", "user", "sess", "acme-project", entries).await?;

// an adapter scoped to that project
let adapter = MemoryServiceAdapter::new(service, "app", "user")
    .with_project_id("acme-project");

استخدم المشاريع لإبقاء، على سبيل المثال، مساعدي العمل والشخصي لمستخدم ما من مشاركة الذاكرة مع الاستمرار في مشاركة الحقائق العامة حقًا.

لا تنفذ كل الواجهات الخلفية نطاق المشروع

عزل المشروع هو قدرة في الواجهة الخلفية، وليس شيئًا يمكن للصفة ضمانه. اسأل قبل الاعتماد عليه:

if service.supports_project_scoping() {
    service.add_entry_to_project("app", "user", "acme-project", entry).await?;
} else {
    // Decide explicitly: refuse, or store globally on purpose.
}
الواجهة الخلفيةتحديد نطاق المشروع
InMemoryMemoryServiceنعم
SqliteMemoryServiceنعم
PostgresMemoryServiceنعم
RedisMemoryServiceنعم
MongoMemoryServiceنعم
Neo4jMemoryServiceنعم
GraphMemoryServiceلا — تُرجِع طرق المشروع خطأً

الخلفية التي لا تدعم المشروع تفشل الاستدعاء بدلًا من الكتابة أو الحذف بشكل عام بصمت. إن توسيع نطاق عملية الكتابة سيجعل البيانات المخصصة لمشروع واحد مرئية لكل ما يقع تحت التطبيق نفسه والمستخدم نفسه، كما أن توسيع نطاق الحذف سيزيل الإدخالات خارج المشروع المحدد — ولا يمكن للمستدعي اكتشاف أيٍّ من ذلك من قيمة الإرجاع. MemoryServiceAdapter::supports_project_scoping يُبلِغ عمّا تدعمه خلفيته.

محو GDPR

delete_user(app, user) يزيل كل ذكريات المستخدم (الإدخالات و التضمينات) عبر جميع المشاريع — وهي البنية الأولية للحق في المحو. الخلفيات التي تخزن البيانات بصورة دائمة تنفذه؛ استدعِه من مسار حذف الحساب.

memory.delete_user("support", "alice").await?;

التالي: الخلفيات →