# หน่วยความจำของ AI Agent ที่ไม่มีสัญญาณรบกวน: ขอบเขตและการลืมด้วย Octobrain

> ให้หน่วยความจำถาวรแก่ AI agent แล้วมันจะจดจำทุกอย่างจนการเรียกคืนจมอยู่ในบันทึกของตัวเอง นี่คือวิธีที่ขอบเขตหน่วยความจำของ Octobrain พื้นผิว MCP สี่เครื่องมือ และการรวมข้อมูลแบบอัตโนมัติ เปลี่ยนลิ้นชักขยะที่เต็มไปด้วยสัญญาณรบกวนให้กลับมาเป็นหน่วยความจำที่ตอบได้จริง พร้อมคู่มือการตั้งค่าเชิงปฏิบัติ

สัปดาห์ที่เราให้หน่วยความจำระยะยาวจริง ๆ แก่ agent ของเรา การเรียกคืนกลับแย่ลง

ไม่ใช่ทันที วันแรกเหมือนมีเวทมนตร์ — agent จำรายละเอียดการ deploy จากเซสชันวันจันทร์ได้ในวันพุธ โดยไม่ต้องอธิบายซ้ำ พอจบสัปดาห์ที่สอง มันกลับคืนโน้ตที่เกือบเหมือนกันห้ารายการสำหรับ "เราจัดการ rate-limiting อย่างไร" แต่ละอันผิดนิดหน่อย และไม่มีอันไหนเป็นอันที่ผมต้องการจริง ๆ เลย agent เรียก `memorize` อย่างขยันขันแข็งกับทุกการสังเกต ทุกทางตัน ทุกการคาดเดาที่ยังไม่สุก มันสร้างลิ้นชักขยะให้ตัวเองและตอนนี้กำลังรื้อค้นในนั้นทุกครั้งที่มี query

นี่คือส่วนของ "ให้หน่วยความจำแก่ agent ของคุณ" ที่ไม่มีใครเตือน หน่วยความจำที่มีแต่โตขึ้นไม่ใช่หน่วยความจำ — มันคือ log และ log ที่คุณค้นด้วยความหมายจะ _มีสัญญาณรบกวนมากขึ้น_ เมื่อมันโต เพราะทุกรายการที่เกือบซ้ำซึ่งคุณเก็บไว้แย่งชิงส่วนเดียวกันของการจัดอันดับ ยิ่งมีความทรงจำมาก การเรียกคืนยิ่งแย่ ตรงกันข้ามกับสิ่งที่คุณตกลงไว้พอดี

