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

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

0.7.0 ทำให้ความจำของ 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 หนึ่ง:

# 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 ของคุณก็แค่เริ่มเจอความรู้ของทีมในการค้นหาของมัน

[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) ที่คุณมองเห็นและเล็งไปหาได้จริง

# 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 พฤติกรรมเดิม ชื่ออ่านได้ ดู การอัปเกรด


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

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

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 วัดพอดี


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

0.7.0 เพิ่ม LongMemEval สำหรับความจำระยะยาว 0.9.0 เพิ่ม harness สำหรับ retrieval ของ 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 แล้วแทนที่จะโฆษณาไว้แต่ถูกเพิกเฉย

    [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 จัดการ

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 ถ้าพบอะไรพัง เปิด issue — เราอ่าน