ข้ามไปยังเนื้อหาหลัก

บทเรียน Jev API: สร้างตัวจัดเส้นทางทิกเก็ตด้วยโมเดล System One ของ TypeSafe AI

เรียนรู้การตั้งค่า Python SDK ของ TypeSafe ถามคำถามแบบ Choice, Score และ Noul ในครั้งเดียว สร้างตัวจัดเส้นทางทิกเก็ตที่เก็บนโยบายไว้ในโค้ดของคุณเอง และรู้ทันจุดที่ Jev สะดุดก่อนปล่อยใช้งาน
อัปเดตแล้ว 24 ก.ย. 2569  · 15 นาที อ่าน

สำรวจด้วย AI

ChatGPTClaudePerplexity

ตลอดสัปดาห์ที่แล้วมีการพูดถึง Jev โมเดล System One ของ TypeSafe AI กันมาก: เห็นทั้งคนที่ยกย่องและคนที่ตำหนิ ซึ่งความจริงก็น่าจะอยู่ตรงกลาง ขึ้นกับความคาดหวังของแต่ละคนต่อโมเดล อยากลองมาก และเพิ่งได้สิทธิ์พรีวิวเมื่อต้นสัปดาห์นี้เอง 

ในบทเรียนนี้ จะแสดงวิธีตั้งค่า Jev ผ่าน Python SDK ของเขา วิธีใช้คำถามทั้งสามชนิดของ Jev และวิธีสร้างชั้นจัดเส้นทางทิกเก็ต ซึ่งเป็นกรณีใช้งานที่ตรงกับจุดแข็งของโมเดล เราจะพูดถึงข้อจำกัดของ Jev ด้วย รวมถึงความหมายของโมเดล System One เผื่อใครยังสงสัยกับคำนี้

กำลังพัฒนาด้วย API โมเดลใน Python อยู่หรือเปล่า Developing LLM Applications with LangChain ครอบคลุมฝั่งกำเนิดข้อความของสแต็กเดียวกัน ทั้งพรอมต์ เชน และเอเจนต์

TL;DR

Jev คือโมเดล System One ของ TypeSafe AI มันไม่สร้างข้อความ ส่ง state พร้อมคำถามแบบกำหนดชนิดเข้าไป จะได้คำตอบแบบกำหนดชนิดพร้อมความน่าจะเป็นกลับมา แล้วโค้ดของคุณเป็นคนตัดสินใจขั้นต่อไป

  • คำถาม 3 ชนิด Choice เลือกหนึ่งตัวเลือกจากเซต Score ให้คะแนนตามระดับที่เรียงไว้ Noul คืนความน่าจะเป็นแบบใช่/ไม่ใช่
  • คำถามรันขนานกัน หกคำถามคิดค่าเป็นหนึ่งครั้งเรียก และหน่วงเวลาเพิ่มขึ้นเล็กน้อยกว่าคำถามเดียว ดังนั้นจึงถามทุกอย่างที่อาจอยากรู้ได้
  • ความเชื่อมั่นคือส่วนที่มีประโยชน์ ช่วยให้สร้างสามเส้นทางได้: อัตโนมัติ ส่งให้มนุษย์ ตรวจจับไม่ได้ก็ปล่อยผ่าน
  • นับเลขไม่ได้ คิดวันที่ไม่ได้ และอ่านคำถามตามตัวอักษร TypeSafe เผยแพร่จุดขรุขระของโมเดลไว้ และมันมีผลจริง

เราจะสร้างตัวจัดเส้นทางทิกเก็ตฝ่ายสนับสนุนด้วยการเรียกครั้งเดียว แล้วค่อยดูจุดที่ Jev สะดุด

ทำไม Jev ไม่สร้างข้อความ?

บอกไปแล้วว่าความคาดหวังต้องสอดคล้องกับตัวโมเดล ประเด็นนี้ยิ่งจริงกับ Jev และเกี่ยวข้องกับชนิดของโมเดลที่เรียกว่า System One

โมเดล System One ตอบเป็นการตัดสินใจแบบกำหนดชนิด แทนที่จะเป็นโทเค็น

โมเดล System One จะคืนการตัดสินใจแบบกำหนดชนิดแทนข้อความ คุณส่งบล็อกของ state พร้อมชุดคำถาม โดยกำหนดขอบเขตคำตอบเอง แล้วโมเดลคืนหนึ่งคำตอบต่อคำถามพร้อมความน่าจะเป็นประกอบ ไม่มีการสร้างทีละโทเค็น จึงไม่ต้องพาร์สสตริง หรือซ่อม JSON ที่เสีย คำนี้ TypeSafe ตั้งขึ้นโดยอิงจากการแบ่งชื่อดังของ Daniel Kahneman:

  • System 1: การตัดสินใจรวดเร็ว เชิงสัญชาตญาณ
  • System 2: การให้เหตุผลเชิงไตร่ตรอง ช้า

