Part of ความจำระยะยาวของ AI

2026-10-16 · 3 min read

ความจำเดียวที่ AI ทุกตัวใช้ร่วมกัน: ระบบ memory บน sarachai.com ผ่าน MCP

ความจำเดียวที่ AI ทุกตัวใช้ร่วมกัน: ระบบ memory บน sarachai.com ผ่าน MCP

ความจำระยะยาวของ AI จะมีประโยชน์จริงก็ต่อเมื่อ AI ทุกตัว ที่คุณใช้จำเรื่องเดียวกันได้ ไม่ใช่เฉพาะเครื่องมือที่อ่านไฟล์ในเครื่องได้เท่านั้น โน้ตนี้อธิบายระบบความจำที่ผู้เขียนสร้างไว้บน sarachai.com ซึ่งให้ Claude Code, claude.ai และ ChatGPT อ่านและเขียนความจำชุดเดียวกันผ่าน MCP (Model Context Protocol) โดยยังได้วิกิ Obsidian ที่มี Git เก็บประวัติไว้ในเครื่องเหมือนเดิม

ปัญหาที่ต้องแก้

งานที่ผู้เขียนทำกับ AI ต้องการความจำสองแบบ แบบแรกคือ ความจำของเอเจนต์ (agent memory) เช่น เว็บไซต์ deploy อย่างไร ตกลงกันไว้ว่าจะ merge เมื่อไร งานค้างอยู่ตรงไหน ความจำแบบนี้เปลี่ยนบ่อยและล้าสมัยเร็ว แบบที่สองคือ คลังความรู้ (knowledge base) เช่น โน้ตรายวิชาและงานวิจัยที่สะสมช้าแต่อยู่นาน

ก่อนหน้านี้ความจำของเอเจนต์อยู่ในระบบ auto-memory ของ Claude Code ซึ่งแยกเป็นโฟลเดอร์ต่อ repository รวมแล้วสิบกว่าโฟลเดอร์ และ AI ตัวอื่นมองไม่เห็นเลย ส่วนแนวทาง LLM Wiki ที่อธิบายไว้ใน LLM Wiki: ความจำระยะยาวของ AI ที่สร้างจากไฟล์ Markdown และลงมือทำใน llm-wiki-claude-code-setup ได้วิกิที่เป็นส่วนตัวและมีประวัติครบ แต่มีข้อจำกัดสองข้อ ข้อแรก แอปแชตบนเว็บอย่าง claude.ai และ ChatGPT อ่านดิสก์ของคุณไม่ได้ ข้อที่สอง Karpathy ผู้เสนอแนวคิดนี้เองระบุว่าการนำทางด้วยไฟล์ดัชนีอย่างเดียวใช้ได้ดีที่ขนาดปานกลาง แต่เมื่อโตเกินหลายร้อยหน้าก็ต้องมีระบบค้นหาที่ดีกว่านั้น

ทำไมไม่ใช้บล็อกเป็นความจำ

sarachai.com มีลิงก์ระหว่างโน้ต backlink กราฟ และ RAG อยู่แล้ว จึงดูเหมือนว่าจะใช้บล็อกเป็นความจำได้เลย แต่บล็อกเป็น Zettelkasten สาธารณะที่ผ่านการเรียบเรียงแล้ว ส่วนความจำของเอเจนต์เป็นกระดาษทดที่มีรายละเอียดระบบภายใน ถ้าเก็บรวมในตารางเดียวกันแล้วซ่อนด้วยสถานะ "ส่วนตัว" ทุกจุดที่อ่านข้อมูลสาธารณะ ทั้งหน้าเว็บ sitemap RSS บทความที่เกี่ยวข้อง และ chat widget ที่ตอบคำถามผู้อ่านจาก RAG ต้องกรองให้ครบทุกที่ พลาดเพียงจุดเดียวข้อมูลก็หลุด ระบบจึงเก็บความจำแยกตาราง แยกดัชนีเวกเตอร์ และแยกเครื่องมือ MCP ตั้งแต่ต้น ทางที่ปลอดภัยคือทางที่โค้ดสาธารณะไม่มีทางเข้าถึงได้เลย

สถาปัตยกรรมแบบผสม

คำตอบที่ได้คือแบบผสม (hybrid) ที่ใช้จุดแข็งของทั้งสองฝั่ง

