Agent Dev Thai Academyprinciples · patterns · architecture

ตั้งใจใช้ Fable ให้หมดโควต้าทุกสัปดาห์ จนได้ยินเรื่อง Anthropic จะ IPO

ข้อความห้าคำราคา $11.45 เพราะ cache หมดอายุตอนกลับมาพิมพ์ในแชทเดิม พักเกินชั่วโมงให้ /wrap แล้ว /clear และรุ่นถูกไม่ได้แปลว่าจ่ายถูกเมื่อ context ใหญ่

ที่ผ่านมาผมใช้ Fable หนักมาก ตั้งใจใช้

เหตุผลง่าย ๆ โควต้ามันรีเซ็ตทุกสัปดาห์ จ่ายไปแล้ว ใช้ไม่หมดก็หายไปเฉย ๆ เพราะงั้นก็ใช้มันให้หมด ไม่ต้องเลือกรุ่นให้เสียเวลา งานอะไรก็ยัดเข้าตัวเดียวกันหมด

จนไปฟัง All-In ตอน #290 เมื่อ 26 ก.ย. ที่ผ่านมา ชื่อตอนคือ Anthropic IPO at Risk

มีสองช่วงในตอนนั้นที่ค้างอยู่ในหัว

ช่วงแรก David Friedberg พูดถึงฝั่งที่เขาทำอยู่ เขาบริหารองค์กรวิจัยด้าน life sciences เขาบอกตรง ๆ ว่าโมเดลของ Anthropic เก่งที่สุดสำหรับงานสายนี้ ดีกว่าเจ้าอื่นจริง

แต่สำหรับงานเขียนโค้ดกับ workflow ภายใน เขาบอกว่างานพวกนั้นไปอยู่กับ open-weight model ที่รันเองได้ แล้วเก็บของแพงไว้เฉพาะงานที่มันเก่งกว่าคนอื่นจริง ๆ

ช่วงที่สองชื่อ Pacing the Frontier เรื่อง OpenAI กับ Anthropic ที่ไปคุยกับรัฐบาลสหรัฐเรื่องการคุมจังหวะของ frontier model ซึ่ง Sacks ก็แซวในรายการว่าพูดเรื่องชะลอ frontier ไปพร้อมกับปล่อย frontier model ตัวใหม่

ไม่มีใครในรายการพูดสักคำว่าราคาจะขึ้น อันนั้นคิดต่อเอง

แต่สิ่งที่ติดมาจริง ๆ ไม่ใช่เรื่องราคา มันคือวิธีคิดของคนที่จ่ายหนักกว่าเราหลายเท่า เขาไม่ได้ถามว่ารุ่นไหนดีที่สุด เขาแบ่งงานก่อน แล้วค่อยเลือกว่างานก้อนไหนคุ้มที่จะจ่ายแพง

แล้วย้อนกลับมาดูของตัวเอง ทั้ง workflow อยู่บนรุ่นเดียว ไม่ได้เพราะมันเหมาะกับงาน แต่เพราะโควต้ามันยังเหลือ

เลยลองไล่ transcript ในเครื่องย้อนหลังสามสัปดาห์ ว่าถ้าตัดเรื่องโควต้าออกไป แล้วคิดตามราคาจริง เงินมันไหลออกตรงไหนบ้าง

ตัวเลขทุกตัวข้างล่างคือประมาณการ เอา token ใน transcript มาคูณราคา list ไม่ใช่ยอดบิล


ข้อความที่แพงที่สุด เป็นประโยคห้าคำ

promptthat one just go out

ตีตามราคา list ประโยคนี้ตกประมาณ $11.45 สำหรับการเรียกครั้งเดียว

ไม่ใช่โจทย์ยาก ไม่ได้ให้เขียนโค้ดทั้งไฟล์ แค่พิมพ์ตอบสั้น ๆ กลับเข้าไปในแชทที่เปิดค้างไว้ หลังลุกไปทำอย่างอื่นมานาน

กลไกคือแบบนี้ ตอนคุยต่อเนื่อง context ก้อนเดิมนอนอยู่ใน cache ราคาอ่านจาก cache ถูกกว่า input หลายเท่า นี่คือเหตุผลที่แชทยาว ๆ ยังคุยไหว

พอปล่อยทิ้งไว้เฉย ๆ สักพัก cache หมดอายุ ข้อความถัดไปสั้นแค่ไหนก็ตาม ต้องเขียน context ทั้งก้อนกลับเข้า cache ใหม่ก่อน แล้วค่อยเริ่มคิด

ราคาเขียนเข้า cache อยู่ที่ 1.25 เท่าของ input

[รูป: statusline — hud-statusline.png]

บรรทัดที่สำคัญที่สุดในรูปคือ Cache until 05:55 PM มันคือนาฬิกาที่บอกว่า cache ก้อนนี้จะหมดอายุกี่โมง

ถ้ากลับมาพิมพ์ก่อนเวลานั้น จ่ายราคา cache read ถ้ากลับมาหลังจากนั้น จ่ายค่าเขียนใหม่ทั้งก้อน

ข้อมูลนี้มีมาตลอด แค่ไม่เคยเอาขึ้นมาวางตรงที่ตาเห็นตอนกำลังจะพิมพ์

เพราะงั้นต้นทุนของ turn นั้นไม่ได้ขึ้นกับว่าพิมพ์อะไรลงไป ขึ้นกับว่าแชทโตไปถึงไหนแล้วตอนที่กลับมา ห้าคำกับห้าร้อยคำ ราคาเท่ากัน

พอรู้ว่าต้องมองหาอะไร มันโผล่ทุกที่ สลับรุ่นด้วย /model กลางแชทที่ context 840K จ่าย $10.50 ก่อนมันจะเริ่มทำงาน สั่ง /ct ตอนเช้าวันถัดมาแทนที่จะสั่งตอนปิดงาน $16 ทั้งที่ถ้าสั่งตอน cache ยังอุ่น ประมาณ $0.5 สั่งเปิดไฟล์วิดีโอผ่าน turn ปกติแทนที่จะใช้ ! นำหน้า $3.20 สำหรับคำสั่งบรรทัดเดียว

มีแชทหนึ่งที่ค่า cold cache คิดเป็น 47% ของทั้ง session เกือบครึ่งของเงินในแชทนั้น ไม่ได้จ่ายไปกับงาน จ่ายไปกับการกลับมา


เรื่องที่คิดผิดมาตลอด รุ่นถูกไม่ได้แปลว่าจ่ายถูก

ตอนแรกคิดว่าถ้าจะประหยัด ก็ถอยไปใช้รุ่นเล็กลง

พอดูตัวเลขจริงแล้วไม่ใช่ เพราะงานที่คุยยาว ๆ ค่าใช้จ่ายส่วนใหญ่ไม่ได้อยู่ที่ output มันอยู่ที่ cache read ที่จ่ายซ้ำทุก turn

ราคา cache read ต่อ MTok เรียงจากถูกไปแพง Opus 5.5 อยู่ที่ $0.20 Fable $0.25 Sonnet $0.30 Opus 5 $0.50

ตัวเลขของ Sonnet เป็นราคาที่สมมติไว้ ไม่ได้ยืนยัน แต่ถ้าใกล้เคียงของจริง แปลว่าถอยจาก Opus 5.5 ไป Sonnet สำหรับงานที่ context ใหญ่ คือจ่ายแพงขึ้นเพื่อได้โมเดลที่อ่อนกว่า

เป้าของการ downgrade เลยไม่ใช่ Sonnet แต่เป็น Opus 5.5 ส่วน Sonnet เก็บไว้ใช้กับงานสั้น ๆ ที่ context ไม่โต

ทั้งการ audit รอบนี้ ตัวแปรที่ใหญ่ที่สุดไม่ใช่นิสัยการใช้ แต่เป็นการเลือกรุ่น ประมาณ 41% ของทั้งหมดอยู่ตรงนี้


ของที่เอาไปวางได้เลย

ส่วนนี้คือของจริงที่เปลี่ยนแล้ว ก็อปไปวางได้ ไม่ต้องรู้ว่าทำโปรเจกต์อะไรอยู่

1. settings.json

เอาไป merge เข้า ~/.claude/settings.json ได้เลย

{
  "env": {
    "CLAUDE_CODE_AUTO_COMPACT_WINDOW": "400000",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-5-5",
    "CLAUDE_CODE_SUBAGENT_MODEL": "claude-opus-5-5"
  },
  "hooks": {
    "UserPromptSubmit": [{ "hooks": [{ "type": "command",
      "command": "python3 ~/.claude/hooks/context-nudge.py", "timeout": 5 }] }],
    "SessionStart": [{ "matcher": "clear|startup", "hooks": [{ "type": "command",
      "command": "python3 ~/.claude/hooks/session-handoff.py", "timeout": 5 }] }]
  },
  "statusLine": { "type": "command",
    "command": "~/.claude/hooks/statusline.sh", "refreshInterval": 30 }
}

CLAUDE_CODE_AUTO_COMPACT_WINDOW บังคับให้ compact ก่อน context จะเลย 400K ไม่ต้องอาศัยวินัย

อีกสองตัวปักว่า alias คำว่า opus กับ subagent ที่ไม่ได้ระบุรุ่น ให้ไปลงที่รุ่นไหน ไม่งั้นมันเลือกให้เอง แล้วเราไม่รู้ว่ารันด้วยอะไรอยู่

ส่วน hooks กับ statusLine คือตัวที่ทำให้เห็นราคาก่อนกด enter กับตัวที่โหลด handoff กลับมาเอง ไฟล์สคริปต์อยู่ในลิงก์คอมเมนต์แรก

