Integration reference · v2

เชื่อมต่อ OIE API

คู่มือสำหรับเรียกบริการอ่านเอกสาร แชตคู่มือ และวิเคราะห์ข้อมูลโครงการจากระบบของทีม

1. ค่าที่ใช้เชื่อมต่อ

ใช้ access token ที่ทีมได้รับจากผู้ดูแล คู่มือออนไลน์ไม่แสดง token

ทุก API endpoint ในคู่มือนี้ต้องส่ง Authorization: Bearer ACCESS_TOKEN ใช้ token เดียวกันสำหรับทั้ง 3 บริการ ไม่ต้องใส่ค่าการเชื่อมต่ออื่น
Authorization: Bearer YOUR_ACCESS_TOKEN

ส่งคำถามเป็น application/json และส่งเอกสารเป็น multipart/form-data ช่อง file รับผลลัพธ์ผ่าน HTTPS

2. API endpoints

MethodPath ต่อจาก Base URLบริการ
POST/api/v2/extractอ่าน PDF / DOCX · JSON รูปแบบเดียวกันทุกไฟล์
POST/api/extractอ่านเอกสาร · รองรับรูปแบบเดิม
GET/api/schema?view=responseJSON Schema ของผลลัพธ์ v2
GET/api/healthสถานะบริการอ่านเอกสาร
POST/api/chatแชตคู่มือ · JSON response
POST/api/chat/streamแชตคู่มือ · stream
POST/api/admin/chat/streamวิเคราะห์ข้อมูลโครงการ · stream
GET/api/admin/statusสถานะและจำนวนแถวข้อมูลโครงการ
GET/api/admin/healthzสถานะบริการวิเคราะห์ข้อมูล

3. อ่านเอกสาร PDF / DOCX

แนะนำ POST /api/v2/extract สำหรับระบบใหม่ รองรับ PDF และ DOCX หนึ่งไฟล์ต่อคำขอ สูงสุด 30 MB / 80 หน้า ประมวลผลทุกหน้าและคืน JSON โดยไม่ตัดหน้าทิ้ง

ส่ง include_ocr=true หากต้องการข้อความและตารางที่อ่านได้รายหน้าด้วย ผลลัพธ์จะมีขนาดใหญ่ขึ้น ตั้ง timeout ฝั่ง client สูงสุด 1,800 วินาทีสำหรับเอกสารยาว

cURL · PDF

curl --fail-with-body --max-time 1800 \
  'https://oie.thinkv.app/api/v2/extract' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -F 'file=@project.pdf'

cURL · DOCX พร้อมข้อมูลรายหน้า

curl --fail-with-body --max-time 1800 \
  'https://oie.thinkv.app/api/v2/extract?include_ocr=true' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -F 'file=@project.docx'

Python · ใช้ requests

from pathlib import Path
import requests

BASE_URL = "https://oie.thinkv.app"
ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"

with Path("project.pdf").open("rb") as document:
    response = requests.post(
        BASE_URL + "/api/v2/extract",
        headers={"Authorization": "Bearer " + ACCESS_TOKEN},
        files={"file": ("project.pdf", document, "application/pdf")},
        timeout=(15, 1800),
    )
response.raise_for_status()
result = response.json()
if not result["document"]["complete"]:
    raise RuntimeError("อ่านเอกสารไม่ครบ")
project_name = result["data"]["project_name"]
budget_baht = result["budget"]["amount_baht"]
needs_review = result["quality"]["needs_review"]
budget_items = result["structured"]["budget_items"]
print(project_name, budget_baht, needs_review)

JavaScript · ใช้ fetch

const BASE_URL = "https://oie.thinkv.app";
const ACCESS_TOKEN = "YOUR_ACCESS_TOKEN";
const form = new FormData();
form.append("file", fileInput.files[0]);

const response = await fetch(BASE_URL + "/api/v2/extract", {
  method: "POST",
  headers: { Authorization: "Bearer " + ACCESS_TOKEN },
  body: form,
  signal: AbortSignal.timeout(1800_000),
});
const result = await response.json();
if (!response.ok) throw new Error(result.code + ": " + result.error);
console.log(result.data, result.structured, result.quality);

// FormData กำหนด multipart boundary ให้เอง ไม่ตั้ง Content-Type เอง
ระบบอ่านหน้าเอกสารแบบขนานสูงสุด 24 หน้า และแยกการรวมข้อมูลโครงการ งบประมาณ ตัวชี้วัด แผนกิจกรรม และรายละเอียดอื่นให้ทำพร้อมกัน หากส่งไฟล์เดิมภายใน 1 ชั่วโมงจะใช้ผลที่อ่านไว้ ผลลัพธ์ API ส่งเฉพาะข้อมูลเอกสารและการตรวจสอบแหล่งที่มา

4. ผลลัพธ์ที่ระบบปลายทางควรใช้

