Prompt Caching for Multi-Tenant คืออะไร? การออกแบบเพื่อลดต้นทุนการประมวลผล (Inference) สำหรับ B2B SaaS

Prompt Caching for Multi-Tenant คืออะไร? การออกแบบเพื่อลดต้นทุนการประมวลผล (Inference) สำหรับ B2B SaaS

บทนำ

Prompt Caching for Multi-Tenant คือแนวทางการออกแบบเพื่อลดต้นทุนและค่าความหน่วง (Latency) ในการอนุมาน (Inference) ของ LLM (Large Language Model) ที่ใช้งานร่วมกันหลายผู้เช่า (Multi-tenant) โดยการแคช System Prompt หรือ Context Window ของแต่ละผู้เช่าไว้

ในธุรกิจ B2B SaaS เมื่อจำนวนผู้เช่าเพิ่มขึ้น จำนวนการเรียกใช้ API ของ LLM มักจะพุ่งสูงขึ้นตามไปด้วย ซึ่งส่งผลให้ต้นทุนของ Input Token กลายเป็นภาระต่อผลกำไร หากออกแบบ Prompt Caching ได้อย่างเหมาะสม จะสามารถลดต้นทุนในการอ่านข้อมูลจากแคชได้อย่างมหาศาล แต่ในขณะเดียวกันก็นำมาซึ่งความเสี่ยงเรื่องข้อมูลรั่วไหลระหว่างผู้เช่า (Data Leakage)

บทความนี้จะอธิบายเนื้อหาตามลำดับขั้นตอนต่อไปนี้ สำหรับวิศวกรและสถาปนิกซอฟต์แวร์:

Prompt Cache คือกลไกที่ช่วยให้เซิร์ฟเวอร์สามารถเก็บส่วนต้นของ Prompt ที่มีความยาว ซึ่งมักถูกส่งไปพร้อมกับคำขอ (Request) ไปยัง LLM ในทุกๆ ครั้งเอาไว้ เพื่อข้ามขั้นตอนการประมวลผลในส่วนนั้นสำหรับการเรียกใช้งานในครั้งถัดไป สำหรับ B2B SaaS ที่มีการใช้ System Prompt หรือบริบท (Context) แบบคงที่ซ้ำๆ กลไกนี้จะส่งผลโดยตรงต่อต้นทุนในการอนุมาน (Inference Cost)

อย่างไรก็ตาม ในสภาพแวดล้อมแบบ Multi-tenant เรื่องนี้ไม่ได้เรียบง่ายเช่นนั้น หากพยายามนำ System Prompt หรือข้อมูลสิทธิ์การเข้าถึงที่แตกต่างกันของแต่ละ Tenant ไปเก็บไว้ใน Cache ปัญหาเรื่องการแยกส่วนข้อมูล (Isolation) ว่า "จะเก็บ Cache ของใครไว้ที่ไหน" ก็จะเกิดขึ้น หากให้ความสำคัญกับการลดต้นทุนด้วยการแชร์ Cache ร่วมกัน ก็จะเกิดความเสี่ยงเรื่องข้อมูลรั่วไหล แต่หากแยก Cache ของแต่ละ Tenant ออกจากกันโดยสมบูรณ์ อัตราการเข้าถึง Cache (Cache Hit Rate) ก็จะลดลง ทำให้ประสิทธิภาพในการลดต้นทุนต่ำลงตามไปด้วย

ในที่นี้ เราจะมาเรียบเรียงตั้งแต่หลักการทำงานพื้นฐานของ Prompt Cache โครงสร้างต้นทุนที่เป็นเอกลักษณ์ของ B2B SaaS ไปจนถึงเหตุผลที่การออกแบบ Cache ในสภาพแวดล้อมแบบ Multi-tenant มีความซับซ้อนตามลำดับ

กลไกของ Prompt Caching และหลักการลดจำนวน Token

Prompt Cache คือกลไกที่เซิร์ฟเวอร์สำหรับประมวลผลการอนุมาน (Inference Server) จะเก็บ "ส่วนต้นที่ไม่เปลี่ยนแปลง (Prefix)" ของคำขอ API ที่ส่งไปยัง LLM ไว้ในหน่วยความจำ เพื่อนำกลับมาใช้ใหม่ในคำขอถัดไป แทนที่จะต้องประมวลผล System Prompt หรือบริบทที่ยาวเหยียดซ้ำทุกครั้ง การอ่านค่า Key-Value Tensor ที่แคชไว้จะช่วยลดต้นทุนในการประมวลผล Input Token ลงได้อย่างมาก

ในตอนแรก หลายคนอาจเข้าใจผิดว่า "แคชคือการบันทึกคำตอบทั้งหมดไว้" แต่ความเข้าใจที่ถูกต้องคือ การแคชจะทำเฉพาะ Prefix ฝั่ง Input ของ Prompt เท่านั้น เนื่องจาก Output จะถูกสร้างขึ้นใหม่ทุกครั้ง ดังนั้นสิ่งที่ลดต้นทุนได้จึงจำกัดอยู่แค่เพียงต้นทุนการประมวลผล Input Token เท่านั้น

สรุปกลไกการทำงานได้ดังนี้:

  • การเขียน (Write): ในคำขอครั้งแรก Prefix จะถูกบันทึกไว้ในรูปแบบ Key-Value Tensor
  • การอ่าน (Read): แคชจะถูกนำกลับมาใช้ใหม่ในคำขอถัดไปที่มี Prefix เดียวกัน
  • ส่วนต่างของต้นทุน: สำหรับ Claude ของ Anthropic ต้นทุนการอ่านแคชจะอยู่ที่ 0.1 เท่า ของราคา Input ปกติ ในขณะที่การเขียนแคชจะอยู่ที่ 1.25 เท่า (กรณี TTL 5 นาที)

จำนวน Token ขั้นต่ำที่ทำให้แคชทำงานได้จะแตกต่างกันไปตามผู้ให้บริการ โดย OpenAI กำหนดไว้ที่ 1,024 Token ขึ้นไป ส่วน Claude Opus บน AWS Bedrock กำหนดไว้ที่ 4,096 Token ขึ้นไป

นอกจากนี้ TTL (Time To Live หรือระยะเวลาคงอยู่ของแคช) ยังเป็นตัวแปรสำคัญในการออกแบบอีกด้วย

รายละเอียดและโจทย์ของต้นทุนการอนุมานใน B2B SaaS

ต้นทุนการอนุมาน (Inference cost) ของ B2B SaaS มีลักษณะเฉพาะคือไม่ได้เพิ่มขึ้นตามจำนวนผู้ใช้งาน แต่เพิ่มขึ้นตามผลคูณของ จำนวนผู้เช่า (Tenant) × จำนวนคำขอ (Request) × จำนวนโทเค็น (Token) ซึ่งต่างจากบริการสำหรับผู้ใช้งานทั่วไปตรงที่ต้องส่ง System Prompt หรือกฎทางธุรกิจเฉพาะของลูกค้าองค์กรแต่ละรายไปทุกครั้ง ทำให้จำนวน Input Token มีขนาดใหญ่ขึ้นอย่างต่อเนื่อง