เพราะขอบเขตคำตอบคือสคีมาที่โค้ดของคุณประกาศไว้ ตามการออกแบบแล้ว System One จะไม่คืนหมวดหมู่ที่คุณไม่ได้กำหนดไว้ แปลว่ามันจะไม่หลุดสคีมา ถึงอย่างนั้นก็ยังตอบผิดได้ และจะยกกรณีล tricky ๆ ไว้ท้ายๆ

สถานะของ Jev ตอนนี้

TypeSafe เปิดตัวออกจาก stealth เมื่อ 15 กันยายน 2026 พร้อม Jev แบบ early access รายงานเวลาโต้ตอบ 70 ถึง 500 มิลลิวินาที และค่าบริการ $0.042 ต่อหนึ่งล้านโทเค็นขาเข้า โดยขาออกฟรี บน benchmark สี่เวิร์กโฟลว์ของตน Jev อยู่ราวความแม่นยำ 68% ระดับกลางของ LLM แต่อยู่ที่ต้นทุนเสี้ยวเดียว 

สำหรับข้อมูลฟีเจอร์และผลงาน benchmark แนะนำให้อ่าน คู่มือ Jev ของเรา

ควรเลือกใช้ Jev แทน LLM เมื่อไร

เขียนคำตอบที่เป็นไปได้ให้ครบก่อนเรียก ถ้าแจกแจงได้ แสดงว่าเป็นปัญหารูปทรงของ Jev:

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

เลือกใช้ LLM เมื่อผลลัพธ์เป็นร้อยแก้วหรือโค้ด ขอบเขตคำตอบเปิดกว้าง หรือเมื่องานต้องเชื่อมเหตุผลหลายทอด Jev ก็ไม่เหมาะกับงานเชิงตัวเลขด้วย จะอธิบายเหตุผลภายหลัง

สำรวจชนิดคำถามใน TypeSafe AI Playground

ก่อนเขียนโค้ด ให้สร้างบัญชี TypeSafe แล้วเปิด Playground ที่นี่สามารถวางข้อความเป็น state เพิ่มคำถาม และดูอ็อบเจ็กต์คำตอบเต็ม ๆ ได้โดยไม่ต้องติดตั้งอะไรเลย วิธีนี้เร็วที่สุดในการเข้าใจว่าแต่ละชนิดคำถามคืนค่าอะไร (และดูว่าคำถามของคุณเขียนแย่หรือเปล่า)

จะใช้ทิกเก็ตสนับสนุนหนึ่งฉบับเป็น state สำหรับตัวอย่างทั้งสาม:

Export to CSV has been broken since Friday. 
It works in Chrome, but half our team is on Safari and they can't pull reports at all. 
We have a board meeting Thursday.

ทุกชนิดคำถามรับ instructions คือคำถามภาษาธรรมดาที่อยากให้ตอบ สิ่งที่ต่างกันคือ criteria และสิ่งที่ได้กลับมา

Choice สำหรับการจัดประเภทเชิงหมวดหมู่

คำถาม Choice เลือกตัวเลือกหนึ่งจากชุดที่คุณกำหนด 

ส่ง criteria เป็นดิกชันนารีที่แมปแต่ละตัวเลือกกับคำอธิบาย จำนวน 1 ถึง 255 รายการ และคำตอบจะกลับมาพร้อม: 

  • ตัวเลือกที่ชนะ
  • ความน่าจะเป็นของทุกตัวเลือก
{
  "department": {
    "type": "choice",
    "instructions": "Which queue should own this ticket?",
    "criteria": {
      "bug_triage": "A defect in a specific feature, reproducible, goes into the backlog",
      "incident_response": "A live breakage affecting multiple users right now, needs a responder today",
      "customer_success": "The account needs managing, not the code"
    }
  }
}

เพื่อดูเอาต์พุตโค้ดแบบที่ได้รับผ่าน API ให้คลิกปุ่ม </> ที่มุมขวาบน แล้วคลิก Run เพื่อให้ Jev ตอบคำถาม

ทดสอบคำถามแบบ Choice สำหรับ Jev ใน TypeSafe playground

กรณีนี้ incident_response เป็น choice ด้วยความน่าจะเป็น 91% และ confidence ของ Jev สำหรับตัวเลือกนี้คือ 86%

การกระจายทั้งหมดคือส่วนที่ควรสนใจ 

  • choice บอกแค่ว่าตัวเลือกไหนชนะ

  • probabilities บอกว่าชนะมากน้อยแค่ไหน

สองอย่างนี้ให้ข้อมูลคนละแบบเมื่อกำลังจะจัดเส้นทางทิกเก็ตอัตโนมัติ การแยก 0.41/0.38/0.21 กับ 0.91/0.09/0 อาจคืน choice เดียวกันได้

Score สำหรับรูบริกที่เรียงลำดับ

Score คือการให้คะแนน state เทียบกับระดับที่เรียงลำดับไว้ ส่ง criteria เป็นอาร์เรย์ ระบุคำอธิบายระดับ 2 ถึง 10 รายการ ไล่จากต่ำไปสูง และคำตอบจะมีคะแนน legendที่แมปตำแหน่งกับคำอธิบายของคุณ ความน่าจะเป็นต่อระดับ และค่า confidence

{
  "goodwill_risk": {
    "type": "score",
    "instructions": "How much patience does this customer have left?",
    "criteria": [
      "Reporting a problem, no sign of frustration",
      "Mildly annoyed, still collaborative",
      "Visibly out of patience, mentions the cost to their work",
      "At the point of escalating over our heads or leaving"
    ]
  }
}

ทดสอบคำถามแบบ Score สำหรับ Jev ใน TypeSafe playground

คะแนนอาจอยู่กึ่งกลางระหว่างระดับ และนั่นคือประเด็นของ legend ที่นี่ score 1.93 หมายถึงโมเดลลังเลระหว่าง "รำคาญเล็กน้อย" กับ "หมดความอดทนอย่างเห็นได้ชัด" โดยเอนเอียงไปอย่างหลัง ซึ่งสอดคล้องกับทิกเก็ตที่ยังสุภาพแต่ย้ำเส้นตายที่จะพลาด 

อีกครั้ง ให้ดูการกระจาย probabilities แทนที่จะดูตัวเลขอย่างเดียว: ความน่าจะเป็นกระจุกที่ระดับเดียวแปลว่ามั่นใจ ส่วนที่กระจายสามระดับแปลว่าได้ค่าเฉลี่ยแทนการตัดสิน

Noul สำหรับความน่าจะเป็นแบบใช่/ไม่ใช่

Noul คือชนิดคำถามแบบไบนารี ชื่อนี้ TypeSafe ตั้งเอง criteria เป็นออปชัน แต่คุณสามารถอธิบายว่า true และ false หมายถึงอะไร ซึ่งควรทำเมื่อ "ใช่" อาจตีความได้สองทาง

ตั้งคำถามให้ค่ามากแปลว่าใช่ เอกสารของ TypeSafe ย้ำเรื่องนี้ และ Noul ที่แมป true เป็น "ไม่" จะมีประสิทธิภาพแย่ลงอย่างเห็นได้ชัด

{
  "is_time_sensitive": {
    "type": "noul",
    "instructions": "The customer names a specific deadline",
    "criteria": {
      "true": "A date, day, or event the work must be done before",
      "false": "Urgency is implied but no deadline is given"
    }
  }
}

ทดสอบคำถามแบบ Noul สำหรับ Jev ใน TypeSafe playground

เนื่องจาก state กล่าวถึงการประชุมบอร์ดในวันพฤหัสฯ ค่า noul ที่สูง 0.97 จึงสอดคล้องความคาดหมาย

ทำไม Noul ถึงไม่มีฟิลด์ confidence?

Choice และ Score คืนค่า confidence เคียงคู่กับ probabilities ส่วน Noul ไม่คืน ทำให้หลายคนสะดุด จึงควรอธิบายให้ชัด

confidence กับ probability เป็นแกนคนละอัน สำหรับ Choice probabilities บอกว่าความเชื่อของโมเดลกระจายระหว่างตัวเลือกอย่างไร และ confidence บอกว่ามันยึดมั่นกับคำตอบนั้นแค่ไหน จึงเห็นได้ว่า Choice อาจมีตัวเลือกบนสุด 0.85 แต่ confidence 0.78 ส่วน Noul มีผลลัพธ์แค่สองค่า ดังนั้นความน่าจะเป็นหนึ่งตัวเลขนั้นแบกรับทั้งสองอย่าง: 0.97 คือใช่แบบมั่นคง 0.03 คือไม่แบบมั่นคง และ 0.52 คือโมเดลบอกว่าไม่แน่ใจ

ดังนั้น ระยะห่างจาก 0.5 คือสัญญาณความเด็ดขาดของคุณ ไม่ใช่ฟิลด์แยกต่างหาก และแปลว่าไม่สามารถยก threshold จาก Noul ไปใช้กับ Choice ได้ จะกลับมาพูดอีกทีในส่วนความขรุขระ เพราะผลกระทบแรงกว่าที่คิด

ตั้งค่า Jev Python SDK

ต้องมีแค่ Python 3.10+ และคีย์ early access ของ TypeSafe

ติดตั้ง SDK