Fieldการใช้งาน
dataข้อมูลโครงการ 24 ช่อง ชนิดเหมือนกันทั้ง PDF และ DOCX
summaryสรุปเนื้อหาโครงการเป็นภาษาไทย
budget.amount_bahtงบประมาณเป็นบาทแบบ decimal string เมื่อยืนยันหน่วยได้; null เมื่อไม่ทราบหรือขัดแย้ง
structuredproject_info, budget_items, kpi_items, schedule, additional_sections, conflicts
tablesตารางรายหน้า พร้อม columns, rows, id, page และ shape_valid
evidenceข้อความอ้างอิงและเลขหน้าแบบ 1-based ต่อ field
quality.needs_reviewtrue เมื่อควรตรวจเอกสารต้นฉบับก่อนนำข้อมูลไปใช้
quality.field_statusmissing / supported / unverified
documentpage_count, pages_processed, complete, page_numbering สำหรับตรวจว่าครบทุกหน้าและอ้างอิงหน้าต้นฉบับ

scalar ที่ไม่พบเป็น null; รายการที่ไม่พบเป็น [] ใช้ budget.amount_baht สำหรับยอดเป็นบาท อย่าถือว่า data.budget เป็นบาทเสมอ เพราะอาจเป็นพันบาทหรือล้านบาทตามต้นฉบับ

ตรวจ document.complete และ quality.needs_review ก่อนบันทึกข้อมูลอัตโนมัติ การอ้างอิงที่ตรวจพบยังควรตรวจความหมายของข้อมูลสำคัญจากต้นฉบับ

ข้อมูลโครงการ 24 ช่อง
FieldTypeความหมาย
data.budgetstring | nullงบประมาณตามหน่วยในต้นฉบับ
data.project_namestring | nullชื่อโครงการ
data.kpistring | nullตัวชี้วัดและเป้าหมาย
data.howstring | nullวิธีดำเนินงานและกิจกรรม
data.methodologystring | nullหลักการและเหตุผล
data.resultstring | nullมูลค่าทางเศรษฐกิจและวิธีคำนวณ
data.flagshipstring | nullชื่อโครงการหลัก / ผลผลิต
data.focusstring | nullประเด็นที่เลือก
data.planstring | nullแผนดำเนินงานและช่วงเวลา
data.targetstring | nullเป้าหมายแผนงานบูรณาการ
data.strategicstring | nullยุทธศาสตร์และแผนที่เกี่ยวข้อง
data.target_industrystring | nullอุตสาหกรรมเป้าหมาย
data.placestring | nullสถานที่ดำเนินงาน
data.integration_goalstring | nullเป้าหมายแผนงานบูรณาการ
data.integration_target_kpistring | nullตัวชี้วัดเป้าหมายแผนงานบูรณาการ
data.integration_approachstring | nullแนวทางดำเนินงานแผนงานบูรณาการ
data.integration_approach_kpistring | nullตัวชี้วัดแนวทางดำเนินงาน
data.responsible_departmentstring | nullหน่วยงานรับผิดชอบ
data.exp_benefitstring[]ผลประโยชน์ที่คาดว่าจะได้รับ
data.objectivestring[]วัตถุประสงค์
data.target_groupstring[]กลุ่มเป้าหมาย
data.linkstring[]ประเด็นเชื่อมโยง
data.is_flagship0 | 1 | null1 = ใช่, 0 = ไม่ใช่, null = ไม่พบ / ไม่ชัดเจน
data.responsible_detailsobject[]ผู้รับผิดชอบ: first_name, last_name, email, phone, department
ตัวอย่าง JSON · คัดเฉพาะ fields ที่ใช้บ่อย
{
  "document_type": "proposal",
  "data": {
    "budget": "2.50",
    "exp_benefit": [
      "ลดต้นทุนการตรวจคุณภาพ",
      "เพิ่มรายได้ของวิสาหกิจชุมชน"
    ],
    "flagship": "โครงการยกระดับอุตสาหกรรมอาหารแห่งอนาคต",
    "focus": null,
    "how": "สำรวจ 12 ชุมชน พัฒนาระบบ และอบรม 24 คน",
    "integration_approach": "พัฒนามาตรฐานอาหาร",
    "integration_approach_kpi": "ผู้ประกอบการผ่านมาตรฐาน 12 ราย",
    "integration_goal": "เพิ่มมูลค่าอาหารปลอดภัย",
    "integration_target_kpi": "รายได้เพิ่มร้อยละ 5",
    "is_flagship": 1,
    "kpi": "ระบบตรวจคุณภาพ 1 ระบบ; ผู้ผ่านการอบรม 24 คน",
    "link": [
      "อุตสาหกรรมอาหารแห่งอนาคต"
    ],
    "methodology": "ผู้ประกอบการชุมชนขาดเครื่องมือตรวจคุณภาพอาหาร",
    "objective": [
      "เพื่อพัฒนาระบบตรวจคุณภาพอาหารชุมชน",
      "เพื่อฝึกอบรมผู้ประกอบการ 24 คน"
    ],
    "place": "ระดับจังหวัด ขอนแก่น",
    "plan": "ตุลาคม 2568: สำรวจ; พฤศจิกายน 2568: พัฒนาระบบ; ธันวาคม 2568: อบรม",
    "project_name": "โครงการทดสอบระบบอาหารปลอดภัยชุมชน",
    "responsible_department": "ศูนย์ทดสอบอาหารชุมชน",
    "responsible_details": [
      {
        "department": "",
        "email": "somchai@example.org",
        "first_name": "สมชาย",
        "last_name": "ใจดี",
        "phone": "02-1234567 ต่อ 89"
      }
    ],
    "result": "ประมาณการมูลค่าทางเศรษฐกิจที่เพิ่มขึ้นจากการดำเนินโครงการ 5 ล้านบาทต่อปี",
    "strategic": "การสร้างความสามารถในการแข่งขัน",
    "target": "เพิ่มมูลค่าอาหารปลอดภัย",
    "target_group": [
      "วิสาหกิจชุมชน 12 แห่ง",
      "บุคลากรห้องปฏิบัติการ 24 คน"
    ],
    "target_industry": "อุตสาหกรรมอาหารแห่งอนาคต"
  },
  "summary": "โครงการทดสอบระบบอาหารปลอดภัยชุมชน ประจำปีงบประมาณ พ.ศ. 2569 มีโครงการเทียบเท่าผลผลิตคือโครงการยกระดับอุตสาหกรรมอาหารแห่งอนาคต และระบุว่าเป็นโครงการสำคัญ เชื่อมโยงกับอุตสาหกรรมอาหารแห่งอนาคตและเป้าหมายเพิ่มมูลค่าอาหารปลอดภัย โดยมีตัวชี้วัดเป้าหมายรายได้เพิ่มร้อยละ 5 และผู้ประกอบการผ่านมาตรฐาน 12 ราย เหตุผลคือผู้ประกอบการชุมชนขาดเครื่องมือตรวจคุณภาพอาหาร มีวัตถุประสงค์เพื่อพัฒนาระบบตรวจคุณภาพอาหารชุมชนและฝึกอบรมผู้ประกอบการ 24 คน กลุ่มเป้าหมายคือวิสาหกิจชุมชน 12 แห่งและบุคลากรห้องปฏิบัติการ 24 คน กิจกรรมประกอบด้วยสำรวจ 12 ชุมชน พัฒนาระบบ และอบรม 24 คน ในเดือนตุลาคม พฤศจิกายน และธันวาคม 2568 ตามลำดับ ตัวชี้วัดโครงการคือระบบตรวจคุณภาพ 1 ระบบและผู้ผ่านการอบรม 24 คน งบประมาณ 2.50 ล้านบาท ดำเนินการระดับจังหวัดที่ขอนแก่น คาดว่าจะลดต้นทุนการตรวจคุณภาพและเพิ่มรายได้ของวิสาหกิจชุมชน โดยประมาณมูลค่าทางเศรษฐกิจที่เพิ่มขึ้น 5 ล้านบาทต่อปี หน่วยงานรับผิดชอบคือศูนย์ทดสอบอาหารชุมชน",
  "budget": {
    "ambiguous": false,
    "amount": "2.50",
    "amount_baht": "2500000.00",
    "raw_text": "2.50 ล้านบาท (สองล้านห้าแสนบาทถ้วน)",
    "unit": "ล้านบาท"
  },
  "structured": {
    "additional_sections": [
      {
        "heading": "ประมาณการมูลค่าทางเศรษฐกิจที่เพิ่มขึ้นจากการดำเนินโครงการ",
        "source_verified": true,
        "sources": [
          {
            "page": 3,
            "quote": "12. ประมาณการมูลค่าทางเศรษฐกิจที่เพิ่มขึ้นจากการดำเนินโครงการ: 5 ล้านบาทต่อปี",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "text": "5 ล้านบาทต่อปี"
      }
    ],
    "budget_items": [
      {
        "amount": "2.50",
        "amount_baht": "2500000.00",
        "arithmetic_status": "not_checked",
        "calculation": "unspecified",
        "currency_unit": "ล้านบาท",
        "item": "งบประมาณโครงการ",
        "quantity": null,
        "quantity_unit": null,
        "row_type": "grand_total",
        "source_verified": true,
        "sources": [
          {
            "page": 3,
            "quote": "9. งบประมาณ 2.50 ล้านบาท (สองล้านห้าแสนบาทถ้วน)",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "unit_price": null
      }
    ],
    "conflicts": [],
    "kpi_items": [
      {
        "indicator": "ระบบตรวจคุณภาพ",
        "level": "output",
        "period": null,
        "source_verified": true,
        "sources": [
          {
            "page": 2,
            "quote": "6. ตัวชี้วัดโครงการ: ระบบตรวจคุณภาพ 1 ระบบ; ผู้ผ่านการอบรม 24 คน",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "target": "1",
        "target_text": "1 ระบบ",
        "unit": "ระบบ"
      },
      {
        "indicator": "ผู้ผ่านการอบรม",
        "level": "output",
        "period": null,
        "source_verified": true,
        "sources": [
          {
            "page": 2,
            "quote": "6. ตัวชี้วัดโครงการ: ระบบตรวจคุณภาพ 1 ระบบ; ผู้ผ่านการอบรม 24 คน",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "target": "24",
        "target_text": "24 คน",
        "unit": "คน"
      }
    ],
    "project_info": {
      "duration": null,
      "fiscal_year": "2569",
      "project_code": null,
      "project_type": null,
      "source_verified": true,
      "sources": [
        {
          "page": 1,
          "quote": "ข้อเสนอโครงการ ประจำปีงบประมาณ พ.ศ. 2569",
          "row": null,
          "table_id": null,
          "verified": true
        }
      ]
    },
    "schedule": [
      {
        "activity": "สำรวจ",
        "deliverable": null,
        "months": [
          "ตุลาคม 2568"
        ],
        "period": "ตุลาคม 2568",
        "source_verified": true,
        "sources": [
          {
            "page": 2,
            "quote": "ตุลาคม 2568 สำรวจ",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "target": null,
        "unit": null,
        "unverified_months": [],
        "unverified_period": null
      },
      {
        "activity": "พัฒนาระบบ",
        "deliverable": null,
        "months": [
          "พฤศจิกายน 2568"
        ],
        "period": "พฤศจิกายน 2568",
        "source_verified": true,
        "sources": [
          {
            "page": 2,
            "quote": "พฤศจิกายน 2568 พัฒนาระบบ",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "target": null,
        "unit": null,
        "unverified_months": [],
        "unverified_period": null
      },
      {
        "activity": "อบรม",
        "deliverable": null,
        "months": [
          "ธันวาคม 2568"
        ],
        "period": "ธันวาคม 2568",
        "source_verified": true,
        "sources": [
          {
            "page": 2,
            "quote": "ธันวาคม 2568 อบรม",
            "row": null,
            "table_id": null,
            "verified": true
          }
        ],
        "target": null,
        "unit": null,
        "unverified_months": [],
        "unverified_period": null
      }
    ]
  },
  "quality": {
    "evidence_validation": "quote_occurrence_only_not_semantic_verification",
    "field_status": {
      "budget": "supported",
      "exp_benefit": "supported",
      "flagship": "supported",
      "focus": "missing",
      "how": "supported",
      "integration_approach": "supported",
      "integration_approach_kpi": "supported",
      "integration_goal": "supported",
      "integration_target_kpi": "supported",
      "is_flagship": "supported",
      "kpi": "supported",
      "link": "supported",
      "methodology": "supported",
      "objective": "supported",
      "place": "supported",
      "plan": "supported",
      "project_name": "supported",
      "responsible_department": "supported",
      "responsible_details": "supported",
      "result": "supported",
      "strategic": "supported",
      "target": "supported",
      "target_group": "supported",
      "target_industry": "supported"
    },
    "missing_fields": [
      "focus"
    ],
    "needs_review": false,
    "unverified_fields": [],
    "validation_issues": [],
    "warnings": []
  },
  "document": {
    "page_count": 3,
    "pages_processed": 3,
    "complete": true,
    "page_numbering": "source_pdf"
  }
}
JSON Schema ของ response v2 ฉบับเต็ม
{
  "$defs": {
    "OutputBudget": {
      "additionalProperties": false,
      "properties": {
        "amount": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Amount"
        },
        "unit": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Unit"
        },
        "amount_baht": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Amount Baht"
        },
        "raw_text": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Raw Text"
        },
        "ambiguous": {
          "title": "Ambiguous",
          "type": "boolean"
        }
      },
      "required": [
        "amount",
        "unit",
        "amount_baht",
        "raw_text",
        "ambiguous"
      ],
      "title": "OutputBudget",
      "type": "object"
    },
    "OutputBudgetItem": {
      "additionalProperties": false,
      "properties": {
        "item": {
          "title": "Item",
          "type": "string"
        },
        "row_type": {
          "enum": [
            "item",
            "subtotal",
            "grand_total"
          ],
          "title": "Row Type",
          "type": "string"
        },
        "quantity": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Quantity"
        },
        "quantity_unit": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Quantity Unit"
        },
        "unit_price": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Unit Price"
        },
        "amount": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Amount"
        },
        "currency_unit": {
          "anyOf": [
            {
              "enum": [
                "บาท",
                "พันบาท",
                "ล้านบาท"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Currency Unit"
        },
        "calculation": {
          "enum": [
            "quantity_times_unit_price",
            "other",
            "unspecified"
          ],
          "title": "Calculation",
          "type": "string"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "title": "Source Verified",
          "type": "boolean"
        },
        "amount_baht": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Amount Baht"
        },
        "arithmetic_status": {
          "enum": [
            "not_checked",
            "matched",
            "mismatch"
          ],
          "title": "Arithmetic Status",
          "type": "string"
        }
      },
      "required": [
        "item",
        "row_type",
        "quantity",
        "quantity_unit",
        "unit_price",
        "amount",
        "currency_unit",
        "calculation",
        "sources",
        "source_verified",
        "amount_baht",
        "arithmetic_status"
      ],
      "title": "OutputBudgetItem",
      "type": "object"
    },
    "OutputConflict": {
      "additionalProperties": false,
      "properties": {
        "field": {
          "title": "Field",
          "type": "string"
        },
        "description": {
          "title": "Description",
          "type": "string"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "title": "Source Verified",
          "type": "boolean"
        }
      },
      "required": [
        "field",
        "description",
        "sources",
        "source_verified"
      ],
      "title": "OutputConflict",
      "type": "object"
    },
    "OutputDocument": {
      "additionalProperties": false,
      "properties": {
        "page_count": {
          "title": "Page Count",
          "type": "integer"
        },
        "pages_processed": {
          "title": "Pages Processed",
          "type": "integer"
        },
        "complete": {
          "title": "Complete",
          "type": "boolean"
        },
        "page_numbering": {
          "enum": [
            "rendered_pdf",
            "source_pdf"
          ],
          "title": "Page Numbering",
          "type": "string"
        }
      },
      "required": [
        "page_count",
        "pages_processed",
        "complete",
        "page_numbering"
      ],
      "title": "OutputDocument",
      "type": "object"
    },
    "OutputInfo": {
      "additionalProperties": false,
      "properties": {
        "project_code": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Project Code"
        },
        "fiscal_year": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Preserve Buddhist/Gregorian year as written; do not convert.",
          "title": "Fiscal Year"
        },
        "project_type": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Project Type"
        },
        "duration": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Duration"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Source Verified"
        }
      },
      "required": [
        "project_code",
        "fiscal_year",
        "project_type",
        "duration",
        "sources",
        "source_verified"
      ],
      "title": "OutputInfo",
      "type": "object"
    },
    "OutputKPI": {
      "additionalProperties": false,
      "properties": {
        "indicator": {
          "title": "Indicator",
          "type": "string"
        },
        "level": {
          "enum": [
            "output",
            "outcome",
            "impact",
            "unspecified"
          ],
          "title": "Level",
          "type": "string"
        },
        "target": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Numeric target only; null if not a single number.",
          "title": "Target"
        },
        "target_text": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Original comparator/range and target, e.g. ไม่น้อยกว่าร้อยละ 80.",
          "title": "Target Text"
        },
        "unit": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Unit"
        },
        "period": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Period"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "title": "Source Verified",
          "type": "boolean"
        }
      },
      "required": [
        "indicator",
        "level",
        "target",
        "target_text",
        "unit",
        "period",
        "sources",
        "source_verified"
      ],
      "title": "OutputKPI",
      "type": "object"
    },
    "OutputQuality": {
      "additionalProperties": false,
      "properties": {
        "needs_review": {
          "title": "Needs Review",
          "type": "boolean"
        },
        "field_status": {
          "additionalProperties": {
            "enum": [
              "missing",
              "supported",
              "unverified"
            ],
            "type": "string"
          },
          "title": "Field Status",
          "type": "object"
        },
        "missing_fields": {
          "items": {
            "type": "string"
          },
          "title": "Missing Fields",
          "type": "array"
        },
        "unverified_fields": {
          "items": {
            "type": "string"
          },
          "title": "Unverified Fields",
          "type": "array"
        },
        "warnings": {
          "items": {
            "type": "string"
          },
          "title": "Warnings",
          "type": "array"
        },
        "validation_issues": {
          "items": {
            "additionalProperties": {
              "type": "string"
            },
            "type": "object"
          },
          "title": "Validation Issues",
          "type": "array"
        },
        "evidence_validation": {
          "title": "Evidence Validation",
          "type": "string"
        }
      },
      "required": [
        "needs_review",
        "field_status",
        "missing_fields",
        "unverified_fields",
        "warnings",
        "validation_issues",
        "evidence_validation"
      ],
      "title": "OutputQuality",
      "type": "object"
    },
    "OutputSchedule": {
      "additionalProperties": false,
      "properties": {
        "activity": {
          "title": "Activity",
          "type": "string"
        },
        "period": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Period"
        },
        "months": {
          "description": "Explicitly active month/quarter column labels only; do not convert fiscal month numbers to calendar months. Ambiguous marks=[] and report conflict.",
          "items": {
            "type": "string"
          },
          "title": "Months",
          "type": "array"
        },
        "deliverable": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Deliverable"
        },
        "target": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Target"
        },
        "unit": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Unit"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "title": "Source Verified",
          "type": "boolean"
        },
        "unverified_months": {
          "description": "Model-read month labels withheld from months when source references cannot be verified; review the original before using.",
          "items": {
            "type": "string"
          },
          "title": "Unverified Months",
          "type": "array"
        },
        "unverified_period": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Model-read period withheld from period when source references cannot be verified.",
          "title": "Unverified Period"
        }
      },
      "required": [
        "activity",
        "period",
        "months",
        "deliverable",
        "target",
        "unit",
        "sources",
        "source_verified",
        "unverified_months",
        "unverified_period"
      ],
      "title": "OutputSchedule",
      "type": "object"
    },
    "OutputSection": {
      "additionalProperties": false,
      "properties": {
        "heading": {
          "title": "Heading",
          "type": "string"
        },
        "text": {
          "title": "Text",
          "type": "string"
        },
        "sources": {
          "items": {
            "$ref": "#/$defs/VerifiedSource"
          },
          "title": "Sources",
          "type": "array"
        },
        "source_verified": {
          "title": "Source Verified",
          "type": "boolean"
        }
      },
      "required": [
        "heading",
        "text",
        "sources",
        "source_verified"
      ],
      "title": "OutputSection",
      "type": "object"
    },
    "OutputStructured": {
      "additionalProperties": false,
      "properties": {
        "project_info": {
          "$ref": "#/$defs/OutputInfo"
        },
        "budget_items": {
          "items": {
            "$ref": "#/$defs/OutputBudgetItem"
          },
          "title": "Budget Items",
          "type": "array"
        },
        "kpi_items": {
          "items": {
            "$ref": "#/$defs/OutputKPI"
          },
          "title": "Kpi Items",
          "type": "array"
        },
        "schedule": {
          "items": {
            "$ref": "#/$defs/OutputSchedule"
          },
          "title": "Schedule",
          "type": "array"
        },
        "additional_sections": {
          "items": {
            "$ref": "#/$defs/OutputSection"
          },
          "title": "Additional Sections",
          "type": "array"
        },
        "conflicts": {
          "items": {
            "$ref": "#/$defs/OutputConflict"
          },
          "title": "Conflicts",
          "type": "array"
        }
      },
      "required": [
        "project_info",
        "budget_items",
        "kpi_items",
        "schedule",
        "additional_sections",
        "conflicts"
      ],
      "title": "OutputStructured",
      "type": "object"
    },
    "OutputTable": {
      "additionalProperties": false,
      "properties": {
        "title": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Title"
        },
        "kind": {
          "enum": [
            "budget",
            "kpi",
            "timeline",
            "strategy",
            "history",
            "other"
          ],
          "title": "Kind",
          "type": "string"
        },
        "columns": {
          "description": "Column labels in visual order, including multi-level header context. Unlabelled column = empty string.",
          "items": {
            "type": "string"
          },
          "title": "Columns",
          "type": "array"
        },
        "rows": {
          "description": "Every visible data row, same width as columns. Preserve text, units, marks and amounts verbatim; empty/merged continuation cells=null. Do not drop totals or sparse rows.",
          "items": {
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ]
            },
            "type": "array"
          },
          "title": "Rows",
          "type": "array"
        },
        "notes": {
          "items": {
            "type": "string"
          },
          "title": "Notes",
          "type": "array"
        },
        "id": {
          "title": "Id",
          "type": "string"
        },
        "page": {
          "title": "Page",
          "type": "integer"
        },
        "shape_valid": {
          "title": "Shape Valid",
          "type": "boolean"
        }
      },
      "required": [
        "title",
        "kind",
        "columns",
        "rows",
        "notes",
        "id",
        "page",
        "shape_valid"
      ],
      "title": "OutputTable",
      "type": "object"
    },
    "Person": {
      "additionalProperties": false,
      "properties": {
        "first_name": {
          "title": "First Name",
          "type": "string"
        },
        "last_name": {
          "title": "Last Name",
          "type": "string"
        },
        "email": {
          "title": "Email",
          "type": "string"
        },
        "phone": {
          "title": "Phone",
          "type": "string"
        },
        "department": {
          "title": "Department",
          "type": "string"
        }
      },
      "required": [
        "first_name",
        "last_name",
        "email",
        "phone",
        "department"
      ],
      "title": "Person",
      "type": "object"
    },
    "ProjectData": {
      "additionalProperties": false,
      "properties": {
        "budget": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Numeric total budget exactly as stated, without commas, in the stated unit. Do not scale. Null if no explicit total.",
          "title": "Budget"
        },
        "project_name": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Actual project title, not parent output/program title. If both โครงการ/ผลผลิต and โครงการ appear choose the latter.",
          "title": "Project Name"
        },
        "kpi": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "All project output/outcome indicators with numeric targets and units; preserve table associations.",
          "title": "Kpi"
        },
        "how": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Implementation activities/methods. Preserve all numbered activities and substantive details.",
          "title": "How"
        },
        "methodology": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "หลักการและเหตุผล: rationale and problem statement, NOT implementation methods.",
          "title": "Methodology"
        },
        "result": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Estimated economic value/impact and its calculation, not generic expected benefits.",
          "title": "Result"
        },
        "flagship": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "ชื่อโครงการเทียบเท่าผลผลิต or parent โครงการ/ผลผลิต title; not the yes/no checkbox.",
          "title": "Flagship"
        },
        "focus": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Explicitly selected Focus issues only; null if absent.",
          "title": "Focus"
        },
        "plan": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Implementation timeline, activities and months/quarters/durations; keep table relationships.",
          "title": "Plan"
        },
        "target": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Integrated program goal, same as integration_goal when supplied.",
          "title": "Target"
        },
        "strategic": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "National strategies/master plans and selected subplans, codes and targets.",
          "title": "Strategic"
        },
        "target_industry": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Explicit target industries only.",
          "title": "Target Industry"
        },
        "place": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Selected/explicit implementation locations only; omit unchecked alternatives.",
          "title": "Place"
        },
        "integration_goal": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "เป้าหมายแผนงานบูรณาการฯ, regardless of numbering.",
          "title": "Integration Goal"
        },
        "integration_target_kpi": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "ตัวชี้วัดเป้าหมายแผนงานบูรณาการฯ, including units/targets.",
          "title": "Integration Target Kpi"
        },
        "integration_approach": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "แนวทางการดำเนินงานแผนงานบูรณาการฯ.",
          "title": "Integration Approach"
        },
        "integration_approach_kpi": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "ตัวชี้วัดแนวทางแผนงานบูรณาการฯ, including units/targets.",
          "title": "Integration Approach Kpi"
        },
        "responsible_department": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Actual department/agency responsible for this project, not the template publisher.",
          "title": "Responsible Department"
        },
        "exp_benefit": {
          "description": "Expected benefits (ผลประโยชน์ที่คาดว่าจะได้รับ), one complete item per entry.",
          "items": {
            "type": "string"
          },
          "title": "Exp Benefit",
          "type": "array"
        },
        "objective": {
          "description": "All stated objectives, one complete objective per entry. Do not invent or merge items.",
          "items": {
            "type": "string"
          },
          "title": "Objective",
          "type": "array"
        },
        "target_group": {
          "description": "Target beneficiary groups, one complete group per entry, including quantities.",
          "items": {
            "type": "string"
          },
          "title": "Target Group",
          "type": "array"
        },
        "link": {
          "description": "Only selected related integration issues in checkbox forms, or explicit integration linkage in other forms.",
          "items": {
            "type": "string"
          },
          "title": "Link",
          "type": "array"
        },
        "is_flagship": {
          "anyOf": [
            {
              "enum": [
                0,
                1
              ],
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "1 if explicitly checked ใช่, 0 if explicitly checked ไม่ใช่; null if absent/ambiguous.",
          "title": "Is Flagship"
        },
        "responsible_details": {
          "description": "All named project contacts. No invented name splits/contact information. Empty string for absent components.",
          "items": {
            "$ref": "#/$defs/Person"
          },
          "title": "Responsible Details",
          "type": "array"
        }
      },
      "required": [
        "budget",
        "project_name",
        "kpi",
        "how",
        "methodology",
        "result",
        "flagship",
        "focus",
        "plan",
        "target",
        "strategic",
        "target_industry",
        "place",
        "integration_goal",
        "integration_target_kpi",
        "integration_approach",
        "integration_approach_kpi",
        "responsible_department",
        "exp_benefit",
        "objective",
        "target_group",
        "link",
        "is_flagship",
        "responsible_details"
      ],
      "title": "ProjectData",
      "type": "object"
    },
    "VerifiedSource": {
      "additionalProperties": false,
      "properties": {
        "page": {
          "title": "Page",
          "type": "integer"
        },
        "quote": {
          "title": "Quote",
          "type": "string"
        },
        "table_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "description": "Server-assigned pN-tM id if source is a table, otherwise null.",
          "title": "Table Id"
        },
        "row": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "description": "1-based data row of source table, otherwise null. Headers are not data rows.",
          "title": "Row"
        },
        "verified": {
          "title": "Verified",
          "type": "boolean"
        }
      },
      "required": [
        "page",
        "quote",
        "table_id",
        "row",
        "verified"
      ],
      "title": "VerifiedSource",
      "type": "object"
    }
  },
  "additionalProperties": false,
  "properties": {
    "document_type": {
      "title": "Document Type",
      "type": "string"
    },
    "document": {
      "$ref": "#/$defs/OutputDocument"
    },
    "data": {
      "$ref": "#/$defs/ProjectData"
    },
    "summary": {
      "title": "Summary",
      "type": "string"
    },
    "budget": {
      "$ref": "#/$defs/OutputBudget"
    },
    "structured": {
      "$ref": "#/$defs/OutputStructured"
    },
    "tables": {
      "items": {
        "$ref": "#/$defs/OutputTable"
      },
      "title": "Tables",
      "type": "array"
    },
    "quality": {
      "$ref": "#/$defs/OutputQuality"
    },
    "evidence": {
      "additionalProperties": {
        "items": {
          "additionalProperties": true,
          "type": "object"
        },
        "type": "array"
      },
      "title": "Evidence",
      "type": "object"
    },
    "ocr_pages": {
      "items": {
        "additionalProperties": true,
        "type": "object"
      },
      "title": "Ocr Pages",
      "type": "array"
    }
  },
  "required": [
    "document_type",
    "document",
    "data",
    "summary",
    "budget",
    "structured",
    "tables",
    "quality",
    "evidence"
  ],
  "title": "CanonicalResponse",
  "type": "object"
}

endpoint เดิม POST /api/extract ยังใช้งานได้ โดยผลใหม่อยู่ใต้ _extraction; ระบบใหม่ควรใช้ v2

5. Chat API

ส่ง message เป็นข้อความที่ไม่ว่าง และ session_id เป็น string ยาวไม่เกิน 128 ตัวอักษร ใช้ค่าเดิมในบทสนทนาเดิม และสร้างค่าใหม่เมื่อเริ่มบทสนทนาใหม่ เช่น UUID ของแต่ละผู้ใช้

แชตคู่มือ · รับ JSON

curl --fail-with-body 'https://oie.thinkv.app/api/chat' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"message":"ขั้นตอนการยื่นข้อเสนอโครงการมีอะไรบ้าง?","session_id":"your-app-user-session-001"}'
{"response":"ข้อความคำตอบภาษาไทย"}

แชตแบบ streaming ทั้ง 2 บริการ

# แชตคู่มือ
curl -N --fail-with-body 'https://oie.thinkv.app/api/chat/stream' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"message":"การติดตามผลโครงการทำอย่างไร?","session_id":"your-app-user-session-001"}'

# แชตวิเคราะห์ข้อมูลโครงการ
curl -N --fail-with-body 'https://oie.thinkv.app/api/admin/chat/stream' \
  -H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"message":"มีโครงการทั้งหมดกี่โครงการ?","session_id":"your-app-user-session-001"}'

