RAG ลบเอกสารแล้วอย่ากลับมา: ใช้ Revision และ Tombstone กันงานเก่าเขียนทับ

งานเก่า revision 8 ถูกปฏิเสธเมื่อเก็บสถานะลบ DELETE revision 9 ไว้ ดัชนีเอกสารยังว่าง

คำตอบสั้น: บันทึก revision ล่าสุดไว้กับสถานะเอกสาร แม้เอกสารนั้นถูกลบแล้ว ทุก event ต้องผ่านจุดตรวจเดียวกัน: รับ revision ที่สูงกว่า, ปฏิเสธ revision ที่ต่ำกว่าโดยไม่เปลี่ยน state และจัดการกรณี revision เท่ากันตามนโยบายที่ระบุไว้ วิธีนี้ป้องกันงาน ingest หรือ reindex เก่าทำให้เอกสารกลับมาได้ เฉพาะเมื่อ revision มาจากแหล่งที่เชื่อถือได้และข้อมูลลบยังถูกเก็บไว้นานพอเทียบกับ event ที่อาจ replay.

บทเรียนนี้ฝึกทักษะเดียว: ออกแบบ revision gate ต่อเอกสารเพื่อกัน event ที่มาช้าเขียนทับการตัดสินใจใหม่กว่า ผู้อ่านจะไล่ event UPSERT 7 → DELETE 9 → UPSERT 8 แล้วกำหนดผลที่รับได้ใน Python mock แบบไม่เชื่อมบริการจริง.

งานเก่า revision 8 ถูกปฏิเสธเมื่อเก็บสถานะลบ DELETE revision 9 ไว้ ดัชนีเอกสารยังว่าง
ภาพแนวคิดจำลอง: เก็บ revision ของสถานะลบเพื่อปฏิเสธงาน reindex เก่า ไม่ใช่ผลทดสอบฐานข้อมูลจริง · ภาพเต็ม

ปัญหา: ลบแล้ว แต่งานเก่ายังมาถึงได้

งาน reindex อาจอ่านเอกสารก่อนถูกลบ แต่ใช้เวลาประมวลผลนานกว่า ขณะที่คำสั่งลบเดินทางถึง index ก่อน ถ้าปลายทางเชื่อทุก UPSERT ที่มาถึง โดยไม่เทียบกับสถานะล่าสุด งานเก่าอาจเขียนเนื้อหากลับเข้า RAG

ตัวอย่าง: ดัชนีเคยรับ UPSERT revision 7 แล้ว worker อีกงานถือ snapshot revision 8 อยู่ ระหว่างนั้น source ลบเอกสารที่ revision 9 และ DELETE มาถึงก่อน UPSERT revision 8 จากงานเก่า เวลามาถึงบอกเพียงลำดับการส่งถึงปลายทาง ไม่ได้บอกว่า event ใดสะท้อนการตัดสินใจใหม่กว่าของ source หากเก็บเพียงว่า “ไม่มีเอกสารนี้แล้ว” โดยลืม revision 9 ระบบอาจแยก event 8 ที่เก่าจากการสร้างเอกสารใหม่ไม่ออก

UPSERT 7 ตามด้วย DELETE 9 แล้ว UPSERT 8 มาช้า ถูกปฏิเสธเพราะ 8 ต่ำกว่า 9
SIMULATION: 7 → 9 → 8 คือลำดับที่งานมาถึง ไม่ใช่ลำดับความใหม่ของข้อมูล หลัง DELETE 9 เนื้อหาว่างแต่ยังเก็บ revision 9 · ภาพเต็ม

แบบจำลอง state และกติกาการตัดสิน

Revision คือหมายเลขลำดับที่ source กำหนดให้การเปลี่ยนแปลงหนึ่งครั้งของเอกสารเดียวกัน โดยสมมติว่าเลขสูงกว่าหมายถึงการตัดสินใจใหม่กว่า Revision gate เป็นจุดตรวจที่เปรียบเทียบ event กับ state ล่าสุดก่อนเขียนข้อมูล Tombstone คือสถานะที่จำว่าเอกสารถูกลบที่ revision ใด แม้เนื้อหาจะไม่ถูกส่งคืนให้การค้นหาแล้วก็ตาม

ตัวอย่างเก็บ state ใน map เดียวต่อ doc_id: revision, operation และข้อความ หากเป็น DELETE ให้ข้อความเป็น None การเก็บ revision กับสถานะลบไว้ด้วยกันทำให้ event เก่าถูกเปรียบเทียบกับ revision 9 แทนการมอง ID ราวกับไม่เคยมีอยู่

กรณีผลของตัวอย่างนี้state
ID ใหม่APPLIEDสร้าง state
revision ต่ำกว่าSTALEคงเดิม
revision เท่ากัน, operation และข้อความเหมือนกันDUPLICATEคงเดิม
revision เท่ากัน, operation หรือข้อความต่างกันCONFLICTคงเดิม รอตรวจสอบ
revision สูงกว่าAPPLIEDแทน state ล่าสุด

การแยก DUPLICATE กับ CONFLICT เป็นนโยบายของ mock นี้: retry ที่เหมือนกันไม่ต้องเปลี่ยน state แต่ข้อมูลต่างกันที่ revision เดียวกันไม่ควรถูกตัดสินด้วย arrival order ส่วน Elasticsearch external versioning กำหนดให้ version ที่ส่งมาต้องมากกว่า version ปัจจุบัน จึงปฏิเสธทั้ง version ที่เท่ากันและต่ำกว่า อย่าอ้างว่า Elasticsearch ใช้กติกา equal-version แบบเดียวกับ mock.

เมื่อมี state เดิม revision ต่ำกว่าคืน STALE เท่ากันแยก DUPLICATE หรือ CONFLICT สูงกว่าจึงแทน state
นโยบายของ mock เมื่อมี state เดิม: revision สูงกว่าจึงแทน state ส่วน equal revision แยกตาม op/text; ID ใหม่รับ event แรกที่ valid ไม่ใช่กฎ equal-version ของ Elasticsearch · ภาพเต็ม

