تصور کنید در دنیای یک بازی، دزدیدن یک سیب ساده بتواند باعث تعقیب و گریز در کل روستا شود. این ایدهٔ اصلی پروژه Hearsay است؛ سیستمی که در هکاتون Graph Hacks برای جایگزینی مترهای ایستا و خستهکنندهٔ شهرت با یک موتور شایعهساز پویا طراحی شده است. این پروژه که حاصل همکاری WeMakeDevs و FalkorDB است، به گونهای طراحی شده که شخصیتهای بازی دیگر به محض اینکه بازیکن از دید آنها خارج شود، دچار «فراموشی» نشوند.
در اکثر بازیهای مدرن، حافظهٔ شخصیتهای غیرقابلبازی (NPC) تنها یک عدد است — مثلاً امتیاز «شرافت» یا «شهرت» — که بهصورت آنی در کل جهان بازی بهروز میشود. این رویکرد کاملاً با واقعیت در تضاد است؛ زیرا در دنیای واقعی، اطلاعات شخصبهشخص منتقل میشود، در طول زمان تخریب میشود و کاملاً به این بستگی دارد که چه کسی به چه کسی اعتماد کند. در سیستمهای قدیمی، هیچکس خبر را از کسی نمیشنود، هیچکس آن را به اشتباه به یاد نمیآورد و هیچکس نمیتواند بگوید منبع خبر کجاست. Hearsay با تبدیل خاطرات و باورها به یک گراف دانش (Knowledge Graph) — شبیه به یک نقشهٔ پیچیده از روابط که به جای لیستهای ساده، پیوندهای بین مفاهیم را ذخیره میکند — این مشکل را حل کرده است.
معماری شایعات
همانطور که در تحلیلهای قبلی ما دربارهی حافظهٔ عاملهای هوش مصنوعی اشاره کردیم، جداسازی دادههای خام از تفسیرهای ذهنی، کلید رسیدن به رفتارهای انسانیتر است. پروژه Hearsay بر بستر FalkorDB بنا شده و «رویداد» (آنچه واقعاً رخ داده) را از «شایعه» (آنچه یک شخصیت باور دارد رخ داده) جدا میکند. طبق مستندات این پروژه، وقتی یک شخصیت شاهدی بر یک اتفاق است، یک گره شایعه با امتیاز اطمینان ۱.۰۰ ایجاد میکند. با پخش شدن داستان، هر بازگویی یک گره جدید برای شنونده میسازد که با رابطه HEARD_FROM به منبع متصل است.

