Last updated: 2026-06-11 14:35 UTC · Download raw Markdown · Email questions

AI Gateway TH — Quick Start (Thai)

สำหรับ Wewasanad + integrators · v0.11+ Last updated: 2026-05-31 Contact: [email protected]


1. รับ Bearer Token

Production endpoint: https://gateway.kiatdamrong.shop

ติดต่อ [email protected] พร้อมระบุ:

จะได้ JWT token ที่ใช้ใน Authorization: Bearer <token> header

ทุก token ผูกกับ tenant_id เฉพาะ — share token ไม่ได้


2. Health Check (ไม่ต้อง auth)

curl https://gateway.kiatdamrong.shop/health
# → {"status":"ok","version":"0.11.0"}

ใช้ใน monitoring/load-balancer probe


2.5 Native SDKs (แนะนำสำหรับ production)

มี SDK ทางการ 4 ภาษา ห่อ HTTP + retry + typed responses ให้แล้ว — ครอบทุก endpoint ของ gateway รวมทั้ง Wizard / Grounding tools / Knowledge query / Smart Engine + grounded flags. ลูกค้าไม่ต้องเขียน prompt+glue เอง.

ภาษา path ใน repo ติดตั้งจาก source*
Python sdks/python/ pip install -e ./sdks/python
Node / TypeScript sdks/node/ cd sdks/node && npm install && npm run build
.NET (net8 / net9) sdks/dotnet/AiGatewayTh/ dotnet add reference sdks/dotnet/AiGatewayTh/AiGatewayTh.csproj
Java + Spring sdks/java/ mvn -f sdks/java/pom.xml install

* Honest: ตอนนี้ติดตั้งจาก source/git เท่านั้น — package registries (PyPI / npm / NuGet / Maven) ยังไม่ publish. รายละเอียดดูที่ sdks/README.md.

Shared API surface (ทุก SDK)

ทุก SDK มีเมธอดเหมือนกัน เปลี่ยนแค่ naming convention ของภาษา (snake_case ใน Python, camelCase ใน Node/JS, PascalCaseAsync ใน .NET, camelCase ใน Java).

กลุ่ม เมธอด endpoint บันทึก
OpenAI-compat health() GET /health liveness probe
models() GET /v1/models model catalog
chat(req, idempotencyKey?) POST /v1/chat/completions streaming + tools + JSON mode
embeddings(req) POST /v1/embeddings gateway-embed-th = BGE-M3 1024-dim Thai
Smart Engine smart(req, options?) POST /v1/smart sync; opt-in writer_grounding + judge_tools
smart_stream(req, options?) POST /v1/smart (SSE) iterator / generator of per-step events
Wizard facades wizard_polish(text, style?) POST /v1/wizard/polish ขัด translationese — Typhoon SAFE Thai-native
wizard_shorten(text, targetChars?, style?) POST /v1/wizard/shorten ย่อให้สั้นโดยรักษาใจความ
wizard_fact_check(claim, packId?) POST /v1/wizard/fact-check tooled cross-family judge
wizard_grounded_summarize(question, urls?) POST /v1/wizard/grounded-summarize grounded writer + citation verifier
wizard_translate_th(text, sourceLang?, style?) POST /v1/wizard/translate-th แปลเข้าไทยแบบเนทีฟ (Typhoon)
wizard_fix_thai(text) POST /v1/wizard/fix-thai ซ่อมสะกด/วรรค/การันต์ภาษาไทย
Grounding tools web_search(query, n?) POST /v1/tools/web_search SearXNG, Thai-first
read_url(url, maxChars?) POST /v1/tools/read_url HTML/PDF + Thai ำ-repair + SSRF guard
read_file(file, filename?, maxChars?) POST /v1/tools/read_file upload PDF / DOCX / XLSX / CSV / TXT (multipart)
knowledge_query(packId, query, ...) POST /v1/knowledge/query curated pack hybrid (vector + tag)
Async jobs submit_job(req) / get_job(id) / wait_for_job(id) / list_jobs(limit) POST/GET /v1/jobs/... heavy compose with webhook
Audit audit(request_id) GET /v1/audit/{id} metadata-only audit trail

ทุก SDK มี retry+exponential backoff อัตโนมัติ สำหรับ 5xx + network errors, idempotency-key opt-in per-call, และ cancellation native ของภาษา (AbortSignal / CancellationToken / thread interrupt).


Python

from ai_gateway_th import GatewayClient

c = GatewayClient(
    base_url="https://gateway.kiatdamrong.shop",
    token="<YOUR_TOKEN>",
    tenant_id="<YOUR_TENANT_ID>",
    max_retries=3,            # exponential backoff on 5xx + network
    timeout=120.0,            # default per-call timeout
)

# 1) Chat (OpenAI-compat — passthrough)
resp = c.chat(
    model="gateway-th",
    messages=[{"role": "user", "content": "อธิบาย scaffolding ใน 3 บรรทัด"}],
    max_tokens=400,
    idempotency_key="onboarding-msg-001",   # safe to retry, replays cache
)
print(resp["choices"][0]["message"]["content"])

# 2) Wizard polish (Typhoon SAFE — ลบ "ที่ซึ่ง / ถูกทำให้")
r = c.wizard_polish(
    "การเรียนรู้แบบลงมือทำที่ซึ่งผู้เรียนถูกทำให้มีส่วนร่วม",
    style="neutral",
)
print(r["polished_text"])

# 3) Wizard grounded-summarize (writer_grounding + citation verifier)
r = c.wizard_grounded_summarize(
    "สรุปแนวทาง active learning การคูณ ป.4 จากแหล่งจริง",
)
print(r["summary"])
print("URLs read:", len(r["writer_grounding"]["urls_read"]))
print("Fabricated:", r["writer_grounding"]["citations"]["urls_fabricated"])

# 4) Grounding tools — เอามาประกอบเอง
hits = c.web_search("แผนการสอนคณิต ป.4 active learning", n=3)
article = c.read_url(hits["results"][0]["url"])
print(article["content"][:500])

# 5) read_file — อัปโหลด PDF/DOCX/XLSX/CSV/TXT
with open("plan.pdf", "rb") as f:
    out = c.read_file(f, max_chars=30000)
print(out["kind"], out["chars"], "chars")
# หรือส่ง bytes ตรงๆ ก็ได้:
out = c.read_file(b"name,score\nA,80\n", filename="grades.csv")

# 6) Smart Engine + opt-in grounding (raw control)
r = c.smart(
    messages=[{"role": "user", "content": "วิเคราะห์ x จากงานวิจัยจริง"}],
    writer_grounding=True,
    judge_tools=True,
    stream=False,             # use smart_stream() for >100s compose
)

# 7) Smart streaming SSE (long compose ผ่าน CF tunnel)
for event in c.smart_stream(
    messages=[{"role": "user", "content": "ออกแบบแผนการสอน ป.4 เรื่องการคูณ"}],
):
    if event["event"] == "smart.complete":
        print(event["data"]["choices"][0]["message"]["content"])
        break

# 8) Async job (>10 นาที compose)
job = c.submit_job(
    messages=[{"role": "user", "content": "งานใหญ่..."}],
    webhook_url="https://your.app/cb",
    webhook_secret="shared",
)
done = c.wait_for_job(job["job_id"])

