THEN
PART 2 · ติดตั้ง

.env, litellm.yaml และ virtual keys

Config walkthrough · virtual keys and budgets

TL;DR — สรุปใน 30 วินาทีtldr

บทนี้คือบทที่ "เฉลยทุกบรรทัด" — ไฟล์ความลับ .env และไฟล์ประตู litellm.yaml ทั้งสองไฟล์นี้กำหนดว่าระบบปลอดภัยแค่ไหน เร็วแค่ไหน และพังยังไง เราจะไล่ทีละบรรทัดพร้อมคำอธิบายภาษาไทย

กฎข้อที่ 1: ค่าจริงอยู่ใน .env เท่านั้น (gitignored, chmod 600) — ไฟล์ example มีแต่ค่า CHANGE_ME ที่ต้องแทนที่หมด

Virtual key คือ กุญแจใบรองที่ออกให้แต่ละช่องทาง — แต่ละใบมี 3 คุณสมบัติ: อนุญาตเฉพาะโมเดลที่กำหนด, มีเพดานงบประมาณต่อวันของตัวเอง และเพิกถอน/หมุนเวียนได้โดยไม่แตะกุญแจหลัก — นี่คือเหตุผลที่ระบบ 5 ช่องทางอยู่ด้วยกันได้อย่างมีระเบียบ: ประตูเดียว หลายบาน ทุกบานมีมิเตอร์

เมื่อไหร่ควรใช้ / When to usewhen to use

ทำหลัง bootstrap (บทที่ ๓) — bootstrap จะไม่ยก stack ให้จนกว่า .env จะสะอาด (ไม่มี CHANGE_ME เหลือ) นะครับ

  • เปิดไฟล์ .env ด้วย editor บน VPS แล้วไล่แทนที่ทีละบล็อกตามด้านล่าง
  • ตั้งชื่อ key ตามรูปแบบ: สุ่มด้วย openssl rand -hex 16 แล้วเติมหน้า sk-

ออกกุญแจทันทีที่ stack ขึ้นแล้ว (บทที่ ๓–๔) และออกใหม่ทุกครั้งที่เพิ่มช่องทาง — บทที่ ๗–๘ จะใช้กุญแจพวกนี้ นะครับ

กุญแจใครใช้โมเดลที่อนุญาตเพดาน/วัน
BOM_KEY_LINELINE botthai-chat-rt$2
BOM_KEY_TELEGRAMTelegram botthai-chat-rt$2
BOM_KEY_MT5MT5 EA (อ่านอย่างเดียว)โมเดลวิเคราะห์ตัวเดียว$1
BOM_KEY_WRITERpipeline บทความ n8nbom-writer$5
BOM_KEY_WEBUIเว็บแชทรายการที่คัดมา$10

วิธีทำ / How-tohow to

ไฟล์ .env — ฉบับเต็มพร้อมคำอธิบายไทย (จาก configs/.env.example):

# ---------- LiteLLM ----------
# กุญแจหลัก (master key) = กุญแจของเจ้าของบ้าน สร้างครั้งเดียว เก็บดี ๆ
LITELLM_MASTER_KEY=sk-CHANGE_ME   # openssl rand -hex 16 แล้วเติมหน้า sk- ครับ
LITELLM_UI_USERNAME=admin
LITELLM_UI_PASSWORD=CHANGE_ME     # รหัสผ่านแข็ง ๆ สำหรับหน้า /ui ครับ

# ---------- Postgres ----------
POSTGRES_USER=litellm
POSTGRES_PASSWORD=CHANGE_ME       # ฐานข้อมูลอยู่ใน container network ไม่เปิดออกนะครับ
DATABASE_URL=postgresql://litellm:...@postgres:5432/litellm

# ---------- Open WebUI ----------
WEBUI_SECRET_KEY=CHANGE_ME        # ใช้เซ็น session ของผู้ใช้เว็บแชทครับ

# ---------- Mac ผ่าน Tailscale ----------
# รูปแบบ: http://100.x.y.z:11434  (Tailscale IP ของ Mac จากบทที่ ๒)
# กฎเหล็ก: ห้ามใส่ IP สาธารณะเด็ดขาด
MAC_OLLAMA_BASE_URL=http://100.CHANGE_ME.CHANGE_ME.CHANGE_ME:11434

# ---------- Tier 3 (คลาวด์ fallback) ----------
# ไม่อยากมีคลาวด์เลย = เว้นว่างไว้ (hard-local 100%)
CLOUD_FALLBACK_API_KEY=CHANGE_ME_or_leave_empty

# ---------- กุญแจรายช่องทาง ----------
# สร้างทีหลังผ่าน /key/generate แล้วค่อยมาแปะ (บทที่ ๔)
BOM_KEY_LINE=sk-CHANGE_ME
BOM_KEY_TELEGRAM=sk-CHANGE_ME
BOM_KEY_MT5=sk-CHANGE_ME
BOM_KEY_WRITER=sk-CHANGE_ME
BOM_KEY_SMOKE=sk-CHANGE_ME

จำไว้: บ้านเราพูดแต่ "โหลดคีย์แล้ว ยาว N ตัว" ใน log/รายงาน — ห้ามพิมพ์ค่าจริงเด็ดขาดนะครับ

หลังแก้เสร็จ ตรวจนาทีละ 30 วินาที: chmod 600 .env และ docker compose up -d แล้วดูว่า LiteLLM parse config ผ่าน ถ้าติดตรงไหน → บทที่ ๙

ออกกุญแจด้วย API ของ LiteLLM โดยตรง (คำสั่งเดียวต่อใบ) ครับ:

curl -X POST http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{"key_alias": "line-bot",
       "max_budget": 2.0,
       "budget_duration": "1d",
       "models": ["thai-chat-rt"]}'

# การตอบกลับมี field "key" = กุญแจจริง -> นำไปแปะใน .env และ n8n credential store
# (ในรายงาน/log พูดแค่ "โหลดคีย์แล้ว ยาว N ตัว" เท่านั้น)

หลักการ: กุญแจอยู่ใน .env (VPS) หรือ credential store ของ n8n เท่านั้น ห้ามฝังใน workflow JSON/โค้ดนะครับ

ตรวจการใช้จ่ายสะสมทุกวัน (นี่คือแหล่งตัวเลข "ใบเสร็จ" ของทั้งเล่ม) ครับ:

-- SQL บน Postgres ของ LiteLLM (รันผ่าน docker exec)
SELECT date_trunc('day', "startTime") AS day,
       count(*) AS calls,
       round(sum(spend)::numeric, 4) AS usd
FROM "LiteLLM_SpendLogs"
GROUP BY 1 ORDER BY 1 DESC LIMIT 30;

ผลจริงสัปดาห์แรกของ ก.ย. 2026: 1,370 calls/$1.10, 1,470/$0.66, 3,448/$2.76 (พีคสุด), 494/$0.31 นะครับ

ถ้าติดตรงไหน → บทที่ ๙

ตัวอย่างการตั้งค่า / Config exampleconfig example

ไฟล์ litellm.yaml — ฉบับย่อส่วนที่สำคัญที่สุด (ฉบับเต็ม 134 บรรทัดอยู่ใน repo):

general_settings:
  master_key: os.environ/LITELLM_MASTER_KEY   # อ้างจาก env เท่านั้นครับ
  max_budget: 5.0        # เพดานใช้จ่ายคลาวด์รวม $5/วัน — ตัวกันบิลระเบิดครับ
  budget_duration: 1d

litellm_settings:
  drop_params: True      # [บทเรียนจริง] พารามิเตอร์ที่ proxy ไม่รู้จักจะถูก "ตัดทิ้งเงียบ ๆ" นะครับ
                         # เคยมี probe พิสูจน์ repeat_penalty=8.0 ผ่านไปได้ แต่ presence_penalty หายไป
  fallbacks: [{ "thai-chat": ["thai-chat-cpu", "thai-chat-cloud"] }]  # บันได 3 ชั้นจากบทที่ ๑ นะครับ

model_list:
  # Tier 1 — Mac (ฟรี หลัก)
  - model_name: thai-chat
    litellm_params:
      model: ollama_chat/qwen38-chat
      api_base: os.environ/MAC_OLLAMA_BASE_URL   # Tailscale เท่านั้นครับ
      num_ctx: 32768        # พื้นระบับ context ครับ
      num_predict: 2048     # เพดานคำตอบ chat ครับ

  # เส้นทางบอท: ปิด think + เพดานตอบกระชับ
  # [บทเรียนจริง] เปิด think + num_predict น้อย = โมเดลเผาโควตาคำตอบไปกับ "ความคิดในใจ" แล้วตอบเปล่า
  - model_name: thai-chat-rt
    litellm_params:
      model: ollama_chat/qwen38-chat
      extra_body: { "think": false }
      num_predict: 1024
      timeout: 51

  # เส้นทางนักเขียน (pipeline บทความ): เพดานสูงเพื่อไม่ให้รายงานยาวโดนตัด
  - model_name: bom-writer
    litellm_params:
      num_ctx: 32768
      num_predict: 8192    # พื้นระบับความปลอดภัยของ completion (บทที่ ๖)

โครงทั้งไฟล์ = route ต่อการใช้งาน หนึ่ง route หนึ่งพฤติกรรม แลกกันที่นี่ ไม่ใช่ที่ Modelfile ของ Mac นะครับ

การใช้ง่ายกุญแจฝั่งผู้เรียก — ทุกช่องทางเรียกที่เดียวกัน ต่างกันแค่ใบกุญแจและชื่อโมเดลนะครับ:

# ทุกช่องทางชี้ที่: https://llm.ชื่อโดเมนคุณ/v1  (OpenAI-compatible 100%)
curl https://llm.example.com/v1/chat/completions \
  -H "Authorization: Bearer $BOM_KEY_LINE" \
  -d '{"model": "thai-chat-rt",
       "messages": [{"role":"user","content":"ทองเที่ยงนี้เป็นไงบ้าง"}]}'

ถ้าใช้กุญแจผิดใบ/เกินงบ proxy ตอบ 401/403 ทันที — ไม่มีทางลอยไปกินเงินส่วนอื่นครับ

Pro tip — เคล็ดลับจากสนามจริงpro tip

★ PROD LESSON
ค่าที่เคยพังเพราะพิมพ์ผิด — repeat_penalty 8.0 (พิมพ์ผิดจาก 1.05) ทำลายคำตอบทั้งก้อน และมัน "ผ่าน" มาได้เพราะ proxy ส่งต่อทุกอย่าง — บทเรียนคืออย่าเชื่อว่า config ที่ไม่ error = config ที่ถูก ต้องรัน bench ยืนยันเสมอ (บทที่ ๖)
★ PRO TIP
อยาก hard-local 100%? — ลบทิ้งเฉพาะบล็อก thai-chat-cloud ใน model_list และเว้น CLOUD_FALLBACK_API_KEY ว่าง — ระบบจะข้อมูลแชตหลักอยู่ในเครื่องของเราเอง — ยกเว้นการค้นเว็บและ fallback คลาวด์ ซึ่งส่งตามเงื่อนไขของผู้ให้บริภายนอกเลย แลกกับความเสี่ยงที่บอทจะเงียบช่วง Mac ล่ม
★ PROD LESSON
เพดานที่ไม่เคยแตะ — เพดานรวม $5/วัน (max_budget ใน litellm.yaml) ตั้งมาตั้งแต่วันแรก ยอดสูงสุดที่เคยเกิดจริงคือ $2.76 = 55% ของเพดาน — การตั้งเพดานไว้ก่อนไม่ได้แปลว่าจะใช้ถึง แต่แปลว่าวันที่มีอะไรเพี้ยน บิลก็ยังถูกล็อกไว้
★ PRO TIP
หมุนเวียนกุญแจไม่เจ็บ — เพราะทุกช่องทางถือกุญแจใบของตัวเอง การเปลี่ยนกุญแจเว็บแชทไม่กระทบบอทเลย — สงสัยว่ารั่วก็ระงับใบนั้นก่อน แล้วออกใบใหม่ (แนวคิด blast radius เล็ก)

คำถามที่พบบ่อย / FAQfaq

ถาม: ใส่ค่าผิดแล้วลงมือไปแล้ว ต้องเริ่มใหม่ไหม? — ไม่ต้อง แก้ .env แล้ว docker compose up -d ใหม่ ตัวเดียวที่แก้ยากคือ master key เพราะต้องไปอัปเดตทุกที่ที่อ้างถึง

ถาม: เก็บ .env ไว้ที่ไหนดี? — บน VPS อย่างเดียว อย่า copy ลงเครื่องพกพา/Drive ส่วนสำรองจริงใช้ตู้เซฟ (เช่น Vaultwarden บน NAS)

คำถามอื่น ๆ ดูที่ FAQ

ถาม: ถ้ากุญแจรั่วจริง ๆ ความเสียหายคือเท่าไหร่? — ถูกกั้นไว้ที่เพดานของใบนั้น ($1–10/วัน) บวกเวลาที่เราเห็นใน spend log — โมเดลที่ใบกุญแจอนุญาตก็ถูกจำกัดอยู่ด้วย ไม่สามารถเอาไปเรียกของแพงกว่าได้

ถาม: ดู dashboard ที่ไหน? — ที่ /ui ของ LiteLLM (หลังผ่าน Cloudflare Access) มีทั้งรายการกุญแจและกราฟ spend รายวัน

ดูเพิ่ม / See alsosee also

ภาพประกอบ / Figuresfigures

เส้นทาง request ของบอทผ่าน litellm.yaml
route thai-chat-rt คือเส้นทางกลางของแผนภาพนี้
หน้า API ของ LiteLLM เวอร์ชัน 1.100.0 ที่ใช้จริงใน production
เวอร์ชันที่เห็นคือเวอร์ชันที่รันอยู่จริงบน VPS — ทุก endpoint ต้องมีกุญแจจึงเรียกได้
แผนภาพผ้ากุญแจเสมือน 5 ใบ
หนึ่ง proxy หลายประตู ทุกประตูมีมิเตอร์ของตัวเอง

คลิปหน้าจอจริงตามบทเรียน · คลิกเล่นเพื่อฟังสรุป · ไม่ใช่คำแนะนำการลงทุนครับ

▶ ดูวิดีโอคู่มือ: https://youtu.be/O4hM39cgDCY