Streaming เป็นรายการ data: JSON คั่นด้วยบรรทัดว่าง และสิ้นสุดเมื่อพบ done หรือ complete:

# แชตคู่มือ
data: {"content":"ข้อความส่วนหนึ่ง", "done":false}

data: {"content":"", "done":true}

# วิเคราะห์ข้อมูลโครงการ
data: {"chunk":"ข้อความส่วนหนึ่ง"}

data: {"complete":true}

หนึ่ง network chunk อาจมีครึ่งรายการหรือหลายรายการ ให้เก็บ buffer ก่อนแยกด้วยบรรทัดว่าง

JavaScript · อ่าน stream พร้อม buffer

async function readChat(message, sessionId, onChunk) {
  const response = await fetch("https://oie.thinkv.app/api/chat/stream", {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_ACCESS_TOKEN",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ message, session_id: sessionId }),
  });
  if (!response.ok) throw new Error("HTTP " + response.status);
  const reader = response.body.getReader();
  const decoder = new TextDecoder();
  let buffer = "";
  while (true) {
    const { value, done } = await reader.read();
    buffer += decoder.decode(value, { stream: !done });
    let boundary;
    while ((boundary = buffer.indexOf("\n\n")) >= 0) {
      const event = buffer.slice(0, boundary);
      buffer = buffer.slice(boundary + 2);
      const line = event.split("\n").find(line => line.startsWith("data: "));
      if (!line) continue;
      const item = JSON.parse(line.slice(6));
      if (item.error) throw new Error(item.error);
      if (item.content || item.chunk) onChunk(item.content || item.chunk);
      if (item.done || item.complete) return;
    }
    if (done) break;
  }
}