เมื่อแยกโครงสร้างต้นทุนออกมา จะพบว่ามี 3 ชั้นซ้อนทับกันอยู่ ชั้นแรกคือ System Prompt Layer ซึ่งรวบรวมกฎทางธุรกิจ ข้อจำกัด และการกำหนดตัวตน (Persona) ของผู้เช่าแต่ละรายไว้ โดยเนื้อหาอาจมีตั้งแต่หลายร้อยไปจนถึงหลายพันโทเค็น ชั้นถัดมาคือ Context Layer ซึ่งประกอบด้วยประวัติการสนทนาและส่วนของเอกสารที่ได้จาก RAG ซึ่งจะถูกส่งใหม่ทุกครั้งที่มีการร้องขอ และชั้นสุดท้ายคือ User Input Layer หรือคำถามที่ผู้ใช้งานพิมพ์เข้ามาจริง ซึ่งมักจะมีจำนวนโทเค็นน้อยกว่าเมื่อเทียบกับส่วนอื่น

หากมีผู้เช่าเพียงหลักสิบราย ต้นทุนรายเดือนมักจะยังอยู่ในระดับที่ยอมรับได้ แต่เมื่อขยายไปถึงระดับหลายร้อยหรือหลายพันราย สถานการณ์จะเปลี่ยนไป ต้นทุนในการส่ง Prompt โครงสร้างเดิมแบบเต็มรูปแบบให้กับผู้เช่าทุกรายจะกลายเป็นสิ่งที่มองข้ามไม่ได้เมื่อมีการขยายขนาด (Scale)

ปัญหาที่รุนแรงเป็นพิเศษคือ "การส่งข้อมูลส่วนที่ซ้ำซ้อนกัน" (Redundant transmission of common parts) ใน B2B SaaS หลายแห่งมีการรวมเนื้อหาที่เหมือนกันในทุกผู้เช่า เช่น ข้อมูลจำเพาะของผลิตภัณฑ์, FAQ และกฎการปฏิบัติตามข้อกำหนด (Compliance rules) ไว้ใน System Prompt แม้เนื้อหาจะเหมือนกันทุกประการในทุกผู้เช่า แต่กลับถูกนับเป็นโทเค็นที่ต้องเสียค่าใช้จ่ายในทุกคำขอ ซึ่งจุดนี้มักกลายเป็นจุดบอดในโครงสร้างต้นทุนได้ง่าย

เหตุผลที่การทำ Cache ในสภาพแวดล้อมแบบ Multi-tenant เป็นเรื่องยาก

「หากแคช System Prompt ของ Tenant A แล้ว จะเกิดเหตุการณ์ที่คำขอของ Tenant B ไปเรียกใช้แคชนั้นหรือไม่?」—— เมื่อเริ่มพิจารณาการทำ Prompt Caching ในสภาพแวดล้อมแบบ Multi-tenant วิศวกรจำนวนมากมักจะเผชิญกับความกังวลนี้เป็นอันดับแรก

ความกังวลนี้ไม่ใช่เรื่องที่คิดไปเองแต่อย่างใด โดยเหตุผลหลักที่ทำให้การทำแคชในสภาพแวดล้อมแบบ Multi-tenant เป็นเรื่องยาก สามารถสรุปได้เป็น 3 ประเด็นดังนี้:

  • ข้อกำหนดในการแยกข้อมูลระหว่าง Tenant: System Prompt ของแต่ละ Tenant จะมีข้อมูลเฉพาะตัว เช่น กฎทางธุรกิจ โครงสร้างราคา และนโยบายความปลอดภัย หากออกแบบ Cache Key ผิดพลาด อาจมีความเสี่ยงที่คำขอของ Tenant อื่นจะไปอ้างอิงถึงแคชเดียวกันได้
  • ความหลากหลายของโครงสร้าง Prompt: เนื่องจากความยาวและองค์ประกอบของ System Prompt ในแต่ละ Tenant แตกต่างกัน ทำให้ส่วนที่เป็น Common Prefix สั้นลง ส่งผลให้อัตรา Cache Hit ลดลง นอกจากนี้ Prompt Caching จะมีประสิทธิภาพน้อยหากไม่มีส่วนที่เหมือนกันเกิน 1,024 Token ซึ่งเป็นจุดที่เพิ่มความยากในการออกแบบ
  • ความไม่สอดคล้องกันระหว่าง TTL และวงจรชีวิตของ Tenant: เมื่อ Tenant มีการเปลี่ยนแปลงสัญญาหรืออัปเดตแพ็กเกจ หากค่า TTL ของแคชยังคงเหลืออยู่ อาจเกิดความเสี่ยงที่ระบบจะยังคงใช้การตั้งค่าเก่าต่อไป สำหรับ Claude ของ Anthropic นั้น ค่า TTL มาตรฐานคือ 5 นาที และสามารถขยายได้สูงสุด 1 ชั่วโมง แต่จำเป็นต้องมีกลไกแยกต่างหากเพื่อสร้างสมดุลระหว่างความถี่ในการเปลี่ยนแปลงการตั้งค่าของ Tenant กับค่า TTL

นอกจากนี้ เมื่อจำนวน Tenant เพิ่มขึ้น จำนวน Cache Entry ก็จะเพิ่มขึ้นตามไปด้วย ซึ่งมีแนวโน้มที่ต้นทุนการจัดการจะขยายตัวมากกว่าแบบเชิงเส้น (Linear)

สิ่งที่ต้องตรวจสอบก่อนออกแบบ: เงื่อนไขเบื้องต้นและการเลือกสถาปัตยกรรม

บทสรุป: ก่อนเริ่มการออกแบบ การตรวจสอบ 3 ประเด็น ได้แก่ ข้อมูลจำเพาะของแคช (Cache specifications), รูปแบบการแยกผู้เช่า (Tenant isolation models) และโครงสร้างของ System prompt ของผู้ให้บริการ LLM ที่จะใช้งาน ถือเป็นเงื่อนไขเบื้องต้นในการเพิ่มประสิทธิภาพการลดต้นทุนให้สูงสุด

เนื่องจากความผิดพลาดในการเลือกแต่ละองค์ประกอบจะนำไปสู่การแก้ไขงานครั้งใหญ่ในขั้นตอนถัดไป เราจึงจะขอสรุปประเด็นต่างๆ ตามลำดับดังนี้

การตรวจสอบผู้ให้บริการ LLM และ API ที่รองรับการทำ Cache

ก่อนที่จะเริ่มออกแบบระบบแคช (Cache) คุณจำเป็นต้องทำความเข้าใจให้ชัดเจนว่าผู้ให้บริการที่คุณเลือกนั้น "มีเงื่อนไขอย่างไรในการเปิดใช้งานแคช" ในช่วงแรกหลายคนมักคิดว่า "สามารถใช้งานได้เหมือนกันในทุกโมเดล" แต่ในความเป็นจริงแล้ว จำนวนโทเค็นขั้นต่ำสำหรับแคช, TTL (Time To Live) และวิธีการเรียกใช้งาน API ของผู้ให้บริการแต่ละรายนั้นมีความแตกต่างกันอย่างมาก

ข้อมูลจำเพาะของผู้ให้บริการหลักมีดังนี้:

  • OpenAI: แคชจะทำงานเมื่อพรอมต์มีขนาดตั้งแต่ 1,024 โทเค็นขึ้นไป คุณสามารถตรวจสอบจำนวนแคชฮิตได้จาก usage.prompt_tokens_details.cached_tokens ในส่วนของ Response โดย In-memory แคชจะหมดอายุภายใน 5-10 นาทีหากไม่มีการใช้งาน และเก็บไว้ได้นานสูงสุด 1 ชั่วโมง ส่วนแบบ Extended สามารถเก็บไว้ได้นานสูงสุด 24 ชั่วโมง ทั้งนี้ แนะนำให้จำกัดการใช้ prompt_cache_key ที่มีคำนำหน้าเดียวกันไว้ที่น้อยกว่า 15 คำขอต่อนาที
  • Anthropic (Claude): TTL เริ่มต้นคือ 5 นาที สำหรับตัวเลือก 1 ชั่วโมงจะมีค่าใช้จ่ายเพิ่มเติม โดยต้นทุนการเขียนแคชจะอยู่ที่ 1.25 เท่า (สำหรับ 5 นาที) หรือ 2 เท่า (สำหรับ 1 ชั่วโมง) ของราคาอินพุตพื้นฐาน ส่วนการอ่านจะอยู่ที่ 0.1 เท่า
  • AWS Bedrock (Claude Sonnet): จำนวนโทเค็นขั้นต่ำสำหรับแคชคือ 1,024 โทเค็น และมี TTL เริ่มต้นที่ 5 นาที สำหรับ Claude Opus จะต้องใช้โทเค็นขั้นต่ำ 4,096 โทเค็น และสามารถตั้งค่าตัวแบ่งแคช (Cache breakpoints) ได้สูงสุด 4 จุด

การเลือกรูปแบบการแยก Tenant (Silo, Pool และ Hybrid)

การเลือกรูปแบบการแยกส่วนผู้เช่า (Tenant Isolation Model) ถือเป็นรากฐานของการออกแบบแคช จึงจำเป็นต้องดำเนินการในช่วงเริ่มต้นของการตัดสินใจด้านสถาปัตยกรรม โดยมีรายละเอียดลักษณะเด่นของ 3 รูปแบบหลักดังนี้:

รูปแบบไซโล (Silo) เป็นรูปแบบที่จัดสรร LLM Endpoint และพื้นที่แคชเฉพาะสำหรับผู้เช่าแต่ละราย มีความเสี่ยงในการปนเปื้อนของข้อมูลต่ำที่สุด จึงเหมาะสำหรับ SaaS ในกลุ่มการเงินหรือการแพทย์ที่มีข้อกำหนดด้านการปฏิบัติตามกฎระเบียบ (Compliance) ที่เข้มงวด อย่างไรก็ตาม เนื่องจากขอบเขตการนำแคชกลับมาใช้ใหม่จำกัดอยู่เพียงภายในผู้เช่ารายนั้นๆ ต้นทุนด้านโครงสร้างพื้นฐานจึงมีแนวโน้มเพิ่มขึ้นเป็นเส้นตรงตามจำนวนผู้เช่าที่เพิ่มขึ้น

รูปแบบพูล (Pool) เป็นรูปแบบที่ผู้เช่าหลายรายใช้พื้นที่แคชและ System Prompt ร่วมกัน ซึ่งจะช่วยเพิ่มอัตราการเข้าถึงแคช (Cache Hit Rate) ของคำนำหน้า (Prefix) ที่ใช้ร่วมกัน และช่วยลดต้นทุนในการประมวลผล (Inference Cost) ได้อย่างมาก ในทางกลับกัน หากการออกแบบ Cache Key ไม่รัดกุมเพียงพอ อาจเกิดความเสี่ยงในการปนเปื้อนของข้อมูลระหว่างผู้เช่าได้ ดังนั้นการออกแบบ Cache Key ที่จะกล่าวถึงในภายหลังจึงมีความสำคัญเป็นพิเศษ

รูปแบบไฮบริด (Hybrid) เป็นรูปแบบผสมผสานที่จัดการส่วนที่ใช้ร่วมกัน (System Prompt ของผลิตภัณฑ์) ด้วยรูปแบบพูล และแยกส่วนบริบทเฉพาะของผู้เช่าด้วยรูปแบบไซโล เป็นรูปแบบที่สร้างสมดุลระหว่างความคุ้มค่าด้านต้นทุนและความปลอดภัยได้ง่าย จึงเหมาะสำหรับ B2B SaaS จำนวนมาก

เกณฑ์ในการตัดสินใจเลือกมีดังนี้: หากผู้เช่าแต่ละรายมีข้อกำหนดด้านอธิปไตยของข้อมูลหรือข้อกำหนดด้านการปฏิบัติตามกฎระเบียบที่แตกต่างกัน รูปแบบไซโลหรือไฮบริดจะเหมาะสมกว่า แต่หากความต้องการระหว่างผู้เช่ามีความสอดคล้องกันและให้ความสำคัญกับการเพิ่มประสิทธิภาพด้านต้นทุนเป็นหลัก รูปแบบพูลจะเป็นตัวเลือกที่เหมาะสมกว่า

แนวทางการออกแบบ Context Window และ System Prompt

「ระบบควรใช้ System Prompt ร่วมกันแค่ไหน และส่วนไหนควรเป็นของเฉพาะ Tenant (ผู้เช่า)」——นี่คือจุดที่หลายทีมมักจะตัดสินใจได้ยาก แนวทางการออกแบบ Context Window และ System Prompt ส่งผลโดยตรงต่อประสิทธิภาพของ Cache จึงจำเป็นต้องจัดระเบียบให้เรียบร้อยก่อนเริ่มการใช้งานจริง

หลักการพื้นฐานของการออกแบบคือ 「การรวมส่วนที่ไม่เปลี่ยนแปลงไว้ที่ส่วนต้น」 เนื่องจาก Prompt Cache ของ LLM (Large Language Model) จะทำการแคช Prefix ที่ตรงกันตั้งแต่ส่วนต้นของ Prompt การวาง Context ส่วนกลางที่ไม่เปลี่ยนแปลงไว้ที่ด้านหน้า และวางข้อมูลเฉพาะของ Tenant ไว้ที่ด้านหลัง จะช่วยเพิ่มอัตรา Cache Hit ได้อย่างมาก

การออกแบบ System Prompt โดยแบ่งออกเป็น 3 เลเยอร์ดังต่อไปนี้จะให้ผลลัพธ์ที่มีประสิทธิภาพ:

  • เลเยอร์ส่วนกลาง (Common Layer - แชร์ร่วมกันทุก Tenant): กฎการทำงานพื้นฐานของผลิตภัณฑ์, รูปแบบการตอบกลับ, ข้อห้ามต่างๆ ฯลฯ โดยรวบรวมส่วนที่มีความยาวมากที่สุดและมีการเปลี่ยนแปลงน้อยไว้ที่นี่
  • เลเยอร์แผนการใช้งาน (Plan Layer - แชร์ตามแผนหรือประเภทธุรกิจ): คำอธิบายฟีเจอร์สำหรับองค์กร (Enterprise) หรือแนวทางปฏิบัติเฉพาะอุตสาหกรรม ซึ่งเป็น Context ที่ Tenant ในแผนเดียวกันสามารถใช้ร่วมกันได้
  • เลเยอร์เฉพาะของ Tenant (Tenant-Specific Layer): ชื่อ Tenant, การตั้งค่าแบบกำหนดเอง, นโยบายเฉพาะ ฯลฯ โดยออกแบบให้สั้นที่สุดเท่าที่จะเป็นไปได้

สำหรับ Claude ของ Anthropic ขนาดขั้นต่ำของ Cache คือ 1,024 Token หากออกแบบให้เลเยอร์ส่วนกลางมีขนาดเกินเกณฑ์นี้ การเขียน Cache จะเริ่มทำงาน สำหรับ Claude Opus บน AWS Bedrock จำเป็นต้องใช้ขั้นต่ำ 4,096 Token ดังนั้นโปรดปรับปริมาณของเลเยอร์ส่วนกลางให้เหมาะสมตามผู้ให้บริการแต่ละราย

วิธีการออกแบบ: รูปแบบการออกแบบ Cache Key สำหรับแต่ละ Tenant

บทสรุป: ความผิดพลาดในการออกแบบแคชคีย์ (Cache Key) จะนำไปสู่การปนเปื้อนของข้อมูลระหว่างผู้เช่า (Tenant) โดยตรง ดังนั้นการกำหนดกฎการตั้งชื่อ (Naming Convention) และโครงสร้างการแยกข้อมูลให้ชัดเจนตั้งแต่ต้นจึงเป็นสิ่งที่จำเป็นอย่างยิ่ง

ในการออกแบบแคชคีย์สำหรับแต่ละผู้เช่า หัวใจสำคัญอยู่ที่การผสมผสานระหว่างคำนำหน้า (Shared Prefix) และคำต่อท้ายเฉพาะของผู้เช่า (Tenant-specific Suffix) รวมถึงวิธีการจัดการค่า TTL (Time-to-Live) ให้เหมาะสม

วิธีการรวม Tenant ID เข้ากับ Cache Key

ความผิดพลาดที่มักเกิดขึ้นเป็นอันดับแรกในการออกแบบ Cache Key คือความคิดที่ว่า "แค่ใช้ User ID หรือ Session ID เป็นคีย์ก็เพียงพอแล้ว" แต่ในความเป็นจริง การวาง Tenant ID ไว้ที่ตำแหน่งบนสุดของ Namespace เสมอ จะช่วยเพิ่มทั้งความมั่นใจในการแยกข้อมูลและความสามารถในการทำ Cache ได้ดีกว่า

เหตุใดจึงต้องวางไว้ที่ตำแหน่งแรก? เนื่องจาก Prompt Cache ของ LLM Provider จะตัดสินการ Hit ของ Cache ด้วยการตรวจสอบความตรงกันจากส่วนหน้าของสตริง (Prefix Match) โครงสร้างของ Cache Key จึงจำเป็นต้องสอดคล้องกับลำดับของ Prompt ด้วยเหตุนี้ โครงสร้างคีย์ที่แนะนำจึงเป็นดังนี้:

{tenant_id}:{model_id}:{prompt_version}:{context_hash}

ในส่วนของ tenant_id ที่ตำแหน่งแรก ให้ใช้ UUID หรือค่าที่ระบุตัวตนของ Tenant ได้อย่างเฉพาะเจาะจง การเริ่มต้นด้วยค่านี้จะช่วยขจัดโอกาสที่ Cache ของ Tenant ต่างกันจะปะปนกันโดยสิ้นเชิง ส่วน model_id ที่ตามมามีไว้เพื่อป้องกันการชนกันของข้อมูลในกรณีที่ Tenant เดียวกันใช้หลายโมเดล, prompt_version มีไว้เพื่อยกเลิกการใช้งาน Cache เก่าโดยอัตโนมัติเมื่อมีการเปลี่ยนแปลง System Prompt และ context_hash คือค่าที่ได้จากการนำ Context เฉพาะของผู้ใช้มาทำ Hash ด้วย SHA-256 เป็นต้น

ทั้งนี้ คุณสามารถนำ Identifier ที่จัดการในฝั่งแอปพลิเคชันมาใช้เป็น Tenant ID ได้โดยตรง แต่การใช้ Internal UUID ที่ไม่เปิดเผยสู่ภายนอกจะช่วยลดความเสี่ยงด้านความปลอดภัยได้ดียิ่งขึ้น

โครงสร้างการแยก Shared Prefix และ Tenant-specific Suffix

โครงสร้างการแยกพรอมต์ (Prompt) ออกเป็น "Shared Prefix" (ส่วนนำที่ใช้ร่วมกัน) และ "Tenant-specific Suffix" (ส่วนท้ายเฉพาะของแต่ละผู้เช่า) เป็นรูปแบบพื้นฐานสำหรับการเพิ่มประสิทธิภาพการแคช (Cache) ให้สูงสุดในสภาพแวดล้อมแบบ Multi-tenant

โครงสร้างพื้นฐานของการแยกส่วน

ประกอบพรอมต์ทั้งหมดโดยแบ่งออกเป็น 3 ชั้น ดังนี้:

  • Shared Prefix Layer: ระบบพรอมต์ที่ใช้ร่วมกันสำหรับทุกผู้เช่า (เช่น คำอธิบายผลิตภัณฑ์, นโยบายความปลอดภัย, คำสั่งรูปแบบเอาต์พุต)
  • Tenant-specific Layer: การตั้งค่าเฉพาะสำหรับแต่ละผู้เช่า (เช่น ชื่อบริษัท, ประเภทธุรกิจ, กฎเฉพาะ)
  • User Request Layer: อินพุตของผู้ใช้ในแต่ละคำขอ

เป้าหมายของการแคชคือ "Shared Prefix Layer" เป็นหลัก เนื่องจากส่วนนี้จะเหมือนกันในทุกคำขอ จึงทำให้มีอัตรา Cache Hit สูงที่สุด

การออกแบบตามเงื่อนไข