مدل گراف برای یک زنجیرهٔ شایعه به این شکل است:
- (توماس)-[:WITNESSED]->(رویداد: دزدی در بازار)
- (توماس)-[:HOLDS {confidence: 1.00}]->(شایعه ۱: «یک سیب برداشت»)
- (پیپ)-[:HOLDS {confidence: 0.45}]->(شایعه ۲: «یک سبد دزدید»)
- (برتا)-[:HOLDS {confidence: 0.75}]->(شایعه ۳: «دکه را غارت کرد»)
- (ایلسا)-[:HOLDS {confidence: 0.51}]->(شایعه ۴: «بازار را زد»)
در این زنجیره، نسخهٔ پیپ از نسخهٔ توماس شنیده شده (HEARD_FROM)، نسخهٔ برتا از پیپ (و توماس) و نسخهٔ ایلسا از برتا شنیده شده است. هر گره یک لینک ORIGIN به حقیقت اولیه دارد، هرچقدر هم که داستان تحریف شده باشد. اعتماد در اینجا جهتدار است؛ برای مثال، رابطه (Ilsa)-[:TRUSTS {weight: 0.75}]->(Bertha) به این معناست که برتا میتواند چیزهایی را به ایلسا بگوید.
برای جلوگیری از تداخل حافظه میان بازیکنان مختلف و تضمین ایزولاسیون، هر بازیکن گراف اختصاصی خود را دارد (hearsay_<playerId>). این گرافها با استفاده از دستور GRAPH.COPY از یک قالب کلی و فقط-خواندنی (Read-only) از روستا ساخته میشوند. این روش باعث میشود توسعهدهنده نیازی به استفاده از فیلترهای دستوپاگیر مانند WHERE player = ... نداشته باشد که احتمال فراموش کردن آنها در کوئریها زیاد است.
منطق اجتماعی و کوئریهای گراف
جریان اطلاعات در این سیستم توسط سه مکانیزم اصلی گراف و کوئریهای خاص Cypher کنترل میشود:
اول، فیلتر مبتنی بر اعتماد: هر ساعت بازی، روستاییان به مکانهای برنامهریزیشده میروند. یک الگوی تطبیق (Pattern Match) گفتگوهای احتمالی را پیدا میکند: MATCH (a:Villager)-[:AT]->(pl:Place)<-[:AT]-(b:Villager) MATCH (b)-[t:TRUSTS]->(a). این تضمین میکند که مثلاً نگهبان هرگز حرف کودکی را نمیشنود اگر به او اعتماد نداشته باشد. وقتی A داستانی را به B میگوید، B نسخهٔ خاص خود را از طریق یک دستور CREATE دریافت میکند که شامل مواردی چون confidence (اطمینان)، تیک زمانی since (از چه زمانی)، تیک last_reinforced (آخرین بار چه زمانی تقویت شد)، text (متن)، severity (شدت) و hops (تعداد گامها یا واسطهها) است.
دوم، زوال اطمینان: با هر بار انتقال خبر، میزان اطمینان کاهش مییابد. فرمول محاسبه به این صورت است: اطمینان گوینده × سطح اعتماد × ۰.۹.
سوم، تأیید متقاطع: اگر شخصی داستان را از قبل بداند، گره جدیدی ساخته نمیشود، بلکه یک فلش HEARD_FROM جدید اضافه شده و اطمینان او از طریق فرمول 1 − (1 − old) × (1 − new) افزایش مییابد. در واقع، وقتی دو نفر یک حرف را میزنند، شما بیشتر باور میکنید.
برای جلوگیری از توهم (Hallucination) — وقتی مدل با اطمینان چیزی میگوید که اصلاً وجود ندارد — این موتور از یک «نردبان تحریف» (Distortion Ladder) استفاده میکند. شدت جرم طبق یک لیست ثابت بالا میرود: «یک سیب برداشت» $ \rightarrow $ «یک سبد دزدید» $ \rightarrow $ «دکه را غارت کرد» $ \rightarrow $ «بازار را زد» $ \rightarrow $ «عضو یک باند دزدان است». برای مثال، پیپ (کودک روستا) در ۸۰٪ مواقع داستانها را اغراق میکند. مدل زبانی بزرگ (LLM) در اینجا فقط وظیفه دارد نتیجهٔ نهایی را با لحن شخصیت بازنویسی کند (مثلاً: «وای خدای من، اون مسافر وسایل توماس رو چنگ زد!»).
یکی از قدرتمندترین قابلیتها، کوئری «چرا؟» است. به دلیل ساختار گرافی، سیستم میتواند در حدود ۲ میلیثانیه، باور یک شخصیت را از طریق زنجیره HEARD_FROM (از صفر تا شش گام) تا شاهد اصلی (جایی که hops = 0) ردیابی کند. این کوئری از دستور MATCH path = (r)-[:HEARD_FROM*0..6]->(o:Rumour)-[:ORIGIN]->(e:Event) استفاده کرده و نتایج را بر اساس طول مسیر مرتب میکند. در یک پایگاهداده SQL سنتی، این کار نیازمند عملیاتهای بازگشتی (Recursive Self-join) بسیار سنگین و کند بود. بازی سپس این مسیر را به یک کمیک استری تبدیل میکند که نشان میدهد داستان چگونه گامبهگام بدتر شده است.
پیادهسازی فنی و عملکرد
برای حفظ سرعت حلقهٔ بازی، توسعهدهنده از مدل Gemma 3 4B که بهصورت محلی روی Ollama اجرا میشود استفاده کرده است (زمان پاسخ ۱ تا ۴ ثانیه). در این معماری، هوش مصنوعی حافظه ندارد؛ بلکه فقط حقایق استخراجشده از گراف را به زبان شخصیت در میآورد. این کار باعث میشود پنجرهٔ زمینه (Context Window) — میزان متنی که مدل همزمان «در ذهن» نگه میدارد — بیش از حد پر نشود و تضمین کند که شخصیتها هرگز اطلاعاتی را ذکر نکنند که به آنها دسترسی ندارند. در تستهای انجام شده روی ۵۰ گفتگوی برنامهریزیشده، در هیچیک از پاسخها (۰ از ۵۰) به روستاییای خارج از محدودهٔ گراف گوینده اشاره نشد.
حلقهٔ عامل (Agent Loop) از یک توالی سختگیرانه پیروی میکند: ادراک (کوئریهای گراف) $ \rightarrow $ تصمیمگیری (کد، بر اساس گرههای Rule) $ \rightarrow $ سخن گفتن (AI، فقط با حقایقی که گراف برگردانده) $ \rightarrow $ به خاطر سپردن (نوشتن در گراف). این رویکرد برای مدیریت دقیق رفتار عاملها ضروری است، چرا که محدودیتهای سیستمی در محیطهای بسته اغلب مانع از اجرای آزادانه و بهینهٔ عاملهای خودمختار میشوند.
بر اساس بنچمارکهای اجرا شده روی لپتاپ با FalkorDB در داکر، نتایج به شرح زیر است:
- خواندن باورها و نظرات: برای ۵۰۰ دنیای همزمان، p50 حدود ۱ میلیثانیه و p95 حدود ۲.۳ میلیثانیه است. عملکرد از ۱۰ تا ۵۰۰ دنیا ثابت باقی ماند.
- شبیهسازی شایعات روستا: یک ساعت کامل (حدود ۲۰ کوئری) تقریباً ۱۵ میلیثانیه زمان میبرد.
- کوئری منشأ (Why?): از ابتدا تا انتها در ۷.۷ میلیثانیه تکمیل میشود.
- پخش شایعه: خبر دزدی در ۵۰ مورد از ۵۰ seed مختلف به گوش نگهبان رسید، با میانگین زمان ۹ ساعت و میانگین شدت ۲.۵.
حفاظها در برابر حلقههای منطقی
برای واقعیتر شدن شبیهسازی، چندین محدودیت رفتاری تعریف شده است:
- حفاظ اتاق پژواک (Echo-Chamber Guard): یک داستان هرگز به کسی بازگردانده نمیشود که خودش منبع آن بوده است. این کار از طریق یک عبارت شرطی با طول متغیر انجام میشود:
NOT (r)-[:HEARD_FROM*1..6]->(mine). بدون این مورد، دو دوست مدام شایعه را برای هم تکرار میکردند تا اطمینانشان به ۱۰۰٪ برسد. - شخصیت شکاک: «میرا آهنگر» هر شایعهای را که کمتر از دو گویندهٔ متفاوت داشته باشد، نادیده میگیرد.
- فراموشی: هر نیمهشب بازی، باورهایی که در ۲۴ ساعت گذشته تقویت نشدهاند، کمرنگ میشوند (اطمینان $ \times $ ۰.۹۷).
- حافظه رویهای (Procedural Memory): رفتارها در گرههای Rule زندگی میکنند (مثلاً: «نگهبان: اگر نظر کمتر از ۰.۳ بود، در را ببند» یا «نگهبان: اگر قبلاً به تو بیاعتماد بود، هدیه را رشوه تلقی کن»). کد این قوانین را ارزیابی میکند و مدل AI هرگز دربارهٔ اقدام تصمیم نمیگیرد.
- کانالهای هماهنگی: در حالی که شایعات غیررسمی هستند، نگهبان میتواند یک اعلان «تحت تعقیب» در دروازه نصب کند که یک وضعیت مشترک (Shared State) برای هر کسی که از آنجا رد میشود ایجاد میکند.
غلبه بر موانع توسعه
ساخت این سیستم چالشهای فنی متعددی داشت. توسعهدهنده در ابتدا با ردیفهای تکراری ناشی از OPTIONAL MATCH هنگام پیادهسازی حفاظ اتاق پژواک مواجه شد که با انتقال بررسی به یک عبارت WHERE حل شد. دستور GRAPH.COPY تحت فشار زیاد باعث شکست در فورک کردن سرور میشد، زیرا کپیها سرور را فورک میکنند؛ این مشکل با سریالسازی کپیها پشت یک قفل (Lock) با تلاش مجدد (Retry) کوتاه حل شد.
علاوه بر این، استفاده از ORDER BY length(path) بعد از RETURN خطا میداد زیرا مسیر (path) پروژکت نشده بود؛ مرتبسازی در یک بلوک WITH ابتدا این مشکل را حل کرد.
در بخش هوش مصنوعی، مدل Gemma 4 در AI Studio اغلب بلوکهای <thought> تولید میکرد که هر فراخوانی را ۱۴ تا ۳۴ ثانیه طولانی میکرد و گاهی توکنها تمام میشد. توسعهدهنده این بلوکها را حذف کرد و بازنویسی متنی را در پسزمینه با استفاده از متنهای الگو (Template) پیاده کرد تا تیکهای بازی منتظر مدل نمانند. در فرانتاند، یک Promise بازگشتی از scrollIntoView() در useEffect باعث میشد React صفحه را Unmount کند، زیرا React سعی میکرد Promise را به عنوان تابع Cleanup فراخوانی کند. این مورد با استفاده از بدنه بلوکی برای افکتها اصلاح شد.
در نهایت، یک خطای استقرار در Vercel — جایی که پلتفرم به دلیل وجود requirements.txt به اشتباه فرانتاند را FastAPI تشخیص داد — با پین کردن فریمورک روی «vite» حل شد تا فرانتاند استاتیک بماند و پایتون فقط برای /api رزرو شود.
فراتر از یک روستا
این تغییر رویکرد از «حافظهٔ متمرکز بر LLM» به «حافظهٔ متمرکز بر گراف»، پیشفرضهای طراحی NPC را تغییر میدهد. با سپردن وضعیت به یک گراف، توسعهدهندگان میتوانند «مکانیکهای کارآگاهی» بسازند که در آن بازیکن باید شایعات را تا منبع اصلی ردیابی کند تا نام خود را پاک کند. این نوع ردیابی دقیق از اطلاعات، یادآور روشهای پیشرفتهای است که سازمانها برای شناسایی نفوذیها و تحلیل رفتارهای مشکوک در محیطهای پیچیده به کار میبرند.
با استفاده از PageRank روی روابط TRUSTS (اعتماد)، موتور میتواند تأثیرگذارترین روستاییان را شناسایی کند. در دموی فعلی، «برتا» صاحب مسافرخانه در صدر است. افزودن algo.WCC برای شناسایی گروهها (مانند «جمع مسافرخانه» یا «دوتاي کلیسا»)، algo.betweenness برای شناسایی واسطهها و shortestPath برای مسیریابی سریع، امکان ایجاد مکانیک «رئیس جاسوسان» را فراهم میکند. برای مثال، بازیکن میتواند بفهمد که کاپیتان ایلسا بیشترین اعتماد را به برتا دارد (۰.۷۵) و برنامهریزی کند که یک کار نیک را در ساعت ۱۰:۰۰ جایی انجام دهد که برتا ببیند، یا آن را در گوش پیپ زمزمه کند که با یک گام به برتا میرسد.
اگرچه این یک دموی کوچک است، اما توسعهدهنده پیشنهاد میکند که دنیایی به اندازه GTA صرفاً به شبیهسازی شایعات با «سطح جزئیات» (Level of Detail) نیاز دارد؛ به این صورت که شبیهسازی کامل فقط در نزدیکی بازیکن رخ دهد و برای سایر مناطق از گرافهای منطقهای و تیکهای دستهای (Batched Ticks) استفاده شود. با سرعت حدود ۱ میلیثانیه برای هر جستجو، استدلال گرافی به ازای هر شخصیت برای دنیاهای مقیاس بزرگ مقرونبهصرفه است.
شما میتوانید گراف زنده و ۴۵ کوئری Cypher مورد استفاده در این سیستم را در مخزن گیتهاب پروژه بررسی کنید. ابزار 🔭 Spyglass در دمو، گراف زنده، PageRank و هر کوئری Cypher را در لحظه اجرا نمایش میدهد.
گام بعدی شما
- اگر توسعهدهنده بازی هستید، به جای ذخیره شهرت در متغیرهای عددی، ساختار گرافهای دانش را برای مدیریت روابط اجتماعی بررسی کنید.
- برای کاهش تأخیر در عاملهای هوش مصنوعی، منطق تصمیمگیری را به کد (Rule-based) و بازنویسی متن را به LLM بسپارید.
- مخزن گیتهاب پروژه Hearsay را برای بررسی ۴۵ کوئری Cypher مورد استفاده در این سیستم مطالعه کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو