ทำไมเซสชัน OpenClaw ของฉันจึงเผาโทเค็นไป 21.5 ล้านโทเค็นในหนึ่งวัน (และสิ่งที่ช่วยแก้ไขได้จริง)
ผู้เขียนต้นฉบับ: MOSHIII
แปลโดย: Peggy, BlockBeats
บรรณาธิการ: ในยุคที่แอปพลิเคชัน Agent กำลังแพร่หลายอย่างรวดเร็ว ทีมหลายทีมพบปรากฏการณ์ที่ดูเหมือนขัดแย้ง: ระบบทำงานได้ปกติทุกอย่าง แต่ค่าใช้จ่ายของ token กลับค่อยๆ เพิ่มขึ้นโดยไม่รู้ตัว บทความนี้通过对一次真实 OpenClaw 工作负载的拆解发现,成本爆炸的原因往往并不来自用户输入或模型输出,而是被忽视的上下文缓存重放(cached prefix replay)。模型在每一轮调用中反复读取庞大的历史上下文,从而产生巨量 token 消耗。
บทความนี้แสดงให้เห็นถึงวิธีที่ผลลัพธ์ของเครื่องมือ ภาพหน้าจอเบราว์เซอร์ ไฟล์ล็อก JSON และผลลัพธ์ขนาดใหญ่อื่นๆ ถูกเขียนลงในบริบทย้อนหลังอย่างต่อเนื่อง และถูกอ่านซ้ำในวงจรของเอเจนต์
ผ่านกรณีศึกษานี้ ผู้เขียนได้เสนอแนวทางการปรับปรุงที่ชัดเจน: ตั้งแต่การออกแบบโครงสร้างบริบท การจัดการผลลัพธ์ของเครื่องมือ ไปจนถึงการกำหนดค่ากลไก compaction สำหรับนักพัฒนาที่กำลังสร้างระบบ Agent นี่ไม่ใช่เพียงบันทึกการแก้ไขปัญหาทางเทคนิค แต่ยังเป็นคู่มือประหยัดเงินจริงๆ
ด้านล่างนี้คือข้อความต้นฉบับ:
ฉันวิเคราะห์ภาระงาน OpenClaw ที่แท้จริงและพบรูปแบบที่ฉันคิดว่าผู้ใช้ Agent ส่วนใหญ่จะจดจำได้:
การใช้งานโทเค็นดูเหมือนจะ “คึกคัก”
การตอบกลับดูปกติเช่นกัน
แต่การใช้โทเค็นกลับเพิ่มขึ้นอย่างฉับพลัน
ด้านล่างนี้คือการวิเคราะห์โครงสร้าง สาเหตุพื้นฐาน และแนวทางการแก้ไขที่สามารถดำเนินการได้จริง
สรุปสั้น
ปัจจัยขับเคลื่อนต้นทุนที่ใหญ่ที่สุดไม่ใช่ข้อความของผู้ใช้ยาวเกินไป แต่เป็นตัวนำที่ถูกแคช (cached prefix) จำนวนมากที่ถูกเล่นซ้ำหลายครั้ง
จากข้อมูลเซสชัน:
จำนวนโทเค็นทั้งหมด: 21,543,714
cacheRead: 17,105,970 (79.40%)
4,345,264 (20.17%)
ผลลัพธ์: 92,480 (0.43%)
กล่าวอีกนัยหนึ่ง: ต้นทุนของการเรียกใช้ส่วนใหญ่ไม่ได้อยู่ที่การจัดการเจตนาของผู้ใช้ใหม่ แต่อยู่ที่การอ่านบริบทย้อนหลังขนาดใหญ่ซ้ำๆ
ช่วงเวลาที่ว่า “เดี๋ยวก่อน ทำไมถึงเป็นแบบนี้?”
ฉันคิดว่าการใช้งานโทเค็นสูงมาจากการป้อนคำสั่งของผู้ใช้ที่ยาวมาก การสร้างเอาต์พุตจำนวนมาก หรือการเรียกใช้เครื่องมือที่มีค่าใช้จ่ายสูง
แต่รูปแบบที่เป็นผู้นำจริงคือ:
หลายร้อยถึงหลายพันโทเค็น
cacheRead:แต่ละครั้งที่เรียกใช้ 170,000 ถึง 180,000 token
กล่าวคือ แบบจำลองในแต่ละรอบจะอ่านซ้ำๆ คำนำหน้าขนาดใหญ่ที่คงที่เดิม
ช่วงข้อมูล
ฉันวิเคราะห์ข้อมูลในสองระดับ:
1. บันทึกการดำเนินงาน (runtime logs)
2. บันทึกการสนทนา (session transcripts)
โปรดทราบว่า:
ไฟล์บันทึกการดำเนินการใช้เพื่อสังเกตสัญญาณพฤติกรรม (เช่น การรีสตาร์ท ข้อผิดพลาด หรือปัญหาการตั้งค่า)
การนับ token ที่แม่นยำมาจากรายการ usage ในไฟล์ JSONL ของเซสชัน
สคริปต์ที่ใช้:
scripts/session_token_breakdown.py
scripts/session_duplicate_waste_analysis.py
ไฟล์การวิเคราะห์ที่สร้างขึ้น:
tmp/session_token_stats_v2.txt
tmp/session_token_stats_v2.json
tmp/session_duplicate_waste.txt
tmp/session_duplicate_waste.json
tmp/session_duplicate_waste.png
โทเค็นถูกใช้ไปที่ไหนบ้าง?
1) ช่วงเวลาที่มีการรวมตัว
มีเซสชันหนึ่งที่ใช้ทรัพยากรสูงกว่าที่อื่นๆ:
570587c3-dc42-47e4-9dd4-985c2a50af86: 19,204,645 โทเค็น
จากนั้นก็ลดลงอย่างชัดเจนอย่างเฉียบพลัน:
ef42abbb-d8a1-48d8-9924-2f869dea6d4a: 1,505,038
ea880b13-f97f-4d45-ba8c-a236cf6f2bb5: 649,584
2) การรวมพฤติกรรม
โทเค็นส่วนใหญ่มาจาก:
toolUse:16,372,294
หยุด: 5,171,420
ปัญหาหลักเกิดจากวงจรการเรียกใช้เครื่องมือ ไม่ใช่การสนทนาทั่วไป
3) เวลาที่รวมกัน
จุดสูงสุดของโทเค็นไม่ได้เกิดขึ้นแบบสุ่ม แต่กระจุกตัวอยู่ในช่วงเวลาไม่กี่ช่วง:
2026-03-08 16:00: 4,105,105
2026-03-08 09:00: 4,036,070
2026-03-08 07:00: 2,793,648
ในเบอร์แคชขนาดใหญ่นั้นมีอะไรบ้าง?
ไม่ใช่เนื้อหาของการสนทนา แต่เป็นผลิตภัณฑ์กลางขนาดใหญ่:
บล็อกข้อมูล toolResult ขนาดใหญ่
การวิเคราะห์/ลำดับความคิดที่ยาวนาน
ภาพถ่าย JSON ขนาดใหญ่
รายการไฟล์
เบราว์เซอร์ดึงข้อมูล
บันทึกการสนทนาของซับเอเจนต์
ในเซสชันที่มากที่สุด จำนวนอักขระประมาณคือ:
366,469 ตัวอักษร
assistant:thinking:331,494 ตัวอักษร
53,039 ตัวอักษร
เมื่อเนื้อหาเหล่านี้ถูกเก็บไว้ในบริบทย้อนหลัง การเรียกใช้แต่ละครั้งในอนาคตอาจอ่านกลับมาผ่านคำนำหน้า cache
ตัวอย่างที่เฉพาะเจาะจง (จากไฟล์เซสชัน)
มีบล็อกบริบทขนาดใหญ่ปรากฏซ้ำที่ตำแหน่งด้านล่าง:
sessions/570587c3-dc42-47e4-9dd4-985c2a50af86.jsonl:70
ไฟล์ล็อก JSON ของเกตเวย์ขนาดใหญ่ (ประมาณ 37,000 ตัวอักษร)
sessions/570587c3-dc42-47e4-9dd4-985c2a50af86.jsonl:134
ภาพหน้าจอเบราว์เซอร์ + การห่อหุ้มอย่างปลอดภัย (ประมาณ 29,000 ตัวอักษร)
sessions/570587c3-dc42-47e4-9dd4-985c2a50af86.jsonl:219
รายการไฟล์ขนาดใหญ่ (ประมาณ 41,000 ตัวอักษร)
sessions/570587c3-dc42-47e4-9dd4-985c2a50af86.jsonl:311
สถานะเซสชัน + โครงสร้างพร้อมต์ขนาดใหญ่ (ประมาณ 30,000 ตัวอักษร)
การสูญเปล่าจากเนื้อหาซ้ำกัน vs ภาระจากการเล่นซ้ำแคช
ฉันยังวัดสัดส่วนเนื้อหาซ้ำภายในการเรียกแต่ละครั้ง:
อัตราการทำซ้ำประมาณ: 1.72%
มีอยู่จริง แต่ไม่ใช่ปัญหาหลัก
ปัญหาที่แท้จริงคือ: ขนาดสัมบูรณ์ของคำนำหน้าแคชใหญ่เกินไป
โครงสร้างคือ: บริบททางประวัติศาสตร์ขนาดใหญ่ อ่านใหม่ทุกครั้งที่เรียกใช้ และเพิ่มข้อมูลใหม่เพียงเล็กน้อยด้านบน
ดังนั้น จุดเน้นของการปรับปรุงไม่ใช่การลบข้อมูลซ้ำ แต่เป็นการออกแบบโครงสร้างบริบท
ทำไมวงจร Agent จึงมีแนวโน้มที่จะเกิดปัญหานี้ได้ง่าย?
กลไกสามประการที่ทับซ้อนกัน:
1. ปริมาณเครื่องมือจำนวนมากถูกบันทึกไว้ในบริบทย้อนหลัง
2. การวนลูปของเครื่องมือจะสร้างการเรียกใช้งานที่มีช่วงเวลาสั้นจำนวนมาก
3. เปลี่ยนแปลงคำนำหน้าน้อยมาก → cache จะต้องอ่านใหม่ทุกครั้ง
หากการบีบอัด context ไม่ถูกกระตุ้นอย่างเสถียร ปัญหาจะขยายตัวอย่างรวดเร็ว
กลยุทธ์การแก้ไขที่สำคัญที่สุด (เรียงตามผลกระทบ)
P0—อย่าใส่ผลลัพธ์เครื่องมือขนาดใหญ่ลงในบริบทระยะยาว
สำหรับผลลัพธ์ของเครื่องมือขนาดใหญ่มาก:
- เก็บสรุป + ลิงก์อ้างอิง / ID
- เขียน payload เดิมลงในไฟล์ artifact
- อย่าเก็บข้อความต้นฉบับทั้งหมดไว้ในประวัติการแชท
จำกัดลำดับความสำคัญสำหรับหมวดหมู่เหล่านี้:
- JSON ขนาดใหญ่
- รายการไดเรกทอรียาว
- การถ่ายภาพหน้าจอแบบสมบูรณ์ของเบราว์เซอร์
- คำบรรยายฉบับสมบูรณ์ของ Agent ย่อย
P1—ตรวจสอบให้แน่ใจว่ากลไกการบีบอัดทำงานจริง
ในข้อมูลนี้ ปัญหาความเข้ากันได้ของการตั้งค่าปรากฏขึ้นหลายครั้ง: คีย์การบีบอัดไม่ถูกต้อง
จะปิดกลไกการปรับปรุงโดยเงียบๆ
วิธีที่ถูกต้อง: ใช้การตั้งค่าที่เข้ากันได้กับเวอร์ชันเท่านั้น
แล้วตรวจสอบ:
openclaw doctor --fix
ตรวจสอบบันทึกการเริ่มต้นเพื่อยืนยันว่าการบีบอัดได้รับการยอมรับ
P1—ลดการจัดเก็บข้อความเหตุผล
หลีกเลี่ยงการรีเพลย์ข้อความการให้เหตุผลยาวๆ
ในสภาพแวดล้อมการผลิต: บันทึกสรุปสั้นๆ แทนที่เหตุผลทั้งหมด
P3—ปรับปรุงการออกแบบการแคช prompt
เป้าหมายไม่ใช่การเพิ่ม cacheRead ให้สูงสุด เป้าหมายคือการใช้ cache บนคำนำหน้าที่กระชับ คงที่ และมีมูลค่าสูง
คำแนะนำ:
- ใส่กฎความเสถียรไว้ใน system prompt
- อย่าใส่ข้อมูลที่ไม่มั่นคงลงในคำนำหน้าที่มั่นคง
- หลีกเลี่ยงการใส่ข้อมูลดีบักจำนวนมากในแต่ละรอบ
แผนการตั้งจุดตัดขาดแบบปฏิบัติจริง (ถ้าฉันต้องจัดการพรุ่งนี้)
1. ค้นหาเซสชันที่มีสัดส่วน cacheRead สูงที่สุด
2. ดำเนินการ /compact สำหรับ runaway session
3. เพิ่มการตัดทอน + การทำให้เป็น artifact สำหรับผลลัพธ์ของเครื่องมือ
4. รันการนับ token ใหม่หลังจากแก้ไขทุกครั้ง
ติดตาม KPI หลักสี่รายการ:
อ่านแคช / จำนวนโทเค็นทั้งหมด
ใช้เครื่องมือ avgTotal/call
จำนวนการเรียกใช้งาน >=100k token
สัดส่วนเซสชันสูงสุด
สัญญาณที่ประสบความสำเร็จ
หากการปรับปรุงมีผล คุณควรเห็น:
การเรียกใช้โทเค็นมากกว่า 100k ลดลงอย่างชัดเจน
สัดส่วน cacheRead ลดลง
น้ำหนักการเรียกใช้เครื่องมือลดลง
ระดับการควบคุมของแต่ละเซสชันลดลง
หากตัวชี้วัดเหล่านี้ไม่เปลี่ยนแปลง แสดงว่ากลยุทธ์บริบทของคุณยังคงหลวมเกินไป
คำสั่งทดลองซ้ำ
python3 scripts/session_token_breakdown.py 'sessions' \
--include-deleted \
-- 20 อันดับแรก \
--outlier-threshold 120000 \
--json-out tmp/session_token_stats_v2.json \
> tmp/session_token_stats_v2.txt
python3 scripts/session_duplicate_waste_analysis.py 'sessions' \
--include-deleted \
-- 20 อันดับแรก \
--png-out tmp/session_duplicate_waste.png \
--json-out tmp/session_duplicate_waste.json \
> tmp/session_duplicate_waste.txt
ข้อสรุป
หากระบบ Agent ของคุณดูเหมือนทำงานปกติ แต่ค่าใช้จ่ายยังคงเพิ่มขึ้นอย่างต่อเนื่อง ให้ตรวจสอบปัญหาหนึ่งก่อน: คุณกำลังจ่ายสำหรับการประมวลผลแบบใหม่ หรือกำลังรีเพลย์บริบทเก่าในปริมาณมาก?
ในกรณีของฉัน ค่าใช้จ่ายส่วนใหญ่มาจากการเล่นกลับบริบท
เมื่อคุณตระหนักถึงจุดนี้ วิธีแก้ไขก็จะชัดเจน: ควบคุมข้อมูลที่เข้าสู่บริบทระยะยาวอย่างเข้มงวด