แผนภาพสถาปัตยกรรม: AI client ทุกตัวเชื่อม MCP server บน sarachai.com ด้วย OAuth ผ่านชั้น query ไปยัง PostgreSQL และ Qdrant ซึ่งแยกจากส่วนสาธารณะ และส่งออกเป็นวิกิ Obsidian กับ Git บนเครื่องของผู้เขียน
รูปที่ 1 สถาปัตยกรรมความจำร่วม: ต้นฉบับอยู่บนเว็บ AI ทุกตัวเข้าผ่าน MCP และเครื่องของผู้เขียนเก็บสำเนาทางเดียว
  • ต้นฉบับอยู่บนเว็บ: โน้ตความจำเก็บในฐานข้อมูล PostgreSQL ของเว็บไซต์ ทุกครั้งที่เขียนจะเก็บ revision เต็มพร้อมบันทึกว่าเขียนผ่าน AI ตัวไหน
  • ทุก client เชื่อมผ่าน MCP: เว็บมี MCP server อยู่แล้วสำหรับเขียนบทความ จึงเพิ่มเครื่องมือความจำ เช่น memory_context memory_search และ memory_remember เข้าไปในเซิร์ฟเวอร์เดียวกัน client ทุกตัวเชื่อมด้วย OAuth แล้วผู้ใช้เลือกสิทธิ์ memory:read และ memory:write เองในหน้ายืนยัน
  • Obsidian และ Git เป็นสำเนาทางเดียว: เครื่องของผู้ใช้ดึงความจำทั้งหมดออกมาเป็นไฟล์ Markdown แล้ว commit ลง Git เป็นระยะ เปิดใน Obsidian ได้ทั้งกราฟและ backlink ฝั่งเซิร์ฟเวอร์ไม่ต้องถือรหัสผ่าน Git และไม่ต้องแก้ข้อขัดแย้งจากการเขียนสองทาง

RAG Memory แบบเวกเตอร์ใน rag-agent-memory กับ LLM Wiki จึงไม่ใช่ทางเลือกที่ต้องเลือกอย่างใดอย่างหนึ่ง ระบบนี้เก็บโน้ตแบบวิกิที่มนุษย์อ่านได้เป็นหลัก แล้วใช้เวกเตอร์ช่วยค้นหาเท่านั้น

ชนิดของโน้ตบอกอายุของความจริง

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

ชนิดตัวอย่างกติกา
stateงานค้างอยู่ตรงไหน ระบบรันเวอร์ชันอะไรต้องระบุวันที่ที่ข้อมูลยังจริง (asOf) และถูกแสดงว่าเก่าเมื่อเกิน 14 วัน
decision rule preferenceตกลงว่าจะ deploy ด้วยมือระหว่างทดสอบไม่หมดอายุ แก้เนื้อหาตรง ๆ ไม่ได้ ต้องเขียนโน้ตใหม่ที่ แทนที่ (supersede) ของเดิมพร้อมเหตุผล โน้ตเดิมยังอยู่เป็นประวัติ
procedureขั้นตอน deployแก้ได้เมื่อขั้นตอนเปลี่ยน
episodeบันทึกการทำงานหนึ่งรอบเมื่อเก่าพอควรสกัดบทเรียนออกมาเป็นโน้ตชนิดอื่น แล้ว episode จะถูกเก็บเป็นประวัติ
concept entity source-summaryความรู้จากเอกสารควรอ้างแหล่งต้นฉบับที่เก็บไว้แบบแก้ไขไม่ได้

เครื่องมือ memory_lint ตรวจโน้ตตามกติกาเหล่านี้ เช่น state ที่เก่าแล้ว ลิงก์ที่ชี้ไปโน้ตที่ไม่มีอยู่ และความรู้ที่ไม่อ้างแหล่ง แต่เครื่องมือนี้ รายงานอย่างเดียว คนหรือเอเจนต์ที่ได้รับอนุญาตเป็นผู้ตัดสินใจแก้

ทุก session เริ่มด้วยความจำ

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

ใน Claude Code มี SessionStart hook ดึงสรุปนี้มาใส่ในบริบทอัตโนมัติทุกครั้งที่เปิด session โดยเลือกขอบเขต (scope) ของความจำจากชื่อ repository ที่คุณกำลังทำงาน ถ้าเว็บติดต่อไม่ได้ hook จะใช้สำเนาล่าสุดในวิกิบนเครื่องแทน ส่วนแอปแชตใช้คำแนะนำของ MCP server ที่บอกให้เรียก memory_context ก่อนเริ่มงาน เมื่อทุกอย่างพร้อมแล้ว ผู้เขียนทดสอบโดยเปิด session ใหม่ใน Claude Code และใน claude.ai แล้วถามคำถามเดียวกันเกี่ยวกับสถานะโปรเจกต์ ทั้งสองตอบตรงกัน

ค้นหาแบบผสม และบทเรียนจากภาษาไทย