# 9) Error handling
from ai_gateway_th import GatewayError
try:
    c.chat(model="bad-model", messages=[{"role": "user", "content": "x"}])
except GatewayError as e:
    print(e.status_code, e.error_type, e.message, e.request_id)

Node / TypeScript

import { GatewayClient, GatewayError } from "@ai-gateway-th/sdk";

const c = new GatewayClient({
  baseUrl: "https://gateway.kiatdamrong.shop",
  token: process.env.GATEWAY_TOKEN!,
  tenantId: process.env.GATEWAY_TENANT_ID,
  maxRetries: 3,
  timeoutMs: 120_000,
});

// 1) Chat
const resp = await c.chat({
  model: "gateway-th",
  messages: [{ role: "user", content: "อธิบาย scaffolding ใน 3 บรรทัด" }],
  max_tokens: 400,
}, { idempotencyKey: "onboarding-msg-001" });

// 2) Wizard polish
const r = await c.wizardPolish(
  "การเรียนรู้แบบลงมือทำที่ซึ่งผู้เรียนถูกทำให้มีส่วนร่วม",
  { style: "neutral" },
);
console.log(r.polished_text);

// 3) Wizard grounded-summarize
const sum = await c.wizardGroundedSummarize(
  "สรุปแนวทาง active learning การคูณ ป.4 จากแหล่งจริง",
);
console.log(sum.summary);
console.log("Fabricated:",
  (sum.writer_grounding as any).citations.urls_fabricated);

// 4) read_file — รับ Blob / Uint8Array / ArrayBuffer
import { readFileSync } from "node:fs";
const bytes = readFileSync("plan.pdf");
const doc = await c.readFile(bytes, {
  filename: "plan.pdf",
  contentType: "application/pdf",
  maxChars: 30000,
});
console.log(doc.kind, doc.chars);

// 5) Smart streaming (async iterator)
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 600_000);   // safety cancel after 10min
for await (const ev of c.smartStream(
  { messages: [{ role: "user", content: "ออกแบบแผนการสอน ป.4" }] },
  { signal: ctrl.signal },
)) {
  if (ev.event === "smart.complete") {
    console.log(ev.data.choices[0].message.content);
  }
}

// 6) Error handling
try {
  await c.chat({ model: "bad", messages: [{ role: "user", content: "x" }] });
} catch (e) {
  if (e instanceof GatewayError) {
    console.error(e.statusCode, e.errorType, e.message, e.requestId);
  } else {
    throw e;
  }
}

