---
read_when:
    - คุณต้องการให้เอเจนต์ตรวจพบเมื่อมนุษย์หรือเอเจนต์อื่นเปลี่ยนแปลงเซสชันโดยที่เอเจนต์นั้นไม่รู้ตัว
    - คุณกำลังดีบักการแจ้งเตือนการเปลี่ยนแปลงสถานะ เคอร์เซอร์การเฝ้าดู หรือ `changesSince` ของ `session_status`
    - คุณต้องการทำความเข้าใจว่าเอเจนต์หลักรักษาการซิงค์กับเซสชันลูกอย่างไร
sidebarTitle: Session state awareness
summary: 'บันทึกสัญญาณสถานะเซสชันแบบคงทน: เวอร์ชันสถานะ ตัวเฝ้าดู การแจ้งเตือนสถานะล้าสมัย และการปรับสถานะให้สอดคล้องกัน'
title: การรับรู้สถานะเซสชัน
x-i18n:
    generated_at: "2026-07-16T19:02:54Z"
    model: gpt-5.6
    postprocess_version: locale-links-v1
    prompt_version: 32
    provider: openai
    source_hash: bb4126a0802e1ca4418f225c792490493a78886089b81c3b4567f72090ce34f4
    source_path: concepts/session-state.md
    workflow: 16
---

เมื่อหลายเซสชันทำงานกับปัญหาเดียวกัน ไม่ว่าจะเป็นผู้จัดการที่มอบหมายงานให้เซสชันลูก มนุษย์ที่เข้าไปยังเซสชันผู้ปฏิบัติงานโดยตรง หรือเอเจนต์สองตัวที่ประสานงานกันผ่าน [`sessions_send`](/th/concepts/session-tool) แต่ละเซสชันจะสร้างข้อสันนิษฐานเกี่ยวกับเซสชันอื่น ข้อสันนิษฐานเหล่านั้นจะล้าสมัยทันทีที่ผู้ดำเนินการรายอื่นเข้ามาแทรกแซง การรับรู้สถานะของเซสชันคือกลไกที่ตรวจพบการแทรกแซง แจ้งเซสชันที่ได้รับผลกระทบหนึ่งครั้ง และเปิดทางให้เซสชันนั้นตามข้อมูลให้ทันได้อย่างประหยัดก่อนดำเนินการ

องค์ประกอบสามส่วนทำงานร่วมกัน:

1. **บันทึกสัญญาณแบบคงทน** บันทึกการเปลี่ยนแปลงสถานะที่เลือกไว้แยกตามเซสชัน
2. **ตัวเฝ้าดู** เก็บเคอร์เซอร์แยกตามเป้าหมายและรับการแจ้งเตือนสถานะล้าสมัยแบบรวมเพียงหนึ่งครั้ง
3. **การปรับสถานะให้สอดคล้อง** ดึงเฉพาะส่วนต่างที่แน่นอนผ่าน `session_status` โดยใช้ `changesSince`

## บันทึกสัญญาณ

OpenClaw เพิ่มเหตุการณ์ที่มีชนิดกำกับลงในฐานข้อมูลสถานะที่ใช้ร่วมกัน (`session_state_events`) เมื่อเซสชันที่ถูกเฝ้าดูมีการเปลี่ยนแปลงอย่างมีนัยสำคัญ เหตุการณ์ประกอบด้วยข้อมูลเมตาและข้อมูลสรุปหนึ่งบรรทัด โดยไม่มีเนื้อหาข้อความรวมอยู่ด้วยเด็ดขาด

| ชนิด                   | บันทึกเมื่อ                                            | แจ้งตัวเฝ้าดู |
| ---------------------- | -------------------------------------------------------- | ----------------- |
| `human_direct_message` | มนุษย์ส่งรอบสนทนาโดยตรงไปยังเซสชันที่ถูกเฝ้าดู       | ใช่               |
| `upstream_missing`     | แหล่งต้นทางของเซสชันที่รับเข้ามาหายไป          | ใช่               |
| `goal_changed`         | สถานะเป้าหมายของเซสชันถูกสร้าง อัปเดต หรือล้าง | ใช่               |
| `child_spawned`        | มีการสร้างเซสชันลูกของเอเจนต์ย่อยหรือ ACP              | ไม่ (ตั้งค่าเริ่มต้นให้เคอร์เซอร์) |
| `run_completed`        | การเรียกใช้เซสชันลูกสิ้นสุดโดยสำเร็จ                            | ไม่ (บันทึกเท่านั้น)     |
| `run_failed`           | การเรียกใช้เซสชันลูกล้มเหลว หมดเวลา หรือถูกยกเลิก            | ไม่ (บันทึกเท่านั้น)     |
| `compacted`            | ประวัติของเซสชันถูกย่อ                       | ไม่ (บันทึกเท่านั้น)     |
| `adopted`              | เซสชันแค็ตตาล็อกถูกรับเข้ามาใน OpenClaw               | ไม่ (บันทึกเท่านั้น)     |

