Guide / Structify

문서 한 장을
필드와 근거로.

PDF나 이미지를 읽어 페이지와 블록으로 돌려주고, JSON Schema 필드를 값, 신뢰도, 근거 블록과 함께 채우는 게이트웨이 API입니다.

미리보기: 게이트웨이 허용 목록에 등록된 조직부터 호출할 수 있습니다. 제품 소개

언제 쓰나

다른 제품이 맞을 때

5분 시작

0. 키와 SDK

export SCHIFT_API_KEY=sch_...   # 콘솔 → API 키: /app/console/keys
export SCHIFT_GATEWAY_URL=https://gw.schift.io

npm install @schift-io/sdk     # TypeScript
pip install schift             # Python

1. OCR: 페이지와 블록

curl "$SCHIFT_GATEWAY_URL/v1/ocr" \
  -H "Authorization: Bearer $SCHIFT_API_KEY" \
  -F "[email protected];type=application/pdf" > ocr.json

jq '.pages[] | {page_no, language, has_table, blocks: (.blocks | length)}' ocr.json
import { readFile } from "node:fs/promises";
import { StructifyClient } from "@schift-io/sdk/structify";

const client = new StructifyClient(); // SCHIFT_API_KEY, SCHIFT_GATEWAY_URL
const ocr = await client.ocr(await readFile("invoice.pdf"), {
  contentType: "application/pdf",
  fileName: "invoice.pdf",
});

for (const page of ocr.pages) {
  console.log(page.page_no, page.language, page.has_table, page.blocks.length);
}
from schift.structify import StructifyClient

client = StructifyClient()  # SCHIFT_API_KEY, SCHIFT_GATEWAY_URL
ocr = client.ocr("invoice.pdf")

for page in ocr["pages"]:
    print(page["page_no"], page["language"], page["has_table"], len(page["blocks"]))

블록마다 block_type, 0..1 bbox, confidence가 있고 표는 table, 수식은 latex, 그림은 image.caption에 담깁니다.

2. Structify: 스키마 필드 채우기

jq -n --slurpfile ocr ocr.json '{
  schema: {
    type: "object",
    properties: {
      vendor:     { type: ["string", "null"] },
      issued_on:  { type: ["string", "null"] },
      total:      { type: ["number", "null"] }
    },
    required: ["vendor", "issued_on", "total"],
    additionalProperties: false
  },
  instructions: "Dates as YYYY-MM-DD. Amounts as numbers in KRW.",
  ocr: $ocr[0]
}' | curl "$SCHIFT_GATEWAY_URL/v1/structify" \
  -H "Authorization: Bearer $SCHIFT_API_KEY" \
  -H "Content-Type: application/json" \
  -d @- | jq '.structured'
const schema = {
  type: "object",
  properties: {
    vendor: { type: ["string", "null"] },
    issued_on: { type: ["string", "null"] },
    total: { type: ["number", "null"] },
  },
  required: ["vendor", "issued_on", "total"],
  additionalProperties: false,
} as const;

const result = await client.structify({
  schema,
  instructions: "Dates as YYYY-MM-DD. Amounts as numbers in KRW.",
  ocr, // 1단계 결과를 그대로 넘긴다
});

for (const [field, value] of Object.entries(result.structured.fields)) {
  const score = result.structured.field_confidence[field]?.combined;
  const cited = result.structured.evidence[field] ?? [];
  console.log(field, value, score, cited);
}
schema = {
    "type": "object",
    "properties": {
        "vendor": {"type": ["string", "null"]},
        "issued_on": {"type": ["string", "null"]},
        "total": {"type": ["number", "null"]},
    },
    "required": ["vendor", "issued_on", "total"],
    "additionalProperties": False,
}

result = client.structify(
    schema,
    ocr=ocr,  # 1단계 결과를 그대로 넘긴다
    instructions="Dates as YYYY-MM-DD. Amounts as numbers in KRW.",
)

structured = result["structured"]
for field, value in structured["fields"].items():
    score = structured["field_confidence"].get(field, {}).get("combined")
    print(field, value, score, structured["evidence"].get(field, []))

1단계 결과를 ocr로 넘기면 문서는 한 번만 읽고 추출만 다시 돌립니다. 파일에서 바로 시작하려면 file(SDK) 또는 file_base64와 content_type(HTTP)을 넘깁니다.

3. 신뢰도로 검토 대상 고르기

// combined 가 기준보다 낮거나 null 인 필드만 사람 검토로 보낸다
const THRESHOLD = 0.85;
const needsReview = Object.keys(result.structured.fields).filter((field) => {
  const combined = result.structured.field_confidence[field]?.combined;
  return combined == null || combined < THRESHOLD;
});

// 근거 블록의 좌표로 원본 위에 하이라이트를 그린다
const page = result.raw.pages.find((p) => p.page_no === 1)!;
const block = page.blocks.find((b) => b.block_index === 0)!;
const px = page.width ? block.bbox.x0 * page.width : null;

combined는 모델 자기 평가와 인용 블록의 OCR 신뢰도 중 낮은 값입니다. 둘 중 하나라도 비면 null이라 검토 대상으로 분류됩니다. 기준값은 문서 종류마다 직접 정합니다.

한도와 단가

입력
application/pdf, image/*. 파일 하나 25MB.
호출 방식
동기 호출. 페이지마다 추출 호출 하나, 최대 4페이지 동시.
그림 캡션
문서당 20개까지, 기본 켜짐. /v1/ocr?captions=false로 끕니다.
스키마
type: "object"와 properties 필수. OpenAI 계열 모델에는 모든 속성을 required에 넣고 additionalProperties: false.
오류
{"error":{"code","message"}}. 키 오류 401, 허용 목록 밖의 조직 403, 입력 오류 422 invalid_request, 공급자 오류 502, 설정 누락 503.
단가
쪽수와 요청 수로 셉니다. 값은 가격 페이지의 카탈로그를 따릅니다.

들어오고 나가기

들어오기

기존 OCR 결과를 쓰던 코드라면 응답 pages[].blocks[]를 지금 쓰는 모델에 매핑합니다. 좌표는 bbox에 페이지 width, height를 곱하면 픽셀 값이 됩니다. Structify의 ocr 입력은 /v1/ocr 응답 모양을 받습니다.

나가기

호출 결과는 조직의 ocr/documents, ocr/pages, ocr/blocks, ocr/structured 행과 원본 파일 blob으로 남고, 같은 키로 /v1/data와 /v1/blobs에서 꺼냅니다.

# 같은 키로 기록된 행을 다시 읽는다 (문서 단위 필터)
curl "$SCHIFT_GATEWAY_URL/v1/data/ocr/structured?f.document_id=$DOC_ID" \
  -H "Authorization: Bearer $SCHIFT_API_KEY"

curl "$SCHIFT_GATEWAY_URL/v1/data/ocr/blocks?f.document_id=$DOC_ID&limit=1000" \
  -H "Authorization: Bearer $SCHIFT_API_KEY"

# 블록 텍스트 의미 검색
curl -G "$SCHIFT_GATEWAY_URL/v1/data/ocr/blocks" \
  --data-urlencode "near=공급가액" -d k=10 \
  -H "Authorization: Bearer $SCHIFT_API_KEY"

SDK 소스: schift-io/schift-ts, schift-io/schift-py