teach-me-ai / Lv2
⟵ กลับหน้า Roadmap
LEVEL 2 · LLM API

ต่อ AI เข้ากับโค้ดของเรา

⏱️ ~25 นาที 🎯 เรียก LLM จากโค้ดได้ พร้อมจัดการ cost/error

บทที่แล้วเราเขียน prompt เก่งขึ้น บทนี้เราจะเอา prompt นั้นไปเรียกจริงจาก โค้ดของเรา ผ่าน API — เปลี่ยนจาก "คุยในแชท" เป็น "ฟีเจอร์ในแอป" ตัวอย่างทั้งหมดใช้แนวคิดของ Anthropic SDK (Claude) และภาษา TypeScript/NestJS

ภาพรวม: request → model → response

เรียก LLM ก็เหมือนเรียก REST API ทั่วไป — เราส่ง ข้อความบทสนทนา ไป แล้วได้ ข้อความตอบ กลับมา พร้อมข้อมูลว่าใช้ไปกี่ token

โค้ดเรา messages + system tokens เข้า LLM tokens ออก คำตอบ + usage
คุณจ่ายเงินตาม token ทั้ง "ขาเข้า" (prompt) และ "ขาออก" (คำตอบ) — จำไว้จาก Lv0

ส่วนประกอบของ request

พารามิเตอร์คืออะไร
modelเลือกโมเดล (เล็ก-เร็ว vs ใหญ่-เก่ง ตาม Lv0)
systemคำสั่งระบบ กำหนดบทบาท/กติกา (จาก Lv1)
messagesประวัติบทสนทนา สลับ role user / assistant
max_tokensจำกัดความยาวคำตอบ (คุม cost + กันตอบยาวเกิน)
temperatureความสุ่ม/สร้างสรรค์ (จาก Lv0)
streamให้ส่งคำตอบทีละชิ้นแบบ real-time หรือไม่

เรียกครั้งแรก (โค้ดพื้นฐาน)

// ติดตั้ง: npm i @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY, // อ่านจาก env เสมอ
});

const res = await client.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 500,
  system: "คุณคือผู้ช่วยสรุปข้อความให้กระชับ เป็นภาษาไทย",
  messages: [
    { role: "user", content: "ช่วยสรุปข้อความนี้: ..." },
  ],
});

console.log(res.content[0].text);
console.log(res.usage); // { input_tokens, output_tokens }
🔐

ห้าม hardcode API key ในโค้ด — เก็บใน environment variable (`.env` + `process.env`) และอย่า commit ขึ้น git เด็ดขาด (ดู security rules)

Streaming — ตอบทีละ token

แทนที่จะรอคำตอบครบก้อนเดียว streaming ส่งคำตอบทีละชิ้นทันทีที่โมเดลผลิตออกมา — ทำให้ UX รู้สึกเร็ว (เห็นตัวอักษรวิ่งเหมือนในแชท) เหมาะกับหน้าจอที่ผู้ใช้รอดู

const stream = await client.messages.stream({
  model: "claude-sonnet-5",
  max_tokens: 500,
  messages: [{ role: "user", content: "เล่าเรื่องสั้นๆ" }],
});

for await (const event of stream) {
  if (event.type === "content_block_delta") {
    process.stdout.write(event.delta.text); // พ่นทีละชิ้น
  }
}

ขอผลเป็น JSON ที่ parse ได้

ถ้าจะเอาผลไปใช้ต่อในโค้ด (ไม่ใช่โชว์ให้คนอ่าน) ให้สั่งโมเดลตอบเป็น JSON ตาม schema ที่กำหนด แล้ว parse — วิธีที่แน่นอนกว่านี้คือ tool use ซึ่งเราจะเรียนใน Lv3

system: "ตอบเป็น JSON เท่านั้น รูปแบบ: {\"sentiment\":\"pos|neg|neu\",\"score\":number}"
// แล้วในโค้ด:
const data = JSON.parse(res.content[0].text);
⚠️

โมเดลอาจแถมข้อความนอก JSON มาบ้าง — ควรห่อ JSON.parse ด้วย try/catch และ validate ด้วย schema (เช่น zod) ก่อนใช้จริง

Error handling ที่ dev ต้องทำ

ปัญหารับมือยังไง
Rate limit (429)retry แบบ exponential backoff (รอนานขึ้นเรื่อยๆ)
Timeout / เครือข่ายล่มตั้ง timeout + retry จำกัดจำนวนครั้ง
คำตอบยาวโดนตัดดู stop_reason; เพิ่ม max_tokens ถ้าจำเป็น
Input ยาวเกิน contextตัด/สรุปก่อนส่ง (โยง Lv0 context window)

ตัวอย่างจริง: mini-service ใน NestJS

แยกเป็น service (คุยกับ LLM) + controller (รับ request) ตามสไตล์ NestJS:

// summarize.service.ts
import { Injectable } from "@nestjs/common";
import Anthropic from "@anthropic-ai/sdk";

@Injectable()
export class SummarizeService {
  private client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

  async summarize(text: string): Promise<string> {
    const res = await this.client.messages.create({
      model: "claude-haiku-4-5-20251001", // งานสรุป → โมเดลเล็ก ประหยัด
      max_tokens: 300,
      system: "สรุปเป็นภาษาไทย 3 bullet",
      messages: [{ role: "user", content: text }],
    });
    return res.content[0].text;
  }
}

งานสรุป/จัดหมวดที่เรียกบ่อยๆ ใช้โมเดลเล็ก (Haiku) ช่วยลด cost ได้มาก — วัดคุณภาพก่อน ถ้าพอก็อยู่ตัวเล็ก

🎯 แบบฝึกหัด
  1. ออกแบบ endpoint POST /tickets/:id/summarize ใน NestJS ที่ดึงเนื้อหา ticket มาสรุป — จะเลือก model อะไร ตั้ง max_tokens เท่าไร และ temperature เท่าไร เพราะอะไร?
  2. ถ้าเจอ error 429 (rate limit) ตอน traffic พุ่ง จะจัดการยังไงไม่ให้ผู้ใช้เห็น error ตรงๆ?
  3. อยากได้ผลเป็น { summary, tags[] } เพื่อเก็บลง Postgres — จะสั่งโมเดลและ validate ยังไง?

ลองตอบในแชท แล้วพิมพ์ /quiz Lv2 เพื่อทดสอบความเข้าใจ