หากส่วนที่เป็นระบบพรอมต์ร่วมกันระหว่างผู้เช่ามีขนาดใหญ่ การกำหนดให้ Shared Prefix มีความยาวมากขึ้นจะช่วยเพิ่มประสิทธิภาพการแคชได้ แต่หากนโยบายหรือการตั้งค่าภาษาของแต่ละผู้เช่ามีความแตกต่างกันมาก การวาง Tenant-specific Layer ไว้ในลำดับก่อนหน้าเพื่อแยกแคชตามผู้เช่าจะเป็นการออกแบบที่เหมาะสมกว่า

ตัวอย่างการนำไปใช้งาน (แนวคิด)

[Shared Prefix: 2,000 โทเค็น]
คุณคือ AI ผู้ช่วยของแพลตฟอร์ม SaaS
เอาต์พุตต้องเป็นภาษาไทย
---

แนวคิดเรื่องวันหมดอายุของ Cache และการจัดการ TTL

「ควรตั้งค่า TTL ของแคชไว้ที่เท่าไหร่ดี」—— นี่คือคำถามแรกที่มักจะทำให้เกิดความลังเลในหน้างานจริงของการพัฒนา

การตั้งค่า TTL ขึ้นอยู่กับการแลกเปลี่ยน (Trade-off) ระหว่างผลลัพธ์ในการลดต้นทุนและความสดใหม่ของข้อมูล โดยสรุปตามข้อกำหนดของผู้ให้บริการหลักได้ดังนี้:

ค่า TTL เริ่มต้นของผู้ให้บริการหลัก

  • Anthropic (Claude): ค่าเริ่มต้น 5 นาที, เลือกได้สูงสุด 1 ชั่วโมง (มีค่าใช้จ่ายเพิ่มเติม)
  • AWS Bedrock (Claude Sonnet): ค่าเริ่มต้น 5 นาที
  • AWS Bedrock (Claude Opus): เลือกได้ระหว่าง 5 นาที หรือ 1 ชั่วโมง
  • OpenAI: หมดอายุภายใน 5-10 นาทีหากไม่มีการใช้งาน, สูงสุด 1 ชั่วโมง (Extended สูงสุด 24 ชั่วโมง)

เกณฑ์ในการตัดสินใจเลือก TTL

ในทางปฏิบัติ การกำหนด TTL ควรพิจารณาจาก 2 แกนหลัก คือ "ความถี่ในการอัปเดต System Prompt" และ "ความหนาแน่นของคำขอ (Request Density)"

  • กรณีที่ความถี่ในการอัปเดตต่ำ และมีช่วงเวลาที่คำขอเข้ามาหนาแน่น → การใช้ TTL 1 ชั่วโมงจะช่วยให้คืนทุนค่าเขียนแคชได้ง่ายกว่า
  • กรณีที่ System Prompt เปลี่ยนแปลงบ่อยในแต่ละ Tenant → การใช้ TTL 5 นาทีจะช่วยรักษาความสดใหม่ของข้อมูล พร้อมทั้งลดต้นทุนในช่วงที่มีการใช้งานหนาแน่นในระยะสั้นได้

สำหรับโครงสร้างราคาของ Anthropic ค่าเขียนแคชแบบ 1 ชั่วโมงจะอยู่ที่ 2 เท่าของราคา Input พื้นฐาน และค่าอ่านอยู่ที่ 0.1 เท่า ดังนั้นเพื่อให้คุ้มทุนกับค่าเขียน 1 ครั้ง จำเป็นต้องมีการเรียกใช้แคชเดิมซ้ำหลายครั้งภายในระยะเวลา TTL ที่กำหนด

วิธีการนำไปใช้งาน: ขั้นตอนการสร้างทีละสเต็ป

บทสรุป: เมื่อกำหนดแนวทางการออกแบบได้แล้ว ให้ดำเนินการพัฒนาตาม 3 ขั้นตอน ได้แก่ การทำเทมเพลต (Templating), การสร้างเลเยอร์แคช (Cache Layer), และการวัดผล (Measurement)

เริ่มจากการทำเทมเพลตสำหรับ System Prompt ตามด้วยการนำเลเยอร์แคชมาใช้งาน และปิดท้ายด้วยการวัดอัตราการเข้าถึงแคช (Hit Rate) โดยรายละเอียดของแต่ละขั้นตอนจะอธิบายไว้ในหัวข้อ H3 ด้านล่างนี้

Step 1: การทำ Template ของ System Prompt แยกตาม Tenant

ในตอนแรก เรามักจะคิดว่า "การเตรียม System Prompt ที่แตกต่างกันโดยสิ้นเชิงสำหรับแต่ละ Tenant น่าจะดีกว่า" แต่ในความเป็นจริงแล้ว โครงสร้างแบบ Template ที่แยกส่วนที่เหมือนกันและส่วนที่แตกต่างกันอย่างชัดเจนนั้นมีประสิทธิภาพมากกว่า ทั้งในด้านอัตราการ Cache Hit และความสามารถในการบำรุงรักษา (Maintainability)

หลักการพื้นฐานของการทำ Template คือการแบ่ง Prompt ออกเป็น 2 ชั้น ได้แก่ "ชั้น Prefix ส่วนกลาง" และ "ชั้นเฉพาะของ Tenant"

ชั้น Prefix ส่วนกลาง (เป้าหมายการทำ Cache)

  • กฎที่ไม่เปลี่ยนแปลง เช่น การกำหนดบทบาทของ AI, คำสั่งรูปแบบการส่งออก (Output Format) และข้อห้ามต่างๆ
  • ตรรกะทางธุรกิจและแนวทางปฏิบัติที่ใช้ร่วมกันในทุก Tenant
  • สำหรับ OpenAI จำเป็นต้องมีอย่างน้อย 1,024 Token และสำหรับ Anthropic (Claude) ก็เช่นกัน ดังนั้นควรออกแบบส่วนกลางให้มีขนาดเกินเกณฑ์นี้

ชั้นเฉพาะของ Tenant (ไม่ทำ Cache)

  • พารามิเตอร์แบบไดนามิก เช่น ชื่อ Tenant, แผนสัญญา, และแฟล็กฟังก์ชันการทำงานที่เปิดใช้งาน
  • การกำหนดโทนเสียงหรือแนวทางแบรนด์เฉพาะของ Tenant

ในการนำไปใช้งาน แนวทางทั่วไปคือการใช้ Template Engine เช่น Jinja2 และจัดการ Prefix ส่วนกลางเป็นสตริงแบบคงที่ (Static String) ส่วนพารามิเตอร์เฉพาะของ Tenant จะถูกใส่ไว้ด้านหลังผ่าน Placeholder เช่น {{ tenant_name }} โดยต้องออกแบบให้ลำดับไบต์ของส่วน Prefix ไม่เปลี่ยนแปลงหลังจากการเรนเดอร์

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

Step 2: การนำ Cache Layer มาใช้และการจัดการ Prompt Hash

