AI Engineer › พื้นฐาน LLM และ API
Messages, ประวัติสนทนา และการสุ่ม (temperature)
ประมาณ 35 นาที
เป้าหมายของบท
- สร้างคำขอแบบ messages (system + user/assistant) และตัดประวัติให้พอดีงบ token
- คำนวณ softmax ที่ปรับด้วย temperature และเข้าใจผลต่อความหลากหลายของคำตอบ
- อธิบาย top-k และ top-p (nucleus) ด้วยการคำนวณเอง
คำขอ LLM คือ “list ของ messages”
API แชตส่วนใหญ่รับข้อมูลเป็น list ของข้อความที่มี role (user / assistant) และ content พร้อมคำสั่งระดับระบบ (system) แยกต่างหาก — และ ไม่มีความจำ: ทุกครั้งที่เรียกต้องส่งประวัติที่จำเป็นกลับไปเองทั้งหมด
request = {
"model": "claude-opus-5",
"max_tokens": 300,
"system": "You are a helpful support assistant for an online store.",
"messages": [
{"role": "user", "content": "How long does standard delivery take?"},
],
}
# ใน notebook (ต้องมี API key):
# import anthropic
# client = anthropic.Anthropic()
# reply = client.messages.create(**request)
# reply.content[0].text, reply.usage.input_tokens, reply.usage.output_tokens
ข้อควรรู้: ข้อความ user กับ assistant สลับกันเสมอ, ประวัติยิ่งยาว input ยิ่งแพง จึงต้อง ตัดประวัติให้พอดีงบ token — วิธีพื้นฐานคือเก็บ system prompt ไว้เสมอ แล้วเก็บข้อความ ใหม่ล่าสุด ย้อนหลังไปเท่าที่งบพอ
(ประวัติตัวอย่าง 5 ข้อความมี 10, 20, 30, 10, 15 token ที่งบ 60 token จะเก็บได้ 3 ข้อความหลังสุดรวม 55 token)
LLM สร้างข้อความโดย “สุ่มเลือก token ถัดไป”
โมเดลให้คะแนน (logit) กับทุก token ที่เป็นไปได้ แล้วแปลงเป็นความน่าจะเป็นด้วย softmax ที่หารด้วย temperature ก่อน:
def softmax(values, temperature=1.0):
z = values / temperature
z = z - z.max() # เพื่อความเสถียรของตัวเลข
e = np.exp(z)
return e / e.sum()
logits = np.array([2.0, 1.0, 0.5, 0.1]) # the, a, cat, dog
softmax(logits, 1.0) # [0.5745, 0.2114, 0.1282, 0.0859]
softmax(logits, 0.5) # [0.8282, 0.1121, 0.0412, 0.0185] คมขึ้น
softmax(logits, 2.0) # [0.4056, 0.2460, 0.1916, 0.1569] แบนลง
- temperature ต่ำ → ความน่าจะเป็นกระจุกที่ตัวเลือกอันดับหนึ่ง คำตอบนิ่ง/ซ้ำได้ (“the” ถูกเลือก 99.6% ของ 1,000 ครั้งที่ T = 0.2 เทียบ 58.2% ที่ T = 1.0 เมื่อสุ่มด้วย seed 42)
- temperature สูง → หลากหลายและเสี่ยงหลุดประเด็นมากขึ้น
- top-k เก็บเฉพาะ k ตัวเลือกแรกแล้วปรับให้ผลรวมเป็น 1 · top-p (nucleus) เก็บชุดเล็กสุดที่ความน่าจะเป็นสะสม ≥ p (ตัวอย่างนี้ p = 0.9 ได้ 3 ตัวเลือก: the, a, cat)
งานที่ต้องการความถูกต้อง (สกัดข้อมูล, ตอบจากเอกสาร) ใช้ temperature ต่ำ; งานสร้างสรรค์ใช้สูงกว่า — แต่ต่ำก็ไม่การันตีว่าผลเหมือนเดิมทุกครั้งเสมอไป (ขึ้นกับผู้ให้บริการ) จึงควรเก็บผลที่ต้องการทำซ้ำได้ไว้ (cache) แทนการหวังให้สุ่มซ้ำได้เหมือนเดิม
ตัวอย่างแก้โจทย์
กด "ขั้นต่อไป" ทีละขั้น ลองรันและแก้โค้ดในแต่ละขั้นได้
softmax กับ temperature
ดูว่า temperature เปลี่ยนความน่าจะเป็นอย่างไร
ที่ T = 1.0 ตัวเลือกแรก ("the") ได้ 0.5745
ตัดประวัติให้พอดีงบ token
เก็บข้อความใหม่ล่าสุดย้อนหลังจนเต็มงบ
ประมาณ token ของแต่ละข้อความ: [10, 20, 30, 10, 15]
เติมช่องว่าง
แทน ___ ด้วยโค้ดที่ถูกต้อง แล้วกดตรวจคำตอบ
เติมสูตร softmax ที่ปรับด้วย temperature แล้วเก็บความน่าจะเป็นของ token แรกที่ T = 0.5 (ปัด 4 ตำแหน่ง) ไว้ใน p_first เติม ___ ให้ครบ
แบบฝึกท้ายบท
บทนี้จะนับว่าเสร็จเมื่อผ่านข้อพื้นฐานและประยุกต์ครบ ข้อท้าทายได้ดาวเพิ่ม
🟢 พื้นฐาน
softmax(logits, 1.0) ที่ให้ไว้ — ความน่าจะเป็นของ token แรก ("the") ปัด 4 ตำแหน่ง เก็บไว้ในตัวแปร p_top
ที่ temperature 0.5 ความน่าจะเป็นของ token แรกเป็นเท่าไร (ปัด 4 ตำแหน่ง) เก็บไว้ในตัวแปร p_top_low
จากประวัติ history ที่ให้ไว้: จำนวนข้อความของ user เก็บไว้ใน n_user และผลรวม token ที่ประมาณของทุกข้อความ (est_tokens ของ content) เก็บไว้ใน history_tokens
ตรวจว่า role ของ history สลับ user/assistant กันตลอดหรือไม่ (ข้อความติดกันต้องมี role ต่างกัน) เก็บค่า True/False ไว้ในตัวแปร alternates
🟡 ประยุกต์
ตัดประวัติให้พอดีงบ 60 token โดยเก็บข้อความใหม่ล่าสุดย้อนหลัง (ไม่เกินงบ, ใช้ est_tokens ของ content) เก็บจำนวนข้อความที่เก็บไว้ใน kept_count และผลรวม token ของมันใน kept_tokens
top-k = 2: เก็บเฉพาะ 2 token ที่ความน่าจะเป็น (T = 1.0) สูงสุด แล้วปรับให้ผลรวมเป็น 1 — ความน่าจะเป็นใหม่ของอันดับ 1 และ 2 (ปัด 4 ตำแหน่ง) เก็บไว้ใน p_top2_first, p_top2_second
สุ่ม token 1,000 ครั้งด้วย np.random.RandomState(42).choice(len(tokens), size=1000, p=...) (สร้าง RandomState ใหม่ทุกครั้ง) ที่ T = 1.0 และ T = 0.2 — สัดส่วนที่ได้ token แรก เก็บไว้ใน frac_t1, frac_t02
🔴 ท้าทาย
ในช่วง temperature 0.05, 0.10, …, 2.00 (ห่างกัน 0.05 ใช้ round(0.05 * i, 2) เมื่อ i = 1..40) ค่า temperature ที่มากที่สุดที่ทำให้ token แรกยังมีความน่าจะเป็น ≥ 0.9 คือเท่าไร เก็บไว้ใน t_for_90
top-p (nucleus) ที่ p = 0.9 ที่ T = 1.0: เรียง token ตามความน่าจะเป็นมากไปน้อย แล้วเลือกชุดเล็กสุดที่ความน่าจะเป็นสะสม ≥ 0.9 — เก็บจำนวนไว้ใน nucleus_size และรายชื่อ token (list ตามลำดับ) ไว้ใน nucleus_tokens
Quiz
สรุป
- API ไม่มีความจำ: ส่ง system + messages ที่จำเป็นทุกครั้ง และตัดประวัติเก่าสุดให้พอดีงบ token (เก็บ system ไว้เสมอ)
- softmax(logits / T): T ต่ำ → คมและนิ่ง, T สูง → แบนและหลากหลาย (T=0.2 เลือก "the" 99.6%, T=1.0 เลือก 58.2% ใน 1,000 ครั้ง seed 42)
- top-k เก็บ k ตัวแรก, top-p เก็บชุดเล็กสุดที่ความน่าจะเป็นสะสม ≥ p; งานที่ต้องแม่นใช้ T ต่ำและ cache ผลที่ต้องทำซ้ำได้
คำถามสัมภาษณ์
LLM API ไม่มีความจำ แล้วแชตบอตจำบทสนทนาได้อย่างไร
ตัวแอปเก็บประวัติเองแล้วส่ง messages ที่เกี่ยวข้องกลับไปทุกคำขอ เมื่อประวัติยาวเกินงบ token ต้องตัดหรือสรุปข้อความเก่า (เก็บ system prompt และข้อความล่าสุด) และอาจดึงข้อมูลที่เกี่ยวข้องมาจากฐานความรู้ภายนอกแทนการพกทุกอย่างไว้ในประวัติ
เมื่อไรควรใช้ temperature ต่ำและเมื่อไรควรสูง
งานที่ต้องการความถูกต้อง สม่ำเสมอ เช่น สกัดข้อมูลเป็น JSON หรือตอบจากเอกสาร ควรใช้ต่ำ ส่วนงานที่ต้องการความหลากหลาย เช่น ระดมไอเดีย เขียนสร้างสรรค์ ใช้สูงกว่า และไม่ควรพึ่ง temperature อย่างเดียวเพื่อให้ผลทำซ้ำได้ ควรเก็บ/แคชผลที่สำคัญ