ติดตั้ง SDK:

pip install typesafe-sdk

หรือด้วย uv:

uv add typesafe-sdk

ส่งออกคีย์ของคุณ

จากนั้นสร้างคีย์ในคอนโซลของ TypeSafe และ export ออกมา ไคลเอนต์จะอ่าน TYPESAFE_API_KEY จาก environment จึงไม่ต้องส่งคีย์ในโค้ด:

export TYPESAFE_API_KEY="your-key"

นำเข้า answer types และไคลเอนต์

นำเข้าเหล่านี้เพื่อใช้งานแบบเดียวกับใน playground:

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

client = TypeSafeClient()

Choice, Noul, และ Score คือชนิดคำถามเดียวกับที่คลิกไปมาในรูปแบบอ็อบเจ็กต์ Python 

การใช้ TypeSafeClient

TypeSafeClient เป็นไคลเอนต์ synchronous และมี AsyncTypeSafeClient ที่หน้าอินเทอร์เฟซเหมือนกันหากเรียก Jev จากบริการแบบ async ทั้งสองใช้เป็น context manager ได้ ซึ่งเหมาะกับงานยาวกว่า script สั้น ๆ:

with TypeSafeClient() as client:
    ...

ปักเวอร์ชันของ Jev

ถ้าปล่อยไว้ ไคลเอนต์จะเรียก jev-latest ซึ่งจะขยับตามที่ TypeSafe ปล่อยรุ่นใหม่ ซึ่งเราจะใช้แบบนี้ตลอดบทเรียน แต่สำหรับงานที่ตั้ง threshold ไว้แล้ว ควรปักเวอร์ชันแทน:

client = TypeSafeClient(model="jev-1.13.0")

คำตอบจะบอกว่าจริง ๆ แล้วใช้โมเดลตัวไหนตอบ และมีส่วนท้ายอธิบายว่าทำไมควรล็อกค่าไว้

การเรียก Jev API ครั้งแรก

ในการเรียก API หา Jev ต้องนิยามรายการ response โดยใช้ TypeSafeClient และฟังก์ชัน system_one() ซึ่งรับบริบทคำถามเป็นพารามิเตอร์ state และรับ questions ในรูปแบบเดียวกับใน playground 

สามารถสร้างการเรียกเดียวที่ตอบทั้ง 3 คำถามจาก playground ได้ เพราะใช้ state ร่วมกัน:

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

ticket = (
    "Export to CSV has been broken since Friday. It works in Chrome, "
    "but half our team is on Safari and they can't pull reports at all. "
    "We have a board meeting Thursday."
)

with TypeSafeClient() as client:
    response = client.system_one(
        state=ticket,
        questions={
            "queue": Choice(
                instructions="Which queue should own this ticket",
                criteria={
                    "bug_triage": "A defect in a specific feature, reproducible, goes into the backlog",
                    "incident_response": "A live breakage affecting multiple users right now, needs a responder today",
                    "customer_success": "The account needs managing, not the code",
                },
            ),
            "goodwill_risk": Score(
                instructions="How much patience does this customer have left",
                criteria=[
                    "Reporting a problem, no sign of frustration",
                    "Mildly annoyed, still collaborative",
                    "Visibly out of patience, mentions the cost to their work",
                    "At the point of escalating over our heads or leaving",
                ],
            ),
            "is_time_sensitive": Noul(
                instructions="The customer names a specific deadline",
                criteria={
                    "true": "A date, day, or event the work must be done before",
                    "false": "Urgency is implied but no deadline is given",
                },
            ),
        },
    )

คำตอบจะกลับมาภายใต้ชื่อเดียวกับที่คุณตั้งให้ในแต่ละคำถาม นี่แหละทำให้ใช้งานได้สบาย:

print(response.model)
print(response.answers["queue"].choice, response.answers["queue"].confidence)
print(response.answers["goodwill_risk"].score)
print(response.answers["is_time_sensitive"].noul)
jev-1.13.0
Incidence_response 0.95
1.95
0.97

แก้ปัญหา TypeError

ถ้าการเรียกครั้งแรกพังด้วย TypeError เกี่ยวกับ output_buffer_limit: นั่นคือปัญหาเวอร์ชันไม่ตรงกันในแบ็กเอนด์บีบอัดของ SDK ไม่ใช่โค้ดของคุณ SDK มี HTTP client ของตัวเองชื่อ httpx2 ซึ่งคลายการบีบอัดผ่าน zstandard และ brotli และสำเนาเก่าของอย่างใดอย่างหนึ่งไม่มีอาร์กิวเมนต์ที่ถูกเรียกใช้ pip install -U typesafe-sdk httpx2 zstandard brotli แก้ได้ในเคสของเรา

เอาต์พุตบอกอะไรเรา

มีสองอย่างที่ควรหยุดดู

อย่างแรก response.model คืน jev-1.13.0 ไม่ใช่ jev-latest คุณขอ alias ที่ขยับได้ และ Jev บอกคุณว่าเวอร์ชันไหนเป็นคนตอบ ซึ่งนี่แหละเหตุผลว่าทำไมการล็อกฟิลด์นี้ถึงคุ้มค่า

อย่างที่สอง อ็อบเจ็กต์คำตอบถูกกำหนดชนิดตามชนิดคำถาม จึงมี .choice, .score, และ .noul เป็นแอททริบิวต์จริง ซึ่งเอดิเตอร์ของคุณรับรู้ได้ ไม่มีสตริง JSON ในโค้ดนี้เลย ไม่ต้องพาร์ส ไม่ต้องเผื่อกิ่งสำหรับเคสที่ตอบกลับมาพัง ถ้าชอบจัดกลุ่ม SDK ก็มี response.choices, response.scores, และ response.nouls ให้ด้วย โดยคีย์เหมือนเดิม

ตรวจ response.usage ด้วย:

print(response.usage.input_tokens, response.usage.output_tokens)
524
78

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

สร้างตัวจัดเส้นทางทิกเก็ตด้วย Jev เพียงครั้งเรียกเดียว

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

กฎการออกแบบที่ควรระบุแต่แรก: Jev ตัดสินว่า "อะไร" โค้ดของคุณตัดสินว่า "แล้วทำอะไร" Jev ไม่เคยจัดเส้นทางเอง มันคืนตัวเลข และการจัดเส้นทางอยู่ในฟังก์ชันปกติที่คุณอ่าน ทดสอบ และเปลี่ยนได้โดยไม่ต้องแตะโมเดล

ถามทุกคำถามในการร้องขอครั้งเดียว

คำถามในคำขอเดียวกันจะประเมินแบบขนาน ดังนั้นคำถามที่หกมีต้นทุนแค่โทเค็นที่เขียน และหน่วงเวลาเพิ่มน้อยมาก นี่เปลี่ยนวิธีการถาม กับ LLM คุณจะรวมคำถามเพื่อลดรอบไป-กลับ แต่ที่นี่ให้ถามทุกอย่างที่อาจอยากรู้เกี่ยวกับบริบทนั้น ๆ รวมถึงคำถามที่อาจจะยังไม่ใช้

มาเพิ่มคำถามจากก่อนหน้าอีกสามข้อแบบ Noul เพื่อให้ได้ข้อมูลสำคัญในการจัดการทิกเก็ต:

  • ทิกเก็ตมีข้อมูลพอให้ทำซ้ำปัญหาได้ไหม
  • กล่าวถึงรายได้ที่หายไปหรือค่าใช้จ่ายเพิ่มเติมจากปัญหานี้ไหม
  • ทิกเก็ตต้องการคำตอบจากมนุษย์ไหม
QUESTIONS = {
    "queue": Choice(
        instructions="Which queue should own this ticket",
        criteria={
            "bug_triage": "A defect in a specific feature, reproducible, goes into the backlog",
            "incident_response": "A live breakage affecting multiple users right now, needs a responder today",
            "customer_success": "The account needs managing, not the code",
        },
    ),
    "goodwill_risk": Score(
        instructions="How much patience does this customer have left",
        criteria=[
            "Reporting a problem, no sign of frustration",
            "Mildly annoyed, still collaborative",
            "Visibly out of patience, mentions the cost to their work",
            "At the point of escalating over our heads or leaving",
        ],
    ),
    "is_time_sensitive": Noul(
        instructions="The customer names a specific deadline",
        criteria={
            "true": "A date, day, or event the work must be done before",
            "false": "Urgency is implied but no deadline is given",
        },
    ),
    "has_reproduction": Noul(
        instructions="The ticket contains enough detail to reproduce the problem",
    ),
    "mentions_money": Noul(
        instructions="The customer mentions lost revenue, refunds, or cancelling",
    ),
    "is_automated": Noul(
        instructions="This ticket is a machine-generated notification, not a person writing in",
    ),
}

with TypeSafeClient() as client:
    response = client.system_one(state=ticket, questions=QUESTIONS)

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

สองนิสัยที่อยากแนะนำ 

  • เก็บชุดคำถามเป็นคอนสแตนต์ระดับโมดูลแทนการสร้าง inline เพราะจะเวอร์ชันร่วมกับ threshold ของคุณ

  • ตั้งชื่อตามสิ่งที่วัด มากกว่าสิ่งที่จะทำกับคำตอบ เพราะ is_time_sensitive อยู่รอดแม้เปลี่ยนนโยบาย แต่ route_to_incident ไม่

  • กรอบคำถามเชิงบวก: เราอาจตั้งชื่อ is_automated ว่า needs_no_reply ก็ได้ แต่ตามที่ TypeSafe ระบุ ประสิทธิภาพของคำถามเชิงบวกดีกว่า