การส่ง System Prompt ที่ทำเป็นเทมเพลตไปยัง API โดยตรงเพียงอย่างเดียว อาจไม่ได้รับประโยชน์จากระบบแคชอย่างเสถียร การสร้างเลเยอร์แคช (Cache Layer) แยกต่างหากเพื่อบริหารจัดการแบบรวมศูนย์ด้วย "Prompt Hash" จึงเป็นสิ่งจำเป็นสำหรับการดำเนินงานที่มั่นคงในสภาพแวดล้อมแบบ Multi-tenant

โครงสร้างพื้นฐานของเลเยอร์แคช

โครงสร้างที่นิยมใช้คือการแทรก Cache Proxy ขนาดเล็กไว้ระหว่างเลเยอร์แอปพลิเคชันและ LLM API โดยมีขั้นตอนการประมวลผลดังนี้:

  1. นำ Tenant ID และหมายเลขเวอร์ชันของ System Prompt มาเชื่อมกัน แล้วสร้างค่าแฮช (Hash Value) ด้วยวิธีอย่าง SHA-256
  2. ลงทะเบียนรายการแคชใน In-memory store เช่น Redis โดยใช้ค่าแฮชเป็นคีย์
  3. หากมีคำขอที่ใช้แฮชเดียวกันเข้ามา ให้ส่งข้อมูลกลับจากแคชและข้ามการเรียกใช้งาน LLM API

เกณฑ์การตัดสินใจในการจัดการ Prompt Hash

สำหรับการออกแบบที่เหมาะสม หากเป็น Tenant ที่มีปริมาณคำขอสูง ควรจัดการแฮชในหน่วยความจำ (Redis) เพื่อลด Latency ให้เหลือน้อยที่สุด แต่หากเป็น Tenant ที่มีความถี่ของคำขอน้อย ควรเลือกการออกแบบที่เน้นความคุ้มค่าโดยการบันทึกแฮชไว้ในฐานข้อมูลแทน

การทำงานร่วมกับผู้ให้บริการ LLM

เนื่องจากค่าใช้จ่ายในการอ่านแคชของ Anthropic อยู่ที่ 0.1 เท่าของราคา Input พื้นฐาน หากแฮชตรงกันและแคชฝั่งผู้ให้บริการถูกเรียกใช้งาน (Hit) จะช่วยลดต้นทุนได้สองต่อ โดยค่าใช้จ่ายในการเขียนแคชจะมี TTL อยู่ที่ 5 นาที 1.

Step 3: การวัดอัตรา Cache Hit และการตั้งค่า AI Observability

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

จุดเริ่มต้นของการวัดผลคือฟิลด์ usage.prompt_tokens_details.cached_tokens ใน API Response โดยให้ดึงจำนวนการ Hit ของแคชจากฟิลด์นี้ และบันทึกตัวชี้วัดต่อไปนี้อย่างต่อเนื่อง:

  • อัตราการ Hit ของแคช (Cache Hit Rate): คำนวณจาก cached_tokens ÷ total_prompt_tokens
  • อัตราการ Hit แยกตาม Tenant: รวบรวมข้อมูลแยกตาม Tenant ID เพื่อระบุ Tenant ที่มีอัตราการ Hit ต่ำ
  • จำนวนครั้งที่ TTL หมดอายุ: บันทึกความต่างของเวลาระหว่างการเขียนแคชและการ Hit เพื่อใช้เป็นข้อมูลประกอบการตัดสินใจว่า TTL 5 นาที หรือ 1 ชั่วโมง แบบใดเหมาะสมกว่ากัน

แนวทางทั่วไปคือการส่งเมทริกซ์เหล่านี้ไปยังแพลตฟอร์ม Observability เช่น Datadog หรือ Grafana เพื่อนำไปแสดงผลบนแดชบอร์ด ดังที่ได้อธิบายไว้ใน วิธีการวัดผลหลังจากนำ AI Agent มาใช้งาน|ตั้งแต่การออกแบบ KPI ไปจนถึงการปรับปรุงอย่างต่อเนื่อง การกำหนด KPI ไว้ล่วงหน้าจะช่วยให้การหมุนเวียนวงจรการปรับปรุงทำได้ง่ายขึ้น

นอกจากนี้ การตั้งค่าการแจ้งเตือน (Alert) ก็เป็นสิ่งที่ขาดไม่ได้ โดยตัวอย่างเกณฑ์ (Threshold) ที่แนะนำมีดังนี้:

วิธีป้องกันข้อมูลรั่วไหลระหว่าง Tenant: การประยุกต์ใช้ Privacy by Isolation

ยิ่งมีการใช้แคช (Cache) มากเท่าใด ความเสี่ยงในการรั่วไหลของข้อมูลระหว่างผู้เช่า (Tenant) ก็ยิ่งสูงขึ้นเท่านั้น ปัญหานี้ไม่สามารถแก้ไขได้ด้วยการปรับปรุงภายหลัง แต่จะต้องถูกรวมเข้าไว้ตั้งแต่ขั้นตอนการออกแบบโครงสร้างแคช ต่อไปนี้จะสรุปความเสี่ยงที่ชัดเจนของ Prompt Leaking และวิธีการนำหลักการ Privacy by Isolation มาประยุกต์ใช้ในการออกแบบแคช

ความเสี่ยงข้อมูลรั่วไหลผ่าน Cache และมาตรการป้องกัน Prompt Leaking

หากมองว่าแคช (Cache) เป็นเพียง "กลไกในการลดต้นทุน" เท่านั้น คุณอาจมองข้ามความเสี่ยงร้ายแรงอย่างการรั่วไหลของข้อมูลระหว่างผู้เช่า (Tenant) ไปได้ ในความเป็นจริง มีรายงานว่าข้อผิดพลาดในการออกแบบแคชกลายเป็นแหล่งบ่มเพาะของ Prompt Leaking (การรั่วไหลของ System Prompt) อยู่บ่อยครั้ง

วิธีรวม Privacy by Isolation เข้ากับการออกแบบ Cache

การนำแนวคิด Privacy by Isolation มาใช้ในการออกแบบแคช (Cache) จุดเริ่มต้นคือการกำหนดให้ชัดเจนก่อนว่า "จะแยกอะไรออกจากกัน"

โดยเฉพาะอย่างยิ่ง การออกแบบการแยกส่วนจะแบ่งออกเป็น 3 ชั้น ดังนี้:

  • ชั้นจัดเก็บแคช (Cache Storage Layer): ต้องระบุ Tenant ID เป็นเนมสเปซ (Namespace) ของคีย์แคชเสมอ เพื่อให้โครงสร้างของคีย์ใน Redis หรือ Memcached ไม่เกิดการทับซ้อนกับ Tenant อื่น
  • ชั้นโครงสร้างพรอมต์ (Prompt Structure Layer): แยกการจัดการระหว่าง System Prompt ที่ใช้ร่วมกันทุก Tenant (Shared Prefix) กับบริบทเฉพาะของแต่ละ Tenant (Suffix) ออกจากกันอย่างชัดเจน
  • ชั้นการเรียก API (API Call Layer): ก่อนการตรวจสอบว่าแคชฮิต (Cache Hit) หรือไม่ ต้องตรวจสอบให้แน่ใจว่า Tenant ID ของผู้ส่งคำขอตรงกับเนมสเปซของคีย์แคชที่ต้องการดึงข้อมูลเสมอ

แนวทางการนำไปใช้งานจะเปลี่ยนไปตามรูปแบบโมเดลการแยก Tenant หากเป็นแบบ Silo (โครงสร้างพื้นฐานแยกอิสระสำหรับแต่ละ Tenant) การจัดการจะทำได้ง่ายขึ้นเนื่องจากสามารถแยกตัวจัดเก็บแคชออกจากกันได้ แต่หากเป็นแบบ Pool (โครงสร้างพื้นฐานร่วมกัน) การจัดการเนมสเปซของคีย์ในระดับแอปพลิเคชันและการตรวจสอบความเป็นเจ้าของก่อนดึงข้อมูลถือเป็นสิ่งที่ขาดไม่ได้

นอกจากนี้ ยังต้องระมัดระวังเนื้อหาที่จะเขียนลงในแคชด้วย โดยหลักการแล้วควรหลีกเลี่ยงการรวมข้อมูลส่วนบุคคลหรือข้อมูลลับเฉพาะของ Tenant ไว้ในพรอมต์ที่จะนำไปแคช และจำกัดสิ่งที่นำไปเก็บไว้ในแคชให้เป็นเพียงบริบทแบบคงที่หรือกึ่งคงที่เท่านั้น เช่น "การตั้งค่า Tenant, การกำหนดบทบาท (Role Definition) และความรู้ทั่วไป (Common Knowledge)"

รูปแบบความผิดพลาดที่พบบ่อยและวิธีหลีกเลี่ยง

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

การชนกันของ Cache Key (Cache Key Collision) และความคลาดเคลื่อนของจังหวะเวลาในการทำ Cache Invalidation เป็นปัญหาที่เกิดขึ้นได้ง่ายโดยเฉพาะในสภาพแวดล้อมแบบ Multi-tenant โดยเราจะสรุปสาเหตุและแนวทางการป้องกันของแต่ละปัญหาไว้ดังนี้

การปนเปื้อนของข้อมูลระหว่าง Tenant จากการชนกันของ Cache Key

การเกิด Cache Key Collision เปรียบเสมือนการที่จดหมายของผู้อาศัยคนละคนถูกส่งไปที่ตู้ไปรษณีย์เดียวกัน หาก System Prompt ของ Tenant A ถูกส่งกลับไปยังคำขอของ Tenant B จะนำไปสู่การรั่วไหลของข้อมูลที่เป็นความลับโดยตรง

สาเหตุทั่วไปของปัญหานี้คือการออกแบบ Cache Key ที่ไม่เพียงพอ ตัวอย่างเช่น หากใช้เพียงเนื้อหาของ Prompt มาทำ Hash เพื่อเป็น Key แล้วบังเอิญว่า Tenant ต่างกันใช้ System Prompt สตริงเดียวกัน ก็จะทำให้เกิด Key เดียวกันขึ้น ส่งผลให้ Context ของ Tenant ที่เขียน Cache ลงไปก่อนหน้าอาจถูกส่งกลับไปยัง Tenant อื่นได้

รูปแบบการเกิด Collision มีอยู่ 3 กรณีหลัก:

  1. กรณีที่ใช้เฉพาะเนื้อหาของ Prompt มาทำ Key โดยไม่ได้รวม Tenant ID เข้าไปใน Key ทำให้เกิดการชนกันเมื่อใช้สตริงเดียวกัน ซึ่งสามารถป้องกันได้โดยการกำหนดรูปแบบ Key ให้เป็น {tenant_id}:{prompt_hash}
  2. กรณีที่ใช้ Global Cache Namespace ร่วมกันระหว่าง Tenant หากใช้ Redis หรือเครื่องมืออื่นโดยไม่ได้แยก Cache Store ออกจากกัน จะทำให้เกิดการปะปนกันในระดับ Namespace ดังนั้นจึงจำเป็นต้องแยก Key Prefix หรือหมายเลข DB สำหรับแต่ละ Tenant
  3. กรณีที่คำนวณ Hash ก่อนการขยาย Template Variables (Template expansion) หากใช้ Template ส่วนกลางเป็น Key โดยตรง จะทำให้เกิด Key เดียวกันแม้ว่าเนื้อหาหลังจากขยายตัวแปรแล้วจะแตกต่างกันก็ตาม ดังนั้นต้องคำนวณ Hash จากสตริงของ Prompt หลังจากขยายตัวแปรเรียบร้อยแล้วเท่านั้น

ต้นทุนที่เพิ่มขึ้นจากการคลาดเคลื่อนของจังหวะการทำ Cache Invalidation

การทำ Cache Invalidation มักจะถูกมองว่า "ถ้ามีการอัปเดต ก็แค่สั่ง Purge ทันทีก็พอ" แต่หากเลือกจังหวะในการทำ Invalidation ผิดพลาด อาจส่งผลย้อนกลับทำให้ต้นทุนในการเขียนข้อมูลใหม่พุ่งสูงขึ้นได้

ในโครงสร้างราคาของ Anthropic การเขียน Cache แบบ 5 นาทีจะมีราคาอยู่ที่ 1.25 เท่าของราคา Input พื้นฐาน และแบบ 1 ชั่วโมงจะอยู่ที่ 2 เท่า หาก Prompt มีการเปลี่ยนแปลงบ่อยครั้ง Cache จะถูกสร้างใหม่ซ้ำๆ ทำให้เกิดวงจรที่ TTL หมดอายุก่อนที่จะได้รับประโยชน์จากการอ่าน (0.1 เท่า) สำหรับผู้ให้บริการที่ตั้งค่า Default TTL ไว้ที่ 5 นาที หากไม่มีคำขอจาก Tenant เดียวกันเข้ามาภายใน 5 นาที อัตราการ Cache Hit ก็แทบจะเป็นศูนย์ ยิ่งไปกว่านั้น หากมีการสั่ง Purge Cache ของทุก Tenant พร้อมกันทุกครั้งที่มีการเปลี่ยนการตั้งค่าในหน้าจัดการ จะทำให้อัตราการ Hit ลดลงอย่างรวดเร็วชั่วคราวและส่งผลให้ต้นทุนพุ่งสูงขึ้น

วิธีแก้ปัญหาที่ใช้งานได้จริงสำหรับเรื่องนี้คือการทำ Lazy Invalidation โดยใช้วิธีจัดการเวอร์ชันของ Prompt แทนการสั่ง Purge ทันที และเปลี่ยนไปใช้เวอร์ชันใหม่เมื่อมีคำขอเข้ามาในครั้งถัดไป ซึ่งจะช่วยยับยั้งการเพิ่มขึ้นของต้นทุนการเขียนที่ต่อเนื่องกันได้

FAQ

Q1. Prompt Caching สามารถใช้งานได้กับ LLM provider ทุกรายหรือไม่?

ผู้ให้บริการรายหลักส่วนใหญ่เริ่มรองรับฟีเจอร์นี้แล้ว แต่เงื่อนไขจะแตกต่างกันไปตามโมเดลและแผนการใช้งาน สำหรับ OpenAI จำเป็นต้องมีอย่างน้อย 1,024 tokens เพื่อเปิดใช้งานแคช และ in-memory cache จะหมดอายุภายใน 5-10 นาที ส่วน Anthropic (Claude) มีค่าเริ่มต้น TTL อยู่ที่ 5 นาที และสามารถเลือกออปชัน 1 ชั่วโมงได้ สำหรับ AWS Bedrock จะมีเกณฑ์ขั้นต่ำที่แตกต่างกันไปตามโมเดล เช่น Claude Opus 4.5 กำหนดไว้ที่อย่างน้อย 4,096 tokens ขอแนะนำให้ตรวจสอบโมเดลที่รองรับและข้อกำหนดจำนวนโทเค็นขั้นต่ำในเอกสารอย่างเป็นทางการของผู้ให้บริการแต่ละรายก่อนเริ่มใช้งาน

Q2. การใช้แคชในสภาพแวดล้อมแบบ Multi-tenant มีความเสี่ยงที่ข้อมูลจะปะปนกันระหว่าง Tenant หรือไม่?

หากออกแบบ Cache Key ผิดพลาด อาจมีความเสี่ยงที่บริบท (context) ของ Tenant อื่นจะถูกผูกเข้ากับรายการแคชเดียวกันได้ แนวทางแก้ไขที่มีประสิทธิภาพคือการรวม Tenant ID ไว้ใน Cache Key เสมอ และออกแบบให้แยก System Prompt เฉพาะของ Tenant ออกจาก Shared Prefix อย่างชัดเจน การประยุกต์ใช้แนวคิด Privacy-by-Isolation เพื่อให้โครงสร้างไม่เอื้อต่อการอ้างอิงแคชข้าม Tenant จึงเป็นเรื่องสำคัญ นอกจากนี้ แนะนำให้ตรวจสอบ Audit Log ของขอบเขต Tenant ควบคู่ไปกับการวัดอัตรา Cache Hit อยู่เสมอ

Q3. ควรตั้งค่า TTL ของแคชอย่างไรให้คุ้มค่าที่สุด?

ค่า TTL ที่เหมาะสมที่สุดขึ้นอยู่กับความถี่ในการร้องขอ (Request frequency) และความถี่ในการอัปเดต System Prompt สำหรับโครงสร้างราคาของ Anthropic ค่าใช้จ่ายในการเขียนแคช (Cache write) จะอยู่ที่ 1.25 เท่า (TTL 5 นาที) หรือ 2 เท่า (TTL 1 ชั่วโมง) ของราคา Input พื้นฐาน แต่ค่าอ่านแคช (Cache read) จะลดลงเหลือเพียง 0.1 เท่า ดังนั้น TTL 1 ชั่วโมงจะคุ้มค่าสำหรับ Tenant ที่มีการเรียกใช้งานบ่อย ส่วน TTL 5 นาทีมักจะเหมาะสมกว่าในแง่ของต้นทุนสำหรับ Tenant ที่มีการเรียกใช้งานน้อย การเลือก TTL ควรพิจารณาจากการปรับปรุงตามค่า Cache Hit จริงที่วัดได้เป็นระยะ

Q4. หากมี Tenant ที่อัปเดต System Prompt บ่อยครั้ง ควรจัดการกับแคชอย่างไร?

เมื่อมีการเปลี่ยนแปลง System Prompt รายการแคชที่เกี่ยวข้องจำเป็นต้องถูกทำให้เป็นโมฆะ (Invalidate) การออกแบบโดยนำค่า Hash ของ Prompt มาเป็นส่วนหนึ่งของ Cache Key จะช่วยให้ระบบสร้างรายการแคชใหม่โดยอัตโนมัติเมื่อ Prompt เปลี่ยนแปลง ซึ่งช่วยป้องกันการอ้างอิงไปยังรายการเก่าได้ สำหรับ Tenant ที่มีการอัปเดตบ่อย อัตรา Cache Hit มักจะต่ำลง จึงแนะนำให้ตรวจสอบอัตรา Hit ของแต่ละ Tenant ผ่าน Dashboard ของ AI Observability และปรับการตั้งค่า TTL หรือกลยุทธ์การแคชแยกเป็นรายกรณี

Q5. จะประเมินประสิทธิภาพของ Prompt Caching ในเชิงปริมาณได้อย่างไร?

ในกรณีของ OpenAI คุณสามารถตรวจสอบจำนวนโทเค็นที่เกิด Cache Hit ได้จากฟิลด์ usage.prompt_tokens_details.cached_tokens ใน API Response การจัดเก็บข้อมูลนี้จะช่วยให้คุณคำนวณอัตรา Cache Hit และจำนวนโทเค็นที่ประหยัดได้ของแต่ละ Tenant สำหรับตัวชี้วัด (KPI) แนะนำให้ใช้การผสมผสานระหว่าง "อัตรา Cache Hit", "จำนวนโทเค็นที่ประหยัดได้" และ "ช่วงเวลาที่ Latency ดีขึ้น" โดยสามารถอ้างอิงเพิ่มเติมได้จาก วิธีการวัดผลหลังการนำ AI Agent ไปใช้งาน|ตั้งแต่การออกแบบ KPI ไปจนถึงการปรับปรุงอย่างต่อเนื่อง และการกำหนดรอบการวัดผลเป็นประจำจะช่วยให้เกิดการเพิ่มประสิทธิภาพด้านต้นทุนอย่างต่อเนื่อง

ผู้เขียน・ผู้ตรวจสอบ

Yusuke Ishihara

Yusuke Ishihara

เริ่มเขียนโปรแกรมตั้งแต่อายุ 13 ปี ด้วย MSX หลังจบการศึกษาจากมหาวิทยาลัย Musashi ได้ทำงานพัฒนาระบบขนาดใหญ่ รวมถึงระบบหลักของสายการบิน และโครงสร้าง Windows Server Hosting/VPS แห่งแรกของญี่ปุ่น ร่วมก่อตั้ง Site Engine Inc. ในปี 2008 ก่อตั้ง Unimon Inc. ในปี 2010 และ Enison Inc. ในปี 2025 นำทีมพัฒนาระบบธุรกิจ การประมวลผลภาษาธรรมชาติ และแพลตฟอร์ม ปัจจุบันมุ่งเน้นการพัฒนาผลิตภัณฑ์และการส่งเสริม AI/DX โดยใช้ generative AI และ Large Language Models (LLM)