.NET (C# — net8 / net9)

using AiGatewayTh;

// Bare construction
using var client = new GatewayClient(new GatewayClientOptions
{
    Token = Environment.GetEnvironmentVariable("GATEWAY_TOKEN")!,
    TenantId = Environment.GetEnvironmentVariable("GATEWAY_TENANT_ID"),
    BaseUrl = "https://gateway.kiatdamrong.shop",
    MaxRetries = 3,
    Timeout = TimeSpan.FromMinutes(2),
});

// 1) Chat
var resp = await client.ChatAsync(new ChatCompletionRequest
{
    Model = "gateway-th",
    Messages = new()
    {
        new ChatMessage { Role = "user",
                          Content = "อธิบาย scaffolding ใน 3 บรรทัด" }
    },
    MaxTokens = 400,
}, idempotencyKey: "onboarding-msg-001");
Console.WriteLine(resp.Choices[0].Message?.Content);

// 2) Wizard polish
var polish = await client.WizardPolishAsync(
    "การเรียนรู้แบบลงมือทำที่ซึ่งผู้เรียนถูกทำให้มีส่วนร่วม",
    style: "neutral");
Console.WriteLine(polish["polished_text"]);

// 3) Wizard grounded-summarize
var sum = await client.WizardGroundedSummarizeAsync(
    "สรุปแนวทาง active learning การคูณ ป.4 จากแหล่งจริง");
Console.WriteLine(sum["summary"]);

// 4) read_file — upload PDF
byte[] pdfBytes = File.ReadAllBytes("plan.pdf");
var doc = await client.ReadFileAsync(
    pdfBytes, filename: "plan.pdf", contentType: "application/pdf",
    maxChars: 30000);
Console.WriteLine($"kind={doc["kind"]} chars={doc["chars"]}");

// 5) Smart streaming
await foreach (var ev in client.SmartStreamAsync(new SmartRequest
{
    Messages = new() { new() { Role = "user",
                                Content = "ออกแบบแผนการสอน ป.4" } },
}))
{
    if (ev.Event == "smart.complete")
    {
        using var doc2 = System.Text.Json.JsonDocument.Parse(ev.DataJson);
        Console.WriteLine(doc2.RootElement
            .GetProperty("choices")[0]
            .GetProperty("message")
            .GetProperty("content").GetString());
    }
}

// 6) Error handling
try
{
    await client.ChatAsync(new ChatCompletionRequest { Model = "bad",
        Messages = new() { new() { Role = "user", Content = "x" } } });
}
catch (GatewayException ex)
{
    Console.WriteLine($"{ex.StatusCode} {ex.ErrorType}: {ex.Message}");
}

ASP.NET Core integration (DI + IHttpClientFactory pooling):

builder.Services.AddHttpClient("ai-gateway", c =>
{
    c.BaseAddress = new Uri("https://gateway.kiatdamrong.shop");
    c.Timeout = TimeSpan.FromMinutes(5);
});

builder.Services.AddSingleton(sp => new GatewayClient(new GatewayClientOptions
{
    Token = builder.Configuration["AiGateway:Token"]!,
    TenantId = builder.Configuration["AiGateway:TenantId"],
    HttpClient = sp.GetRequiredService<IHttpClientFactory>()
                   .CreateClient("ai-gateway"),
}));

// then inject as `GatewayClient` constructor arg

Java + Spring (JDK 17+)

import shop.kiatdamrong.aigateway.*;

GatewayClient client = new GatewayClient(
    GatewayClientOptions.builder()
        .token(System.getenv("GATEWAY_TOKEN"))
        .tenantId(System.getenv("GATEWAY_TENANT_ID"))
        .baseUrl("https://gateway.kiatdamrong.shop")
        .maxRetries(3)
        .timeout(java.time.Duration.ofMinutes(2))
        .build());

// 1) Chat
Types.ChatCompletionRequest req = new Types.ChatCompletionRequest();
req.model = "gateway-th";
req.messages = java.util.List.of(
    new Types.ChatMessage("user", "อธิบาย scaffolding ใน 3 บรรทัด"));
req.maxTokens = 400;
Types.ChatCompletionResponse resp = client.chat(req, "onboarding-msg-001");
System.out.println(resp.choices.get(0).message.content);

// 2) Wizard polish
Map<String,Object> polish = client.wizardPolish(
    "การเรียนรู้แบบลงมือทำที่ซึ่งผู้เรียนถูกทำให้มีส่วนร่วม",
    "neutral", null, null);
System.out.println(polish.get("polished_text"));

// 3) Wizard grounded-summarize
Map<String,Object> sum = client.wizardGroundedSummarize(
    "สรุปแนวทาง active learning การคูณ ป.4 จากแหล่งจริง",
    null, null, null);
System.out.println(sum.get("summary"));

// 4) read_file — upload PDF
byte[] pdfBytes = java.nio.file.Files.readAllBytes(
    java.nio.file.Paths.get("plan.pdf"));
Map<String,Object> doc = client.readFile(
    pdfBytes, "plan.pdf", "application/pdf", 30000);
System.out.println("kind=" + doc.get("kind") +
                   " chars=" + doc.get("chars"));

// 5) Smart streaming
Types.SmartRequest sr = new Types.SmartRequest();
sr.messages = java.util.List.of(
    new Types.ChatMessage("user", "ออกแบบแผนการสอน ป.4"));
try (GatewayClient.SmartStreamIterator it = client.smartStream(sr, false)) {
    while (it.hasNext()) {
        Types.SseEvent ev = it.next();
        if ("smart.complete".equals(ev.event)) {
            // ev.dataJson = JSON string of the completion
            System.out.println(ev.dataJson);
        }
    }
}

// 6) Error handling
try {
    Types.ChatCompletionRequest bad = new Types.ChatCompletionRequest();
    bad.model = "bad-model";
    bad.messages = java.util.List.of(new Types.ChatMessage("user", "x"));
    client.chat(bad);
} catch (GatewayException ex) {
    System.err.printf("%d %s: %s%n",
        ex.getStatusCode(), ex.getErrorType(), ex.getMessage());
}

Spring Boot auto-configuration (no manual wiring):

application.yml:

ai-gateway:
  token: ${GATEWAY_TOKEN}
  tenant-id: ${GATEWAY_TENANT_ID}
  base-url: https://gateway.kiatdamrong.shop

Then @Autowired GatewayClient ใน service ของคุณ — auto-config โหลด จาก META-INF/spring/...AutoConfiguration.imports (มากับ jar).


Common patterns (ทุก SDK)

Idempotency — SDK v0.5.0 ขึ้นไป สร้าง key ให้อัตโนมัติ จาก เนื้อหา request (ปิดได้ด้วย auto_idempotency=False) — ไม่ต้องส่งเองแล้ว. ผลที่ได้:

ถ้าต้องการคุม key เอง (เช่น แยกตาม business transaction) ก็ส่งเองได้เหมือนเดิม:

SDK usage
Python c.chat(..., idempotency_key="my-key-1")
Node await c.chat(req, { idempotencyKey: "my-key-1" })
.NET await c.ChatAsync(req, idempotencyKey: "my-key-1")
Java c.chat(req, "my-key-1")

Cancellation — กัน compose ค้างเกิน budget:

SDK usage
Python c.smart_stream(...) ภายใน try/except KeyboardInterrupt
Node AbortController → signal: ctrl.signal
.NET CancellationToken arg ทุกเมธอด *Async
Java thread interrupt → catch InterruptedException

Streaming (long compose >100s through CF tunnel) — ใช้ *_stream methods เสมอ. SDK ส่ง SSE keepalive comments (: ping) อัตโนมัติ — กัน CF tunnel idle-timeout ที่ 100s.

Why use the native SDK (vs OpenAI-compat client)

OpenAI SDK (passthrough) Native SDK
chat / embeddings ✅ ✅ typed
smart + grounded flags ❌ (must hand-roll JSON) ✅ typed
wizard/* (polish / fact-check / grounded-summarize) ❌ ✅ one method per wizard
tools/web_search / read_url / read_file ❌ ✅
knowledge_query ❌ ✅
Idempotency-Key manual header ✅ method arg
Retry + backoff client default ✅ built-in (5xx + network)
Streaming SSE works (text only) ✅ + per-step events typed
Anti-fabrication metadata not surfaced ✅ writer_grounding.citations field

ใช้ OpenAI SDK ตอนย้าย legacy code; production ใหม่ใช้ native SDK ของเรา จะคุ้มกว่า — ได้ Wizard / Grounding / read_file endpoints ครบ + typed responses + retry built-in.


SDK reference files

ทุก SDK มีไฟล์รายละเอียดเฉพาะภาษา (install, build, examples, API list ครบ):


3. List Models (/v1/models)

curl https://gateway.kiatdamrong.shop/v1/models \
  -H "Authorization: Bearer <YOUR_TOKEN>"

Models สำคัญที่ควรรู้:

public_id tier description
gateway-th local Smart Thai routing — แนะนำสำหรับ Thai content (ฟรี, in-country)
gateway-embed local Embedding model (nomic-embed-text 768-dim)
local-fast local Qwen3.6 35B-A3B Q8 — fast Thai chat
frontier-claude frontier Anthropic Claude Sonnet — สำหรับงานยาก
frontier-gemini frontier Google Gemini Pro — JSON mode ดี
cloud-large cloud GPT-OSS 120B (free, out-of-country)

Tier policy:


4. Chat Completions (OpenAI-compat)

curl

curl -X POST https://gateway.kiatdamrong.shop/v1/chat/completions \
  -H "Authorization: Bearer <TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gateway-th",
    "messages": [{"role": "user", "content": "อธิบาย scaffolding ใน 2 บรรทัด"}]
  }'

Python (OpenAI SDK)

from openai import OpenAI

client = OpenAI(
    base_url="https://gateway.kiatdamrong.shop/v1",
    api_key="<TOKEN>",
)

resp = client.chat.completions.create(
    model="gateway-th",
    messages=[{"role": "user", "content": "อธิบาย scaffolding ใน 2 บรรทัด"}],
)
print(resp.choices[0].message.content)

Node.js

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://gateway.kiatdamrong.shop/v1",
  apiKey: process.env.GATEWAY_TOKEN,
});

const r = await client.chat.completions.create({
  model: "gateway-th",
  messages: [{ role: "user", content: "อธิบาย scaffolding ใน 2 บรรทัด" }],
});
console.log(r.choices[0].message.content);

PHP (orhanerday/open-ai)

require 'vendor/autoload.php';
use Orhanerday\OpenAi\OpenAi;

$client = new OpenAi($_ENV['GATEWAY_TOKEN']);
$client->setBaseURL("https://gateway.kiatdamrong.shop/v1");

$resp = $client->chat([
    'model' => 'gateway-th',
    'messages' => [['role' => 'user', 'content' => 'อธิบาย scaffolding']],
]);

5. Smart Engine (/v1/smart) — ใช้แทน /v1/chat สำหรับงาน structured

/v1/smart คือ flagship endpoint — auto-classify intent + route packs + compose write→judge→revise → enforce JSON schema

curl -N -X POST https://gateway.kiatdamrong.shop/v1/smart \
  -H "Authorization: Bearer <TOKEN>" \
  -H "X-Tenant-Id: <YOUR_TENANT_ID>" \
  -H "Content-Type: application/json" \
  -d '{
    "stream": true,
    "messages": [{
      "role": "user",
      "content": "ออกแบบแผนการสอนคณิตศาสตร์ ป.4 เรื่อง การคูณ โดยใช้ active learning + ระบุตัวชี้วัด"
    }]
  }'

stream: true สำคัญสำหรับ /v1/smart — compose ใช้เวลา 30-300s (งานหนัก ที่เข้า judge→revise→fresh_think อาจถึง ~6 นาที); CF Tunnel จะตัดการเชื่อมต่อ แบบ synchronous ที่ 100s (ได้ HTTP 524) ถ้าไม่ stream

SSE event order:

smart.received → smart.classified → smart.routed → compose.* → smart.enforce → smart.complete → [DONE]

Keepalive (verified 2026-05-31): ระหว่าง step ที่เงียบ (judge / revise / fresh_think / JSON-enforce) server จะส่ง SSE comment : keepalive ทุก ~15s เพื่อกัน proxy idle-timeout — client ที่ทำตาม SSE spec จะข้ามบรรทัดที่ ขึ้นต้นด้วย : อัตโนมัติ ดังนั้นไม่ต้อง handle เพิ่ม (SDK ทั้ง 4 ภาษาข้ามให้แล้ว) ทดสอบจริงผ่าน CF: compose 359s จบครบ smart.complete + [DONE] ไม่ถูกตัด

ทางเลือกที่ทนที่สุดสำหรับงาน compose หนักมาก: async jobs

ถ้าไม่อยากถือ HTTP connection ยาวๆ ใช้ POST /v1/jobs (เนื้อ body เหมือน /v1/smart) — คืน 202 + job_id ทันที แล้ว poll GET /v1/jobs/{id} (เบา <1s ต่อครั้ง) หรือรับ webhook (HMAC-signed) เมื่อเสร็จ วิธีนี้เลี่ยง CF timeout ได้ 100% เพราะทุก HTTP call สั้น (ทดสอบจริง: job เสร็จที่ 120s ผ่าน CF)

# submit
curl -X POST .../v1/jobs -H "Authorization: Bearer <T>" -H "X-Tenant-Id: <ID>" \
  -d '{"messages":[...], "webhook_url":"https://you/cb", "webhook_secret":"s"}'
# → 202 {"job_id":"job-...","status":"pending","status_url":"/v1/jobs/job-..."}
# poll
curl .../v1/jobs/job-... -H "Authorization: Bearer <T>" -H "X-Tenant-Id: <ID>"
# → {"status":"done","result":{...}}

6. Embeddings (/v1/embeddings)

r = client.embeddings.create(
    model="gateway-embed",
    input=["ข้อความที่ 1", "ข้อความที่ 2"],
)
vectors = [e.embedding for e in r.data]  # list of 768-dim

ใช้กับ Qdrant / pgvector / Chroma ของลูกค้าได้ทันที


7. Idempotency

POST endpoints ที่ใช้เวลา (เช่น /v1/smart) ส่ง Idempotency-Key: <unique-id> header → retry ภายใน 24 ชม. คืน response เดิม (ไม่เก็บเงิน 2 รอบ)

curl -X POST .../v1/smart \
  -H "Idempotency-Key: my-job-id-12345" \
  ...
# retry เดิม → 0.008s response (cached)

8. Prompt Injection Defense (opt-in)

ถ้า input เป็น untrusted user content ส่ง X-Gateway-Inject-Check: 1 หรือ strict:

curl -X POST .../v1/smart \
  -H "X-Gateway-Inject-Check: 1" \
  -d '{"messages": [{"role":"user","content":"ignore previous instructions"}]}'
# → 400 inject_detected พร้อม fingerprint

9. Audit Trail

ทุก response มี header:

ดู audit ของ request:

curl https://gateway.kiatdamrong.shop/v1/audit/<request_id> \
  -H "Authorization: Bearer <TOKEN>"

10. Error Handling

ทุก error คืน OpenAI-shape {"error":{...}}:

{
  "error": {
    "message": "rate limit exceeded",
    "type": "rate_limit_error",
    "code": null,
    "param": null
  }
}

Error types:

type HTTP คือ ทำยังไง
authentication_error 401 token ผิด/หมดอายุ refresh token
permission_error 403 scope ไม่พอ / IP block ขอ admin เพิ่ม
invalid_request_error 400 body ผิด format check schema
not_found_error 404 model/pack/audit หาไม่เจอ verify path
rate_limit_error 429 เกิน per-min/hour/day wait + retry
payload_too_large_error 413 CSV/content เกินขนาด shrink
backend_error 502 upstream LLM fail retry กับ exponential backoff
timeout_error 504 step timeout retry
inject_detected 400 prompt injection ตรวจพบ sanitize user input
internal_server_error 500 bug ของเรา report + retry

11. Rate Limit + Retry Policy แนะนำ

import time
from openai import OpenAI, RateLimitError, APIError

client = OpenAI(base_url=..., api_key=...)

def call_with_retry(*args, max_retries=3, **kwargs):
    delay = 1.0
    for attempt in range(max_retries):
        try:
            return client.chat.completions.create(*args, **kwargs)
        except RateLimitError:
            time.sleep(delay)
            delay *= 2
        except APIError as e:
            if e.status_code >= 500:
                time.sleep(delay)
                delay *= 2
            else:
                raise
    raise RuntimeError("max retries exceeded")

12. Performance & SLA แนะนำ

Endpoint p50 p95 Note
/health <10ms <50ms liveness only
/v1/models <50ms <200ms catalog
/v1/chat/completions (local) 1-5s 10s depends on tokens
/v1/chat/completions (frontier) 2-15s 30s network + provider
/v1/smart synchronous 45-90s 180s ใช้ stream:true
/v1/smart streaming first byte <2s full <300s SSE
/v1/embeddings <100ms/text <500ms local nomic

SLA ที่เราการันตี:


12.5 Grounding Tools (สำหรับ AI ที่ต้องอ้างของจริง)

หลักการ: AI ของเราอ่านแหล่งจริง ไม่มโน. 3 tools ที่ Smart Engine / Wizard เรียกใช้เพื่อ ground คำตอบกับข้อมูลจริง:

POST /v1/tools/web_search

ค้นเว็บภาษาไทย (SearXNG ในประเทศ) คืน title + url + snippet ตามจำนวนที่ขอ.

curl -X POST .../v1/tools/web_search \
  -H "Authorization: Bearer <TOKEN>" \
  -d '{"query":"แผนการสอนคณิต ป.4 active learning","n":5}'

POST /v1/tools/read_url

ดึงเนื้อหาจริงจาก URL (HTML ผ่าน trafilatura, PDF ผ่าน PyMuPDF + ซ่อมสระไทยที่แตก ำ). SSRF-guarded — block private / loopback / link-local IPs.

curl -X POST .../v1/tools/read_url \
  -d '{"url":"https://example.com/article","max_chars":8000}'

POST /v1/tools/read_file

อัปโหลดไฟล์จริง (PDF / DOCX / XLSX / CSV / TXT / MD) → ดึงข้อความสะอาด. ใช้สำหรับให้ AI วิเคราะห์/สรุป/ถอดความเอกสารที่ลูกค้าอัปโหลด.

Multipart upload (ไม่ใช่ JSON):

curl -X POST .../v1/tools/read_file \
  -H "Authorization: Bearer <TOKEN>" \
  -H "X-Tenant-Id: <TENANT_ID>" \
  -F "[email protected]" \
  -F "max_chars=30000"

Response:

{
  "kind": "pdf",         // pdf | docx | xlsx | csv | text | unknown
  "chars": 12345,
  "content": "...ข้อความที่ดึงออกมา...",
  "filename": "report.pdf"
}
Format Extractor หมายเหตุ
PDF PyMuPDF (fitz) + ซ่อมสระไทย ำ ที่ extractor PDF แตกออกเป็น "<พยัญชนะ> า" — verified safe (เวลา/ปัญหา ไม่พัง)
DOCX python-docx รวม paragraph + table cells
XLSX/XLSM openpyxl ทุก sheet, cap 500 rows/sheet
CSV/TSV csv stdlib auto delimiter (.csv=comma, .tsv=tab), cap 500 rows
TXT/MD/log UTF-8 → CP874 → Latin-1 fallback chain สำหรับ Thai legacy encoding

Limits: ไฟล์สูงสุด 10 MB · max_chars default 30000, สูงสุด 60000. Format detection: magic bytes ก่อน → filename extension fallback.

# Python (requests)
with open("plan.pdf", "rb") as f:
    r = requests.post(
        "https://gateway.kiatdamrong.shop/v1/tools/read_file",
        headers={"Authorization": f"Bearer {TOKEN}",
                 "X-Tenant-Id": TENANT},
        files={"file": ("plan.pdf", f, "application/pdf")},
        data={"max_chars": 30000},
    )
text = r.json()["content"]

POST /v1/knowledge/query

ค้นจาก curated knowledge pack (pedagogy-th, cs-fundamentals-th, dev-reference-en, thai-medical-pdpa-th, ...). มีอยู่ก่อนแล้ว.

ทำไมสำคัญ: raw LLM (Claude/GPT/Gemini) เวลาขอให้ "ค้นและสรุป" มัน มโน citation บ่อย (ใช้ชื่อโดเมนจริง + ใส่ชื่อบทความ/ปีปลอม). 4 tools นี้บังคับให้ AI เปิดอ่านจริงก่อน — แล้ว Smart Engine เก็บ tool-call log ไว้ verify ทุก URL/source ในคำตอบ.


12.6 Smart Engine — opt-in grounding flags

/v1/smart มี 2 flag ใหม่ที่ปลด anti-fabrication ระดับ engine:

flag ทำงานเมื่อไร ใช้ตอนไหน
writer_grounding: true writer ค้น+อ่าน real sources ผ่าน tools ก่อนเขียน, citation verifier เทียบ URL ที่อ้างกับ tool-call log งาน analysis/synthesis ที่ต้องอ้างอิงแหล่งจริง
judge_tools: true judge เรียก tools (lookup_knowledge / web_search / read_url) ก่อนตัดสิน, verdict มี citation จากของจริง งานตรวจสอบเนื้อหาว่าตรงหลักการ/ตัวชี้วัด/งานวิจัยจริงไหม

PDPA gate: ทั้ง 2 flag เปิดได้เฉพาะตอน prompt ไม่มี PII (engine classify อัตโนมัติ). ถ้ามี PII → fallback เป็น path local ไม่กระเทือน.

ตัวอย่าง:

POST /v1/smart
{
  "messages":[{"role":"user","content":"สังเคราะห์แนวทาง AL คณิต ป.4"}],
  "writer_grounding": true,
  "judge_tools": true,
  "stream": true
}

Response มี smart.writer_grounding:

{
  "enabled": true,
  "tool_calls": 13,
  "urls_read": ["https://nuir.lib.nu.ac.th/...", "https://moelearn.com/..."],
  "queries": ["active learning ป.4", ...],
  "citations": {
    "urls_total": 12,
    "urls_grounded": 12,
    "urls_fabricated": 0,
    "fabricated_list": []
  }
}

citations.fabricated_list = URL ที่ AI อ้างในคำตอบแต่ไม่ได้เปิดอ่านจริง — ใช้ตัดสินใจว่าจะแสดงผลให้ end-user ดีไหม.

Streaming mode — รับ citations ระหว่างทาง (gateway 0.12.0+)

ตอน "stream": true จะมี SSE event ใหม่ ชื่อ smart.grounding.complete ยิงออกมา หลัง smart.enforce และ ก่อน smart.complete — ทำให้ UI/SDK ที่อ่าน SSE ตรงๆ (เช่น เว็บผ่าน Cloudflare Tunnel ที่ต้องหลีกเลี่ยง 100s edge timeout) แสดง 📚 อ้างอิง ได้ทันทีโดยไม่ต้องรอ body จบ:

event: smart.enforce
data: {...}

event: smart.grounding.complete
data: {"tool_calls": 3,
       "urls_read": ["https://...", "https://..."],
       "queries": ["bitcoin price USD"]}

event: smart.complete
data: {... full SmartResponse envelope ...}

Python SDK 0.3.0+:

for ev in client.smart_stream(
    messages=[{"role":"user","content":"ราคา BTC วันนี้?"}],
    writer_grounding=True,
):
    if ev["event"] == "smart.grounding.complete":
        urls = ev["data"]["urls_read"]
        print(f"📚 ใช้ {len(urls)} แหล่ง:", urls)
    elif ev["event"] == "smart.complete":
        finalize(ev["data"])
        break

Node SDK 0.3.0+ (TypeScript):

import type { SmartGroundingComplete } from "@ai-gateway-th/sdk";

for await (const ev of client.smartStream({
  messages: [{ role: "user", content: "ราคา BTC วันนี้?" }],
  writer_grounding: true,
})) {
  if (ev.event === "smart.grounding.complete") {
    const g = ev.data as SmartGroundingComplete;
    console.log(`📚 ใช้ ${g.urls_read.length} แหล่ง`, g.urls_read);
  } else if (ev.event === "smart.complete") {
    finalize(ev.data);
    break;
  }
}

Raw HTTP (ไม่ใช้ SDK): อ่าน SSE line-by-line ตามมาตรฐาน W3C, parse event: + data: คู่. response headers ที่เพิ่มมาช่วยให้ client เห็น "ตอนนี้ใช้กี่ tool แล้ว" จาก outside:

smart.grounding.complete จะไม่ยิง ถ้า writer ไม่ได้เรียก tool เลย (tool_calls = 0) — ดังนั้น absence ของ event = ไม่มี grounded sources, ไม่ใช่ event หาย.


12.7 Wizard endpoints — สำเร็จรูป (ไม่ต้องเขียน prompt เอง)

Wizard = preset ของ Smart Engine ออกมาเป็น URL ตรง ลูกค้าไม่ต้องประกอบ SmartApiRequest เอง. ใช้ engine + tools เดียวกัน → ได้ PDPA/audit/load-aware offload/idempotency/streaming ฟรี.

POST /v1/wizard/polish — ขัดเกลาภาษาไทยให้เป็นเนทีฟ

curl -X POST .../v1/wizard/polish \
  -d '{"text":"...ที่ซึ่งผู้เรียนถูกทำให้มีส่วนร่วม...","style":"neutral"}'

Default writer = Typhoon 2.5 SAFE (Thai-native SOTA, stop-token-hardened). Style: formal | neutral | casual. Returns polished_text + change list. ใช้ตอน: Claude/GPT/Gemini เขียนไทยมาแล้วได้สำนวนแปลตรงตัว POST ผ่านนี้ก็เนทีฟ.

POST /v1/wizard/fact-check

curl -X POST .../v1/wizard/fact-check \
  -d '{"claim":"แผนการสอน...","pack_id":"pedagogy-th"}'

Tooled judge — verdict + grounded issues + citation. ใช้ตอน: ตรวจแผนสอน / บทความ / ข้ออ้าง.

POST /v1/wizard/grounded-summarize

curl -X POST .../v1/wizard/grounded-summarize \
  -d '{"question":"สรุปแนวทาง AL การคูณ ป.4","urls":["https://..."]}'

Grounded writer + citation verifier. ใช้ตอน: สังเคราะห์งานวิจัย / สรุปเอกสารหลายแหล่งให้ผู้บริหารอ่าน.

POST /v1/wizard/shorten — ตัดให้สั้นโดยรักษาใจความ

curl -X POST .../v1/wizard/shorten \
  -d '{"text":"ข้อความยาว...","target_chars":120,"style":"neutral"}'

Optional target_chars (ระบุยาวที่ต้องการ), style: formal|neutral|casual, tier: standard|premium|frontier. Returns revised_text + summary of cuts. ใช้ตอน: ย่อบทความ / สรุป exec brief / ตัดคำอวยให้กระชับ.

POST /v1/wizard/translate-th — แปลเข้าไทยแบบเนทีฟ

curl -X POST .../v1/wizard/translate-th \
  -d '{"text":"Active learning is...","source_lang":"en","style":"neutral"}'

source_lang optional (auto-detect ได้). Default writer = Typhoon 2.5 SAFE → ผลลัพธ์เป็นไทยเนทีฟ ไม่ใช่แปลตรงตัว. ใช้ตอน: แปลเอกสารวิจัย / slogan / SOP ของบริษัทต่างชาติเข้าไทย.

POST /v1/wizard/fix-thai — ซ่อมสะกด/วรรค/การันต์

curl -X POST .../v1/wizard/fix-thai \
  -d '{"text":"การเรียนรุ้แบบลงมือทำ..."}'

แก้ทีละจุด พร้อม changelog (ก่อน→หลัง). ใช้ตอน: OCR ออกมาผิด / เอกสารที่ user พิมพ์เร่งๆ / clean dataset ก่อน fine-tune.

Wizard endpoints ทั้ง 6 ใช้ engine เดียวกับ Smart — ได้ PDPA/audit/streaming ฟรี. ดู Playground ที่ /try สำหรับลองทุก wizard สด.


12.8 Knowledge Flywheel (admin) — ขยาย pack จากเว็บจริง

/v1/packs/<pack_id>/draft-from-url → review → approve → ingest. ทุก chunk ใน pack จะมี source URL + fetched_at = ตรวจสอบที่มาได้ตลอด.

# 1) Admin ดึงบทความจริงเป็น draft
curl -X POST .../v1/packs/pedagogy-th/draft-from-url \
  -H "Authorization: Bearer <ADMIN>" \
  -d '{"url":"https://nuir.lib.nu.ac.th/.../paper.pdf"}'

# 2) ดู drafts ที่รอ
curl .../v1/packs/pedagogy-th/drafts -H "Authorization: Bearer <ADMIN>"

# 3) อ่าน draft ก่อนอนุมัติ
curl .../v1/packs/pedagogy-th/drafts/<draft_id> -H "Authorization: Bearer <ADMIN>"

# 4) อนุมัติ → ย้ายเข้า sources/ + re-embed
curl -X POST .../v1/packs/pedagogy-th/drafts/<draft_id>/approve \
  -H "Authorization: Bearer <ADMIN>"

# (หรือปฏิเสธ)
curl -X POST .../v1/packs/pedagogy-th/drafts/<draft_id>/reject \
  -H "Authorization: Bearer <ADMIN>"

Admin tenant เท่านั้น (is_admin_tenant). SSRF guard ที่ shared layer.


12.9 Thai writer aliases

alias model ใช้เมื่อ
local-writer qwen3.6 35B-a3b-q4 (DGX) general writer, bulk Thai prose
local-writer-th Typhoon 2.5 SAFE (SCB 10X, stop-hardened) Thai polish / rewrite — default ของ wizard polish
local-analyst-th Pathumma 8B-think (NECTEC) backstage support only — vis analytical context, ไม่ขัดเกลาให้
local-editor qwen3-next:80b-a3b-q8 judge / fact-check (raw path)
cloud-fast GLM-4.7 (Ollama Cloud free) cross-family judge (tooled, default)
cloud-large GPT-OSS 120B (Ollama Cloud free) grounded writer (tooled, default)

Pathumma เก่ง Thai cultural context แต่ ไม่เคารพ rewrite instruction (think-tuned → อธิบายแทน) ใช้เป็น override/internal helper เท่านั้น


12.9 Compose / Charts / Knowledge packs (in SDK)

3 endpoint นี้ตอนนี้ มีใน native SDK ทั้ง 4 ภาษาแล้ว (2026-06-01). ตัวอย่าง curl ด้านล่างไว้สำหรับ debug / integration test — production code ใช้ SDK method ตรงๆ ได้เลย.

POST /v1/compose — Orchestrated write+judge+revise

KoishiAI / AInovel ใช้ pipeline นี้. /v1/smart เป็น OpenAI-compat wrapper รอบมัน — ใช้ smart() ก็พอสำหรับ 95% ของ use case. ใช้ compose() เมื่อต้อง control writer/editor/fallback model split, score threshold, หรือ knowledge pack injection โดยตรง.

SDK methods:

Raw HTTP:

curl -X POST https://gateway.kiatdamrong.shop/v1/compose \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-Id: $TENANT" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role":"user","content":"เขียนบทความ active learning ป.4"}],
    "max_revisions": 3,
    "editor_score_threshold": 80
  }'

Response มี OpenAI-shape choices + compose.trace แสดงทุก step ของ pipeline.

POST /v1/charts/render — Render PNG / SVG / Vega-Lite

ChartSpec รองรับ 6 ประเภท: bar, line, scatter, histogram, boxplot, pie. Data field ขึ้นกับ type:

SDK methods:

Raw HTTP:

curl -X POST https://gateway.kiatdamrong.shop/v1/charts/render \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "chart": {"type":"bar","title":"Demo","categories":{"A":3,"B":5,"C":2}},
    "format": "svg",
    "width": 300, "height": 200
  }'

Returns {format, chart_type, width, height, render_ms, image_base64|svg|vega_lite_spec}. PNG ~15-50KB base64, SVG ~5-20KB.

GET /v1/knowledge/packs — List knowledge packs

SDK methods:

Returns {"object": "list", "data": [{pack_id, version, curator, languages, description, ...}, ...]}. ใช้ list ก่อนเรียก knowledge_query(pack_id, query).

Demo task endpoints (tags: Demo Tasks)

/v1/analyze-csv, /v1/cohorts/analyze, /v1/exam — สำหรับ sales demo ในประเทศไทย, ไม่ stable contract. ใช้ผ่าน /try playground หรือทาง HTTP, แต่อย่า build production บนของพวกนี้.


12.10 Tier system — paid backends (Phase 1 shipped 2026-06-01)

ลูกค้าสามารถ opt-in ใช้ commercial AI provider สำหรับ judge step เพื่อ แลก ความเร็ว + ความนิ่ง กับค่าใช้จ่ายต่อ token. เรียกใช้โดยส่ง tier field ใน request body. Gateway เก็บ token count + per-step breakdown ให้ในทุก response → ลูกค้าคำนวณค่าใช้จ่ายฝั่งระบบของตัวเอง.

4 tiers ที่ใช้ได้

Tier Judge backend p95 typical Cost (upstream rates) PDPA classes
standard (default, ฟรี) cloud-fast (Ollama Cloud GPT-OSS 120B) 3-32s variance ฿0 ทุก class
premium Gemini 2.5 Flash 2-5s reliable ~$0.075/M input + $0.30/M output NONE · REDACTABLE
frontier Claude Haiku 4.5 1.7-4s reliable ~$0.80/M input + $4/M output NONE only
auto (Phase 4 — TBD) gateway decides varies ≤ premium varies

Writer ยังเป็น Typhoon 2.5 local เสมอ (Thai SOTA, PDPA-safe). tier แก้แค่ judge step.

วิธีเรียกใช้

# default (ฟรี, เดิม)
curl -X POST $BASE/v1/wizard/polish \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-Id: $TENANT" \
  -H "Content-Type: application/json" \
  -d '{"text":"..."}'

# premium — Gemini Flash judge
curl -X POST $BASE/v1/wizard/polish \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Tenant-Id: $TENANT" \
  -H "Content-Type: application/json" \
  -d '{"text":"...","tier":"premium"}'

# frontier — Claude Haiku judge (เร็วสุด)
curl -X POST $BASE/v1/wizard/polish \
  -d '{"text":"...","tier":"frontier"}'

ใช้ได้ที่: /v1/wizard/polish, /v1/wizard/fact-check, /v1/wizard/grounded-summarize, /v1/smart.

Response headers (observability)

Header ความหมาย
X-Gateway-Tier-Requested tier ที่ลูกค้าส่งมา
X-Gateway-Tier-Used tier ที่ระบบใช้จริง (อาจถูก downgrade)
X-Gateway-Tier-Downgraded-Reason (มีเฉพาะตอน downgrade) e.g. pdpa_pinned
X-Gateway-Compose-Judge model ที่ judge ใช้จริง (e.g. frontier-gemini)
X-Gateway-Compose-Judge-Reason เหตุผลในการเลือก judge
X-Gateway-Compose-Judge-Tools on ถ้า judge ใช้ tools (paid tiers ก็ใช้ได้)

Response body — usage field สำหรับคิดเงิน

{
  "polished_text": "...",
  "compose": {...},
  "usage": {
    "prompt_tokens": 2184,
    "completion_tokens": 1329,
    "total_tokens": 3513,
    "per_step": [
      {"public_id":"local-writer-th","tier":"local",
       "prompt_tokens":962,"completion_tokens":255,"total_tokens":1217},
      {"public_id":"frontier-gemini","tier":"frontier",
       "prompt_tokens":1222,"completion_tokens":1074,"total_tokens":2296}
    ]
  }
}

ลูกค้าใช้ per_step ดูว่า backend ตัวไหนเผา token เท่าไร แล้วเอาไปคูณกับ rate ของ provider นั้น → คิดค่าใช้จ่ายแล้วเก็บจาก user ของลูกค้าเอง.

PDPA — auto-downgrade (ไม่ใช่ silent)

ถ้าลูกค้าส่ง tier=premium แต่ classification = REDACTABLE/SENSITIVE → ระบบจะ pin judge ที่ LOCAL อัตโนมัติพร้อมส่ง:

X-Gateway-Tier-Requested: premium
X-Gateway-Tier-Used: standard
X-Gateway-Tier-Downgraded-Reason: pdpa_pinned
X-Gateway-Compose-Judge: local-editor
X-Gateway-Compose-Judge-Reason: local (downgraded from premium: pdpa_pinned)

→ ลูกค้าตรวจ header ใน middleware แล้วตัดสินใจว่า:

Smart Engine tools ทำงานกับทุก tier

นี่คือ value-add ที่ผู้เรียก Gemini/Claude API ตรงไม่ได้ — เมื่อใช้ judge_tools=true หรือเรียก /v1/wizard/fact-check, paid judges ยังเข้าถึง tool ecosystem ของเรา ได้ครบ:

Tool ทำอะไร
lookup_knowledge(query, pack_id) hybrid search บน curated Thai knowledge packs (BGE-M3)
web_search(query, n) private SearXNG ของเรา
read_url(url) fetch + clean (PDPA-safe SSRF guard + Thai ำ-repair)

ตัวอย่าง — Gemini Flash judge ground เนื้อหา factcheck ผ่าน knowledge pack จริง:

curl -X POST $BASE/v1/wizard/fact-check \
  -d '{"claim":"...","pack_id":"thai-edu","tier":"premium"}'

# Response headers:
#   X-Gateway-Compose-Judge: frontier-gemini
#   X-Gateway-Compose-Judge-Tools: on    ← Gemini เรียก tools ของเราจริง

ลูกค้าได้ทั้งความเร็วของ commercial API + ความถูกต้องของ grounded retrieval — ทำเองที่ฝั่งลูกค้าจะต้อง wire ทุก provider ทุก tool เอง.

Integration recipe

# ลูกค้าตัดสินใจฝั่งเขาเองว่าจะ pay tier ไหน
tier = "premium" if user.has_premium_plan else "standard"

resp = httpx.post(f"{BASE}/v1/wizard/polish",
    headers={"Authorization": f"Bearer {TOKEN}", "X-Tenant-Id": TENANT},
    json={"text": user_input, "tier": tier},
    timeout=180.0).json()

# คิดค่าใช้จ่ายจาก usage.per_step
paid_step = next((s for s in resp["usage"]["per_step"]
                  if s["tier"] == "frontier"), None)
if paid_step:
    upstream_cost_usd = (
        paid_step["prompt_tokens"] * GEMINI_INPUT_USD_PER_M / 1e6 +
        paid_step["completion_tokens"] * GEMINI_OUTPUT_USD_PER_M / 1e6
    )
    charge_user(upstream_cost_usd * MARKUP)  # ลูกค้า bill ผู้ใช้ของตัวเอง

return resp["polished_text"]

Gateway ไม่จัดการ billing/cap/quota ฝั่งเรา — เป็น infrastructure provider. ลูกค้าใช้ usage.per_step คำนวณ + คิดเงิน + บังคับ quota ในระบบของเขาเอง.


12.11 PDPA Auto-Cut — ใช้ cloud โดยไม่ส่ง PII ออกนอก (shipped 2026-06-10)

ทุก request ที่ classify เป็น REDACTABLE (พบ PII เช่น ชื่อ, เบอร์, อีเมล, เลขบัตร, ที่อยู่) gateway จะ ตัด PII เป็น placeholder ก่อนส่ง cloud แล้ว คืนค่าจริงให้ในคำตอบ อัตโนมัติ — cloud ไม่เคยเห็นข้อมูลจริง:

"ผมชื่อ นายสมชาย ใจดี เบอร์ 081-234-5678"
   → cloud เห็น: "ผมชื่อ [[PII_PERSON_1]] เบอร์ [[PII_PHONE_1]]"
   → ลูกค้าได้คำตอบที่มีชื่อจริงกลับมาตามปกติ

ตรวจจับ 3 ชั้น: regex (เลขบัตร/เบอร์/อีเมล) + Thai heuristic (คำนำหน้าชื่อ/คำบ่งชี้ที่อยู่) + ThaiNER ML (ชื่อไม่มีคำนำหน้า). กฎเหล็ก: ถ้า placeholder หลุดมาในคำตอบ → gateway ไม่ส่งคำตอบนั้น (502).

Response headers ที่อ่านได้ทุก request:

Header ความหมาย
X-Gateway-PII-Class NONE / REDACTABLE / SENSITIVE
X-Gateway-PII-Counts เช่น PII_PERSON=2,PII_PHONE=1
X-Gateway-Redacted-Tokens จำนวน placeholder ที่ใช้
X-Gateway-Redaction-Layers regex,heuristic-th,ml-thainer
X-Gateway-Redaction-Ms เวลาที่ใช้ตัด (ms)

ใช้ headers เหล่านี้แสดง badge "🛡️ Auto-protected" ใน UI ของคุณได้เลย — ดูตัวอย่างจริงที่ lms-demo.kiatdamrong.shop (โหมด 🛡️ Auto-Cut PDPA / 🚀🛡️ PDPA Pro).

SENSITIVE (เช่น ข้อมูลสุขภาพ) ไม่ออก cloud เลย — ปักหมุดรันใน local model.

12.12 Document Intelligence — เอกสาร 1000+ หน้า → รายงาน/สไลด์ (shipped 2026-06-11)

ชุดความสามารถใหม่ 4 ตัวที่ทำงานต่อกันเป็นสาย: Corpus → Longform → Export (+ตั้งเวลารันประจำ) — ออกแบบมาเพื่องาน batch ขนาดใหญ่ที่ read_file (cap 60K chars) รับไม่ไหว

Benchmark จริง (PDF 1,000 หน้า / 1.07M ตัวอักษรไทย — production)

ขั้น เวลาวัดจริง
Ingest (334 chunks + PII profile) 16.1 วินาที
Longform map-reduce (384 nodes, local 100%) 93.7 นาที · ฿0
Export DOCX + PPTX ~2 วินาที
โดน kill กลางทางแล้ว resume เอง overhead ~31 วินาที

1) Corpus — คลังเอกสารต่อโปรเจค

# สร้าง corpus
curl -X POST $BASE/v1/corpus -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name": "งานวิจัยปี 2569"}'
# → {"id": "abc123...", ...}

# อัปโหลดเอกสาร (PDF/DOCX/TXT — ไม่จำกัดหน้า, ทีละไฟล์ ซ้ำได้หลายไฟล์)
curl -X POST $BASE/v1/corpus/{id}/documents -H "$AUTH" \
  -F "file=@report_1000pages.pdf"
# → {"pages": 1000, "chunks": 334, "pii_chunks": 91,
#    "corpus": {"pii_profile": {"pii_share": 0.27,
#               "recommended_pii_mode": "autocut_cloud"}}}

# ค้นหา (explicit tool-call — ไม่มี silent RAG)
curl -X POST $BASE/v1/corpus/{id}/query -H "$AUTH" \
  -H "Content-Type: application/json" \
  -d '{"query": "ผลการศึกษาด้านต้นทุน", "top_k": 8}'
# → hits พร้อม doc + page_start/page_end (ใช้อ้างอิงเลขหน้าได้)

ระบบตรวจ PII ตั้งแต่ตอน ingest แล้วแนะนำ pii_mode ที่เหมาะ — การตัดสินใจขั้นสุดท้ายเป็นของคุณเสมอ

2) Longform — สรุป/เรียบเรียงทั้ง corpus (resumable)

curl -X POST $BASE/v1/longform -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"corpus_id": "{id}",
       "instruction": "สรุปประเด็นหลักพร้อมอ้างอิงเลขหน้า",
       "pii_mode": "local_only"}'
# → {"job_id": "job-...", "plan": {"chunks": 334, "total_nodes": 384}}

# ติดตามความคืบหน้า real-time
curl $BASE/v1/jobs/{job_id} -H "$AUTH"
# → {"status": "running", "progress": {"stage": "map",
#     "nodes_done": 120, "nodes_total": 384}}

pii_mode 3 โหมด (เลือกต่องาน):

โหมด พฤติกรรม เหมาะกับ
local_only ทุก node รันในเครื่อง — ข้อมูลไม่ออกเลย, ฿0 เอกสารลับ/PII สูง
autocut_cloud map=local · reduce/final=cloud ผ่านการตัด PII (cloud เห็นแค่ [[PII_*]] placeholder) สมดุลเร็ว+ปลอดภัย
none ใช้ model ที่เลือกทุก node เอกสารสาธารณะ

ถ้า corpus มี PII สูงแต่ส่ง pii_mode=none → ระบบปฏิเสธ (400) พร้อมคำแนะนำ

Resume หลัง crash/restart: ระบบ respawn เองตอน boot (อัตโนมัติ) หรือสั่ง ผ่าน API: ส่ง resume_job_id ใน POST เดิม — node ที่เสร็จแล้วคืนจาก checkpoint cache ฟรี จ่ายเฉพาะส่วนที่เหลือ

3) Export — ออกไฟล์พร้อมใช้

# จาก job ที่เสร็จแล้ว → DOCX/PPTX/MD โดยตรง
curl -G "$BASE/v1/jobs/{job_id}/export" -H "$AUTH" \
  --data-urlencode "format=docx" \
  --data-urlencode "title=รายงานวิจัย" -o report.docx

# หรือจาก markdown ใดๆ
curl -X POST $BASE/v1/export -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"markdown": "# รายงาน...", "format": "pptx", "title": "นำเสนอ"}' \
  -o slides.pptx

DOCX: A4 ฟอนต์ TH Sarabun New + ปก + เลขหน้า + ตาราง · PPTX: 16:9, H1/H2 = สไลด์ใหม่ — พร้อมพิมพ์/นำเสนอทันที

4) Schedules — รันประจำอัตโนมัติ

curl -X POST $BASE/v1/longform/schedules -H "$AUTH" \
  -H "Content-Type: application/json" \
  -d '{"corpus_id": "{id}", "instruction": "สรุปรายสัปดาห์",
       "pii_mode": "local_only", "every_hours": 168}'
# GET /v1/longform/schedules · DELETE /v1/longform/schedules/{sid}

SDK note: ความสามารถชุดนี้เรียกผ่าน HTTP ตรงได้ทันทีตามตัวอย่างบน — native SDK methods กำลังตามมา (Python ก่อน)

13. Support


14. Compliance Artifacts

ขอได้ผ่าน [email protected]: