# Octobrain 0.9.0: ความรู้เข้าสู่โหมด Multiplayer

> Octobrain 0.9.0 เปลี่ยน knowledge layer ให้เป็นสิ่งที่ทั้งทีมแชร์กันได้: knowledge box ที่หนุนด้วย git ส่ง rules และ docs ที่จัดมาแล้วผ่าน repo, sync ในเบื้องหลัง และคง scope ต่อโปรเจกต์หรือต่อ org ได้ memory scope ได้ชื่อที่มนุษย์อ่านออกแทนแฮชที่อ่านไม่ออก และ recall ก็คมขึ้น — RRF multi-query fusion ทำให้การแตก query ได้ผลจริง, Supersedes edge ปล่อยให้ fact ปัจจุบันเอาชนะอันเก่าที่ค้าง และ temporal filter ใหม่ตอบคำถามว่า "สัปดาห์ที่แล้วฉันตัดสินใจอะไรไป" นี่คือสิ่งที่เปลี่ยนและวิธีอัปเกรด

# Octobrain 0.9.0: ความรู้เข้าสู่โหมด Multiplayer

ทีมของคุณเขียนเอกสารไว้แล้ว ทั้ง deploy runbook, กฎ "ห้ามแตะสิ่งนี้โดยไม่มี migration", โน้ตที่ได้มาอย่างยากลำบากว่าทำไม payments service ถึง retry แบบที่มันทำ ทั้งหมดนั้นอยู่ใน wiki ที่ไม่มีใครอ่าน, ข้อความ Slack ที่ปักหมุดไว้ และในหัวของคนสามคน ทุก session ของ agent ใหม่ — และเพื่อนร่วมทีมใหม่ทุกคน — เริ่มจากศูนย์และเรียนรู้มันใหม่แบบยากลำบาก

[0.7.0](/blog/octobrain-0-7-0-memory-that-sleeps) ทำให้ความจำของ agent แต่ละตัว**ดูแลตัวเอง**ได้ — sleep consolidation, decay, goal-anchored folding นั่นคือเรื่องของความจำหนึ่งอันที่ฉลาดขึ้นด้วยตัวมันเอง

0.9.0 เกี่ยวกับอีกครึ่งหนึ่งของปัญหา: ความรู้ที่ไม่ควรต้องเรียนรู้ใหม่ในทุกเครื่อง มันทำให้ knowledge layer ของ Octobrain **แชร์กันได้** — เก็บเวอร์ชันใน git, scope ด้วยชื่อ, sync อัตโนมัติ — และทำให้ recall คมขึ้นเพื่อให้ fact ที่ถูกต้องชนะบ่อยขึ้น (เราพับงานพื้นฐานของ 0.8.0 รวมไว้ในนี้ด้วย)

---

## Git-Backed Knowledge Boxes — ส่งความรู้แบบเดียวกับโค้ด

นี่คือพระเอก **knowledge box** คือ git repository ที่เต็มไปด้วย markdown — rules, docs, playbooks, โน้ตสถาปัตยกรรม — ที่ Octobrain clone, index และคอย sync ให้ จัดระเบียบมันครั้งเดียว เก็บเวอร์ชันมันเหมือนโค้ด แล้ว agent ทุกตัวบนทุกเครื่องที่ subscribe จะได้ความรู้ที่ค้นหาได้ชุดเดียวกัน

มีอยู่สองแบบ และมันครอบคลุมสองกรณีจริง:

**Project-local — แค่ commit ไดเรกทอรี `.box/`** วางความรู้ที่มีชีวิตของโปรเจกต์คุณลงใน `.box/` ที่ root ของ repo ใครก็ตามที่ clone repo แล้วรัน Octobrain จะได้มัน index อัตโนมัติ — ไม่ต้องมีคำสั่งเพิ่ม ไม่ต้อง config remote มันเดินทางไปพร้อมกับโค้ดเพราะมัน*อยู่ใน*โค้ด

**Remote — subscribe กล่องที่แชร์กัน** ชี้ Octobrain ไปที่ git URL แล้วมันจะ shallow-clone กล่องนั้นและ index มันภายใต้ scope หนึ่ง:

```bash
# Subscribe to your org's shared knowledge box
octobrain box import https://github.com/acme/engineering-knowledge

# Index it at the global scope — visible in every project on this machine
octobrain box import https://github.com/acme/conventions --global

# Pull + smart-reindex every subscribed box and the local .box/
octobrain box sync

# See what you're subscribed to, or drop one
octobrain box list
octobrain box remove github.com/acme/engineering-knowledge
```

ทุก chunk ที่มาจากกล่องจะได้ source URI แบบ `box://<host>/<org>/<repo>/...` ที่คงที่ ดังนั้นมันถูกจัดกลุ่ม ระบุที่มาได้ และลบออกได้สะอาด — ดึงกล่องออกแล้วทุก row ของมันก็ไปด้วย การ re-sync เป็นแบบ **อิงแฮชและฉลาด**: เฉพาะไฟล์ที่เปลี่ยนเท่านั้นที่ถูก re-embed ดังนั้น `sync` บนกล่องที่ไม่มีอะไรเปลี่ยนแทบไม่มีต้นทุนเลย

และภายใต้ MCP คุณไม่ต้องเรียก `sync` ด้วยซ้ำ — server จะค้นพบ `.box/` ของแต่ละโปรเจกต์, ตรวจหา org box และรีเฟรช remote ที่ subscribe ไว้ **ในเบื้องหลัง ครั้งเดียวต่อ session** แบบ single-flight ดังนั้นมันไม่เคยบล็อก request agent ของคุณก็แค่เริ่มเจอความรู้ของทีมในการค้นหาของมัน

```toml
[knowledge]
chunk_size = 1200   # boxes index through the same chunker as everything else
```

ประเด็นคือ: runbook เลิกอยู่ในหัวของใครบางคน มันอยู่ใน repo, ค้นหาได้ด้วยความหมาย และโผล่ขึ้นมาใน context ของ agent คุณทันทีที่มันเกี่ยวข้อง — สำหรับทุกคน

---

## Scopes แทนที่ Project Hashes — ความจำที่มีชื่อ

ที่ผ่านมา Octobrain แยกความจำต่อโปรเจกต์ด้วยการแฮช Git remote URL ของคุณให้เป็นไดเรกทอรี SHA-256 ที่อ่านไม่ออก มันใช้งานได้ แต่คุณอ่านมันไม่ได้ เล็งไปที่มันไม่ได้ และแชร์มันไม่ได้

0.9.0 แทนที่สิ่งนั้นด้วย **scopes**: สตริงที่มนุษย์อ่านออก, ได้มาอัตโนมัติจาก Git remote ของคุณ (หรือ local path เป็น fallback) ที่คุณมองเห็นและเล็งไปหาได้จริง

```bash
# Auto-detected from the git remote — nothing to configure
octobrain memory remember "rate limiting approach"

# Or target a scope explicitly
octobrain memory remember "rate limiting approach" --scope acme/payments

# Store something that should be visible everywhere, not just this project
octobrain memory memorize --title "Team convention: trunk-based, no long-lived branches" \
  --content "..." --global
```

Scopes คือสิ่งที่ทำให้ box เป็นอันหนึ่งอันเดียวกัน: remote box ผูกกับ scope ระดับ org โดยค่าเริ่มต้น, `.box/` ผูกกับ scope ของโปรเจกต์ของมัน และ `--global` วางความรู้หรือความจำไว้ต่อหน้าทุกโปรเจกต์บนเครื่อง ระบบตั้งชื่อเดียว ใช้ทุกที่ — storage path, search filter, stats และ log ตอนนี้อ่านเป็นภาษาธรรมดาแล้วแทนที่จะเป็นแฮช