Worked example: UPSERT 7 → DELETE 9 → UPSERT 8

เริ่มจาก map ว่าง ส่ง UPSERT(guide-1, 7, “คู่มือเดิม”): ผล APPLIED และ lookup คืนข้อความ ต่อมา DELETE(guide-1, 9): ผล APPLIED; state ยังคงเก็บ ID, revision 9 และ operation DELETE แต่ไม่เก็บข้อความให้ค้นได้ lookup จึงคืน None

เมื่อ UPSERT(guide-1, 8, “งาน reindex ที่มาช้า”) มาถึง gate เปรียบเทียบ 8 กับ 9 แล้วตอบ STALE โดยไม่เขียน state ซ้ำ ผลสุดท้ายยังเป็น revision 9, DELETE และ lookup ยังว่าง นี่คือเกณฑ์สำคัญ: การลบไม่ได้ทำให้ revision ล่าสุดหายไปพร้อมเนื้อหา

หาก source ที่มีอำนาจส่ง UPSERT revision 10 ภายหลัง ตัวอย่างจะรับเพราะใหม่กว่า ซึ่งอาจเป็นการ restore ที่ตั้งใจไว้ แต่ gate ยืนยันได้เพียงลำดับ revision ไม่ได้ยืนยันสิทธิ์ในการกู้คืน ผู้ส่งต้องผ่าน authentication และกฎอนุญาต restore ที่ขอบเขต ingestion ก่อน ห้ามให้ consumer แก้ revision เก่าให้สูงขึ้นเพื่อให้ event ผ่าน.

เปรียบเทียบ UPSERT 8 หลังลบ revision 9 เก็บ tombstone แล้วปฏิเสธ แต่ลืมสถานะแล้วงานเก่าอาจกลับมา
SIMULATION: งานเก่าเดียวกันถูกปฏิเสธเมื่อ retained state เป็น DELETE 9; หากลืม marker ตัวอย่างอาจรับเป็น ID ใหม่ ส่วน revision สูงกว่ายังอาจเป็นการคืนโดยเจตนาที่ต้องอนุญาตแยก · ภาพเต็ม

ขอบเขตของ revision

ตัวอย่างสมมติว่า source ที่เชื่อถือได้เป็นผู้กำหนด revision บวกที่เพิ่มขึ้นต่อเอกสาร โค้ดตรวจรูปแบบและช่วงตัวเลข แต่ยืนยันไม่ได้ว่า producer มีสิทธิ์ออก revision ดังกล่าวหรือไม่ ต้องยืนยันตัวตนและ authorization ก่อนเรียก gate.

ตัวอย่างมี source เดียวและไม่มีการ reset counter, epoch หรือหลาย writer หากเลข revision เริ่มใหม่หลังย้ายระบบ ต้องกำหนด generation หรือกติกา ordering เพิ่มเติม อย่าใช้เวลาที่รับ event เป็นลำดับแทนโดยไม่จัดการ clock skew, timestamp ซ้ำ และผู้เขียนหลายราย.

โครง mock และ acceptance cases

code/revision_gate.py ใช้ Python standard library เก็บ Event, State แบบ immutable และ RevisionIndex ใน dictionary เดียว ก่อนเปลี่ยน state จะตรวจ event ให้ครบ: ID เป็น ASCII 1–80 ตัว เริ่มด้วยตัวอักษรหรือตัวเลข และอาจมี ., _, :, - ในตำแหน่งถัดไป; revision เป็น integer บวกไม่เกิน 263−1 (ปฏิเสธ Boolean); operation เป็น UPSERT หรือ DELETE; ข้อความ UPSERT เป็น UTF-8 ที่ไม่ว่างและไม่เกิน 65,536 bytes; DELETE ต้องไม่มีข้อความ

การตรวจทั้งหมดก่อน mutation หมายความว่า event ผิดรูปแบบจะไม่แก้ state แม้เลขสูงกว่าเดิม โค้ดคืน APPLIED, STALE, DUPLICATE หรือ CONFLICT ให้ผู้เรียกแยกผลได้ ชุดทดสอบที่เขียนไว้ครอบคลุมลำดับหลัก, equal-version, delete ก่อนพบ ID, restore, ID แยก, ชนิดข้อมูลผิด, ขอบเขต ID/revision และ UTF-8 กรณีเหล่านั้นยังไม่ถูกรัน

ตัวอย่างเป็น process เดียวใน memory: ไม่มี persistence, transaction, locking, หรือการรับประกันเมื่อหลาย worker ทำงานพร้อมกัน การเขียน dictionary ครั้งเดียวเป็นภาพประกอบของ state transition ไม่ใช่ atomic compare-and-set ข้ามเครื่อง ระบบจริงต้องอ่าน revision ล่าสุด เปรียบเทียบ และ commit revision, operation และเนื้อหาที่เกี่ยวข้องภายใต้ transaction หรือ conditional-write เดียว การเขียนเนื้อหาและ marker แยกกันโดยไม่มี atomicity อาจเปิดช่องให้ event เก่าแทรก

ผลลัพธ์แต่ละแบบควรมีการจัดการต่างกัน: APPLIED หมายถึง mock แทน state แล้ว ไม่ใช่สิทธิ์ให้ worker เก่าเขียน vector ภายหลังโดยไม่ตรวจ revision อีก; STALE ปกติไม่ควร retry event เดิม เพราะ revision ก็ยังเก่า; DUPLICATE ยืนยันการรับซ้ำได้โดยไม่เขียนซ้ำ; CONFLICT ควรแยกตรวจสอบแทนการสุ่มเลือก payload หนึ่ง ค่าเหล่านี้เป็นผลเชิงนโยบายของตัวอย่าง ไม่ใช่การออกแบบ queue acknowledgment ให้ระบบจริง