แต่ละเหตุการณ์ระบุผู้ดำเนินการ (`human`, `agent` หรือ `system`) การเรียกใช้เซสชันลูกที่ถูกยกเลิกและหมดเวลาจะถูกบันทึกเป็นความล้มเหลว โดยคงผลลัพธ์ที่แน่นอน (`cancelled`, `timeout` หรือ `error`) ไว้ในเพย์โหลดของเหตุการณ์

**เวอร์ชันสถานะ** ของเซสชันคือหมายเลขลำดับสูงสุดในบันทึกของเซสชันนั้น โดยติดตามไว้ในส่วนหัวแบบคงทนประจำเซสชันซึ่งยังคงอยู่หลังการตัดทอน แถว `sessions_list` จะมี `stateVersion` เมื่อเซสชันบันทึกการเปลี่ยนแปลงแล้ว ส่วน `session_status` จะรายงานค่านี้เสมอ

ชนิดที่ใช้บันทึกเท่านั้นมีไว้สำหรับประวัติการปรับสถานะให้สอดคล้อง ไม่ใช่การแจ้งเตือน การส่งการแจ้งเตือนตามปกติเมื่อการเรียกใช้เซสชันลูกเสร็จสมบูรณ์ยังคงเป็นหน้าที่ของ [การประกาศจากเอเจนต์ย่อย](/th/tools/subagents) และบันทึกสัญญาณจะไม่สร้างรายการซ้ำ

## ตัวเฝ้าดู

ตัวเฝ้าดูคือเซสชันที่เก็บเคอร์เซอร์ (`session_watch_cursors`) ของเป้าหมาย เคอร์เซอร์มาจากสองแหล่ง:

- **โดยนัย (ขอบเขตการสร้างเซสชัน)** เมื่อเซสชันสร้างเอเจนต์ย่อยหรือเซสชันลูกของ ACP เคอร์เซอร์ของเซสชันแม่จะได้รับค่าเริ่มต้นโดยอัตโนมัติเป็นเวอร์ชันขณะสร้างเซสชันลูก เซสชันแม่ไม่ต้องสมัครรับข้อมูลด้วยตนเอง
- **โดยชัดแจ้ง (`sessions_send watch: true`)** ผู้ประสานงานใดๆ สามารถเฝ้าดูเป้าหมายที่ไม่ได้สร้างขึ้นจากตนเองได้ โดยส่ง `watch: true` ให้กับ `sessions_send` และหลังจากส่งข้อความสำเร็จ ผู้ส่งจะได้รับการลงทะเบียนเป็นตัวเฝ้าดูเซสชันที่ได้รับข้อความจริง การลงทะเบียนเริ่มต้นที่เวอร์ชันสถานะปัจจุบันของเป้าหมาย ประวัติก่อนหน้านั้นจะไม่ก่อให้เกิดการแจ้งเตือน ผลลัพธ์ของเครื่องมือจะรายงาน `watched: true|false` เมื่อตั้งค่าพารามิเตอร์นี้

ข้อมูลประจำตัวของตัวเฝ้าดูต้องเป็นคีย์เซสชันที่ระบุเอเจนต์ ภายใต้ `session.scope="global"` คีย์ `global` ที่ใช้ร่วมกันมีความกำกวมระหว่างเอเจนต์ ดังนั้นเซสชันดังกล่าวจะมีบันทึกแบบคงทนและ `changesSince` แต่ไม่มีการแจ้งเตือนเชิงรุก

การเฝ้าดูจะล้างตัวเอง แถวเคอร์เซอร์จะหมดอายุตามระยะเวลาเก็บรักษาบันทึกสัญญาณ ถูกนำออกเมื่อเซสชันตัวเฝ้าดูรีเซ็ต และถูกลบเมื่อเซสชันฝ่ายใดฝ่ายหนึ่งถูกลบ ไม่มีคำสั่งยกเลิกการเฝ้าดูใน v1

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

หากแหล่งต้นทางของเซสชันที่รับเข้ามาถูกลบจากภายนอก การตรวจสอบว่าไม่พบติดต่อกันสามครั้ง (ประมาณสามรอบการตรวจสอบ) จะสร้างสัญญาณ `upstream_missing` หนึ่งครั้งให้ตัวเฝ้าดูและนำลิงก์ต้นทางออก เมื่อดำเนินเซสชันแค็ตตาล็อกต่ออีกครั้ง ระบบจะสร้างลิงก์ใหม่

## การแจ้งเตือน: หนึ่งครั้ง ไม่ใช่หลายครั้ง

