Chain of Recall คืออะไร? Stable IDs ช่วยให้ AI Agent มีความจำได้อย่างไร
Chain of Recall คือวิธีเพิ่มความจำให้กับ AI Agent ที่ทำงานต่อเนื่องระยะยาว (long-running agent) โดยไม่ต้องอัดทุกอย่างลงใน context window ทุก Trigger, Thread และ Worker จะได้รับ stable ID ที่ไม่เปลี่ยนแปลง ส่วนในบริบทจะเก็บไว้เพียงแค่ ID, สรุปย่อสั้น ๆ และข้อความข้างเคียงเท่านั้น จากนั้น Agent จะไล่ตามลิงก์ระหว่าง ID เพื่อดึงข้อมูลในอดีตกลับมาได้ภายในไม่กี่ขั้นตอน (hops)
คำนี้มาจากบทความด้านการออกแบบของ DeepSeek Harness ที่ชื่อ "Exploring Super-agent Design" ซึ่งเผยแพร่เมื่อวันที่ 01-11-2026 อย่างไรก็ตาม มีข้อควรทราบก่อนจะอ่านต่อ: บทความนี้อธิบายถึงสถาปัตยกรรม ไม่ใช่ฟีเจอร์สำเร็จรูปที่ปล่อยออกมาให้ใช้งานจริง โดยบางส่วนอยู่ในปลั๊กอินต้นแบบ dsh-superagent-prototype ซึ่งผู้เขียนระบุว่า "ไม่ใช่ผลิตภัณฑ์อย่างเป็นทางการของ DeepSeek" ดังนั้นจึงไม่มีสวิตช์สำหรับเปิดใช้งาน Chain of Recall แต่อย่างใด แต่นี่คือรูปแบบการออกแบบ (pattern) ที่คุณต้องสร้างขึ้นมาเอง
ปัญหาของ Context Window ใน Agent ที่ทำงานต่อเนื่องระยะยาว
Agent ที่ทำงานต่อเนื่องระยะยาวมักจะชนขีดจำกัดของ context limit เพราะมีงานไหลเข้ามาอย่างไม่ขาดสาย Super-agent ที่คอยเฝ้าติดตามห้องแชต, GitHub และ CI ต้องพบเจอกับอีเวนต์หลายร้อยรายการต่อวัน และพื้นที่ใน window ที่เคยรองรับงานของเมื่อวานก็ไม่สามารถจุงานของวันนี้เพิ่มได้อีกแล้ว
แนวทางรับมือแบบเดิม ๆ ล้วนมีข้อแลกเปลี่ยนที่ต้องจ่าย:
- เก็บทุกอย่างไว้: พื้นที่ window จะเต็มอย่างรวดเร็ว และค่าใช้จ่ายจะพุ่งสูงขึ้นในทุก ๆ รอบการสนทนา (turn)
- สรุปเนื้อหาเก่า ๆ: การสรุปย่ออาจทำให้รายละเอียดสำคัญตกหล่น เช่น ข้อความ error หรือเลข PR ที่คุณต้องใช้อ้างอิงในภายหลัง
- ค้นหาด้วยความคล้ายคลึง (Similarity search): ใช้งานได้ดีจนกระทั่งข้อมูลที่คุณต้องการไม่ได้มีหน้าตาหรือถ้อยคำที่คล้ายกับคำค้นหาของคุณ
คู่มือของ Anthropic เรื่อง context engineering for AI agents มองว่าบริบทเป็นทรัพยากรที่มีอยู่อย่างจำกัดซึ่งคุณจำเป็นต้องคัดสรรอย่างรอบคอบ Chain of Recall ถือเป็นรูปธรรมหนึ่งในการคัดสรรดังกล่าว และบทความของเราเกี่ยวกับ หลักการทำงานของหน่วยความจำใน AI Agent ก็ได้ครอบคลุมตัวเลือกอื่น ๆ ที่หลากหลายยิ่งขึ้น
Inline กับ Spill: มองบริบทให้เหมือนแคช
บทความดังกล่าวเปรียบ context window เสมือนแคชที่ใช้กลยุทธ์หลัก 2 ประการ:
- Inline: เก็บสิ่งที่ Agent น่าจะต้องใช้งานในทันทีเอาไว้ ได้แก่ ID, สรุปย่อสั้น ๆ และข้อความข้างเคียง
- Spill: ผลักสิ่งอื่น ๆ ทั้งหมดออกไป โดยตัดทอนเนื้อหาลงแล้วทิ้งพอยน์เตอร์ไว้ให้ Agent ตามไปเปิดดูได้ในภายหลัง
ข้อมูลที่ถูก spill ออกไปไม่ได้ถูกลบทิ้ง มีเพียงพอยน์เตอร์ของมันเท่านั้นที่ยังคงอยู่ในพรอมต์
นี่คือตัวอย่างหน้าตาของข้อมูลเมื่อเกิดเหตุการณ์ CI ล้มเหลวส่งเข้ามายัง Router ซึ่งโครงสร้างนี้เป็นเพียงการยกตัวอย่างให้เห็นภาพ ไม่ใช่โค้ดจริงของ DeepSeek:
{ "trigger_id": "trg_8f21", "source": "github.check_run", "thread_id": "thr_pr_412", "summary": "CI failed on PR #412: 3 of 118 tests red in payments/", "neighbours": ["trg_8f1c: review comment asking for retry logic"], "spilled": { "full_log": "GET /triggers/trg_8f21/payload (41 KB, truncated)" } } หากข้อความสรุปบรรทัดเดียวเพียงพอแล้ว Agent ก็ไม่ต้องเสียค่าใช้จ่ายในการประมวลผลไฟล์ล็อกขนาด 41 KB นั้นเลย แต่หากยังไม่พอ Agent ก็สามารถดึงล็อกฉบับเต็มมาดูผ่าน ID ได้เสมอ
Stable ID กับกราฟการอ้างอิง (Reference Graph)
Chain of Recall ทำงานได้เพราะ ID ไม่มีการเปลี่ยนแปลง ในการออกแบบของบทความดังกล่าว มีอ็อบเจกต์ 3 ประเภทที่ถือ ID เหล่านี้:
| อ็อบเจกต์ | คำอธิบาย | ตัวอย่าง ID |
|---|---|---|
| Trigger | อีเวนต์ที่ส่งเข้ามาหนึ่งรายการ: ข้อความแชต, เว็บฮุก หรือ CI callback | trg_8f21 |
| Thread | ชุดของอีเวนต์ที่เกี่ยวข้องกันใน PR เดียวกัน, หัวข้อในแชต หรือสายการสนทนาทางอีเมล | thr_pr_412 |
| Worker | เซสชัน Agent แยกเดี่ยวที่จัดการงานหนึ่งงาน | wkr_77a0 |
Trigger จะสังกัดอยู่ใน Thread, โดย Thread จะถูกถือครองโดย Worker ตั้งแต่หนึ่งตัวขึ้นไป และรายงานของ Worker จะอ้างอิงถึง Trigger ที่มันได้ดำเนินการ ลิงก์เชื่อมโยงเหล่านี้จะประกอบร่างกันเป็นกราฟการอ้างอิง (reference graph) ซึ่งกราฟนี้เองคือหน่วยความจำของ Agent
การระลึกความจำ (Recall) จะเกิดขึ้นโดยการไล่ตามลิงก์ไปทีละขั้น (hop) แทนที่จะต้องโหลดประวัติทั้งหมดหรือค้นหาข้อความที่มีความคล้ายคลึงกัน บทความคู่ขนานเรื่อง วิธีสร้าง Super-agent จะช่วยอธิบายว่า Trigger, Thread และ Worker อยู่ในตำแหน่งใดของการออกแบบระบบ Router และ Worker ฉบับเต็ม
เจาะลึกกระบวนการ Recall แบบหลายขั้นตอน (Multi-hop Walkthrough)
สมมุติว่าในวันศุกร์ เพื่อนร่วมทีมถาม Router ว่า: "ทำไมเราถึงเพิ่ม retry logic เข้าไปใน payments client?" ทั้งที่การพูดคุยเรื่องนี้เกิดขึ้นตั้งแต่วันอังคารและถูก spill ออกไปนานแล้ว ขั้นตอนการสืบค้นจะเป็นดังนี้:
- ขั้นที่ 1: ค้นหา Thread ในบริบทของ Router ยังคงเก็บรายการดัชนีบรรทัดเดียวไว้:
thr_pr_412: payments retry PR, merged Wedจากนั้นจึงเรียกข้อมูลGET /threads/thr_pr_412 - ขั้นที่ 2: ค้นหา Worker ข้อมูลใน Thread ระบุ
owner_workers: ["wkr_77a0"]Router จึงเรียกข้อมูลGET /workers/wkr_77a0และอ่านรายงานสรุปผลการทำงาน - ขั้นที่ 3: ค้นหาสาเหตุ รายงานดังกล่าวอ้างอิงถึง
trg_8f21ซึ่งเป็นเหตุการณ์ CI ล้มเหลว Router จึงดึงเพย์โหลดของ Trigger นั้นขึ้นมาอ่าน error เรื่อง timeout ในล็อก - ตอบคำถาม: "CI ของ PR #412 ล้มเหลวเนื่องจากเกิด payment timeout 3 ครั้งเมื่อวันอังคาร Worker wkr_77a0 จึงได้เพิ่ม retry พร้อม backoff เข้าไป และ PR ได้รับการ merge เรียบร้อยแล้วในวันพุธ"
เพียงการเรียกข้อมูลที่แม่นยำและมีขนาดเล็ก 3 ครั้ง โดยไม่มีการเดา และไม่มีข้อมูลใดต้องแช่ค้างอยู่ในพรอมต์ตลอด 3 วันเลย
Chain of Recall เทียบกับ Vector Memory และการทำ Summary
Chain of Recall ไม่ได้มาเพื่อแทนที่เทคนิคการจัดการหน่วยความจำแบบอื่น แต่มันตอบคำถามคนละโจทย์กัน โดยการค้นหาแบบเวกเตอร์ (Vector retrieval) ซึ่งเป็นที่รู้จักอย่างแพร่หลายจากเปเปอร์ว่าด้วย Retrieval-Augmented Generation (RAG) จะถามว่า "สิ่งใดที่มีความคล้ายคลึงกับสิ่งนี้?" ในขณะที่ Chain of Recall จะถามว่า "สิ่งใดที่มีความเชื่อมโยงกับสิ่งนี้?"
| Chain of Recall | Vector memory (RAG) | Rolling summaries | Memory MCP servers | |
|---|---|---|---|---|
| วิธีการดึงความจำ | ไล่ตามลิงก์ของ stable ID | ค้นหาความคล้ายคลึงจาก embedding | อ่านประวัติที่ถูกบีบอัดสรุป | เรียกใช้ memory tool ที่เซิร์ฟเวอร์เปิดไว้ให้ |
| ความแม่นยำ | ได้ระเบียนข้อมูลที่ตรงจุดตาม ID อย่างแน่นอน | เป็นการประมาณค่า จัดอันดับตามคะแนนความคล้าย | มีข้อมูลสูญหายตามธรรมชาติของการสรุป | ขึ้นอยู่กับที่จัดเก็บข้อมูลของเซิร์ฟเวอร์ |
| เหมาะสำหรับ | ประวัติอีเวนต์ที่เป็นลำดับ: PR, ทิกเก็ต, รอบการรัน CI | เอกสารชุดใหญ่, คำถามที่มีความคลุมเครือหรือไม่เจาะจง | เซสชันสั้น ๆ, การรักษาความต่อเนื่องของการแชต | ข้อมูลข้อเท็จจริงที่แชร์ร่วมกันข้ามเครื่องมือและเซสชัน |
| สิ่งที่จำเป็นต้องมี | ID ที่ไม่มีวันเปลี่ยน, API สำหรับดึงข้อมูลตาม ID | โมเดล embedding และระบบ index | ขั้นตอนการสรุปความ | MCP server ที่เปิดทำงานอยู่ |
| จุดอ่อน | ไม่สามารถค้นหาข้อมูลที่ไม่มีความเชื่อมโยงกันได้ | อาจพลาดข้อมูลหากใช้คำหรือการเรียบเรียงประโยคที่ต่างออกไป | สูญเสียรายละเอียดปลีกย่อย | มีอีกหนึ่งเซอร์วิสที่ต้องคอยดูแลระบบและดูแลความปลอดภัย |
ในทางปฏิบัติ คุณสามารถนำทั้งสองวิธีมาผสานกันได้ เช่น ใช้ vector index เพื่อค้นหา Thread ID เริ่มต้น จากนั้นให้ Chain of Recall ไล่หาข้อเท็จจริงที่แม่นยำต่อไป โดยแนวคิดลำดับชั้นหน่วยความจำสไตล์ระบบปฏิบัติการจากเปเปอร์ของ MemGPT ก็มีความใกล้เคียงกันอย่างมาก หากคุณต้องการเริ่มต้นด้วยระบบจัดเก็บข้อมูลสำเร็จรูป สามารถดูคู่มือของเราเกี่ยวกับการเพิ่มหน่วยความจำถาวรให้ AI Agent ด้วย MCP
อย่างไรก็ดี มีข้อจำกัด 2 ประการ: บทความดังกล่าวระบุว่าหน่วยความจำระยะยาว (long-term memory) ยังคงเป็นปัญหาปลายเปิด (open problem) และไม่ได้อ้างว่า Chain of Recall จะแก้ปัญหานี้ได้อย่างสมบูรณ์ และในการทดลองของบทความ การตัด Thread pass-through ออก ทำให้ cache-read tokens เพิ่มขึ้นจาก 23.2 ล้าน เป็น 31.8 ล้านต่อการรันหนึ่งครั้ง ซึ่งเพิ่มขึ้นประมาณ 37% นั่นคือต้นทุนที่เกิดขึ้นจากการขาดฟีเจอร์นี้ ไม่ใช่การประหยัดโทเคนจาก Chain of Recall เอง
ข้อกำหนดที่ API เบื้องหลังต้องรับประกัน
ทุกย่างก้าว (hop) ในกระบวนการไล่หานั้นคือการเรียกใช้ API หากมีครั้งใดที่คืนค่าเป็นระเบียนข้อมูลอื่นหรือไม่คืนค่ากลับมาเลย สายโซ่การค้นหาก็จะขาดลง ส่งผลให้ Agent ชะงักหรือต้องตอบคำถามด้วยการคาดเดา ดังนั้น API เบื้องหลัง Chain of Recall จึงต้องมีพันธสัญญา (contracts) 4 ประการ ดังนี้:
- ID ต้องไม่มีวันเปลี่ยน: ID ที่ส่งกลับมาในวันนี้จะต้องชี้ไปยังอ็อบเจกต์เดิมเสมอในเดือนถัดไป ห้ามนำ ID ที่ลบไปแล้วกลับมาใช้ซ้ำ และห้ามเปลี่ยนลำดับเลขใหม่หลังการย้ายระบบ (migration)
- การดึงข้อมูลตาม ID ต้องรวดเร็ว ประหยัด และตรงจุด:
GET /threads/{id}จะต้องคืนค่าเฉพาะระเบียนข้อมูลนั้นเพียงรายการเดียว ไม่ใช่การค้นหา และต้องไม่คืนค่าผลลัพธ์ที่ "ใกล้เคียงที่สุด" - ID ที่ไม่พบต้องแจ้งข้อผิดพลาดอย่างชัดเจน (Fail loudly): หากไม่พบ ID จะต้องส่งรหัส
404 Not Foundตามนิยามของ RFC 9110 พร้อมข้อมูล error body ที่ชัดเจน การส่ง200พร้อมกับอ็อบเจกต์ว่างเปล่าจะทำให้ Agent เข้าใจผิดว่ามีความจำนั้นอยู่จริง ทั้งที่ไม่มี - Cursor ต้องแบ่งหน้า (Page) ได้อย่างต่อเนื่องไม่ตกหล่น:
GET /threads/{id}/events?cursor=ต้องไล่อ่าน Thread ขนาดยาวได้ตามลำดับ ทุกอีเวนต์ต้องปรากฏเพียงครั้งเดียว ไม่มีข้อมูลใดถูกข้าม และ cursor ยังคงต้องใช้การได้แม้ว่าจะมีอีเวนต์ใหม่ ๆ ทยอยเข้ามาก็ตาม
พันธสัญญาเหล่านี้มักจะพังลงอย่างเงียบ ๆ ระหว่างการรีแฟกเตอร์โค้ด (refactor) จึงจำเป็นต้องมี Automated Test คอยตรวจสอบ นอกจากนี้ ข้อมูลที่เครื่องมือตอบกลับมา (tool response) ก็ต้องมีขนาดกะทัดรัดเช่นกัน ดังที่คู่มือของเราเกี่ยวกับ การตอบกลับของ Agent Tool กับ Context Window ได้อธิบายไว้อย่างละเอียด
ให้ Coding Agent ช่วยทดสอบ Recall API ด้วย Apidog CLI
ความน่าเชื่อถือของ Chain of Recall ขึ้นอยู่กับเอนด์พอยต์ fetch-by-ID โดยตรง และ Coding Agent ก็สามารถช่วยคุณเขียนและรันการตรวจสอบเหล่านั้นซ้ำได้ Apidog CLI มีชุดคำสั่งที่ช่วยให้ Agent สามารถกำหนดเอนด์พอยต์, สร้างเทสเคส และสั่งรันเทสได้โดยตรงจากเทอร์มินัล
ตั้งค่าเพียงครั้งเดียว:
npm install -g apidog-cli@latest apidog auth login --with-token <YOUR_TOKEN> npx -y skills add https://apidog.com # pick your agent when prompted จากนั้นเพียงป้อนพรอมต์ต่อไปนี้ให้กับ Claude Code, Codex หรือ Agent ตัวใดก็ตามที่คุณใช้งาน:
In Apidog project <projectId>, define three endpoints for our agent memory service: GET /threads/{id}, GET /threads/{id}/events?cursor=, and GET /workers/{id}. Then create test cases that check: 1. A thread_id returned by GET /threads/{id} resolves to the same record on a second call, and each owner worker ID resolves via GET /workers/{id}. 2. An unknown thread ID and an unknown worker ID both return 404. 3. Paging GET /threads/{id}/events with the cursor returns every event once, in order, with no gaps. Run them against the staging environment and summarize any failures. Agent จะอ่าน schema ของแต่ละ payload ตรวจสอบความถูกต้องของ JSON ก่อนที่จะเริ่มเขียนข้อมูล จากนั้นจะสร้างและรันเทสเคสต่าง ๆ:
apidog cli-schema get endpoint-create apidog cli-schema validate endpoint-create --file thread-get.json apidog endpoint create --project <projectId> --file thread-get.json apidog cli-schema get test-case-create apidog cli-schema validate test-case-create --file id-resolves.json apidog test-case create --project <projectId> --file id-resolves.json apidog run --test-case <caseId> --project <projectId> -e <envId> -r cli,json รีพอร์ตเตอร์แบบ cli จะแสดงผลว่า assertion แต่ละตัวผ่านหรือล้มเหลวบนเทอร์มินัล ส่วนรีพอร์ตแบบ json จะสร้างไฟล์ที่ Agent สามารถอ่านเพื่อวิเคราะห์ได้ว่าขั้นตอน (hop) ใดเกิดปัญหา หาก ID ไม่สามารถค้นหาข้อมูลได้หลังจากการย้ายระบบ คุณจะเห็นข้อผิดพลาดนี้ในรายงานทันทีก่อนที่ Agent จะเริ่มตอบคำถามจากสายโซ่ข้อมูลที่เสียหาย หากต้องการรันการตรวจสอบเดียวกันนี้ในทุกครั้งที่มีการ merge ให้เปลี่ยนรีพอร์ตเตอร์เป็น -r junit โดยบทความแนะนำ การใช้งาน Apidog CLI ร่วมกับ Claude Code ของเราจะแสดงให้เห็นวงจรการทำงาน (loop) ของ Agent แบบครบวงจร
Chain of Recall เหมาะกับการใช้งานในกรณีใด
เลือกใช้ Chain of Recall เมื่อหน่วยความจำของ Agent เป็นประวัติของเหตุการณ์ที่เกิดขึ้นแยกจากกันแต่สามารถเชื่อมโยงกันได้ เช่น PR, ทิกเก็ต, รอบการรัน CI, การสนทนาซัพพอร์ต หรือการดีพลอย ซึ่งสิ่งเหล่านี้มักจะมี ID ในตัวเองอยู่แล้ว และคำถามที่ผู้คนมักจะถามถึงสิ่งเหล่านี้ (เช่น "ทำไมเราถึงทำแบบนี้?", "เกิดอะไรขึ้นหลังจากนั้น?") ก็มักจะเป็นคำถามเกี่ยวกับความเชื่อมโยงของเหตุการณ์
เลือกใช้การค้นหาแบบเวกเตอร์ (Vector retrieval) สำหรับเอกสารที่ไม่มีโครงสร้างแน่นอนและคำถามที่ไม่มีขอบเขตตายตัว และเลือกใช้การสรุปย่อ (Summaries) สำหรับเซสชันสั้น ๆ ที่การสูญเสียรายละเอียดปลีกย่อยไม่ใช่ปัญหา
Chain of Recall มีจุดเริ่มต้นมาจากบทความหนึ่งชิ้นและตัวต้นแบบหนึ่งตัว ซึ่งเป็นแนวคิดที่ชัดเจนและทดสอบได้ ไม่ใช่ผลิตภัณฑ์สำเร็จรูป สำหรับเครื่องมือที่เป็นเบื้องหลังของบทความนี้ สามารถเริ่มต้นอ่านเพิ่มเติมได้ที่ DeepSeek Harness คืออะไร
คำถามที่พบบ่อย (FAQ)
Chain of Recall เป็นฟีเจอร์ของ DeepSeek ที่สามารถเปิดใช้งานได้เลยหรือไม่? ไม่ใช่ นี่เป็นคำที่มาจากบทความเกี่ยวกับ Super-agent ของ DeepSeek Harness และบางส่วนอยู่ในปลั๊กอินต้นแบบที่ผู้เขียนระบุว่าไม่ใช่ผลิตภัณฑ์อย่างเป็นทางการของ DeepSeek คุณจะต้องนำรูปแบบการออกแบบ (pattern) นี้ไปพัฒนาขึ้นมาเอง
ความแตกต่างระหว่าง Inline กับ Spill คืออะไร? Inline คือการเก็บ ID, สรุปย่อสั้น ๆ และข้อความข้างเคียงไว้ในพรอมต์ ส่วน Spill คือการตัดทอนเนื้อหาส่วนที่เหลือออก แล้วทิ้งพอยน์เตอร์ไว้ให้ Agent ใช้ดึงข้อมูลตาม ID เมื่อจำเป็น
Chain of Recall มาแทนที่ RAG หรือไม่? ไม่ใช่ RAG ทำหน้าที่ค้นหาเนื้อหาที่มีความคล้ายคลึงกัน ส่วน Chain of Recall จะไล่ตามลิงก์ของ ID ที่แม่นยำ ซึ่ง Agent จำนวนมากเลือกใช้งานร่วมกันทั้งสองแบบ
Chain of Recall ช่วยลดโทเคนลงได้ 37% จริงหรือไม่? ไม่ใช่ ตัวเลข 37% นั้นเป็นเรื่องของ Thread pass-through โดยในการทดลองของบทความ การตัดฟีเจอร์ดังกล่าวออกทำให้ cache-read tokens เพิ่มขึ้นจาก 23.2 ล้าน เป็น 31.8 ล้านต่อรอบการรัน บทความไม่ได้ระบุถึงการประหยัดโทเคนจาก Chain of Recall โดยตรง
API แบบ fetch-by-ID ควรตอบกลับอย่างไรหากไม่พบ ID? ควรส่งรหัส 404 Not Found พร้อมข้อความ error body ที่ชัดเจน การส่ง 200 พร้อมอ็อบเจกต์ว่างเปล่าจะทำให้ Agent หลงเชื่อว่ามีระเบียนข้อมูลนั้นอยู่จริง ซึ่งจะทำให้สายโซ่การสืบค้นข้อมูลล้มเหลวโดยไม่รู้ตัว
เราจะทดสอบได้อย่างไรว่า API จัดการหน่วยความจำของ Agent ยังคงทำงานได้เสถียร? ให้เขียนเทสเคสเพื่อตรวจสอบยืนยันว่า ID ที่ได้จากการเรียกครั้งหนึ่งสามารถนำไปค้นหาต่อในครั้งถัดไปได้จริง, ID ที่ไม่มีอยู่จริงจะต้องส่งค่า 404 และ cursor ต้องแบ่งหน้าได้ครบถ้วนโดยไม่มีข้อมูลตกหล่น ซึ่ง Coding Agent สามารถช่วยสร้างและรันการทดสอบเหล่านี้ผ่าน Apidog CLI ในทุก ๆ การ merge ได้