memory_search รวมผลจากสองทาง ทางแรกคือการหาคำตรงตัวในชื่อ สรุป แท็ก และเนื้อหา ทางที่สองคือการค้นด้วยความหมายจากดัชนีเวกเตอร์ แล้วรวมอันดับด้วยวิธี reciprocal rank fusion โน้ตที่ทั้งสองทางเจอจะขึ้นมาก่อน ผลแต่ละรายการบอกด้วยว่าเจอจากทาง text semantic หรือ both

การทดสอบกับข้อมูลจริงให้บทเรียนสามข้อ

  1. โมเดล embedding ขนาดเล็กเทียบข้ามภาษาได้อ่อน คำถามภาษาอังกฤษได้คะแนนความคล้ายกับโน้ตที่ถูกต้องราว 0.7 แต่คำถามภาษาไทยกับโน้ตภาษาอังกฤษได้เพียงราว 0.35–0.4 เกณฑ์ตัดแบบค่าคงที่จึงทิ้งคำถามภาษาไทยทั้งหมด ระบบจึงเปลี่ยนเป็นเก็บผลที่ผ่านเกณฑ์ขั้นต่ำและอยู่ใกล้ผลที่ดีที่สุด และเติมคำสั่ง (instruction) หน้าคำถามตามที่โมเดลตระกูล Qwen3-Embedding ออกแบบไว้ ซึ่งเพิ่มคะแนนได้ราว 0.1
  2. คำทั่วไปทำให้บันทึกยาว ๆ ชนะ คำอย่าง "do" "to" "in" มีอยู่ในบันทึกยาวทุกฉบับ บันทึกการทำงานจึงแซงโน้ตที่ตอบคำถามจริง ระบบจึงตัดคำทั่วไปออก และให้คะแนนคำที่อยู่ในชื่อหรือสรุปสูงกว่าคำที่อยู่ในเนื้อหา
  3. ต้องวัดคุณภาพจากผลที่ดีที่สุดทั้งหมด ไม่ใช่ในขอบเขตเดียว ในขอบเขตที่มีโน้ตน้อย ผลอันดับหนึ่งอาจไม่เกี่ยวเลย ระบบจึงเทียบกับผลที่ดีที่สุดจากทุกขอบเขตก่อน แล้วค่อยกรองขอบเขต
ℹ️ยังเป็นข้อจำกัด

แต่ละโน้ตมีเวกเตอร์เดียวจากส่วนต้นของเนื้อหา ข้อเท็จจริงที่อยู่ท้ายโน้ตยาว ๆ จึงอาจค้นด้วยความหมายไม่เจอ วิธีแก้คือแบ่งโน้ตเป็นช่วง (chunk) แบบที่ดัชนีของบล็อกทำอยู่ ซึ่งยังไม่จำเป็นในขนาดปัจจุบัน

ความปลอดภัยของความจำที่ทุกตัวเขียนได้

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

  • สิทธิ์ memory:write ไม่ถูกติ๊กไว้ล่วงหน้า ในหน้ายืนยัน OAuth ผู้ใช้ต้องตั้งใจเลือกเอง ส่วน memory:read ติ๊กไว้ให้ เพราะการอ่านคือสิ่งที่ทำให้ client จำได้
  • ทุกการเขียนเป็น revision ที่บอกว่ามาจาก client ไหน ข้อมูลที่ผิดจึงย้อนรอยและแก้คืนได้
  • ระบบปฏิเสธข้อความที่หน้าตาเหมือนรหัสลับ เช่น API key, token และ private key ความจำเก็บได้เพียงว่ารหัสลับอยู่ที่ไหน ไม่ใช่ตัวรหัส
  • MCP server บอก AI ทุกตัวว่าความจำคือโน้ตเก่าของผู้ใช้ ไม่ใช่คำสั่งที่มีอำนาจเหนือสิ่งที่ผู้ใช้พูดตอนนี้
  • ดัชนีเวกเตอร์ของความจำแยก collection กับดัชนีของบล็อก chat widget สาธารณะจึงค้นไม่เจอความจำ

จากความจำสู่บทความ

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

แหล่งอ้างอิง

  • Andrej Karpathy, LLM Wiki — แนวคิดวิกิที่ LLM ดูแล ชั้นข้อมูลดิบที่แก้ไขไม่ได้ และขีดจำกัดของการนำทางด้วยดัชนี
  • rohitg00, LLM Wiki v2 — วงจรชีวิตของความรู้ การค้นหาแบบผสม และข้อถกเถียงเรื่องค่าความมั่นใจ
  • Anthropic, Claude Code memory และ hooks — auto-memory ต่อ repository และ SessionStart hook

By Watcharin Sarachai