[Octobrain](https://github.com/muvon/octobrain) คือทางแก้แบบโอเพนซอร์สของเราสำหรับเรื่องนี้ — เซิร์ฟเวอร์หน่วยความจำ MCP ที่เขียนด้วย Rust ซึ่งให้หน่วยความจำระยะยาวแบบถาวรและค้นได้ด้วยความหมายแก่ agent ใด ๆ เรา[แนะนำมันไปตั้งแต่เดือนเมษายน](/blog/introducing-octobrain-mcp-memory-server) และใน[รีลีส 0.7.0](/blog/octobrain-0-7-0-memory-that-sleeps) หน่วยความจำได้เรียนรู้ที่จะลืม บทความนี้คือคู่มือที่ผมอยากจะมีตั้งแต่แรก: วิธีตั้งค่าหน่วยความจำ LLM แบบถาวรที่ยังคมชัด โดยใช้สองแนวคิด — **กำหนดขอบเขตต่อโปรเจกต์ และปล่อยให้มันรวมข้อมูลเอง** — บวกกับคำถามเชิงปฏิบัติว่าควรเก็บอะไรและควรปล่อยอะไรไว้

---

## ปัญหาที่แท้จริงคือการเรียกคืน ไม่ใช่การจัดเก็บ

การเก็บของนั้นง่าย key-value store ใด ๆ ก็ทำได้ ส่วนที่ยากของหน่วยความจำระยะยาวของ agent คือส่วนที่ยากเดียวกับหน่วยความจำของมนุษย์: ดึงสิ่งที่ _ถูกต้อง_ กลับมาในช่วงเวลาที่ _ถูกต้อง_ จากกองที่ไม่เคยหยุดโต

Octobrain ดึงข้อมูลด้วยความหมาย ไม่ใช่คีย์เวิร์ด เบื้องหลังมันฝัง (embed) ความทรงจำทุกอันลงในเวกเตอร์ด้วยโมเดลภายในเครื่อง (ค่าเริ่มต้นคือ `fastembed:nomic-ai/nomic-embed-text-v1.5` ขนาด 768 มิติ ทำงานบน CPU ไม่ต้องมี API key) แล้วเก็บไว้ในฐานข้อมูลเวกเตอร์ [LanceDB](https://lancedb.com/) บนดิสก์ query `remember` จะถูกฝังด้วยวิธีเดียวกัน แล้วเวกเตอร์ที่ใกล้ที่สุดจะถูกส่งกลับมา เพื่อไม่ให้การค้นแบบความหมายล้วนพลาดสตริงที่ตรงเป๊ะ การค้นจึงเป็นแบบ **ไฮบริด**: ข้อความเต็ม BM25 และความคล้ายเวกเตอร์ถูกรวมกันด้วย Reciprocal Rank Fusion ด้านบนยังมีการจัดอันดับใหม่ด้วย cross-encoder แบบเลือกได้

กลไกนี้ดี บนเบนช์มาร์ก SciFact ของ [BEIR](https://github.com/beir-cellar/beir) การดึงข้อมูลแบบไฮบริดของ Octobrain ได้คะแนน **0.742 nDCG@10** เทียบกับ 0.665 ของ BM25 แบบคลาสสิกและ 0.713 ของ embedding เปล่า — การรวมเพิ่มราว +2 nDCG@10 เหนือ embedder เพียงอย่างเดียว คุณภาพการดึงข้อมูลไม่เคยเป็นคอขวดเลย

คอขวดคือ _สิ่งที่เราป้อนเข้าไป_ โน้ตห้าอันที่ทับซ้อนกันเกี่ยวกับบั๊กเดียวกันไม่ได้ทำให้อันใดอันหนึ่งอันดับสูงขึ้น มันแบ่งสัญญาณ การดึงข้อมูลที่ดีบน corpus ที่มีสัญญาณรบกวนก็ยังให้คำตอบที่มีสัญญาณรบกวนอยู่ดี ดังนั้นทางแก้จึงไม่ใช่ embedder ที่ดีกว่า แต่คือการดูแล corpus — แบบอัตโนมัติ เพราะไม่มีมนุษย์คนไหนจะมานั่งตัดแต่งหน่วยความจำของ agent ด้วยมือ

---

## แนวคิดที่หนึ่ง: กำหนดขอบเขตหน่วยความจำต่อโปรเจกต์

แหล่งสัญญาณรบกวนแรกคือการปนเปื้อนข้ามโปรเจกต์ หากไม่มีการแยก ความทรงจำของทุกโปรเจกต์จะอยู่ในแอ่งเดียวกัน และ query เรื่อง "auth flow" ในเว็บแอปของคุณก็จะลากโน้ต auth จากสามบริการที่ไม่เกี่ยวข้องเข้ามาด้วย

Octobrain แยกความทรงจำตาม **ขอบเขต (scope)** ขอบเขตคือสตริง และโดยค่าเริ่มต้นจะถูกอนุมานจาก git remote ของคุณ เปิด repository ของบล็อกนี้เองแล้วขอบเขตคือ `github.com/muvon/muvon-web` การอนุมานเป็นอย่างที่คุณคงเดาได้พอดี:

```
git remote get-url origin  →  host/org/repo
```

ดังนั้น `git@github.com:muvon/octobrain.git` จึงกลายเป็น `github.com/muvon/octobrain` ไม่มี git remote? มันจะถอยไปใช้ขอบเขตแบบอิงพาธ `local/<parent>/<dir>` เพื่อให้แม้แต่ไดเรกทอรีทดลองก็ยังได้รับการแยก และมีขอบเขตสงวนหนึ่งอัน — สตริงว่าง `""` — สำหรับความทรงจำ **global** ที่ควรมองเห็นได้ทุกที่

การแบ่ง global/project นั้นคือโมเดลความคิดทั้งหมด และมันสอดคล้องกับความแตกต่างที่เป็นจริง:

| ขอบเขต                               | เก็บที่นี่                                | ตัวอย่าง                                                                                                                        |
| ------------------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **โปรเจกต์** (`github.com/org/repo`) | ข้อเท็จจริงที่จริง _ภายใน repository นี้_ | "เทสต์รันด้วย `pnpm -r test:unit`", "staging API ต้องมี header `X-Acme-Token`", "ที่นี่เราเลือก rate limiting แบบ token-bucket" |
| **Global** (`""`)                    | ข้อเท็จจริงที่จริง _เกี่ยวกับคุณ ทุกที่_  | "ชอบ tab", "ต้องการข้อความคอมมิตแบบ conventional เสมอ", "ใช้ fish ไม่ใช่ bash"                                                  |

ผลตอบแทน: เมื่อ agent ของคุณรันใน repository ของบล็อก การเรียก `remember` จะจัดอันดับเฉพาะกับความทรงจำของ repository บล็อกบวกกับค่าที่คุณตั้งไว้แบบ global เท่านั้น โน้ต auth จากบริการอื่นนั้นไม่อยู่ในชุดผู้เข้าชิงเลย ผู้เข้าชิงสะอาดกว่า การเรียกคืนคมชัดกว่า — ก่อนที่การรวมข้อมูลใด ๆ จะทำงานเสียอีก

### ขอบเขตถูกกำหนดอย่างไร

หากคุณขับ Octobrain ผ่าน MCP client ที่ทำ handshake ของเซสชัน ขอบเขตจะถูกล็อกจากไดเรกทอรีทำงานโดยอัตโนมัติ — agent ไม่ต้องคิดเรื่องนี้เลย และพารามิเตอร์ `scope` จะถูกถอดออกจากสคีมาของเครื่องมือทั้งหมดเพื่อไม่ให้มันทำพลาดได้ เซิร์ฟเวอร์ยังประกาศขอบเขตที่ใช้ได้ในคำสั่งของ MCP ด้วยซ้ำ:

```
Available project scopes (pass as the 'scope' parameter):
  github.com/muvon/muvon-web
```

เมื่อเซสชัน _ไม่ได้_ ถูกล็อก เครื่องมือจะเปิด `scope` ออกมาตรง ๆ และ `memorize` จะรับแฟล็ก `global: true` เพื่อจงใจวางข้อเท็จจริงลงในขอบเขตที่ใช้ร่วมกันไม่ว่าคุณจะอยู่ที่ไหน แฟล็กนั้นจะปรากฏก็ต่อเมื่อมีขอบเขตโปรเจกต์ที่ถูกล็อกให้ override เท่านั้น มิฉะนั้นคุณก็แค่ส่ง `scope: ""` เอง คำแนะนำที่ฝังอยู่ในคำอธิบายเครื่องมือนั้นตรงไปตรงมา: ใช้ `global` สำหรับข้อเท็จจริงข้ามโปรเจกต์อย่างค่าที่ตั้งและธรรมเนียมปฏิบัติ; **อย่า** ใช้สำหรับความรู้เฉพาะโปรเจกต์ การรั่วของขอบเขตคือสัญญาณรบกวนที่คุณพยายามหลีกเลี่ยง

---

## พื้นผิวเครื่องมือที่คุณเรียกใช้จริง

Octobrain จงใจเปิดพื้นผิว MCP ขนาดเล็ก ทุกเครื่องมือที่คุณเปิดมีค่าใช้จ่ายในแง่ context ของ agent ใน _ทุกเทิร์น_ — มันอ่านสคีมาก่อนทำอะไรก็ตาม — ดังนั้น[รีลีส 0.7.0 จึงลดพื้นผิวลง](/blog/octobrain-0-7-0-memory-that-sleeps) และ 0.8.0 ก็คงความเบาไว้ สี่เครื่องมือ สี่งาน:

```
memorize    เก็บความทรงจำ (พร้อมความสัมพันธ์แบบ inline ได้)
remember    การเรียกคืนเชิงความหมาย (คืนเพื่อนบ้านในกราฟระยะ 1 hop)
forget      ลบ (ต้องมี confirm=true)
knowledge   ทำดัชนี/ค้น/อ่าน/จับคู่เอกสารและ URL ภายนอก
```

### memorize

คำกริยาสำหรับการเก็บ จำเป็น: `title` (5–200 ตัวอักษร) และ `content` (10–10000 ตัวอักษร) ที่เหลือเป็นทางเลือกแต่ควรเข้าใจ:

- **`memory_type`** — หมวดหมู่อย่าง `architecture`, `bug_fix`, `decision`, `user_preference`, `goal` ใช้สำหรับกรองในการเรียกคืน
- **`importance`** — 0.0–1.0 คำอธิบายเครื่องมือให้จุดยึดที่เป็นจริง: ข้อเท็จจริงที่ผู้ใช้ระบุ 0.8–1.0, การตัดสินใจ 0.7–0.9, การแก้บั๊ก 0.6–0.8, การอนุมานของ AI 0.3–0.6
- **`source`** — ระดับความเชื่อถือ ได้แก่ `user_confirmed` (ผู้ใช้พูดอย่างชัดเจน) หรือ `agent_inferred` (agent สรุปเอง) นี่ไม่ใช่แค่ความสวยงาม: ข้อเท็จจริงที่ยืนยันแล้วจัดอันดับสูงกว่าในการดึงข้อมูล และผลลัพธ์กลับมาพร้อมป้าย `[CONFIRMED]` / `[INFERRED]` เพื่อให้ agent รู้ว่าควรเชื่อถือแต่ละอันมากแค่ไหน
- **`tags`**, **`related_files`** — สำหรับการกรองและการผูกความทรงจำกับโค้ดที่มันอธิบาย
- **`related_to`** — อันที่น่าสนใจ อาเรย์ของลิงก์แบบมีชนิดไปยังความทรงจำที่ _มีอยู่แล้ว_ สร้างในการเรียกครั้งเดียวกัน นี่คือวิธีสร้างกราฟโดยไม่ต้องไป-กลับรอบที่สอง

ตัวอย่างการเก็บที่เป็นรูปธรรม อย่างที่ agent จะส่งออกมา:

```json
{
	"title": "Rate limiting uses a token bucket",
	"content": "We rate-limit the public API with a token bucket (100 req/min/key), returning 429 + Retry-After on exhaustion. Chosen over fixed-window to avoid burst-at-boundary.",
	"memory_type": "decision",
	"importance": 0.85,
	"source": "user_confirmed",
	"tags": ["api", "rate-limiting"],
	"related_files": ["src/middleware/ratelimit.rs"]
}
```

### remember

คำกริยาสำหรับการเรียกคืน และเป็นอันที่คุณควรเรียก _ก่อน_ — ก่อน memorize (เพื่อเลี่ยงการเก็บซ้ำ) และตอนเริ่มงาน (เพื่อโหลด context) มันรับ `query` ที่เป็นได้ทั้งสตริงเดี่ยว หรือดีกว่านั้นคืออาเรย์ของคำที่เกี่ยวข้อง 2–5 คำเพื่อความครอบคลุมเชิงความหมายที่กว้างขึ้น มันคืนผลลัพธ์ที่ตรงที่สุด _พร้อมกับ_ เพื่อนบ้านในกราฟที่อยู่ติดกันโดยอัตโนมัติ ดังนั้นการตรงกับความทรงจำหนึ่งจะดึงความทรงจำที่เชื่อมโยงกับมันขึ้นมาด้วย

มันยังรับตัวกรองที่ใช้งานได้จริงในรีลีสล่าสุด: `created_after` / `created_before` (ISO-8601) สำหรับคำถามเชิงเวลาอย่าง "เราตัดสินใจอะไรเมื่อสัปดาห์ก่อน", `min_relevance` สำหรับเกณฑ์คุณภาพขั้นต่ำ และ `memory_types` / `tags` สำหรับการจำกัด การขยาย query ที่ช่วยกู้คำถามคลุมเครือ — pseudo-relevance feedback แบบ HyDE-lite ของ Octobrain — ทำงานอัตโนมัติและเพิ่มการเรียกคืนตามที่อ้างไว้ **+10–30% บน query แบบ long-tail** โดยไม่ต้องมี LLM และไม่ต้องตั้งค่า

### forget

การลบถาวร ต้องมี `confirm: true` — ไม่มีการลืมโดยบังเอิญ ลบด้วย `memory_id` สำหรับอันเดียว หรือด้วย `query` + ตัวกรองสำหรับการกวาดเป็นจำนวนมาก คำอธิบายเครื่องมือมีกฎที่ควรซึมซับไว้: **อย่าลืมความทรงจำเพียงเพราะมันเก่า** — การเสื่อม (decay) จัดการเรื่องนั้น (อยู่ด้านล่าง) ใช้ `forget` ก็ต่อเมื่อบางอย่าง _ผิด_ จริง ๆ หรือถูกแทนที่ไปแล้ว

### knowledge

อีกครึ่งหนึ่งของหน่วยความจำ: เอกสารภายนอก `memorize` มีไว้สำหรับข้อคิดที่กลั่นกรองซึ่ง agent _ผลิตขึ้น_; `knowledge` มีไว้สำหรับวัสดุต้นทางที่ agent _อ่าน_ ห้าคำสั่ง ส่งผ่านฟิลด์ `command`:

```bash
# ทำดัชนีและค้นในแหล่งเดียว (ทำดัชนีทันที)
knowledge command=search query="how to handle async tasks" source=https://docs.rs/tokio/

# อ่านข้อความเต็มของไฟล์หรือ URL หนึ่ง (ทางเลือกสุดท้ายเมื่อการค้นได้ผลน้อย)
knowledge command=read source=/path/to/design-doc.md

# grep เนื้อหาที่ทำดัชนีแล้วด้วย regex
knowledge command=match pattern="spawn_blocking|block_in_place"

# เก็บและลบข้อความดิบภายใต้คีย์ (ขอบเขตเซสชัน)
knowledge command=store key=meeting-notes content="..."
knowledge command=delete key=meeting-notes
```

มีกฎเข้มข้นหนึ่งข้อที่สคีมาบังคับและเซิร์ฟเวอร์ย้ำในคำสั่งของมัน: **`source` คือไฟล์หรือ URL เดียวเสมอ ไม่ใช่ไดเรกทอรี** หากส่งไดเรกทอรีเข้าไป มันจะถูกปฏิเสธ ชนิดที่รองรับคือ `.html`, `.txt`, `.md`, `.pdf`, `.docx` รวมถึง URL แบบ http/https

---

## แนวคิดที่สอง: ปล่อยให้หน่วยความจำรวมข้อมูลด้วยตัวเอง

การกำหนดขอบเขตตัดสัญญาณรบกวนข้ามโปรเจกต์ แต่ไม่ได้ทำอะไรกับปัญหาโน้ตเกือบซ้ำห้าอันในโปรเจกต์เดียว นั่นคือสิ่งที่การรวมข้อมูลมีไว้ และทางเลือกการออกแบบสำคัญใน 0.7.0 คือการทำให้มัน **เป็นอิสระอัตโนมัติ** — เพราะสิ่งหนึ่งที่คุณพึ่งพาไม่ได้คือการที่ agent (หรือมนุษย์) จะจำได้ว่าต้องเก็บกวาด

### การรวมข้อมูลแบบหลับ (sleep consolidation)

เมื่อเริ่มทำงาน หากผ่านไปหนึ่งวันนับจากรอบก่อน Octobrain จะรันรอบ "หลับ": มันหากลุ่มของความทรงจำที่คล้ายกัน เพิ่งสร้าง และยังอยู่สถานะ `Working` แล้วพับแต่ละกลุ่มเป็นข้อคิดที่รวมแล้วหนึ่งอัน ต้นฉบับไม่ได้ถูกลบ — มันเปลี่ยนไปสู่สถานะ `Consolidated` ด้วยความสำคัญที่ถูกลดทอน เชื่อมไว้ใต้ parent ใหม่ในฐานะที่มา โน้ตคลุมเครือห้าอันเรื่อง rate-limiting กลายเป็นความทรงจำเดียวที่สะอาดและมั่นใจสูงขึ้น และร่องรอยย้อนกลับไปยังโน้ตดิบยังคงอยู่เพื่อการตรวจสอบ

มันถูกควบคุมด้วยไฟล์ marker จึงเป็นแบบ lazy ไม่ใช่ cron หรือ scheduler เบื้องหลัง และรอบที่ช้าหรือล้มเหลวไม่เคยขวางอะไร ค่าเริ่มต้นเป็นแบบระมัดระวัง:

```toml
[memory]
sleep_consolidation_enabled          = true   # opt-out, not opt-in
sleep_consolidation_interval_hours   = 24     # at most once a day
sleep_consolidation_threshold        = 0.85   # cosine similarity to cluster
sleep_consolidation_min_cluster_size = 3      # need 3+ to bother
sleep_consolidation_max_age_days     = 7      # only recent Working memories
```

คุณสามารถบังคับรอบจาก CLI เพื่อทดสอบได้ — `octobrain memory sleep-consolidate --threshold 0.85 --min-size 3` — แต่ประเด็นคือคุณไม่ต้องทำเลย

### การรวมข้อมูลที่ยึดเป้าหมาย (goal-anchored)

การรวมข้อมูลแบบหลับจับกลุ่มตาม _ความคล้าย_ เวอร์ชันที่คมกว่าจับกลุ่มตาม _เจตนา_ Octobrain มีชนิดความทรงจำ `goal` และวงจรชีวิตจริง — `Working → Consolidated → Archived` ขั้นตอนทำงาน ซึ่งทั้งหมดแสดงผ่านลิงก์ `related_to` ของ `memorize` คือ:

1. `memorize` ความทรงจำชนิด `goal` ที่จับสิ่งที่คุณพยายามจะทำ
2. สำหรับทุกการค้นพบระหว่างทาง `memorize` ด้วย `related_to: [{ target_id: <goal_id>, relationship_type: "achieves" }]`
3. เมื่องานปิดลง `memorize` โน้ตบทเรียนที่ได้เรียนรู้ด้วย `relationship_type: "closes"`

ลิงก์ `closes` นั้นจะ trigger การรวมข้อมูลโดยอัตโนมัติ — ไม่ต้องเรียกเครื่องมือแยก โน้ตปิดท้ายจะกลายเป็น parent ที่รวมแล้ว (ความสำคัญถูกดันขึ้นเป็น `max(sources) * 1.1`) และทุกแหล่ง `achieves` จะเปลี่ยนไปสู่สถานะ `Consolidated` ด้วยความสำคัญที่ถูกลดทอน ยังคงค้นได้เพื่อร่องรอยการตรวจสอบ การค้นพบที่กระจัดกระจายห้าอันยุบรวมเป็นความทรงจำเดียวที่มีความหมาย ยึดกับเจตนาที่สร้างมันขึ้นมา

นี่ยังอธิบายว่าทำไมเครื่องมือ `consolidate` แบบแยกจึงไม่อยู่ในพื้นผิว MCP อีกต่อไป: การปิดเป้าหมายตอนนี้เป็นแค่ `memorize` ที่มีลิงก์ `closes` และการรวมข้อมูลแบบหลับก็รันเอง เครื่องมือน้อยลง พลังเท่าเดิม (คำสั่ง CLI `octobrain memory consolidate <goal-id>` ยังอยู่สำหรับการใช้งานแบบ manual และการดูแลระบบ)

### การเสื่อมแบบครึ่งชีวิต: การลืมในฐานะสัญญาณจัดอันดับ

กลไกที่สามทำงานต่อเนื่อง ความสำคัญของทุกความทรงจำเสื่อมลงตามตารางครึ่งชีวิตแบบ [Ebbinghaus](https://en.wikipedia.org/wiki/Forgetting_curve) — และทุกครั้งที่คุณเข้าถึงความทรงจำ มันจะได้รับการเสริมแรง ความทรงจำที่ถูกเรียกคืนบ่อยจะยังคงแข็งแรง ส่วนอันที่ไม่มีอะไรไปแตะจะค่อย ๆ จางในการจัดอันดับ

```toml
[memory]
decay_enabled            = true
decay_half_life_days     = 90    # higher = memories stay relevant longer
access_boost_factor      = 1.2   # reinforcement multiplier per access
min_importance_threshold = 0.05  # floor — decay never deletes
```

มีตัวกันสองอย่างที่ทำให้สิ่งนี้ปลอดภัย มี _พื้น_ ของความสำคัญ — ความทรงจำที่เก่าจะจมไปด้านหลังแต่ไม่เคยหายไป เพราะการเสื่อมเป็นสัญญาณการจัดอันดับใหม่ ไม่ใช่การลบ และตัวนับการเข้าถึงคงอยู่ข้ามการรีสตาร์ต ดังนั้นความรู้สึกของหน่วยความจำว่า "อะไรกำลังมาแรง" จึงไม่รีเซ็ตทุกเซสชัน ผลสุทธิคือความทรงจำที่คุณใช้จริงลอยขึ้น ส่วนอันที่คุณเก็บครั้งเดียวแล้วไม่เคยต้องใช้จะจมลง โดยคุณไม่ต้องดูแลเลย นี่แหละคือเหตุผลที่เครื่องมือ `forget` บอก agent ว่า _อย่า_ ลบสิ่งของเพียงเพราะมันเก่า — การเสื่อมจัดการเรื่องนั้นอยู่แล้ว อย่างนุ่มนวล โดยไม่สูญเสียประวัติ

---

## ควรเก็บอะไร และควรปล่อยอะไรไว้

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

**เก็บ:**

- **การตัดสินใจและเหตุผลของมัน** "เราเลือก token-bucket แทน fixed-window เพราะ burst-at-boundary" _ทำไม_ คือส่วนที่แพงในการสร้างขึ้นใหม่
- **ข้อเท็จจริงที่ได้มายากเกี่ยวกับ repository นี้** คำสั่งเทสต์ที่ไม่ชัดเจน, header ของ staging, migration ที่ต้องรันก่อน สิ่งที่ agent มิฉะนั้นจะค้นพบใหม่ (หรือเดาใหม่) ทุกเซสชัน
- **ค่าที่ผู้ใช้ตั้ง** — ในขอบเขต global พูดครั้งเดียว จริงทุกที่
- **การแก้บั๊กพร้อมสาเหตุที่แท้จริง** ไม่ใช่ "แก้ webhook แล้ว" แต่ "การ retry ของ webhook ต้องมี idempotency key; หากไม่มี การ retry จะคิดเงินซ้ำ"

**อย่าเก็บ:**

- **สถานะชั่วคราว** branch ปัจจุบัน, อะไรอยู่ใน working tree, เทสต์ไหนล้มเหลววันนี้ มันเก่าก่อนเซสชันถัดไปและเป็นสัญญาณรบกวนล้วน ๆ ตลอดไปหลังจากนั้น
- **อะไรก็ตามที่สร้างขึ้นใหม่ได้ง่าย ๆ** หากคำสั่ง shell บรรทัดเดียวสร้างมันขึ้นมาใหม่ได้ คำสั่งนั้นแหละคือความทรงจำ — ไม่ใช่ผลลัพธ์ของมัน
- **การคาดเดาที่ยังไม่ตรวจสอบ ราวกับเป็นข้อเท็จจริง** หาก agent _ต้อง_ บันทึกลางสังหรณ์ นั่นคือสิ่งที่ `source: agent_inferred` และความสำคัญต่ำมีไว้ ติดป้ายความไม่แน่นอนไว้เพื่อให้การดึงข้อมูลจัดมันต่ำกว่าความรู้ที่ยืนยันแล้ว
- **ข้อคิดเดียวกันห้าครั้ง** เรียก `remember` _ก่อน_ `memorize` หากมีอันที่เกือบตรงกันกลับมา ให้อัปเดตหรือแทนที่มันแทนการกองอันที่เกือบซ้ำเพิ่มอีก

ประเด็นสุดท้ายนั้นมีเครื่องมือรองรับ เมื่อข้อเท็จจริงใหม่แทนที่อันเก่า — ค่าหนึ่งเปลี่ยนไป การตัดสินใจถูกกลับ — ให้ `memorize` ข้อเท็จจริงใหม่ด้วย `related_to: [{ target_id: <old_id>, relationship_type: "supersedes" }]` การดึงข้อมูลจะจัดข้อเท็จจริงปัจจุบันเหนืออันที่ล้าสมัย ซึ่งยังคงค้นได้เพื่อประวัติ คุณแก้บันทึกโดยไม่สูญเสียมันไป

---

## การตั้งค่า

ติดตั้งแล้วชี้ MCP client มาที่มัน

```bash
# crates.io
cargo install octobrain

# หรือบน macOS ผ่าน Homebrew
brew install muvon/tap/octobrain
```

ลองวงรอบเต็มจาก CLI ก่อน:

```bash
octobrain memory memorize --title "API Design Pattern" \
  --content "Use REST for CRUD, GraphQL for complex queries" \
  --memory-type architecture --tags "api,design"

octobrain memory remember "how should I design APIs"
```

จากนั้นเชื่อมเข้ากับ MCP client สำหรับ Claude Desktop ให้เพิ่มลงใน `claude_desktop_config.json`:

```json
{
	"mcpServers": {
		"octobrain": {
			"command": "/path/to/octobrain",
			"args": ["mcp"]
		}
	}
}
```

รัน `octobrain mcp` สำหรับ stdio หรือ `octobrain mcp --bind 0.0.0.0:12345` สำหรับ HTTP ที่จัดเก็บอยู่ใน `~/.local/share/octobrain/` (macOS/Linux) หรือ `%APPDATA%\octobrain\` (Windows) และความทรงจำของโปรเจกต์ถูกแยกตามขอบเขตตามที่อธิบายไว้ข้างต้น ทุกอย่างมีค่าเริ่มต้นที่สมเหตุสมผล — การรวมข้อมูลแบบหลับ, การเสื่อม, การค้นแบบไฮบริด และ HyDE เปิดอยู่ตั้งแต่แกะกล่อง ชุดปุ่มปรับทั้งหมดมีเอกสารใน `config-templates/default.toml` ใน repository

การตัดสินใจด้านการตั้งค่าหนึ่งเดียวที่คุ้มค่าจะทำอย่างจงใจคือ `decay_half_life_days` ค่าเริ่มต้น 90 วันเหมาะกับ codebase ที่ค่อย ๆ พัฒนาอย่างสม่ำเสมอ ตั้งให้สูงขึ้นสำหรับโปรเจกต์ที่เสถียรซึ่งการตัดสินใจยังใช้ได้ยาวนาน; ลดลงสำหรับ prototype ที่เปลี่ยนเร็วซึ่งความจริงของเดือนที่แล้วคือเรื่องโกหกของเดือนนี้

---

## คำถามที่พบบ่อย

**การลืมทำให้สูญเสียข้อมูลไหม?**

ไม่ การเสื่อมเป็นสัญญาณจัดอันดับที่มีพื้น — ความทรงจำที่เข้าถึงน้อยจะจมแต่ไม่เคยถูกลบ การรวมข้อมูลย้ายแหล่งไปสู่สถานะ `Consolidated` ถูกลดทอนแต่ยังค้นได้ เชื่อมไว้ใต้ parent ในฐานะที่มา สิ่งเดียวที่ลบจริง ๆ คือ `forget` ที่ชัดแจ้งด้วย `confirm: true` คุณย้อนรอยกลับได้เสมอ

**`memorize` ต่างจาก `knowledge` อย่างไร?**

`memorize` เก็บข้อคิดที่ agent _ผลิต_ — การตัดสินใจ, การแก้ไข, ข้อสรุป `knowledge` ทำดัชนีเอกสารที่ agent _อ่าน_ — เอกสาร, ไฟล์ออกแบบ, URL — เพื่อให้มันค้นและอ้างอิงได้ ปัญญาที่กลั่นกรองแล้วกับวัสดุต้นทาง วงจรชีวิตต่างกัน เครื่องมือต่างกัน

**ฉันต้องจัดการขอบเขตด้วยมือไหม?**

โดยปกติไม่ต้อง ด้วย MCP client ที่ทำ handshake ของเซสชัน ขอบเขตจะถูกล็อกจาก git remote ของไดเรกทอรีทำงานของคุณ และพารามิเตอร์ `scope` จะถูกถอดออกจากสคีมา คุณแตะขอบเขตอย่างชัดแจ้งเฉพาะเพื่อเก็บค่าที่ตั้งแบบ global (`global: true` หรือ `scope: ""`) เท่านั้น

**ทำไมมีแค่สี่เครื่องมือ MCP? เซิร์ฟเวอร์หน่วยความจำอื่นเปิดเป็นโหล**

เพราะทุกเครื่องมือที่คุณเปิดคือสคีมาที่โมเดลอ่านซ้ำในทุกเทิร์น และความแม่นยำในการเลือกเครื่องมือเสื่อมลงเมื่อแคตตาล็อกโต การรวมข้อมูลกลายเป็นอิสระอัตโนมัติ และ `relate`/`consolidate` ถูกพับเข้าไปในลิงก์ความสัมพันธ์ของ `memorize` ดังนั้นงานที่เคยต้องมีเครื่องมือของตัวเองตอนนี้จึงไม่ต้องแล้ว CLI ยังมีชุดเต็มสำหรับงานดูแลระบบ

**ฉันต้องมี API key หรือ GPU ไหม?**

ไม่ โมเดล embedding เริ่มต้นรันภายในเครื่องบน CPU ผ่าน fastembed — ไม่มีคีย์ ไม่มีเครือข่าย embedder บนคลาวด์ (Voyage, OpenAI, Google, Jina) รองรับหากคุณต้องการคุณภาพสูงขึ้น แต่เป็นทางเลือกเสริม

---

สิ่งที่แก้การเรียกคืนของเราไม่ใช่อัลกอริทึมการค้นที่ดีกว่า — การดึงข้อมูลของ Octobrain เอาชนะ BM25 บนเบนช์มาร์กอยู่แล้วในขณะที่เรากำลังจมน้ำ มันคือการยอมรับว่าหน่วยความจำต้องลืมเพื่อจะยังมีประโยชน์ กำหนดขอบเขตให้สิ่งที่ถูกต้องได้เป็น _ผู้เข้าชิง_ ตั้งแต่แรก; ปล่อยให้มันรวมข้อมูลเพื่อให้รายการซ้ำยุบรวม; ปล่อยให้มันเสื่อมเพื่อให้น้ำหนักที่ตายแล้วจมลง แล้ว agent จะหยุดรื้อค้นในลิ้นชักขยะและเริ่มตอบจากหน่วยความจำ

เก็บการตัดสินใจและข้อเท็จจริงที่ได้มายาก ข้ามสัญญาณรบกวนชั่วคราว เรียก `remember` ก่อน `memorize` ที่เหลือปล่อยให้มันรันเอง

— Don

---

_[Octobrain](https://github.com/muvon/octobrain) เป็นโอเพนซอร์สภายใต้ Apache-2.0 กลไกการรวมข้อมูลและการเสื่อมที่อธิบายไว้ที่นี่มาในเวอร์ชัน [0.7.0](/blog/octobrain-0-7-0-memory-that-sleeps); ระบบขอบเขตและพื้นผิวเครื่องมือที่เบาขึ้นต่อเนื่องมาถึง 0.8.0 หากรายละเอียดการตั้งค่าใดทำให้คุณสะดุด หรือคุณอยากให้เปลี่ยนค่าเริ่มต้นของขอบเขต/การเสื่อม [เปิด issue](https://github.com/muvon/octobrain/issues) — เราอ่านมัน_
