Guide / Structify
문서 한 장을
필드와 근거로.
PDF나 이미지를 읽어 페이지와 블록으로 돌려주고, JSON Schema 필드를 값, 신뢰도, 근거 블록과 함께 채우는 게이트웨이 API입니다.
미리보기: 게이트웨이 허용 목록에 등록된 조직부터 호출할 수 있습니다. 제품 소개
언제 쓰나
- 청구서, 영수증, 신청서처럼 정해진 칸을 코드에서 JSON으로 받아야 할 때
- 필드마다 신뢰도가 있어야 낮은 값만 사람 검토로 보낼 수 있을 때
- 추출한 값이 원본 어디에서 왔는지 페이지와 좌표로 보여줘야 할 때
- 표, 수식, 그림이 섞인 문서를 블록 단위로 다뤄야 할 때
다른 제품이 맞을 때
- 문서 묶음에 질문하고 인용 달린 답을 받으려면 Context 버킷 업로드. HWP와 오피스 문서도 거기서 처리합니다.
- 받은 견적서와 발주서를 콘솔에서 원본과 대조하고 표로 관리하려면 Structify 앱.
- 현장에서 새 기록을 입력받으려면 Foundry.
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 # Python1. 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.jsonimport { 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, 입력 오류 422invalid_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