Part 3
코드로 더 세밀하게
다뤄봅니다
파이썬으로 만들면 결과 형식을 고정하고, 외부 자료를 끌어오고, 여러 문서를 한 번에 처리합니다.
이 Part가 다루는 것
Studio로 만들기 어려운 세 가지를 코드로 해결합니다. 형식 고정 · 외부 자료 연결 · 대량 처리입니다.
대화 이어가기
여러 번 주고받는 흐름을 만드는 방법을 봅니다.
정해진 형식으로 받기
항상 같은 모양의 결과를 받아 프로그램에서 바로 쓸 수 있게 합니다.
도구를 쓰게 하기
모델이 필요할 때 내가 만든 함수나 데이터베이스를 부르도록 합니다.
문서를 다루기
Document Parse API로 여러 문서를 처리하고 결과를 이어 붙입니다.
코딩 에이전트에게 시키기
직접 타이핑하지 않고 만드실 때, 무엇을 알려 주어야 제대로 나오는지 정리합니다.
Solar는 OpenAI API와 호환되므로, 라이브러리는 그대로 쓰시고 주소만 바꾸시면 됩니다. 공식 문서는 Generate · API Quickstart에 있습니다.
pip install --upgrade openai requests
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["UPSTAGE_API_KEY"],
base_url="https://api.upstage.ai/v1",
)
MODEL = "solar-pro4" # AI Initiative 지원 모델
3.1대화 이어가기
Solar는 이전 대화를 서버에 저장하지 않습니다. 따라서 맥락을 이어 가시려면 지금까지 오간 내용을 매번 함께 보내 주셔야 합니다.
messages = [
{"role": "user", "content": "제가 좋아하는 색은 초록색이에요."}
]
first = client.chat.completions.create(model=MODEL, messages=messages)
messages.append({"role": "assistant", "content": first.choices[0].message.content})
# 두 번째 질문. 앞의 내용을 함께 보냈기 때문에 답할 수 있습니다
messages.append({"role": "user", "content": "제가 좋아하는 색이 뭐라고 했죠?"})
second = client.chat.completions.create(model=MODEL, messages=messages)
print(second.choices[0].message.content)
역할 세 가지
| role | 무엇을 담나요 |
|---|---|
system | 모델이 지켜야 할 규칙과 말투를 적습니다. 맨 앞에 한 번 넣습니다 |
user | 사용자의 요청과 자료를 담습니다 |
assistant | 모델이 앞서 한 답변입니다. 대화를 이어 갈 때 다시 넣어 줍니다 |
다시 보낸 내용은 모두 사용량으로 계산됩니다. 대화가 길어질수록 한 번에 보내는 분량이 늘어나므로, 오래된 내용은 정리하거나 요약해서 보내 주세요.
Part 0에서 보신 분당 50,000 토큰 기준도 여기에 함께 적용됩니다. 등급별 한도는 Rate limits 문서에 있습니다.
Solar는 대화를 기억하지 않습니다. 맥락은 매번 함께 보내 주셔야 하고, 그만큼 사용량도 늘어납니다.
3.2정해진 형식으로 받기
업무에 쓰시려면 결과가 항상 같은 모양이어야 합니다. 프롬프트로 형식을 부탁하는 대신, 형식을 아예 지정해 두는 방법이 있습니다.
두 가지 방법
| 항목 | JSON mode | Structured outputs |
|---|---|---|
| 결과가 JSON인가 | 보장됩니다 | 보장됩니다 |
| 항목 이름과 형태가 고정되는가 | 보장되지 않습니다 | 보장됩니다 |
| 형식을 미리 적어야 하는가 | 필요 없습니다 | 필요합니다 |
업무에 쓰실 거라면 Structured outputs를 쓰세요. 항목 이름과 형태가 매번 같아야 프로그램이 그 값을 믿고 씁니다. 공식 문서는 Structured outputs에 있습니다.
문의 메일을 분류하고 답변 초안까지
import json
response = client.chat.completions.create(
model=MODEL,
messages=[{
"role": "user",
"content": "이 문의를 분류하고 짧은 답변 초안을 써 주세요: 구독료가 두 번 결제되었습니다."
}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "inquiry",
"strict": True,
"schema": {
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["결제", "기술", "계정"],
"description": "문의가 속한 분류"
},
"urgent": {
"type": "boolean",
"description": "즉시 대응이 필요한지 여부"
},
"reply": {
"type": "string",
"description": "고객에게 보낼 짧은 답변"
}
},
"required": ["category", "urgent", "reply"],
"additionalProperties": False
}
}
}
)
if response.choices[0].finish_reason != "stop":
raise RuntimeError("응답이 중간에 끊겼습니다. max_tokens 를 늘리거나 다시 시도해 주세요.")
result = json.loads(response.choices[0].message.content)
print(result["category"], result["urgent"])
print(result["reply"])
enum으로 값을 세 가지로 고정했기 때문에, 결과는 항상 결제·기술·계정 중 하나입니다. 모델이 비슷한 다른 표현을 만들어 내지 않습니다.
형식을 적을 때 지켜야 할 규칙
- 맨 바깥은 반드시
object여야 합니다. - 모든 항목을
required에 넣습니다. - 값이 없을 수도 있는 항목은 지우지 마시고
"type": ["string", "null"]처럼 적습니다. 항목은 항상 나오고 값만 비게 됩니다. strict는true,additionalProperties는false로 둡니다.- 쓸 수 있는 형태는 문자·숫자·정수·참거짓·객체·배열입니다.
finish_reason을 확인해 주세요
stop이면 끝까지 만들어졌다는 뜻이라 안심하고 읽으셔도 됩니다.
length라면 분량 한계에 걸려 중간에서 잘린 상태입니다. 이때는 읽으려 하지 마시고 max_tokens를 늘리시거나 다시 요청해 주세요.
형식이 고정된다는 것은 "모양이 같다"는 뜻이지 "내용이 옳다"는 뜻이 아닙니다. 금액이나 날짜처럼 중요한 값은 업무 규칙으로 한 번 더 검증해 주세요.
형식을 지정하면 결과가 항상 같은 모양으로 돌아옵니다. 다만 모양이 맞다고 값이 옳은 것은 아니므로 검증은 따로 해 주세요.
3.3도구를 쓰게 하기
모델이 알지 못하는 정보가 필요할 때, 내가 만든 함수를 대신 부르게 할 수 있습니다. 데이터베이스 조회, 사내 시스템 확인, 계산 같은 일에 씁니다. 공식 문서는 Tool calling에 있습니다.
어떻게 동작하나요
쓸 수 있는 도구를 알려 줍니다
함수 이름, 설명, 필요한 값을 tools에 적어 함께 보냅니다. 모델은 이 설명을 보고 언제 부를지 판단합니다.
모델이 부르겠다고 답합니다
모델은 함수를 직접 실행하지 않습니다. "이 함수를 이런 값으로 불러 주세요"라는 요청만 돌려줍니다.
내 코드가 실제로 실행합니다
돌려받은 값을 확인한 뒤 함수를 실행합니다. 값이 예상과 다를 수 있으므로 반드시 검사한 뒤 실행해 주세요.
결과를 다시 보냅니다
실행 결과를 대화에 덧붙여 한 번 더 요청하면, 모델이 그 값을 반영한 최종 답변을 만들어 줍니다.
tools = [{
"type": "function",
"function": {
"name": "get_budget",
"description": "부서와 연도로 예산 집행 현황을 조회합니다.",
"parameters": {
"type": "object",
"properties": {
"department": {"type": "string", "description": "부서명"},
"year": {"type": "integer", "description": "조회 연도"}
},
"required": ["department", "year"],
"additionalProperties": False
}
}
}]
messages = [{"role": "user", "content": "교육팀의 2026년 예산 집행률을 알려 주세요."}]
response = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=tools,
tool_choice="auto",
)
import json
tool_calls = response.choices[0].message.tool_calls
if tool_calls:
messages.append(response.choices[0].message)
for call in tool_calls:
# 1) 허용한 함수가 맞는지 확인합니다
if call.function.name != "get_budget":
raise ValueError(f"허용되지 않은 도구입니다: {call.function.name}")
# 2) 값이 올바른 형태인지 확인합니다
args = json.loads(call.function.arguments)
department = args.get("department")
year = args.get("year")
if not isinstance(department, str) or not department.strip():
raise ValueError("department 는 비어 있지 않은 문자열이어야 합니다")
if not isinstance(year, int):
raise ValueError("year 는 정수여야 합니다")
# 3) 확인이 끝난 뒤에 실행합니다
result = get_budget(department=department, year=year)
messages.append({
"tool_call_id": call.id,
"role": "tool",
"name": call.function.name,
"content": json.dumps(result, ensure_ascii=False),
})
final = client.chat.completions.create(model=MODEL, messages=messages)
print(final.choices[0].message.content)
모델이 돌려주는 값은 예상과 다를 수 있습니다. 허용한 함수가 맞는지, 값의 형태가 올바른지 확인한 뒤 실행해 주세요.
파일 삭제, 메일 발송, 결제처럼 되돌리기 어려운 일은 아예 도구로 열지 마세요. 꼭 열어야 한다면 사람이 확인한 뒤에 실행되게 만들어 주세요.
parallel_tool_calls=True로 두시면 서로 무관한 조회를 한 번에 요청합니다. 여러 곳에서 자료를 모아야 할 때 시간이 줄어듭니다.
이때는 돌려받은 모든 요청을 처리해 결과를 붙인 뒤에 다음 요청을 보내야 합니다.
모델은 도구를 부르겠다고 알려 줄 뿐 직접 실행하지 않습니다. 실행은 내 코드의 몫이고, 검사도 내 코드의 몫입니다.
3.4문서 다루기
Document Parse는 Solar와 주소가 다릅니다. 파일을 함께 올리는 방식이라 요청 형태도, 돌아오는 응답의 모양도 다릅니다. 공식 문서는 Parse · API Quickstart에 있습니다.
import os
import requests
url = "https://api.upstage.ai/v1/document-digitization"
headers = {"Authorization": f"Bearer {os.environ['UPSTAGE_API_KEY']}"}
with open("sample.pdf", "rb") as f:
response = requests.post(
url,
headers=headers,
files={"document": f},
data={"model": "document-parse", "ocr": "force"},
)
result = response.json()
print(result["content"]["html"][:500]) # 구조가 살아 있는 결과
print(result["usage"]["pages"], "페이지 처리")
Solar와는 응답 구조가 다릅니다
같은 Upstage API여도 두 제품은 하는 일이 다릅니다. 그래서 돌려주는 모양도 다르죠. Solar 코드를 복사해 오실 때 여기서 가장 많이 막히십니다.
| 비교 항목 | Solar | Document Parse |
|---|---|---|
| 주소 | /v1/chat/completions | /v1/document-digitization |
| 보내는 방법 | openai 라이브러리로 messages 전달 | requests로 파일 첨부(multipart/form-data) |
| 돌아오는 것 | 모델이 새로 쓴 글 한 덩어리 | 원문을 구조 그대로 옮긴 데이터 |
| 꺼내는 자리 | choices[0].message.content | content.html · content.markdown · elements[] |
| 사용량 단위 | usage.total_tokens (토큰) | usage.pages (페이지) |
| 같은 입력을 다시 넣으면 | 생성 결과라 표현이 조금씩 달라질 수 있습니다 | 변환 결과라 실행마다 크게 달라지지 않습니다 |
Document Parse 응답에는 choices가 아예 없습니다. Solar 예제를 그대로 가져와 response.choices[0]으로 읽으려 하시면 값이 없다는 오류가 납니다. 반대로 Solar 응답에는 elements나 pages가 없습니다.
결과 안에 무엇이 들어 있나요
| 위치 | 내용 |
|---|---|
content.html | 제목·문단·표가 구조 그대로 담긴 결과입니다. Solar에게 건네기에 적합합니다 |
content.markdown | Markdown 형태의 결과입니다 |
elements[] | 요소 하나하나가 담깁니다. 종류(제목·문단·표·목록), 페이지 번호, 위치가 함께 들어 있습니다 |
usage.pages | 처리된 페이지 수입니다 |
elements[]에 페이지 번호와 위치가 들어 있어, 나중에 "이 값이 원문 몇 쪽에서 나왔는지" 되짚어 보실 수 있습니다. 검토가 필요한 업무에서 요긴합니다. 항목별 설명은 Understanding output에 있습니다.
문서를 여러 건 처리할 때
Document Parse는 초당 1건(동기 방식)이 기준입니다. 여러 건을 한꺼번에 보내시면 기준을 넘기기 쉬우므로, 한 건씩 간격을 두고 보내 주세요.
import time
import glob
results = {}
for path in glob.glob("documents/*.pdf"):
try:
with open(path, "rb") as f:
response = requests.post(
url,
headers=headers,
files={"document": f},
data={"model": "document-parse", "ocr": "force"},
)
if response.status_code == 429:
time.sleep(3) # 기준을 넘었으니 잠시 기다립니다
continue
response.raise_for_status()
results[path] = response.json()["content"]["html"]
print(f"완료: {path}")
except Exception as e:
print(f"실패: {path} — {e}")
time.sleep(1) # 초당 1건 기준을 지킵니다
동기 방식은 한 파일에 100페이지까지 처리합니다. 그보다 긴 문서는 나누어 보내시거나, 1,000페이지까지 지원하는 비동기 방식을 사용하시면 됩니다. 방법은 Handling large documents에 있습니다.
파일 크기는 50MB까지입니다. 지원 형식과 한도 전체는 Document Parse 모델 문서에 있습니다.
문서를 읽고 판단까지 이어 붙이기
import json
# 1단계. 문서를 글로 바꿉니다
with open("receipt.pdf", "rb") as f:
parsed = requests.post(
url, headers=headers,
files={"document": f},
data={"model": "document-parse", "ocr": "force"},
).json()
document_text = parsed["content"]["html"]
# 2단계 + 3단계. 값을 뽑고 기준에 비추어 판단합니다
response = client.chat.completions.create(
model=MODEL,
messages=[{
"role": "user",
"content": f"아래 영수증에서 상호명·발행일·총액을 찾고, "
f"총액이 100000원을 넘으면 보류로 판단해 주세요.\n\n{document_text}"
}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "receipt",
"strict": True,
"schema": {
"type": "object",
"properties": {
"store_name": {"type": ["string", "null"], "description": "영수증 상단의 상호명"},
"issued_date": {"type": ["string", "null"], "description": "발행 일자"},
"total_amount": {"type": ["integer", "null"], "description": "부가세 포함 최종 결제 금액"},
"decision": {"type": "string", "enum": ["승인", "보류", "확인필요"]},
"reason": {"type": "string", "description": "그렇게 판단한 근거 한 문장"}
},
"required": ["store_name", "issued_date", "total_amount", "decision", "reason"],
"additionalProperties": False
}
}
}
)
print(json.loads(response.choices[0].message.content))
값을 찾지 못할 수도 있으므로 ["string", "null"]처럼 비어 있을 수 있게 열어 두었습니다. 그리고 그런 경우를 위해 확인필요라는 판단 값을 함께 두었습니다.
Document Parse로 글을 만들고, 그 글을 Solar에게 형식과 함께 건네면 문서 자동화 한 벌이 완성됩니다.
3.5코딩 에이전트에게 시키기
요즘은 코드를 직접 타이핑하지 않고 Claude Code나 Cursor 같은 코딩 에이전트에게 시키는 경우가 흔합니다. 결과를 가르는 건 코드 실력이 아니라 얼마나 일러 주었느냐입니다.
그냥 시키면 왜 틀리나요
에이전트는 학습한 시점의 기억으로 코드를 씁니다. Upstage API는 그 기억에 거의 없거나 낡아 있어서 아무 설명 없이 시키면 그럴듯해 보이지만 돌아가지 않는 코드가 나옵니다. 반복해서 나오는 건 아래 세 가지입니다.
| 에이전트가 흔히 쓰는 것 | 실제로 벌어지는 일 |
|---|---|
model="solar-mini"처럼 옛 모델 이름 | 학습 시점의 기억으로 지어냅니다. 지원 범위 밖이라 응답이 오지 않습니다 |
Document Parse를 /v1/chat/completions로 호출 | 주소가 달라서 실패합니다. 오류 메시지는 "모델이 잘못되었다"고 나와 원인을 찾기 어렵습니다 |
항목 추출에 information-extract 사용 | 동작은 하지만 크레딧이 차감됩니다. 같은 일을 Parse + Solar로 무상 범위에서 할 수 있습니다 |
그래서 필요한 것은 두 가지입니다. 공식 문서를 먼저 읽히고, 지킬 규칙을 파일로 못 박아 두는 것입니다.
1단계. 공식 문서를 먼저 읽힙니다
Upstage는 코딩 에이전트가 읽기 좋은 형태로 API 문서를 한 주소에 모아 두었습니다. 작업 전에 이 주소부터 읽히시면 모델 이름이나 주소를 지어내는 일이 크게 줄어듭니다.
https://console.upstage.ai/api/docs/for-agents/raw
위 주소를 먼저 읽어 주세요.
Upstage API 는 이 문서에 적힌 스펙만 사용하고,
기억에 있는 내용으로 채우지 말아 주세요.
안내 페이지는 For AI Coding Assistant에 있습니다.
2단계. 제약을 파일로 고정합니다
같은 설명을 매번 반복하지 않으시려면 프로젝트 폴더에 규칙 파일을 하나 두시면 됩니다. Claude Code는 CLAUDE.md를, 그 밖의 도구는 대체로 AGENTS.md를 자동으로 읽습니다. 파일 이름만 맞춰 두시면 이후 모든 요청에 이 내용이 함께 적용됩니다.
# 이 프로젝트의 Upstage API 규칙
## 먼저 읽을 것
- https://console.upstage.ai/api/docs/for-agents/raw
- 기억에 의존하지 말고 위 문서의 스펙만 사용한다.
## 반드시 지킬 것
- 언어 모델은 `solar-pro4` 만 쓴다.
`solar-mini` 등 그 밖의 Solar 계열은 금지. AI Initiative 지원 범위 밖이라 호출이 실패한다.
(`solar-pro2`, `solar-pro3` 도 지원 범위지만 새로 만들 때는 `solar-pro4` 로 통일한다.)
- 문서 처리는 `document-parse` 만 쓴다.
`information-extract`, `document-classify`, 임베딩 계열
(`solar-embedding-2-query`, `solar-embedding-2-passage`)은 금지. 크레딧이 차감된다.
- 항목 추출과 문서 분류는 별도 제품을 쓰지 않는다.
Document Parse 로 텍스트를 만든 뒤 Solar 의 structured outputs 로 처리한다.
## 주소
- Solar : https://api.upstage.ai/v1 (OpenAI 호환, openai 라이브러리)
- Document Parse : https://api.upstage.ai/v1/document-digitization (multipart/form-data)
- 두 제품은 응답 구조가 다르다.
Solar 는 choices[0].message.content, Document Parse 는 content.html / elements[].
## 키
- 키를 코드에 직접 적지 않는다. 항상 os.environ["UPSTAGE_API_KEY"] 로 읽는다.
- 키 값을 로그, 주석, 커밋에 남기지 않는다.
## 한도
- Tier 0 기준 분당 100회 · 50,000 토큰. Document Parse 는 초당 1건.
- 반복 호출에는 호출 간 대기와 429 재시도를 반드시 넣는다.
## 결과 처리
- structured outputs 는 strict: true, additionalProperties: false,
모든 항목을 required 에 넣는다. 비어 있을 수 있는 항목은 ["string", "null"] 로 연다.
- 응답을 읽기 전에 finish_reason == "stop" 인지 확인한다.
- 금액과 날짜는 사람이 확인하는 단계를 남긴다.
규칙 파일에서 정작 효과가 큰 쪽은 하지 말라는 목록입니다. 에이전트가 틀리는 자리는 정해져 있어서, 그 자리만 닫아 두면 나머지는 알아서 합니다.
3단계. 조건을 적어 주세요
"영수증 처리하는 거 만들어 줘"라고 하시면 빈칸은 에이전트가 알아서 메웁니다. 그 추측이 그대로 나중에 고칠 자리가 되죠. 요청에 아래 다섯 가지를 넣어 주세요.
입력이 어디에 몇 건 있는지
"documents/ 폴더의 PDF 30건" 처럼 위치와 건수를 적어 주시면 반복 처리와 호출 간격까지 같이 짜 줍니다.
뽑을 항목과 각각의 형태
"상호명(문자) · 발행일(문자) · 총액(정수)" 처럼 이름과 형태를 지정해 주세요. 3.2의 형식 지정이 그대로 코드가 됩니다.
판단 기준을 숫자와 조건으로
"금액이 크면 보류"가 아니라 "총액이 100,000원을 넘으면 보류"라고 적어 주세요. 기준이 모호하면 결과도 모호해집니다.
값을 못 찾았을 때 어떻게 할지
"빈칸이 아니라 확인필요로 적는다"처럼 정해 주지 않으시면 에이전트가 빈 문자열이나 0을 채워 넣습니다.
결과를 어디에 어떤 형식으로 남길지
"result.csv 한 장에, 원문 파일명과 페이지 번호를 함께" 처럼 적어 주시면 나중에 되짚어 보실 수 있습니다.
4단계. 받은 코드에서 다섯 줄만 확인합니다
코드를 처음부터 끝까지 읽지 않으셔도 됩니다. 아래 다섯 가지는 찾아보기만 해도 확인됩니다. 사고가 나는 자리도 대개 여기입니다.
model=이solar-pro4인가요.solar-mini처럼 다른 이름이라면 바꿔 달라고 하시면 됩니다.base_url이https://api.upstage.ai/v1인가요.- Document Parse 주소가
/v1/document-digitization인가요. - API 키가 코드 안에 문자열로 적혀 있지 않은가요.
os.environ으로 읽어야 합니다. - 반복문 안에 대기(
time.sleep)와 429 처리가 들어 있나요.
에이전트는 실패 원인을 코드 문제로 추측하는 경향이 있습니다. 그래서 멀쩡한 코드를 계속 고치다 더 나빠지는 경우가 생깁니다.
이럴 때는 코드를 고치기 전에 Part 5의 확인 순서(결제수단 · 모델 이름 · 주소 · Bearer)를 먼저 짚어 보시고, 오류 메시지 전문을 그대로 붙여 주세요. 요약해서 전달하시면 원인이 담긴 마지막 줄이 사라집니다.
Upstage는 Claude Code를 Solar 모델로 실행하는 스크립트를 제공합니다(Claude Code 연동 문서).
기본 모델이 solar-pro4라 별도 설정 없이 그대로 쓰셔도 지원 범위 안입니다.
코딩 에이전트에게는 만들 것보다 쓰면 안 되는 것을 먼저 알려 주세요. 공식 문서 주소 하나와 규칙 파일 하나가 대부분의 실패를 막아 줍니다.
3.6지원 범위 밖 기능
AI Initiative가 무상 지원하는 것은 Solar Pro 2 · 3 · 4와 Document Parse입니다. 이 중 Solar Pro 2는 2026년 10월 중순에 지원이 종료되므로 solar-pro4로 잡아 두시는 편이 좋습니다. Document AI 계열이라고 해서 모두 무상인 것은 아닙니다. 아래 모델은 부르시는 즉시 보유하신 크레딧에서 차감됩니다.
| 모델 | 무엇을 하나요 | 공개 단가 | 이 프로그램 |
|---|---|---|---|
| Information Extract | 문서에서 항목을 바로 뽑아 줍니다 | $0.04/page (Enhanced $0.06) | 크레딧 사용 |
| Document Classify | 문서를 종류별로 나눕니다 | $0.004/page | 크레딧 사용 |
Embedsolar-embedding-2-query · solar-embedding-2-passage | 글의 의미를 숫자로 바꿔 비슷한 문서를 찾습니다 | $0.10/1M 토큰 | 크레딧 사용 |
| Solar Mini 등 | Pro 2 · 3 · 4를 뺀 그 밖의 Solar 계열입니다 | 모델마다 다릅니다 | 크레딧 사용 |
단가는 2026년 8월 기준 공개 요금표이며 부가세 별도입니다. 할인과 개편이 있으므로 결정을 내리시기 전에 원문을 한 번 확인해 주세요. 참고로 무상 지원되는 Document Parse는 $0.01/page(Enhanced $0.03)입니다.
화면에서 블록을 연결하는 Studio에서도 이 프로그램이 지원하는 것은 Parse 블록 하나입니다. Extract 블록은 페이지당 요금이 더 붙습니다(Parse $0.01 + Extract $0.03 = $0.04/page).
Classify와 Instruct 블록은 현재 Beta라 무료입니다. 다만 이것은 프로그램 혜택이 아니라 Upstage의 한시적 정책이고 가격은 추후 공지될 예정입니다. 운영 비용을 잡으실 때는 유료로 전환될 수 있다고 보고 계산해 주세요.
반대로 코드로 만드시면 Document Parse와 Solar 두 가지 모두 지원 범위입니다. 반복해서 많이 돌리실 계획이라면 코드로 만드시는 편이 유리합니다.
항목 추출은 3.2의 형식 지정으로 대신하실 수 있습니다. Document Parse로 글을 만든 뒤 Solar에게 형식과 함께 건네면 됩니다.
문서 분류도 마찬가지입니다. enum으로 분류 값을 고정해 두시면 정해진 종류 중 하나로 답합니다.
Document Parse와 Solar, 이 둘이면 웬만한 문서 업무는 다 됩니다.
이 제품은 주소가 다릅니다. Solar용으로 만들어 두신 클라이언트를 그대로 쓰시면 "모델이 잘못되었다"는 응답이 돌아오는데, 실제 원인은 주소입니다. 전용 주소 https://api.upstage.ai/v1/information-extraction으로 별도의 클라이언트를 만들어 주세요.
사용법은 Extract 문서에 있습니다. 다시 한 번 말씀드리면 이 호출은 크레딧에서 차감됩니다.
추출도 분류도 지원 범위 안의 Document Parse와 Solar로 해결됩니다. 별도 제품을 쓰실 일이 없습니다.
3.7[활동] 스키마 만들기
Part 1과 Part 2에서 정리하신 업무를 이번에는 코드로 옮겨 봅니다. 형식을 먼저 정하시면 나머지는 따라옵니다.
A. 빈칸 템플릿
# 내 업무용 형식 설계
## 입력
- 문서 형식:
- 한 번에 처리할 건수:
- Document Parse 가 필요한가요 (예 / 아니오):
## 뽑을 항목
| 항목 이름(영문) | 설명 | 형태 | 비어 있을 수 있나요 |
|---|---|---|---|
| | | | |
| | | | |
| | | | |
## 판단 값
- 고를 수 있는 값 (enum):
- 각 값의 기준을 숫자와 조건으로:
## 검증
- 사람이 반드시 확인할 항목:
- 값이 이상할 때 어떻게 처리하나요:
## 실패 처리
- 429 를 받으면:
- 값이 비어 오면:
B. 채운 예시 (보조금 신청서 확인)
## 뽑을 항목
| 항목 이름 | 설명 | 형태 | 비어 있을 수 있나요 |
| applicant_org | 신청 기관명 | 문자 | 아니오 |
| project_title | 사업명 | 문자 | 아니오 |
| requested_amount| 요청 금액 (원 단위 정수) | 정수 | 예 |
| attachments | 첨부 서류 이름 목록 | 배열 | 예 |
## 판단 값
- enum: ["접수가능", "보완요청", "확인필요"]
- 접수가능: 필수 항목이 모두 있고 요청 금액이 5천만 원 이하
- 보완요청: 필수 첨부 서류가 하나라도 빠짐
- 확인필요: 금액이나 기관명을 찾지 못함
## 검증
- 사람이 확인할 항목: requested_amount (금액은 항상 사람이 대조)
- 값이 이상할 때: 확인필요로 표시하고 담당자에게 전달
## 실패 처리
- 429 를 받으면: 3초 기다린 뒤 다시 시도, 세 번까지
- 값이 비어 오면: 확인필요로 표시하고 원문 페이지 번호를 함께 기록
C. 점검표
- 모든 항목을
required에 넣었습니다. - 비어 있을 수 있는 항목은
null을 허용하도록 적었습니다. - 판단 값을
enum으로 고정했습니다. - 값을 찾지 못한 경우를 위한 판단 값을 하나 두었습니다.
finish_reason을 확인한 뒤 결과를 읽도록 했습니다.- 429와 빈 값을 어떻게 처리할지 정했습니다.
공식 문서 바로가기
이 Part에서 다룬 내용의 원문입니다. 더 자세한 옵션이 필요할 때, 코딩 에이전트에게 근거를 건넬 때 쓰세요.
| 무엇을 찾으실 때 | 문서 | 이 Part의 위치 |
|---|---|---|
| Solar 첫 호출과 기본 사용법 | Generate · API Quickstart | 3.1 |
| 결과 형식 고정하기 | Structured outputs | 3.2 |
| 모델이 내 함수를 부르게 하기 | Tool calling | 3.3 |
| 문서를 텍스트로 바꾸기 | Parse · API Quickstart | 3.4 |
| Parse 응답 항목 읽는 법 | Understanding output | 3.4 |
| 100페이지가 넘는 문서 | Handling large documents | 3.4 |
| 코딩 에이전트용 API 문서 | For AI Coding Assistant | 3.5 |
| Claude Code를 Solar로 실행 | Claude Code 연동 | 3.5 |
| 모델별 한도 · 지원 형식 | Models catalog | 3.6 |
| 호출 한도(Tier) | Rate limits | 3.1 |
| 모델별 요금 | API 요금표 | 3.6 |
| 화면에서 만들기(코드 없이) | Upstage Studio | Part 2 |
공식 문서의 예제 코드는 model="solar-pro4"로 적혀 있습니다. Solar Pro 4가 지원 대상이라 고치실 부분 없이 그대로 사용하실 수 있습니다.
Part 3 핵심 정리
- Solar는 대화를 기억하지 않습니다. 맥락은 매번 함께 보내 주세요.
- 형식을 지정하면 항상 같은 모양으로 돌아옵니다. 다만 값의 정확성은 따로 검증해 주세요.
- 도구를 알려 주면 모델이 부르겠다고 알려 줄 뿐, 실행과 검사는 내 코드의 몫입니다.
- Document Parse는 Solar와 주소도 응답 구조도 다릅니다. choices가 아니라 content.html에서 꺼냅니다.
- 코딩 에이전트에게는 공식 문서 주소와 규칙 파일을 먼저 주세요. 지어낸 모델 이름과 잘못된 주소가 가장 흔한 실패입니다.
- Document Parse + Solar 두 가지만으로 추출과 분류를 모두 하실 수 있습니다.
- Information Extract · Document Classify는 무상 지원 대상이 아닙니다. 호출하면 크레딧이 차감됩니다.
다음으로
이제 소속과 목적에 맞는 사례를 보시면서 내 업무에 어떻게 적용할지 그려 보세요.