6. ข้อผิดพลาดและการลองใหม่

{"error":"รายละเอียดข้อผิดพลาด", "code":"missing_file"}
HTTPสาเหตุ / วิธีรับมือ
400ไม่มี file หรือ message; แก้ request ก่อนส่งใหม่
401ไม่ได้ส่ง access token หรือ token ไม่ถูกต้อง; ตรวจ Authorization header
413ไฟล์เกินขนาด จำนวนหน้า หรือข้อความเกินขีดจำกัด
415รองรับเฉพาะ PDF / DOCX
422เอกสารเสียหาย มีรหัสผ่าน ไม่มีข้อความ หรือไม่ใช่เอกสารโครงการที่รองรับ
502ผลประมวลผลไม่ครบหรือไม่ผ่านการตรวจรูปแบบ
503ระบบไม่พร้อมหรือ busy; สำหรับ busy / provider_rate_limit ให้รอตาม Retry-After แล้วลองใหม่แบบเพิ่มระยะห่าง
504คำขอหมดเวลา; ลองใหม่และส่งค่า X-Request-ID จาก response header ให้ผู้ดูแลหากเกิดซ้ำ

งานเอกสารที่อ่านไม่สำเร็จจะไม่ส่งข้อมูลบางส่วนเป็นผลสำเร็จ สำหรับ streaming ให้ตรวจ error ในรายการด้วย แม้ HTTP response จะเป็น 200

7. ทดลองเรียก API จากไฟล์คู่มือนี้

ค่าการเชื่อมต่อใช้ Base URL และ access token ด้านบน เลือกเอกสารแล้วกดส่งเพื่ออ่านจริง