**นี่คือ breaking change อันเดียวที่กระทบคุณ:** flag `--project` และพารามิเตอร์เครื่องมือ `project` ตอนนี้กลายเป็น `--scope` พฤติกรรมเดิม ชื่ออ่านได้ ดู [การอัปเกรด](#upgrading)

---

## Multi-Query Recall ที่ได้ผลจริง — RRF Fusion

Octobrain รองรับการค้นหาแบบ multi-query มานานแล้ว — แตกคำถามคลุมเครือออกเป็นหลายคำถามที่คมกว่าแล้วค้นหาพวกมันพร้อมกัน:

```bash
octobrain memory remember "authentication" "session security" "jwt expiry"
```

ปัญหาอยู่ที่การ merge ตรรกะเดิมเก็บคะแนนเดี่ยวที่ดีที่สุดของแต่ละความจำไว้ แล้วบวกเพิ่มแบบคงที่ 10% ต่อ match เพิ่มเติม — ผสมคะแนนต่อ query ที่คำนวณบน **สเกลที่เทียบกันไม่ได้** ความจำที่ติดอันดับมั่นคงทั้งสาม query อาจแพ้ให้กับผลอันดับต้นแบบครั้งเดียวบน query เดียวได้ การ decompose ไม่ได้ช่วยจริงๆ บางครั้งมันทำให้แย่ลงด้วยซ้ำ

0.9.0 แทนที่สิ่งนั้นด้วย **Reciprocal Rank Fusion** — การ fusion อิงอันดับแบบ `k=60` เดียวกับที่ Octobrain ใช้รวม BM25 และ vector search ใน store อยู่แล้ว RRF อิงอันดับและไม่ขึ้นกับสเกล: ความจำที่ติดอันดับดีในหลาย query เอาชนะอันที่พุ่งสูงบน query เดียวได้ ตอนนี้การ decompose query ได้ผลแบบที่มันควรจะเป็น มันเป็นคณิตศาสตร์ล้วน — ไม่ต้องใช้ LLM ไม่ต้อง config เพิ่ม — และ `rrf_fuse` เป็นฟังก์ชันเล็กๆ ที่ผ่าน unit-test ที่คุณเชื่อใจได้

---

## Recall รู้ว่าอะไรคือปัจจุบัน — การจัดอันดับที่รู้จัก Supersedes

Fact หมดอายุ คุณตัดสินใจใช้ Postgres แล้วหกสัปดาห์ต่อมาย้ายไป SQLite โน้ตทั้งสองอันอยู่ในความจำ ทั้งคู่ match กับ "เราใช้ database อะไรอยู่" — และคำตอบเก่าก็ไม่ควรมีสิทธิ์ชนะ

Octobrain มีความสัมพันธ์ชนิด `Supersedes` มาตลอด แต่มันถูกนิยาม, parse และจัดเก็บ — แล้วก็**ไม่เคยถูกอ่านตอน retrieval** ความสามารถที่สร้างไว้แล้ว นั่งตายอยู่เฉยๆ

0.9.0 ต่อสายมันเข้ามา เมื่อ edge `X Supersedes M` ที่ active มาร์กผลลัพธ์หนึ่งว่าล้าสมัย retrieval จะ **soft-down-rank** อันเก่านั้น (×0.1 — ไม่เคยถูกลบ) ดังนั้น fact ปัจจุบันจึงลอยขึ้นด้านบนขณะที่ประวัติยัง query ได้ มีราวกันสองอย่างที่จงใจไว้:

- **Edge ถูกเคารพ ไม่เคยถูกสร้างอัตโนมัติ** การตรวจจับความขัดแย้งที่แท้จริงต้องใช้การตัดสินเชิงความหมาย และ Octobrain จงใจเก็บมันออกจาก hot path คุณ (หรือ agent ของคุณ) มาร์ก supersede; retrieval เคารพมัน ผ่าน MCP ตอนนี้เครื่องมือ `knowledge` ชี้นำ agent อย่างจริงจังให้สร้างลิงก์ supersedes เมื่อพวกมันอัปเดต fact
- **Best-effort** ความล้มเหลวในการ lookup ความสัมพันธ์จะปล่อยผลลัพธ์ไว้ตามเดิมแทนที่จะทิ้งมัน กรณีแย่ที่สุดคือคุณกลับไปที่พฤติกรรมเดิม ไม่เคยแย่กว่านั้น

การตัดสินใจปัจจุบันชนะ; เส้นทางย้อนกลับไปยังอันเก่ายังรอด

---

## Recall แบบจำกัดเวลา — ตัวกรอง Temporal และ Relevance

"สัปดาห์ที่แล้วฉันตัดสินใจอะไรไป?" เป็นคำถามที่มนุษย์ถามตลอดเวลา และ semantic search เพียงอย่างเดียวตอบมันได้แย่ — relevance ไม่รู้เรื่องเวลา

`MemoryQuery` รองรับ `created_after` / `created_before` (ส่งตรงลงไปที่ index `created_at`) และ `min_relevance` อยู่แล้ว — แต่เครื่องมือ MCP `remember` ไม่เคยใส่ค่าให้พวกมัน อีกหนึ่งความสามารถที่สร้างไว้แต่ตายอยู่ 0.9.0 เปิดเผยทั้งคู่:

- **`created_after` / `created_before`** — วันที่แบบ ISO-8601 (`2026-06-01`) หรือ timestamp แบบ RFC3339 เต็ม วันที่เปล่าๆ หมายถึงเที่ยงคืน UTC; อะไรก็ตามที่ parse ไม่ได้จะข้ามตัวกรองไปเฉยๆ แทนที่จะ error
- **`min_relevance`** — พื้นคุณภาพผลลัพธ์ต่อการเรียกหนึ่งครั้ง ที่ override ค่าเริ่มต้นระดับ global

เครื่องมือบอกให้ agent **คำนวณช่วงเวลาเอง** — มันรู้วันที่วันนี้อยู่แล้ว — ดังนั้นจึงไม่มีการ parse วันที่จากภาษาธรรมชาติที่เปราะบางฝังอยู่ในเครื่องมือ "สัปดาห์ที่แล้วฉันตัดสินใจอะไร" กลายเป็นการ scan ช่วงบน index ที่สะอาด ซึ่งเป็นคำถามแบบยึดกับเวลาแบบที่ [LongMemEval](https://github.com/xiaowu0162/LongMemEval) วัดพอดี

---

## เราใส่ตัวเลขให้มันแล้ว — BEIR Retrieval Benchmarks

0.7.0 เพิ่ม LongMemEval สำหรับความจำระยะยาว 0.9.0 เพิ่ม harness สำหรับ retrieval ของ [BEIR](https://github.com/beir-cellar/beir) เพื่อให้เราให้คะแนน **ranking layer** — embedding + BM25 fusion + reranking — บน dataset มาตรฐานที่มี qrels จริง ทำงานในเครื่องทั้งหมด ไม่ใช้ LLM judge

ใช้ embedder local ค่าเริ่มต้น (`bge-small-en-v1.5`, 384-dim, 33M params) วัดเป็น **nDCG@10**:

| ชุดข้อมูล                       | Octobrain vector | Octobrain hybrid | BM25  | bge-small-en-v1.5 |
| ------------------------------- | ---------------- | ---------------- | ----- | ----------------- |
| **SciFact** (5.2K docs, 300 q)  | 0.722            | **0.742**        | 0.665 | 0.713             |
| **NFCorpus** (3.6K docs, 323 q) | 0.341            | **0.363**        | 0.325 | 0.343             |

คอลัมน์ dense-only ทำซ้ำตัวเลข BEIR ที่ embedder เผยแพร่ไว้ได้ — นั่นคือ harness ตรวจสอบตัวเอง คอลัมน์ **hybrid** (BM25 + vector ที่ fuse ด้วย RRF, ค่าเริ่มต้นของ Octobrain) เพิ่ม **+2 nDCG@10** เหนือ embedding เปล่าๆ และเอาชนะ BM25 แบบคลาสสิกได้บนทั้งสอง dataset ทำซ้ำเองได้: `cd benches && bash scripts/run_retrieval.sh` ข้ออ้างเรื่อง recall ข้างบนเป็นข้ออ้างที่เรายืนยันได้

---

## เบื้องหลัง

การเปลี่ยนแปลงเงียบๆ ที่ทำให้ทุกอย่างเร็วและคาดเดาได้ขณะที่ corpus โตขึ้น:

- **ปุ่มปรับ search ใหม่สองตัว ใน config** `search.similarity_threshold` (ค่าเริ่มต้น `0.3`) คือพื้น relevance ระดับ global สำหรับ semantic query; `search.max_results` (ค่าเริ่มต้น `50`) คือเพดานแข็งที่ผู้เรียกไม่มีทางเกินได้ ตอนนี้ทั้งคู่ถูกต่อสายแบบ end-to-end แล้วแทนที่จะโฆษณาไว้แต่ถูกเพิกเฉย

  ```toml
  [search]
  similarity_threshold = 0.3   # min relevance for semantic queries (0.0–1.0)
  max_results          = 50    # hard ceiling on results from any search
  ```

- **Timeout สำหรับ embeddings และ reranker** การเรียก provider ที่ช้าหรือค้างจะไม่ทำให้การค้นหาหยุดชะงักอีกต่อไป — `timeout_secs` (ค่าเริ่มต้น `30`) จำกัดทั้งคู่ พร้อมตัวอย่างใน config ของ reranker
- **การ initialize ในเบื้องหลัง** งานเริ่มต้นที่หนักถูก spawn ออกจาก hot path ดังนั้น request แรกหลังเปิดตัวจึงไม่ต้องจ่ายค่า warm-up ของ index
- **การค้นพบ scope อัตโนมัติ และการ override config (จาก 0.8.0)** Octobrain ดึง scope ของคุณจาก working directory โดยอัตโนมัติ และ `OCTOBRAIN_CONFIG_PATH` ให้คุณชี้ไปที่ไฟล์ config ที่กำหนดเองได้ — สะดวกสำหรับ CI, container และการตั้งค่าแบบ multi-tenant การล็อก project และ role ถูกแยกออกจากกันเพื่อให้ session ที่ไม่เกี่ยวข้องเลิกแย่งกัน
- **Docker แบบ multi-arch** ตอนนี้ release build และ push image แบบ multi-arch ดังนั้น `arm64` (Apple Silicon, Graviton) และ `amd64` ต่างก็เป็น first-class ทั้งคู่
- **คณิตศาสตร์การจัดอันดับที่ harden แล้ว** การแก้ไข reranker candidate limit, การ normalize RRF, การกัน archived-memory และ PRF guard ช่วยอุดช่อง edge case ใน retrieval path ใหม่ การค้นหา knowledge ตอนนี้ยังคืน parent section เต็มด้วย ไม่ใช่เศษเสี้ยวที่ถูกตัด

คุณจะไม่ไปแตะส่วนใหญ่ของพวกนี้โดยตรง นั่นแหละคือจุดประสงค์

---

## 0.9.0 เป็นอย่างไรในทางปฏิบัติ

วิศวกรใหม่เข้าร่วมทีม payments:

1. **เขา clone repo** ไดเรกทอรี `.box/` ของมัน — deploy runbook, กฎ "อย่า bypass idempotency layer", โน้ตว่าทำไมเราถึง retry — ถูก index อัตโนมัติในครั้งแรกที่ agent ของเขารัน ไม่ต้องตั้งค่าอะไรเลย
2. **เครื่องของเขา subscribe org box** (`octobrain box import …`) ดังนั้น convention ระดับทั้งบริษัทจึงโผล่ขึ้นมาภายใต้ global scope ในทุกโปรเจกต์
3. **เขาถาม agent ด้วยคำถามคลุมเครือ** — "เราจัดการ webhook failures ที่นี่ยังไง?" การ decompose แบบ multi-query กระจายมันออก, RRF fuse อันดับเข้าด้วยกัน และรายการ runbook ของทีมก็มาอยู่ด้านบนสุด
4. **การตัดสินใจหนึ่งเปลี่ยนไปเมื่อเดือนที่แล้ว** — ทีมย้ายออกจาก queue ที่เคยแนะนำกัน โน้ตเก่าถูกมาร์กว่า superseded ดังนั้นวิธีปัจจุบันชนะ และประวัติก็ยังห่างออกไปแค่ query เดียว
5. **เขาถาม "สัปดาห์ที่แล้วเราเปลี่ยนอะไรไป?"** — ตัวกรอง temporal, ช่วง index ที่สะอาด, ไม่มี noise จากหกเดือนก่อน

ไม่มีใครต้องอธิบายระบบใหม่ ความรู้ถูกเขียนไว้แล้ว — Octobrain แค่ทำให้มันค้นเจอได้ เป็นปัจจุบัน และถูกแชร์

---

## การอัปเกรด

จาก 0.7.x หรือ 0.8.x การ migrate เล็กน้อย

**Config — `--project` กลายเป็น `--scope`** นี่คือ breaking change อันเดียว ที่ไหนก็ตามที่คุณส่ง `--project <id>` บน CLI หรือพารามิเตอร์ `project` ให้เครื่องมือ MCP ให้ใช้ `--scope <name>` แทน อัปเกรดใหญ่: ตอนนี้ค่าเป็นสตริงที่มนุษย์อ่านออก (ได้มาอัตโนมัติจาก Git remote ของคุณ) ไม่ใช่แฮช — ดังนั้นส่วนใหญ่คุณทิ้ง flag ไปทั้งหมดได้เลยและปล่อยให้ discovery จัดการ

```bash
octobrain memory remember "api design" --scope acme/web   # was: --project <hash>
```

**Storage — ไม่ต้องทำอะไร** คอลัมน์ `scope` และ index ของมันถูกเพิ่มในการรันครั้งแรก และความจำที่มีอยู่ migrate ในที่ ไม่มีขั้นตอนด้วยมือ ไม่มี downtime

**Config ใหม่เป็นแบบเพิ่มเข้ามา** `search.similarity_threshold`, `search.max_results` และ `timeout_secs` ของ embedding/reranker ทั้งหมดมาพร้อมค่าเริ่มต้นที่ใช้งานได้ เพิ่มมันเฉพาะเมื่อคุณอยากปรับจูนเท่านั้น

**Knowledge box เป็นแบบ opt-in** ไม่มีอะไรเปลี่ยนจนกว่าคุณจะ commit ไดเรกทอรี `.box/` หรือรัน `octobrain box import` แหล่งที่ index ไว้อยู่แล้วไม่ถูกแตะต้อง

**MCP clients — รีเฟรช tool list ของคุณ** `remember` ตอนนี้รับ `created_after`, `created_before` และ `min_relevance`; `memorize` รับ `--global` ไม่มีการเปลี่ยนชื่อเครื่องมือหลักห้าตัว — แค่ field ตัวเลือกใหม่ที่ agent ของคุณใช้ได้

Source, binaries และ Docker image ที่ [github.com/muvon/octobrain](https://github.com/muvon/octobrain) ถ้าพบอะไรพัง เปิด issue — เราอ่าน