เมื่อเกิดเหตุการณ์ที่เข้าเกณฑ์การแจ้งเตือนและเคอร์เซอร์ของตัวเฝ้าดูล้าหลัง ตัวเฝ้าดูจะได้รับการแจ้งเตือนจากระบบหนึ่งครั้งในรอบสนทนาถัดไป:

```
เซสชัน "agent:main:subagent:child" มีการเปลี่ยนแปลง (ผู้ดำเนินการรายอื่น) ปรับสถานะให้สอดคล้องก่อนดำเนินการ: session_status sessionKey "agent:main:subagent:child" changesSince 12
```

ตัวเฝ้าดูที่เป็นเซสชันหลักจะถูกปลุกทันทีผ่านการปลุกด้วย Heartbeat ส่วนตัวเฝ้าดูที่เป็นเอเจนต์ย่อยซ้อนกันจะได้รับการแจ้งเตือนในรอบสนทนาถัดไป

โปรโตคอลนี้ออกแบบมาเพื่อป้องกันสแปมโดยเจตนา:

- **การแจ้งเตือนที่รอดำเนินการหนึ่งรายการต่อคู่ตัวเฝ้าดู/เป้าหมาย** ข้อความแจ้งเตือนจะคงค่าแบบไบต์ต่อไบต์ขณะรอดำเนินการ และคิวเหตุการณ์ระบบจะกำจัดรายการซ้ำตามข้อความนั้น ดังนั้นแม้เป้าหมายเดียวกันจะเปลี่ยนแปลงอย่างรวดเร็วถึงยี่สิบครั้ง ก็ยังสร้างเพียงหนึ่งบรรทัดในพรอมต์ของตัวเฝ้าดู
- **วอเตอร์มาร์กที่ตรึงไว้** เคอร์เซอร์จะตรึงตำแหน่งที่แจ้งเตือนเมื่อการแจ้งเตือนถูกเพิ่มลงในคิว เหตุการณ์สำคัญที่เกิดขึ้นต่อจากนั้นจะเลื่อนเฉพาะวอเตอร์มาร์กของเหตุการณ์สำคัญ โดยไม่แจ้งเตือนซ้ำ
- **รับทราบเมื่อดึงออกจากคิว และเปิดใหม่เฉพาะเมื่อมีงานแทรกเข้ามา** เมื่อรอบสนทนาของตัวเฝ้าดูใช้การแจ้งเตือนแล้ว เคอร์เซอร์จะเลื่อนไปข้างหน้า หากมีเหตุการณ์สำคัญเพิ่มเติมเกิดขึ้นระหว่างการเพิ่มลงในคิวและการดึงออกจากคิว ระบบจะเปิดการแจ้งเตือนใหม่เพียงหนึ่งรายการสำหรับส่วนที่เหลือ
- **ระงับการแจ้งเตือนตนเอง** ตัวเฝ้าดูจะไม่ได้รับการแจ้งเตือนเกี่ยวกับเหตุการณ์ที่ตนเองเป็นผู้ก่อ
- **การกู้คืนหลังรีสตาร์ต** การแจ้งเตือนที่รอดำเนินการอยู่ในคิวในหน่วยความจำ การกวาดตรวจเมื่อเริ่มต้นระบบจะสร้างการแจ้งเตือนเหล่านั้นขึ้นใหม่จากเคอร์เซอร์แบบคงทนหลัง Gateway รีสตาร์ต

## การปรับสถานะให้สอดคล้อง

การแจ้งเตือนระบุสิ่งที่ตัวเฝ้าดูต้องทำอย่างชัดเจน `session_status` พร้อม `changesSince: <version>` จะส่งคืนเหตุการณ์ที่มีชนิดกำกับหลังจากเวอร์ชันนั้น (สูงสุด 200 รายการ) โดยไม่เลื่อนเคอร์เซอร์ใดๆ:

```json
{
  "stateVersion": 19,
  "stateChanges": {
    "events": [
      {
        "sequence": 14,
        "kind": "human_direct_message",
        "actorType": "human",
        "summary": "ข้อความจากมนุษย์ผ่าน Telegram"
      },
      { "sequence": 19, "kind": "goal_changed", "actorType": "human", "summary": "อัปเดตเป้าหมายแล้ว" }
    ],
    "historyGap": false
  }
}
```

`historyGap: true` หมายความว่าเวอร์ชันที่ร้องขออยู่ก่อนประวัติที่ยังเก็บรักษาไว้ ให้รีเฟรชสถานะทั้งหมดของเซสชัน (`sessions_history`, `session_status`) แทนการถือว่าการตอบกลับเป็นส่วนต่างที่แน่นอน สัญญาณช่องว่างนี้แม่นยำ เพราะมาจากวอเตอร์มาร์กที่ถูกตัดทอนประจำเซสชัน ไม่ได้อนุมานจากการคำนวณเลขลำดับ

## พื้นที่จัดเก็บและขีดจำกัด