แปลงคำตอบเป็นการกระทำด้วย threshold ของความเชื่อมั่น

ส่วนที่ Jev ไม่ทำคือส่วนนี้ ทุกคำตอบมาพร้อมค่า confidence หรือ probability และตัวเลขที่สองนี่แหละทำให้สร้างสามเส้นทางแทนที่จะมีแค่สอง:

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

Jev ตัดสินว่าอะไร โค้ดของคุณตัดสินว่าจะทำอะไร

แปลงสิ่งนี้เป็นกฎสำหรับจัดเส้นทางทิกเก็ต:

  • ถ้าทิกเก็ตน่าจะถูกสร้างโดยเครื่อง ให้เก็บถาวรและทำอัตโนมัติ
  • ถ้าลูกค้าดูหงุดหงิดและกล่าวถึงความเสียหายทางการเงิน ให้ส่งให้ทีม customer success ที่เป็นมนุษย์
  • ถ้าโมเดลยังไม่มั่นใจพอว่าคิวไหน ให้ส่งให้มนุษย์ในคิวที่น่าจะใช่มากที่สุด
  • ถ้าทิกเก็ตน่าจะเร่งด่วน ให้ทำวันนี้
AUTO_ROUTE_CONFIDENCE = 0.75
YES = 0.8
FRUSTRATED = 2.0

def route(answers):
    if answers["is_automated"].noul > YES:
        return "archive", "auto"

    queue = answers["queue"]
    urgent = answers["is_time_sensitive"].noul > YES
    unhappy = answers["goodwill_risk"].score >= FRUSTRATED

    if unhappy and answers["mentions_money"].noul > YES:
        return "customer_success", "human_first"

    if queue.confidence < AUTO_ROUTE_CONFIDENCE:
        return queue.choice, "human_first"

    priority = "today" if urgent else "normal"
    return queue.choice, priority

อ่านให้ชัดว่าฟังก์ชันนี้ทำอะไร โมเดลให้คำตัดสินหกข้อ และนโยบายก็ตัดสินว่าหนึ่งในนั้น คือ mentions_money ร่วมกับลูกค้าที่หงุดหงิด มีความสำคัญกว่าคิวที่ Jev เลือก การ override แบบนี้เป็นการตัดสินเชิงธุรกิจ ควรอยู่ในโค้ด และคุณเปลี่ยนได้เย็นวันศุกร์โดยไม่ต้องทดสอบโมเดลใหม่

has_reproduction ไม่เคยถูกใช้ ตั้งใจทิ้งไว้ เพราะลักษณะการ fan-out ในทางปฏิบัติคือแบบนี้: ขอมากกว่าสิ่งที่นโยบายปัจจุบันใช้ บันทึกทั้งหมดไว้ แล้วเมื่อมีคนถามว่าทิกเก็ต bug_triage ที่ไม่มีขั้นทำซ้ำปัญหาใช้เวลาปิดนานกว่าหรือไม่ คุณก็มีข้อมูลหกสัปดาห์พร้อม

threshold ข้างบนเป็นตัวอย่างเท่านั้น การหาค่าที่เหมาะคือการคาลิเบรต และมีส่วนท้ายอธิบายการตั้งด้วยข้อมูลแทนความรู้สึก

รันสคริปต์

เข้าถึงสคริปต์เต็มได้จาก รีโป GitHub ที่แนบมา เมื่อรันสคริปต์ Python กับสถานการณ์ของเรา ผลการตัดสินคือส่งทิกเก็ตให้ทีม incident response ภายในวันนี้

python routing.py
('incident_response', 'today')

จุดที่ Jev สะดุด: อ่านรายการความขรุขระ

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

นี่คือห้าข้อที่น่าจะทำให้เสียเวลามากสุด

Noul กับ Choice ไม่สอดคล้องกัน

ไม่สามารถยก threshold ข้ามชนิดคำถามได้ ตัวอย่างของ TypeSafe เองถามว่า "ลูกค้าขอเงินคืนหรือไม่" ด้วยสองแบบในทิกเก็ตเดียวกัน: Noul คืน 0.22 ส่วน Choice แบบใช่/ไม่ใช่คืน 0.01 สำหรับใช่ พร้อม confidence 0.97 คำถามเดียว ตัวเลขสองตัว ต่างกันสองหลัก

การปฏิเสธก็ไม่ลงรอยกัน Noul กับคำถามตรงข้ามกันคืนค่า 0.72 และ 0.47 ซึ่งบวกกันได้ 1.19