ระบบที่นำแนวคิดนี้ไปต่อควรบันทึก execution ID, document ID ที่ไม่เปิดข้อมูลเกินจำเป็น, revision, operation และผลตัดสิน เพื่อไล่เหตุขัดแย้งได้ พร้อมกำหนดอายุ log ให้สอดคล้องกับนโยบายข้อมูลที่ลบ ไม่ควรเก็บข้อความเอกสารใน log เพื่อช่วย debug โดยไม่มีเหตุผลและสิทธิ์ชัดเจน การวัดอัตรา STALE หรือ CONFLICT ช่วยสังเกตคุณภาพ pipeline แต่ไม่ได้แทนการยืนยันว่าข้อมูลทุกสำเนาถูกลบแล้ว

Retention, restart และ replay

mock ไม่มี garbage collection: marker อยู่ตลอดอายุ process แต่เมื่อ process หยุด state ทั้งหมดก็หาย นี่แสดงข้อจำกัดสองด้านพร้อมกัน—เก็บ marker นานช่วยปฏิเสธ event เก่า แต่ state ในหน่วยความจำไม่ทนต่อ restart จึงไม่ใช่รูปแบบ production.

ระบบจริงต้องกำหนด marker retention จากอายุสูงสุดของ event ที่อาจ replay, snapshot consumer, backup และ full reindex หาก marker หมดอายุขณะที่ event เก่ายัง replay ได้ UPSERT เก่าอาจดูเหมือน event แรกของ ID นั้น เอกสาร Elasticsearch ระบุว่าข้อมูล version ของเอกสารที่ลบเก็บไว้ช่วงสั้นตาม index.gc_deletes; จึงไม่ควรกล่าวว่าจำเวอร์ชันลบตลอดไป เอกสาร Kafka 4.2 ระบุ retention ของ delete marker ใน compacted topic และให้ snapshot consumer สแกนให้เสร็จก่อน marker ถูกเก็บกวาด ระบบเหล่านี้มีขอบเขตของตนเอง ไม่ใช่ค่า retention สำเร็จรูปสำหรับทุก RAG

ก่อนกำหนด retention ให้ตอบว่า event เก่าที่สุด replay ได้เมื่อไร, snapshot ใช้เวลาสูงสุดเท่าไร, backup คืน event เดิมได้หรือไม่ และผู้ดูแลเริ่ม reindex จาก snapshot รุ่นเก่าได้หรือไม่ หากไม่รู้ขอบเขตเหล่านี้ อย่าลบ marker เพียงเพื่อประหยัดพื้นที่โดยไม่แก้สัญญา recovery ไปพร้อมกัน

การเชื่อมต่อ RAG เป็นงานถัดไป

ในระบบจริง ทุกทางที่เปลี่ยนเอกสาร—สร้าง, แก้, ลบ, retry และ reindex—ต้องผ่านกติกา ordering เดียวกันก่อนเขียน index หรือเวกเตอร์ หากบาง path ลัด gate การรับประกันก็ไม่ครอบคลุม path นั้น การลบจาก retrieval, ป้องกัน ingestion เก่า, และจัดการสำเนาใน raw storage/cache/backup เป็นคนละภาระงาน marker ไม่ได้ลบทุกสำเนาหรือพิสูจน์การปฏิบัติตามนโยบายข้อมูล

ก่อนเลือก datastore ให้ยืนยันว่ามี conditional write หรือ transaction ที่รองรับ state transition นี้จริง และทดสอบ adapter ใน sandbox แยกข้อมูล อย่าอนุมานพฤติกรรมของ vector database จาก Python mock นี้ บทเรียนที่เกี่ยวข้องกับการผูก citation กับ revision และ byte span คือ RAG Citation Gate; ส่วนการตรวจสิทธิ์ก่อนคืน cache อ่าน RAG Answer Cache สองเรื่องนั้นครอบคลุมคนละส่วนของ pipeline.

Copyable task brief

เป้าหมาย: สร้าง revision gate ต่อ document ID เพื่อป้องกัน stale ingestion/reindex เขียนทับการตัดสินใจใหม่กว่า
เครื่องมือ: Python 3.12 standard library; in-memory mock; ไม่มี API หรือฐานข้อมูล
ขอบเขต: ใช้ synthetic fixtures เท่านั้น; ห้าม network, credentials และข้อมูลจริง; Event มี doc_id, revision, op, text; สมมติ source revision เชื่อถือได้; state ล่าสุดอยู่ใน map เดียว
สิ่งส่งมอบ: apply/lookup, expected example, acceptance cases และ README ระบุข้อจำกัด
เกณฑ์รับงาน: revision สูงกว่าแทน state; ต่ำกว่าคง state; DELETE เก็บ revision; lookup ของ DELETE ว่าง; equal same แยกจาก conflict
จุดหยุด: UPSERT 7, DELETE 9, UPSERT 8 จบที่ DELETE 9; ไม่เพิ่ม persistence หรือ integration

ก่อนนำแนวคิดไปต่อ ให้ตรวจรายการนี้: ทุก producer แนบ revision จาก source เดียวกัน; DELETE ไม่ลบ revision state; ทุก retry ใช้ revision เดิม; restore ผ่าน authorization แยก; retention ครอบคลุม replay ที่ยังอนุญาต; และการเขียน state ทำแบบ atomic ใน datastore จริง รายการนี้เป็นจุดเริ่ม review ไม่ใช่หลักฐานว่าระบบผ่านการทดสอบหรือพร้อมใช้งาน