ประวัติอยู่ในฐานข้อมูลสถานะที่ใช้ร่วมกัน โดยจำกัดไว้ที่ 30 วันและ 50,000 แถว ส่วนหัวประจำเซสชันยังคงเพิ่มขึ้นทางเดียวหลังการตัดทอน การบันทึกเป็นแบบพยายามให้ดีที่สุด การเพิ่มข้อมูลที่ล้มเหลวจะถูกบันทึกไว้และจะไม่ทำให้รอบสนทนาต้นทางล้มเหลว ดังนั้น `stateVersion` จึงเป็นส่วนหัวของบันทึกสัญญาณ ไม่ใช่เวอร์ชันการบันทึกข้อมูลการเปลี่ยนแปลงแบบทรานแซกชัน

ขีดจำกัดปัจจุบัน:

- การส่งการแจ้งเตือนถือว่ามีกระบวนการ Gateway หนึ่งกระบวนการเป็นเจ้าของฐานข้อมูลสถานะที่ใช้ร่วมกัน Gateway หลายตัวใช้บันทึกแบบคงทนและ `changesSince` ร่วมกันได้ แต่ v1 จะไม่ส่งการแจ้งเตือนข้ามกระบวนการ
- เหตุการณ์ Compaction ครอบคลุมเจ้าของ Compaction ของรันไทม์แบบฝัง แต่ Compaction ที่เกิดขึ้นเฉพาะในเนทีฟฮาร์เนสยังไม่ได้รับการบันทึกอย่างครบถ้วน
- รายละเอียดเพย์โหลดของผลลัพธ์ที่ถูกยกเลิกในปัจจุบันสร้างโดยการเรียกใช้เซสชันลูกของ ACP ส่วนการยกเลิกเอเจนต์ย่อยแบบเนทีฟจะแสดงเป็นความล้มเหลวทั่วไป
- การตรวจจับเสียงสะท้อนจากตนเองในแหล่งต้นทางจะเปรียบเทียบข้อความผู้ใช้ที่ปรับรูปแบบแล้ว พรอมต์ภายนอกที่ตรงกับหนึ่งใน 10 ข้อความล่าสุดจากผู้ใช้ฝั่ง OpenClaw ของเซสชันจะถือว่าเป็นเสียงสะท้อนจากตนเอง
- แถว JSONL ภายในเครื่องของ Claude เพียงแถวเดียวที่มีขนาดใหญ่กว่าขีดจำกัดการสแกน 1 MiB ต่อรอบ จะขัดขวางเคอร์เซอร์ของเซสชันนั้นใน v1 ระบบจะไม่ข้ามไบต์ที่ยังไม่ได้จำแนกเด็ดขาด
- การตรวจสอบ Claude บน Node ที่จับคู่กันจะจำแนกรายการทรานสคริปต์ล่าสุด 50 รายการต่อรอบ ข้อมูลจำนวนมากกว่านี้อาจอยู่นอกหน้าต่างการสแกนของ v1
- การอ่านประวัติ Claude บน Node ที่จับคู่กันจะไม่แสดงผลลัพธ์ที่ยืนยันได้ว่าไม่พบเธรด ดังนั้นการลบ Claude ระยะไกลจึงไม่ถูกจำแนกเป็น `upstream_missing` ใน v1
- เซสชันแค็ตตาล็อกที่ยังไม่ได้รับเข้ามาจะยังคงอยู่นอกชั้นการรับรู้ใน v1
- เซสชันที่รับเข้ามาก่อนมีฟีเจอร์นี้จะไม่มีลิงก์ต้นทาง ให้ดำเนินเซสชันเหล่านั้นต่อจากแค็ตตาล็อกหนึ่งครั้งเพื่อเริ่มการตรวจสอบต้นทาง
- ลิงก์ต้นทางถือว่าคีย์ของแต่ละเซสชันที่รับเข้ามาจับคู่กับเอเจนต์เจ้าของเพียงหนึ่งตัว (การรับเข้ามาใช้เอเจนต์ของพื้นที่จัดเก็บเริ่มต้น) v1 จะไม่ตรวจสอบการรับเธรดภายนอกเดียวกันเข้ามาโดยเอเจนต์หลายตัว

## ที่เกี่ยวข้อง

- [เครื่องมือเซสชัน](/th/concepts/session-tool) — `sessions_send`, `session_status`, `sessions_list`
- [เอเจนต์ย่อย](/th/tools/subagents) — ขอบเขตการสร้างเซสชันและการประกาศเมื่อเสร็จสมบูรณ์
- [Heartbeat](/th/gateway/heartbeat) — วิธีที่การแจ้งเตือนในคิวปลุกเซสชันหลัก
- [การจัดการเซสชัน](/th/concepts/session) — คีย์เซสชัน ขอบเขต และวงจรชีวิต