เหตุผลคือสองชนิดถามคนละเรื่อง Choice เป็นเชิงสัมพัทธ์และชี้ว่าตัวเลือกไหนชนะ ขณะที่ Noul แต่ละข้อเป็นเชิงสัมบูรณ์ และอาจได้ค่าต่ำสำหรับทุกตัวเลือก ตั้ง threshold รายคำถามตามรูปแบบที่จะปล่อยใช้จริง และอย่าคิดว่า P(yes) กับ 1 - P(no) เป็นเลขเดียวกัน

Score คืออันดับ ไม่ใช่การวัดค่า

ระดับของ Score เรียงลำดับ ไม่ได้มีระยะห่างเท่ากัน ค่า 1.6 บอกว่าโมเดลอยู่ระหว่างระดับสองกับสาม เอนเข้าหาสาม และบอกได้แค่นั้น

สิ่งที่ทำไม่ได้คือแทรกค่าจริงจากมัน ถ้าระดับคือ "ต่ำกว่าชั่วโมง", "ไม่กี่ชั่วโมง", และ "หนึ่งวัน" ค่า 1.5 ไม่ได้แปลว่า 5 ชั่วโมง ใช้คะแนนเพื่อตรวจ threshold แล้วเก็บตัวเลขจริงทั้งหมดไว้ในโค้ด

State สามารถชี้นำคำตอบของตัวเองได้

Jev มอง state เป็นข้อมูล แต่ยังไม่ได้แข็งแรงพอจะกันโค้ดที่เขียนใน state เพื่อชี้นำคำตอบได้ คำสั่งที่ฉีดเข้ามา กรอบคำอธิบายที่ทำให้เข้าใจผิด หรือข้อความที่โต้แย้งเพื่อจัดประเภทตัวเอง สามารถขยับคำตอบได้ และ TypeSafe ระบุว่าจะพัฒนาในจุดนี้ ถ้าเพิ่งรู้จักผิวโจมตีแบบนี้ เราครอบคลุมเคสทั่วไปไว้ใน คู่มือ prompt injection

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

Jev นับเลขไม่ได้ คิดวันที่หรือเลขไม่ได้

การนับไม่น่าเชื่อถือและยิ่งแย่เมื่อจำนวนเพิ่ม เพราะโมเดลรู้จักรูปแบบคำตอบมากกว่าการนับจริง วันที่ถูกอ่านเป็นข้อความ จึงล้มเหลวทั้งการเรียง ระยะห่าง และช่วงเวลา การเข้ารหัสเชิงตัวเลขทำผลงานแย่กว่าความหมายเชิงภาษา ดังนั้นให้ถามหา "สีแดง" ดีกว่า #FF0000.

ทางแก้เหมือนกันทั้งสามกรณี: แยกงานออกจากกัน

  • การสกัดคือการตัดสินใจ ให้ Jev ทำในรูป Choice บนตัวเลือกที่แจกแจงไว้ 
  • คณิตศาสตร์เก็บไว้ในโค้ดเท่านั้น

ถ้าต้องการนับ ให้วนในโค้ดและถาม Noul ต่อรายการ:

count = sum(
    result.nouls[f"item_{i}"].noul > 0.5
    for i in range(len(items))
)

การอ่านตามตัวอักษร การอ้อม และ state ที่พอง

อีกสามข้อเล็ก ๆ ที่มีสาเหตุร่วมกัน Jev ตอบคำถามที่คุณเขียน ไม่ใช่สิ่งที่คุณตั้งใจ ดังนั้นคำจำกัดขอบเขตและการปฏิเสธจะถูกอ่านตรงตัว ปฏิเสธซ้อนปฏิเสธ และคำถามเกี่ยวกับคุณสมบัติของคุณสมบัติ ทำให้ความแม่นยำลดลง และ state ขนาดใหญ่ที่ยัดรายละเอียดไม่เกี่ยวข้องทำให้ความแม่นยำลดลงและเสียเงินเพิ่ม เพราะสิ่งไม่เกี่ยวข้องทำหน้าที่เป็นตัวเบี่ยงเบน

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

สิ่งที่ควรทำก่อนนำ Jev เข้าผลิตจริง

จากสิ่งที่เรียนรู้มา ต่อไปนี้คือแนวทางปฏิบัติที่ดีที่สุดเพื่อใช้ Jev ให้คุ้มค่า

เขียนคำถามให้ Jev ตอบได้ดี

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

  • จำกัดหนึ่งการตัดสินต่อหนึ่งคำถาม
  • เลือก criteria ที่ครอบคลุมเคสชายขอบ
  • เลือกสำนวนที่ค่ามากแปลว่าใช่ 
  • ส่งเฉพาะ state ที่จำเป็นต่อคำถามนั้น

ปักเวอร์ชันและล็อกสิ่งที่ Jev ตอบ

jev-latest ขยับเสมอ ปักเวอร์ชันเมื่อมี threshold ใด ๆ ขึ้นกับพฤติกรรมของโมเดล:

client = TypeSafeClient(model="jev-1.13.0")

ล็อก response.model คู่กับคำตอบเต็มทุกครั้ง ไม่ใช่แค่ค่าที่คุณนำไปใช้ เมื่อ threshold เริ่มงอแง ล็อกนี้คือวิธีเดียวที่จะบอกได้ว่าเป็นการเปลี่ยนโมเดลหรือดริฟท์ในทิกเก็ตของคุณ

ทดสอบ threshold ก่อนเชื่อใจ

สุดท้าย threshold ไม่ใช่ของตาย แต่เป็นผลจากการทดลอง

  • รวบรวมทิกเก็ตจริง 20 รายการขึ้นไป พร้อมคำตอบที่คุณอยากได้ รัน Jev เคียงข้างการจัดเส้นทางเดิมโดยไม่เปลี่ยนพฤติกรรม แล้วเปรียบเทียบ
  • แก้คำถามก่อน แล้วค่อยแก้ threshold จากนั้นทำอัตโนมัติในเส้นทางที่พลาดแล้วต้นทุนถูก ที่เหลือปล่อยให้มนุษย์
  • เวอร์ชันคำถาม criteria และ threshold ร่วมกัน และ replay ชุดทดสอบทุกครั้งที่มีการเปลี่ยนของสามอย่างนี้

ข้อคิดส่งท้าย

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

สิ่งที่อยากแย้งคือกรอบว่า "ไม่เพ้อ" จริงในความหมายแคบ ๆ ว่าโมเดลจะไม่ตอบนอกสคีมา แต่มันไม่ได้บอกว่าคำตอบถูกหรือไม่ Choice จะคืนคิวที่ถูกต้องตามสคีมาเสมอ แต่อาจเป็นคิวที่ผิดด้วย confidence 0.9 ได้ และเอาต์พุตแบบกำหนดชนิดทำให้ความล้มเหลวนี้เงียบกว่า

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

ถ้าอยากเริ่มสร้างระบบที่ใช้ AI แนะนำหลักสูตรอาชีพ Associate AI Engineer for Developers ซึ่งสอนการทำงานกับ OpenAI API, MCP, LangChain และอีกมาก

FAQs

ควรใช้คำถามชนิดไหนของ Jev?

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

สามารถถาม Jev หลายคำถามในหนึ่ง API call ได้ไหม?

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

Noul คืนคะแนนความเชื่อมั่นไหม?

ไม่ได้ Choice และ Score มีฟิลด์ confidence แต่ Noul คืนแค่ความน่าจะเป็น เพราะเมื่อมีสองผลลัพธ์ ตัวเลขเดียวก็บอกทั้งสองอย่างได้แล้ว ระยะห่างจาก 0.5 คือสัญญาณความเด็ดขาดของคุณ ดังนั้น 0.97 คือใช่อย่างมั่นคง ส่วน 0.52 แปลว่าโมเดลยังไม่รู้

Jev นับเลขหรือคำนวณได้ไหม?

ไม่ได้ การนับไม่น่าเชื่อถือและแย่ลงตามจำนวน วันที่ถูกอ่านเป็นข้อความแทนค่าที่เรียงลำดับ และการเข้ารหัสเชิงเลขทำผลงานแย่กว่าภาษาธรรมดา แยกงาน: ให้ Jev ตัดสินใจ และเก็บคณิตศาสตร์ไว้ในโค้ดของคุณเอง

ควรปักเวอร์ชันโมเดล Jev ไหม?

ควรทำ เมื่อโค้ดของคุณมี threshold ใดขึ้นกับพฤติกรรมโมเดล jev-latest จะขยับเมื่อ TypeSafe ปล่อยรุ่นใหม่ และมีรายการความขรุขระแยกตามเวอร์ชัน ดังนั้นโหมดความล้มเหลวก็เปลี่ยนด้วย ส่งเวอร์ชันที่ชัดให้ไคลเอนต์และล็อก response.model ทุกครั้ง

หัวข้อ
ปัญญาประดิษฐ์

เรียนวิศวกรรม AI กับ DataCamp!

Tracks

วิศวกร AI ระดับ Associate สำหรับนักพัฒนา

26 ชม.
เรียนรู้วิธีผสาน AI เข้ากับแอปพลิเคชันซอฟต์แวร์โดยใช้ API และไลบรารีโอเพนซอร์ส เริ่มต้นเส้นทางสู่การเป็น AI Engineer ของคุณวันนี้!
ดูรายละเอียดRight Arrow
เริ่มหลักสูตร
ดูเพิ่มเติมRight Arrow