Acceptance criteria และจุดหยุด

  • Validation เกิดก่อน mutation; event ผิดรูปแบบไม่แก้ state
  • revision ต่ำกว่าคืน STALE และคง state; revision สูงกว่าจึงแทน state
  • DELETE เก็บ revision ใน state เดียวกัน แต่ lookup ไม่คืนข้อความ
  • equal revision ที่เหมือนกันคืน DUPLICATE; operation/ข้อความต่างคืน CONFLICT โดยไม่เปลี่ยน state
  • ลำดับ 7 UPSERT → 9 DELETE → 8 UPSERT จบด้วย DELETE 9 และ lookup ว่าง
  • README บอก runtime, dependencies, input, expected output, permissions และสถานะรัน

จุดหยุดของงานคือครบ acceptance ข้างต้นและมีกรณีทดสอบเขียนไว้ ไม่เพิ่ม adapter, database, retention policy สำหรับ production หรือ deploy ไม่มีผล code test, integration test หรือ runtime test ที่สังเกตได้ใน deliverable นี้; UI QA เป็น UNKNOWN.

ห้องทดลองแบบจำลอง · Memory / RAG

เก็บ Tombstone ไว้ เพื่อกันข้อมูลเก่ากลับมา

เลือกชุดเหตุการณ์และวิธีเก็บสถานะ แล้วดูว่า UPSERT ที่มาช้าจะเปลี่ยนเอกสารในมุมมองค้นคืนหรือไม่

ขอบเขตการจำลอง: นี่คือ browser simulation ของกติกาเดียวกับ Python mock ที่อธิบายในบทความ ไม่ได้รัน Python เก็บสถานะต่อเอกสาร และสมมติว่าแหล่งเหตุการณ์ที่เชื่อถือได้ส่ง revision เพิ่มขึ้นต่อเอกสาร การเก็บ tombstone ใช้ปฏิเสธ revision เก่า ส่วนการลบ marker ทำให้แบบจำลองลืม revision เดิม ตัวเลขเป็น revision สมมติ ไม่ใช่ผลวัดหรือการทดสอบระบบจริง

สถานะสุดท้ายของ doc-A

{rev: 9, op: DELETE, text: null}

จำลอง: r8 ต่ำกว่า r9 จึงไม่เปลี่ยนสถานะ เอกสารยังไม่อยู่ในมุมมองค้นคืน

เหตุผลจากเหตุการณ์ล่าสุด

เมื่อเก็บ tombstone revision 9 ไว้ ระบบเปรียบเทียบ UPSERT r8 กับสถานะล่าสุดและปฏิเสธว่าเก่ากว่า

operation ที่เท่ากัน: เหมือนกัน = DUPLICATE; ต่างกัน = CONFLICT

ทดลองเปรียบเทียบ · สถานะที่จำไว้มีผล

เหตุการณ์เดียวกัน แต่ความจำต่างกัน

ใช้ลำดับ UPSERT r7 → DELETE r9 → UPSERT r8 มาช้า แล้วเปรียบเทียบสองกรณี ไม่มี vector database หรือการเรียกบริการจริง

เก็บ tombstone revision 9

UPSERT r8 → STALE · ไม่มีการเปลี่ยนสถานะ · doc-A ไม่อยู่ในมุมมอง

ลืมสถานะหลัง DELETE

UPSERT r8 เป็น event แรกที่พบ · รับตามกติกาตัวอย่าง · doc-A กลับเข้ามุมมอง

ทั้งสองผลใช้ incoming event เดียวกัน ตารางด้านล่างเป็นผลคาดหมายแบบคงที่สำหรับอ่านได้เมื่อปิด JavaScript

ผลคาดหมายตามกติกาตัวอย่าง (SIMULATION)
เหตุการณ์เดียวกันหลัง DELETE r9เก็บ tombstone r9ลืมสถานะ
UPSERT r8 มาช้าSTALE · ไม่มีรับเป็น event แรก · มี
DELETE r9 ซ้ำแบบเดิมDUPLICATE · ไม่มีรับ DELETE เป็น event แรก · ไม่มี
UPSERT r9 เนื้อหาต่างกันCONFLICT · ไม่มีรับ UPSERT เป็น event แรก · มี
UPSERT r10 ใหม่กว่าAPPLIED · มีรับ UPSERT เป็น event แรก · มี

การคืนข้อมูลโดยตั้งใจ: หากมี tombstone r9 แล้ว UPSERT r10 ใหม่กว่า จะรับเป็นการคืนข้อมูลตามกติกาตัวอย่าง การคืนข้อมูลนี้ไม่ทำให้ tombstone หมดอายุเอง

กติกาและข้อจำกัดของแบบจำลอง

revision ต่ำกว่า: STALE และไม่เปลี่ยน; revision เท่ากันพร้อม operation/text เหมือนเดิม: DUPLICATE และไม่เปลี่ยน; revision เท่ากันแต่เนื้อหาต่าง: CONFLICT และไม่เปลี่ยน; revision สูงกว่า: รับสถานะใหม่ UPSERT เก็บข้อความ ส่วน DELETE เก็บ tombstone และ text เป็น null การลบก่อนมี UPSERT ก็สร้าง tombstone ได้

การปฏิบัติต่อ revision เท่ากันเป็นทางเลือกเชิงออกแบบของตัวอย่าง ไม่ใช่ข้ออ้างว่า Elastic external versioning ใช้กติกาเดียวกัน

ตัวอย่างนี้ไม่ครอบคลุมการหมดอายุของ tombstone, การ restart, ความเป็น atomic ในระบบกระจาย, พฤติกรรม vector database, การลบสำเนาทุกแห่ง หรือการยืนยันตัวตนของแหล่งข้อมูล ใช้ source authorization และ retention policy จริงก่อนนำแนวคิดไปออกแบบระบบ

Video Lab: อธิบายงานเก่าที่มาช้าให้เห็นภาพ

ใช้ Lab วิดีโอด้านล่างวางแผนคลิปอธิบายลำดับ UPSERT 7 → DELETE 9 → UPSERT 8 เลือกหัวข้อและรูปแบบวิดีโอ แล้วนำ prompt ที่ได้มาเทียบ brief หกส่วนนี้และเพิ่มส่วนที่ยังขาด Lab นี้จำลองงานวิดีโอ ไม่ได้รัน Python หรือใช้ revision gate ให้ index จริง ค่าเวลาและเงินที่ปรับเป็นสมมติฐาน ไม่ใช่ผลวัดหรือราคาผู้ให้บริการ

เป้าหมาย: วางแผนคลิปอธิบายว่า revision 8 ต่ำกว่า deletion revision 9 จึงถูกปฏิเสธ
เครื่องมือ/ข้อมูล: Video Lab จำลองและเหตุการณ์สมมติ 7 → 9 → 8 เท่านั้น
ขอบเขต: สร้าง brief/storyboard/prompt ไม่เรียก API ไม่ลบหรือสร้างเอกสารจริง
สิ่งส่งมอบ: แผนคลิปที่เห็นเอกสารหายแต่ยังเก็บ marker 9
เกณฑ์รับงาน: แยกลำดับ arrival จาก revision และกล่าวถึง replay/retention
จุดหยุด: เมื่อ brief ครบ หยุดก่อนผลิตหรือเผยแพร่วิดีโอจริง

ตรวจด้วยว่าภาพหรือคำบรรยายไม่ทำให้ผู้ชมเข้าใจว่า marker พิสูจน์การลบทุกสำเนา หรือว่าตัวเลขสูงขึ้นเองแปลว่ามีสิทธิ์กู้คืน การ generate/copy prompt และ UI จริงยังไม่ได้ทดสอบ

H✦HYBRID MINDINTERACTIVE EDITION LIVE EXPERIENCE
HYBRID MIND ORIGINAL / AI AGENT WORKFLOW / 2026

จากคลิปดิบ
สู่จักรวาลคอนเทนต์

ChatGPT ตัดต่อวิดีโอแทนคนได้จริงแค่ไหน? สำรวจระบบ AI Video Editing Agent ผ่านฉากสามมิติที่โต้ตอบได้ พร้อมแยกข้อเท็จจริง สร้างพรอมป์ต์ และจำลองต้นทุนก่อนลงมือทำจริง

06 EDITING STAGES03 CONTENT FORMATS01 INTERACTIVE LAB

ออกแบบระบบตัดต่อในมุมมอง 3D

ลากบนฉากเพื่อหมุนมุมมอง แตะโหนดเพื่อดูหน้าที่ของ AI ในแต่ละขั้นตอน

แผนภาพ 6 ขั้นตอน: Footage, Story, Edit, Captions, Graphics, Render
AGENT PIPELINE / VIEWPORT ORBIT 3D
DRAG / SWIPE TO ROTATE
SELECT NODE TO INSPECT
01 / 06นำเข้าคลิปดิบ

จัดระเบียบไฟล์ ตรวจคุณภาพภาพและเสียง และกำหนดสิทธิ์เข้าถึงให้ AI Agent

● SOURCE
จุดสำคัญ: AI Agent ทำหน้าที่วางแผนและควบคุมเครื่องมือ ส่วนการตัดต่อจริงต้องมีซอฟต์แวร์หรือระบบเรนเดอร์รองรับ ฉากนี้เป็น แบบจำลองเชิงอธิบาย ไม่ใช่ระบบอัปโหลดหรือตัดต่อวิดีโอ

แยกความสามารถจริงจากคำโฆษณา

กดแต่ละประเด็นเพื่ออ่านรายละเอียดและแหล่งข้อมูล

EVIDENCE-FIRST
✓ChatGPT Work เข้าถึงไฟล์ในเครื่องได้ตรวจสอบแล้ว / มีเงื่อนไข+
รองรับการทำงานกับโฟลเดอร์หรือแอปบนคอมพิวเตอร์เดสก์ท็อป เมื่อแผนและการตั้งค่าอนุญาตและผู้ใช้ให้สิทธิ์ ส่วน Work บนมือถือหรือเว็บไม่ได้เข้าถึงโฟลเดอร์ในคอมพิวเตอร์ของคุณโดยตรง ที่มา: OpenAI Help ↗
✓Astra ใช้โปรแกรมคอมพิวเตอร์ทำงานได้ตรวจสอบแล้ว / ไม่ใช่โปรแกรมตัดต่อในตัว+
OpenAI อธิบายความสามารถด้าน computer use ของ Astra รวมถึงงานสื่อที่ใช้เครื่องมือ แต่ต้องแยกบทบาท AI ผู้ควบคุมออกจากซอฟต์แวร์ประมวลผลวิดีโอและการเรนเดอร์ไฟล์ ที่มา: OpenAI ↗
?ตัดเสร็จใน 5–10 นาทีทุกกรณียังพิสูจน์เป็นเกณฑ์ทั่วไปไม่ได้+
เวลาขึ้นกับความยาวคลิป จำนวนทรานซิชัน คุณภาพซับ เครื่องมือ และประสิทธิภาพคอมพิวเตอร์ ควรใช้การจับเวลางานจริงแบบต้นทางเดียวกัน ไม่ใช้ตัวเลขจากโพสต์เป็นเกณฑ์อ้างอิง
?ถูกกว่าจ้างคน 10 เท่าต้องกำหนดต้นทุนให้เทียบกันได้+
ต้องรวมเวลาตรวจแก้ ลิขสิทธิ์เสียง เครื่องมือที่จ่ายเพิ่ม และต้นทุนโมเดล ยังไม่มีหลักฐานว่าประหยัด 10 เท่าสำหรับทุกงาน ทดลองสมมติฐานส่วนตัวได้ใน Cost Simulator ด้านล่าง

หนึ่งไอเดีย สามรูปแบบคอนเทนต์

ปรับรูปแบบวิดีโอ แล้วดูกรอบภาพตัวอย่างและ Prompt เปลี่ยนตามแบบเรียลไทม์

WORKING INTERACTIVE
15 SEC180 SEC
LIVE CREATIVE PREVIEW9:16 · 45 SEC
HYBRID MIND / AI
AI EDITOR / EXPLAINEDAI Agent
ช่วยตัดต่อ?
ใส่ซับตามประโยคได้อัตโนมัติ
▶◉↗
CONCEPT PREVIEW · NOT RENDERED VIDEO
VERTICAL · SHORT FORMVisual Mockup

พร้อมนำไปใช้กับ AI Agent

ตัวสร้าง Prompt นี้ไม่อัปโหลดคลิป ไม่เรียก API เสียเงิน และไม่ตัดต่อวิดีโอจริง ต้องใช้กับเครื่องมือที่ผู้ใช้อนุญาตภายหลัง

จำลองเวลาและต้นทุนของคุณเอง

เลื่อนตัวเลขเพื่อเปรียบเทียบสถานการณ์สมมติ ไม่ใช่ผล benchmark ที่ยืนยันแล้ว

SCENARIO · NOT BENCHMARK
ESTIMATED TIME SAVED11.0 ชม./เดือน
18.0 ชม.
7.0 ชม.
ส่วนต่างต้นทุนเวลา + เครื่องมือ (สมมติ)฿3,920

ตัวเลขเป็นตัวอย่างเพื่อช่วยตัดสินใจ ไม่ใช่ผลประหยัดที่รับประกัน

AI ที่ตัดคลิปเป็น ไม่ได้ทำงานลำพัง

สิ่งที่น่าสนใจของ GPT-6 Astra และ GPT-6.1 Sol ไม่ได้อยู่ที่ว่าตัวโมเดลมีปุ่ม Render MP4 ซ่อนอยู่ แต่คือความสามารถในการจัดการงานหลายขั้นตอนร่วมกับเครื่องมืออื่น: อ่านไฟล์ วางลำดับเรื่อง ใช้แอป ตรวจผลลัพธ์ และแก้งานตามข้อเสนอแนะของมนุษย์

สำหรับคนทำคอนเทนต์ ความต่างสำคัญคือการเปลี่ยนจาก “ส่งคลิปให้ AI แล้วหวังว่าจะได้ผลงาน” ไปเป็น Workflow ที่ตรวจรับได้ทุกขั้น ตั้งแต่การเลือกช่วงเด็ด การสร้างซับ การตรวจคำผิด จนถึงการเรนเดอร์ไฟล์ตามสัดส่วนของแต่ละแพลตฟอร์ม

ข้อควรระวังคือระบบ Work ที่รันบนเว็บหรือมือถือไม่ได้หมายความว่า AI เข้าถึงคลิปบนคอมพิวเตอร์บ้านได้ทันที ต้องทำงานในสภาพแวดล้อมที่เปิดให้เข้าถึงไฟล์จริง มีเครื่องมือตัดต่อ และมีสิทธิ์ใช้งานอย่างชัดเจน

01จัดคลิปให้พร้อม

รวบรวมไฟล์ในโฟลเดอร์ที่เข้าถึงได้ ระบุสิทธิ์และข้อจำกัด

02ตรวจสคริปต์และไทม์ไลน์

ให้ Agent เสนอ Hook, ช่วงตัด และโครงเรื่องก่อนตัดต่อ

03สร้างซับ + กราฟิก

ตรวจชื่อเฉพาะ ตัวเลข ภาษาไทย และความถูกต้องของคำกล่าวอ้าง

04ตรวจไฟล์ MP4 จริง

ตรวจความละเอียด เสียง ระยะเวลา safe zone และคำบรรยาย

แบบฝึกหัด

ทำนายผลและ lookup หลังลำดับนี้: UPSERT(manual-a, 4, “A”); UPSERT revision 6 “B”; DELETE revision 5; UPSERT revision 6 “อื่น”; DELETE revision 7; UPSERT revision 6 “rebuild เก่า” ระบุด้วยว่า event ไหนเปลี่ยน state และเพราะเหตุใด arrival time จึงไม่ใช้เลือกข้อมูล.

เฉลย: APPLIED A; APPLIED B; STALE คง B; CONFLICT คง B; APPLIED DELETE 7 และ lookup ว่าง; STALE คง DELETE 7. เกณฑ์ประเมิน: state ไม่เปลี่ยนหลัง rejection, แยก STALE กับ CONFLICT และอธิบายว่า consumer ห้ามเพิ่ม revision เอง.

Quick Answer

เก็บ revision ล่าสุดคู่กับ state รวมทั้งสถานะลบ รับเฉพาะ revision ที่สูงกว่า และปฏิเสธ revision ต่ำกว่าโดยไม่เขียนข้อมูลกลับ ตรวจแหล่ง revision และสิทธิ์ก่อนถึง gate; กำหนด retention จาก replay และ recovery ของระบบ ตัวอย่างเป็น mock ที่ยังไม่ได้รัน ไม่ใช่ integration หรือ production guarantee.

FAQ

ลบเวกเตอร์แล้วพอหรือไม่?

ไม่พอถ้างานเก่ายังเขียนกลับได้ ต้องมี state ลบที่ gate ใช้ปฏิเสธ event เก่า และจัดการสำเนาตามนโยบายข้อมูลแยกกัน.

revision เท่ากันแต่ข้อมูลต่างกันทำอย่างไร?

อย่าเลือกตามเวลามาถึง Mock คืน CONFLICT และคง state เดิม; ระบบจริงต้องกำหนด reconciliation หรือ quarantine.

เก็บ marker นานเท่าไร?

สัมพันธ์กับ event replay, snapshot, backup และ reindex ที่ยังเกิดได้ ไม่มีตัวเลขสากลจากตัวอย่างนี้.

ใช้หลาย worker ได้ไหม?

ไม่ได้ตามที่พิสูจน์ใน mock นี้; ต้องมี atomic compare-and-set หรือ transaction และทดสอบ adapter จริงใน sandbox.

