ต่อ AI เข้ากับโค้ดของเรา
บทที่แล้วเราเขียน prompt เก่งขึ้น บทนี้เราจะเอา prompt นั้นไปเรียกจริงจาก โค้ดของเรา ผ่าน API — เปลี่ยนจาก "คุยในแชท" เป็น "ฟีเจอร์ในแอป" ตัวอย่างทั้งหมดใช้แนวคิดของ Anthropic SDK (Claude) และภาษา TypeScript/NestJS
ภาพรวม: request → model → response
เรียก LLM ก็เหมือนเรียก REST API ทั่วไป — เราส่ง ข้อความบทสนทนา ไป แล้วได้ ข้อความตอบ กลับมา พร้อมข้อมูลว่าใช้ไปกี่ token
ส่วนประกอบของ 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 ได้มาก — วัดคุณภาพก่อน ถ้าพอก็อยู่ตัวเล็ก
- ออกแบบ endpoint
POST /tickets/:id/summarizeใน NestJS ที่ดึงเนื้อหา ticket มาสรุป — จะเลือก model อะไร ตั้งmax_tokensเท่าไร และtemperatureเท่าไร เพราะอะไร? - ถ้าเจอ error 429 (rate limit) ตอน traffic พุ่ง จะจัดการยังไงไม่ให้ผู้ใช้เห็น error ตรงๆ?
- อยากได้ผลเป็น
{ summary, tags[] }เพื่อเก็บลง Postgres — จะสั่งโมเดลและ validate ยังไง?
ลองตอบในแชท แล้วพิมพ์ /quiz Lv2 เพื่อทดสอบความเข้าใจ