2. บล็อก routing ใน CLAUDE.md

# Model Routing
- Default: Opus 5.5
- Fable: เฉพาะงานตัดสินใจเรื่อง taste หรือ direction และต้องเปิด session ใหม่
- Sonnet: งานสั้น เสี่ยงต่ำ context ไม่โต
- Haiku: งานกวาดรายการแบบ mechanical ต่ำกว่า ~150K
- เช็คที่ตอบได้ด้วยสคริปต์ (diff, นับ byte, parse YAML, lint): ไม่ต้องใช้โมเดล
- ทุก agent() ต้องระบุ model + effort ห้ามปล่อยให้ inherit จาก session
- verifier loop ต้องมีเพดานรอบ
- ได้ค่า null ให้ fail ดัง ๆ ห้าม fallback เป็น 'UNVERIFIED'
- ถามขอบเขตหนึ่งคำถาม ก่อนยิง agent เกิน ~10 ตัว

สองข้อสุดท้ายไม่ได้เกี่ยวกับเงินโดยตรง แต่เจอมากับตัว ตอน verify มันพัง 529 แล้ว fallback เป็นคำว่า UNVERIFIED ผลคือเงินค่า verify หายไปเฉย ๆ แล้วรายงานยังดูเหมือนเสร็จสมบูรณ์

3. /wrap แล้ว /clear

skill ชื่อ wrap ทำหน้าที่สรุป session ปัจจุบันไม่เกิน 60 บรรทัด ลงไฟล์นอก repo โครงเป็นแบบนี้

# Wrap — <dir> — <YYYY-MM-DD HH:MM>
**Next:** งานถัดไปอันเดียว

## State
- กำลังทำอะไร · branch · ไฟล์ที่ยังไม่ commit
- รอบนี้ทำอะไรไปแล้ว (แนบ file:line หรือ commit sha)

## Decisions (locked — ห้ามรื้อ)
## Open
## Gotchas
## Resume
- ไฟล์ที่ต้องอ่านก่อน · คำสั่งที่ต้องรันซ้ำ

กติกาที่ทำให้มันใช้ได้จริง เก็บเฉพาะของที่ session หน้าหาเองจาก repo ไม่ได้ ห้ามแปะโค้ด ให้ชี้เป็น file:line แทน และข้อเท็จจริงที่แก้กลางทางให้ลง Gotchas

แล้ว /clear ทิ้ง

ฝั่ง SessionStart hook โหลดไฟล์นั้นกลับมาเองตอนเปิด session ใหม่ใน directory เดิม แล้วเปลี่ยนชื่อไฟล์เป็น consumed เพื่อไม่ให้โหลดซ้ำ (เปิดใหม่เฉย ๆ ก็โหลด ถ้าไฟล์อายุไม่เกิน 24 ชม.)

ผลคือไม่ต้องเล่าใหม่ และไม่ต้องแบก context เก่าทั้งก้อนไปจ่ายค่า cache ทุก turn

4. กฎเดียวที่ต้องจำ

พักเกินประมาณหนึ่งชั่วโมง อย่ากลับไปพิมพ์ต่อในแชทเดิม /wrap แล้ว /clear

คำสั่ง shell บรรทัดเดียวใช้ ! นำหน้า ไม่ต้องให้โมเดลอ่านทั้งแชทเพื่อมาพิมพ์คำว่า open


ผลกับสิ่งที่ยังไม่ได้แก้

ตั้งแต่ 4 ต.ค. ทุก session ไม่มีค่าใช้จ่ายใน main loop เหนือเส้น 400K เลย

พูดตรง ๆ อันนี้ไม่ใช่วินัย มันคือ setting ที่บังคับไว้ ถอด setting ออกเมื่อไหร่ นิสัยเดิมกลับมาทันที

ของที่ยังไม่ได้แก้ก็มี default model ของ main loop ยังเป็น Fable อยู่ในไฟล์ settings จนถึงตอนที่เขียนโพสต์นี้ ยังไม่ตัดใจ

และทั้งหมดนี้ผมทำตอนที่ราคายังไม่ขึ้น ซึ่งเป็นตอนเดียวที่ทำแล้วไม่เจ็บ


ศัพท์ที่โผล่ในโพสต์นี้

cache read — ราคาอ่าน context ก้อนเดิมที่ยังอยู่ใน cache ถูกที่สุดในสามตัว

cache write — ราคาเขียน context เข้า cache 1.25 เท่าของ input

cold cache — cache หมดอายุแล้ว ข้อความถัดไปต้องจ่ายค่าเขียนใหม่ทั้งก้อน

auto-compact — บีบ context อัตโนมัติเมื่อถึงเส้นที่ตั้งไว้

handoff — สรุปสั้น ๆ ที่เขียนไว้ก่อน /clear ให้ session ถัดไปรับงานต่อ