แหล่งอ้างอิง

ข้อกำหนด API ข้างต้นเป็น documented behavior ของแต่ละผลิตภัณฑ์; revision gate แบบ map และผลตัวอย่างเป็น engineering analysis/simulation เท่านั้น ไม่ได้ทดสอบกับ Elasticsearch, Kafka หรือ vector database.

โค้ดตัวอย่างและคำสั่งใช้งาน: code/revision_gate.py · code/example_revision_gate.py · code/test_revision_gate.py · README-code.md · ทั้งหมดยัง NOT RUN

Source Code และวิธีลองด้วยตนเอง · NOT RUN

code/revision_gate.py · ยังไม่ได้รัน
"""Bounded, in-memory revision gate mock for per-document RAG ingestion."""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
import re

_MAX_REVISION = 2**63 - 1
_MAX_TEXT_BYTES = 65_536
_DOC_ID = re.compile(r"[A-Za-z0-9][A-Za-z0-9._:-]{0,79}\Z", re.ASCII)


class Operation(str, Enum):
    UPSERT = "UPSERT"
    DELETE = "DELETE"


class Result(str, Enum):
    APPLIED = "APPLIED"
    STALE = "STALE"
    DUPLICATE = "DUPLICATE"
    CONFLICT = "CONFLICT"


@dataclass(frozen=True)
class Event:
    doc_id: str
    revision: int
    op: str
    text: str | None = None


@dataclass(frozen=True)
class State:
    revision: int
    op: Operation
    text: str | None


class RevisionIndex:
    """One-process model; not persistent, distributed, or concurrency-safe."""

    def __init__(self) -> None:
        self._states: dict[str, State] = {}

    @staticmethod
    def _validate(event: Event) -> tuple[str, int, Operation, str | None]:
        if type(event) is not Event:
            raise TypeError("event must be an Event")
        if type(event.doc_id) is not str or _DOC_ID.fullmatch(event.doc_id) is None:
            raise ValueError("doc_id must start with ASCII letter/digit; following characters may use ASCII letters, digits, . _ : or - (max 80)")
        if type(event.revision) is not int or not (1 <= event.revision <= _MAX_REVISION):
            raise ValueError("revision must be a positive signed 64-bit integer")
        if type(event.op) is not str or event.op not in ("UPSERT", "DELETE"):
            raise ValueError("op must be exactly UPSERT or DELETE")
        op = Operation(event.op)
        if op is Operation.DELETE:
            if event.text is not None:
                raise ValueError("DELETE must not include text")
            return event.doc_id, event.revision, op, None
        if type(event.text) is not str or event.text == "":
            raise ValueError("UPSERT text must be a non-empty string")
        try:
            encoded = event.text.encode("utf-8", errors="strict")
        except UnicodeEncodeError as exc:
            raise ValueError("UPSERT text must be valid UTF-8") from exc
        if len(encoded) > _MAX_TEXT_BYTES:
            raise ValueError("UPSERT text must be at most 65536 UTF-8 bytes")
        return event.doc_id, event.revision, op, event.text

    def apply(self, event: Event) -> Result:
        # Validate the complete event before reading or changing state.
        doc_id, revision, op, text = self._validate(event)
        current = self._states.get(doc_id)
        if current is None or revision > current.revision:
            self._states[doc_id] = State(revision=revision, op=op, text=text)
            return Result.APPLIED
        if revision < current.revision:
            return Result.STALE
        if current.op is op and current.text == text:
            return Result.DUPLICATE
        return Result.CONFLICT

    def lookup(self, doc_id: str) -> str | None:
        if type(doc_id) is not str or _DOC_ID.fullmatch(doc_id) is None:
            raise ValueError("doc_id must be 1-80 ASCII letters, digits, . _ : or -")
        state = self._states.get(doc_id)
        if state is None or state.op is Operation.DELETE:
            return None
        return state.text

    def state(self, doc_id: str) -> State | None:
        if type(doc_id) is not str or _DOC_ID.fullmatch(doc_id) is None:
            raise ValueError("doc_id must be 1-80 ASCII letters, digits, . _ : or -")
        return self._states.get(doc_id)
code/example_revision_gate.py · ยังไม่ได้รัน
"""Print the expected stale-ingestion example; authored but NOT RUN."""

from revision_gate import Event, RevisionIndex


def main() -> None:
    index = RevisionIndex()
    events = (
        Event("guide-1", 7, "UPSERT", "คู่มือฉบับเก่า"),
        Event("guide-1", 9, "DELETE"),
        Event("guide-1", 8, "UPSERT", "งาน reindex ที่มาช้า"),
    )
    for event in events:
        print(index.apply(event).value)
    print(index.lookup("guide-1"))
    state = index.state("guide-1")
    assert state is not None
    print(f"{state.revision} {state.op.value}")


if __name__ == "__main__":
    main()
code/test_revision_gate.py · ยังไม่ได้รัน
"""Authored acceptance cases for revision_gate; NOT RUN."""

import unittest

from revision_gate import Event, Operation, Result, RevisionIndex


class RevisionIndexTests(unittest.TestCase):
    def setUp(self) -> None:
        self.index = RevisionIndex()

    def test_example_accepts_newer_delete_and_rejects_late_upsert(self) -> None:
        self.assertEqual(self.index.apply(Event("guide-1", 7, "UPSERT", "old")), Result.APPLIED)
        self.assertEqual(self.index.apply(Event("guide-1", 9, "DELETE")), Result.APPLIED)
        self.assertEqual(self.index.apply(Event("guide-1", 8, "UPSERT", "late")), Result.STALE)
        self.assertIsNone(self.index.lookup("guide-1"))
        self.assertEqual(self.index.state("guide-1").revision, 9)
        self.assertIs(self.index.state("guide-1").op, Operation.DELETE)

    def test_same_revision_same_event_is_duplicate(self) -> None:
        self.index.apply(Event("doc", 3, "UPSERT", "text"))
        self.assertEqual(self.index.apply(Event("doc", 3, "UPSERT", "text")), Result.DUPLICATE)

    def test_same_revision_different_text_is_conflict(self) -> None:
        self.index.apply(Event("doc", 3, "UPSERT", "first"))
        self.assertEqual(self.index.apply(Event("doc", 3, "UPSERT", "other")), Result.CONFLICT)
        self.assertEqual(self.index.lookup("doc"), "first")

    def test_same_revision_different_operation_is_conflict(self) -> None:
        self.index.apply(Event("doc", 3, "UPSERT", "first"))
        self.assertEqual(self.index.apply(Event("doc", 3, "DELETE")), Result.CONFLICT)
        self.assertEqual(self.index.lookup("doc"), "first")

    def test_delete_for_unseen_id_creates_marker(self) -> None:
        self.assertEqual(self.index.apply(Event("doc", 4, "DELETE")), Result.APPLIED)
        self.assertEqual(self.index.state("doc").revision, 4)
        self.assertIsNone(self.index.lookup("doc"))

    def test_later_higher_revision_can_restore_content(self) -> None:
        self.index.apply(Event("doc", 4, "DELETE"))
        self.assertEqual(self.index.apply(Event("doc", 5, "UPSERT", "restored")), Result.APPLIED)
        self.assertEqual(self.index.lookup("doc"), "restored")

    def test_documents_advance_independently(self) -> None:
        self.index.apply(Event("a", 10, "DELETE"))
        self.assertEqual(self.index.apply(Event("b", 1, "UPSERT", "live")), Result.APPLIED)
        self.assertEqual(self.index.lookup("b"), "live")

    def test_boolean_revision_is_rejected_without_mutation(self) -> None:
        with self.assertRaises(ValueError):
            self.index.apply(Event("doc", True, "DELETE"))
        self.assertIsNone(self.index.state("doc"))

    def test_non_ascii_or_invalid_id_is_rejected_without_mutation(self) -> None:
        for doc_id in ("", "ไทย", "has space", "x" * 81):
            with self.subTest(doc_id=doc_id), self.assertRaises(ValueError):
                self.index.apply(Event(doc_id, 1, "DELETE"))
        self.assertEqual(self.index.state("valid"), None)

    def test_invalid_operation_is_rejected(self) -> None:
        with self.assertRaises(ValueError):
            self.index.apply(Event("doc", 1, "delete"))
        self.assertIsNone(self.index.state("doc"))

    def test_delete_with_text_is_rejected_without_mutation(self) -> None:
        with self.assertRaises(ValueError):
            self.index.apply(Event("doc", 1, "DELETE", "unexpected"))
        self.assertIsNone(self.index.state("doc"))

    def test_invalid_utf8_surrogate_is_rejected_without_mutation(self) -> None:
        with self.assertRaises(ValueError):
            self.index.apply(Event("doc", 1, "UPSERT", "bad\ud800"))
        self.assertIsNone(self.index.state("doc"))

    def test_text_limit_uses_utf8_bytes(self) -> None:
        self.index.apply(Event("doc", 1, "UPSERT", "ก" * 21_845))
        with self.assertRaises(ValueError):
            self.index.apply(Event("other", 1, "UPSERT", "ก" * 21_846))
        self.assertIsNone(self.index.state("other"))

    def test_revision_must_be_positive_and_bounded(self) -> None:
        for revision in (0, -1, 2**63, 1.5):
            with self.subTest(revision=revision), self.assertRaises(ValueError):
                self.index.apply(Event("doc", revision, "DELETE"))
        self.assertIsNone(self.index.state("doc"))


if __name__ == "__main__":
    unittest.main()
README-code.md · ยังไม่ได้รัน
# Revision gate mock

This run contains a standard-library-only Python 3.12 example for per-document RAG ingestion ordering. It is an in-memory teaching mock and does not connect to Elasticsearch, Kafka, a vector database, WordPress, or any API.

## Runtime and dependencies

- Runtime target: Python 3.12 or newer.
- Dependencies: Python standard library only (`dataclasses`, `enum`, `re`, `unittest`).
- Environment variables: none. Installation: none.
- Network, API keys, GPU, deployment, or external services: not used. No API or GPU costs are incurred; no cost measurement was performed.

## Example input and expected output

The driver submits UPSERT revision 7, DELETE revision 9, then late UPSERT revision 8 for `guide-1`.

Command from this run directory:

`python code/example_revision_gate.py`

Expected output by inspection of authored code (NOT OBSERVED):

`APPLIED`

`APPLIED`

`STALE`

`None`

`9 DELETE`

The last two lines show that retrieval yields no text while the retained state remembers the deletion revision. This is synthetic input, not a live document.

## Authored acceptance suite

A developer could run the authored suite from this run directory with:

`python -m unittest discover -s code -p "test_*.py" -v`

This command is documentation only. Tests were NOT RUN. The example driver was NOT RUN. No syntax check, import, compile, lint, or runtime check was performed.

## Limits and permissions

- The state map is in memory and disappears when the process stops; there is no persistence or garbage collection policy.
- The mock is single-process and makes no claim about concurrency, transactions, multiple workers, or replicas.
- Source revision is assumed authoritative. Shape validation does not authenticate or authorize a producer.
- Higher-revision UPSERT may restore content; the mock does not decide whether a restore is allowed.
- No database, queue, file, OS, external API, or production write is performed.

Mock means authored local logic with synthetic inputs. Sandbox would mean an isolated integration environment with disposable data and test permissions. Live means a real external service and real permissions. This deliverable is Mock only; Sandbox and Live behavior are not established.

Authored code, driver, and tests: NOT RUN. Integration: NOT RUN. Optional UI/browser QA: UNKNOWN.

ใส่ความเห็น

Discover more from HYBRID MIND

Subscribe now to keep reading and get access to the full archive.

Continue reading