Structured output
Structured output을 사용하면 예측 가능한 형식으로 데이터를 반환할 수 있습니다. JSON 객체, Pydantic 또는 dataclass 형태의 구조화된 데이터를 얻을 수 있습니다.
create_agent는 structured output을 자동으로 처리합니다. structured output schema를 설정하면, 모델이 구조화된 데이터를 생성할 때, 에이전트 state의 'structured_response' 키에 structured output을 반환합니다.
def create_agent(
...
response_format: Union[
ToolStrategy[StructuredResponseT],
ProviderStrategy[StructuredResponseT],
type[StructuredResponseT],
]Response Format
에이전트가 구조화된 데이터를 반환하는 방식을 제어합니다.
ToolStrategy[StructuredResponseT]: 구조화된 출력을 위해 도구 호출을 사용합니다ProviderStrategy[StructuredResponseT]: 제공자 네이티브 구조화된 출력을 사용합니다type[StructuredResponseT]: 스키마 타입 - 모델 기능에 따라 최적의 전략을 자동으로 선택합니다None: 구조화된 출력 없음
스키마 타입이 직접 제공되면, LangChain이 자동으로 선택합니다:
- 네이티브 구조화된 출력을 지원하는 모델의 경우:
ProviderStrategy(예: OpenAI, Grok) - 그 외 모든 모델의 경우:
ToolStrategy
Structured response은 에이전트 state의 structured_response 키에 반환됩니다.
Provider strategy
일부 모델 제공자는 API를 통해 네이티브로 구조화된 출력을 지원합니다(현재 OpenAI와 Grok만 해당). 이는 사용 가능한 경우 가장 신뢰할 수 있는 방법입니다.
이 전략을 사용하려면 ProviderStrategy를 구성하세요:
class ProviderStrategy(Generic[SchemaT]):
schema: type[SchemaT]설정 옵션
schema(required): Structured output 형식을 정의하는 schema입니다. 지원하는 형식:- Pydantic models: 필드 검증이 포함된
BaseModel서브 클래스 - Dataclasses: 타입 어노테이션이 있는 파이썬 dataclass
- TypedDict: 타입이 지정된 딕셔너리 클래스
- JSON schema: JSON 스키마 사양이 포함된 딕셔너리
- Pydantic models: 필드 검증이 포함된
모델이 네이티브 구조화된 출력을 지원하는 경우, 스키마 타입을 create_agent.response_format에 직접 전달하면 LangChain이 자동으로 ProviderStrategy를 사용합니다.
Pydantic Model
from pydantic import BaseModel, Field
from langchain.agents import create_agent
class ContactInfo(BaseModel):
"""연락처 정보입니다."""
name: str = Field(description="사람의 이름")
email: str = Field(description="사람의 이메일 주소")
phone: str = Field(description="사람의 전화번호")
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ContactInfo, # ProviderStrategy 자동 선택
)inputs = {
"messages": [
{
"role": "user",
"content": "다음에서 연락처 정보를 추출하세요: John Doe, [email protected], (555) 123-4567",
}
]
}
result = agent.invoke(inputs)
result["structured_response"] # ContactInfo 객체로 출력됨ContactInfo(name='John Doe', email='[email protected]', phone='(555) 123-4567')
Dataclass
from dataclasses import dataclass
@dataclass
class ContactInfo:
"""연락처 정보입니다."""
name: str # 사람의 이름
email: str # 사람의 이메일 주소
phone: str # 사람의 전화번호
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ContactInfo, # ProviderStrategy 자동 선택
)result = agent.invoke(inputs)
result["structured_response"] # ContactInfo 객체로 출력됨ContactInfo(name='John Doe', email='[email protected]', phone='(555) 123-4567')
TypedDict
from typing_extensions import TypedDict
class ContactInfo(TypedDict):
"""연락처 정보입니다."""
name: str # 사람의 이름
email: str # 사람의 이메일 주소
phone: str # 사람의 전화번호
agent = create_agent(
model="openai:gpt-4.1-mini",
response_format=ContactInfo, # ProviderStrategy 자동 선택
)result = agent.invoke(inputs)
result["structured_response"] # 딕셔너리 객체로 출력됨{'name': 'John Doe', 'email': '[email protected]', 'phone': '(555) 123-4567'}
JSON Schema
contact_info_schema = {
"type": "object",
"description": "연락처 정보입니다.",
"properties": {
"name": {"type": "string", "description": "사람의 이름"},
"email": {"type": "string", "description": "사람의 이메일 주소"},
"phone": {"type": "string", "description": "사람의 전화번호"},
},
"required": ["name", "email", "phone"],
}
agent = create_agent(
model="openai:gpt-4.1-mini",
response_format=contact_info_schema, # ProviderStrategy 자동 선택
)
result = agent.invoke(inputs)
result["structured_response"] # 딕셔너리 객체로 출력됨{'name': 'John Doe', 'email': '[email protected]', 'phone': '(555) 123-4567'}
Provider 네이티브 구조화된 출력은 모델 제공자가 스키마를 강제하기 때문에 높은 신뢰성과 엄격한 검증을 제공합니다. 사용 가능한 경우 이를 사용하세요.
Provider가 선택한 모델에 대해 네이티브로 구조화된 출력을 지원하는 경우,
response_format=ProductReview대신response_format=ToolStrategy(ProductReview)로 작성하는 것과 기능적으로 동일합니다. 두 경우 모두 구조화된 출력이 지원되지 않으면 에이전트가 도구 호출 전략으로 폴백합니다.
Tool calling strategy
네이티브 구조화된 출력을 지원하지 않는 모델의 경우, LangChain은 도구 호출을 사용하여 동일한 결과를 달성합니다. 이는 도구 호출을 지원하는 모든 모델에서 작동하며, 대부분의 최신 모델이 이에 해당합니다.
이 전략을 사용하려면 ToolStrategy를 구성하세요:
class ToolStrategy(Generic[SchemaT]):
schema: type[SchemaT]
tool_message_content: str | None
handle_errors: Union[
bool,
str,
type[Exception],
tuple[type[Exception], ...],
Callable[[Exception], str],
]설정 옵션
-
schema(required): Structured output 형식을 정의하는 schema입니다. 지원 형식:- Pydantic 모델: 필드 검증이 포함된
BaseModel서브클래스 - Dataclasses: 타입 어노테이션이 있는 Python 데이터클래스
- TypedDict: 타입이 지정된 딕셔너리 클래스
- JSON Schema: JSON 스키마 사양이 포함된 딕셔너리
- Union types: 여러 스키마 옵션. 모델이 컨텍스트를 기반으로 가장 적절한 스키마를 선택합니다.
- Pydantic 모델: 필드 검증이 포함된
-
tool_message_content: Structured output이 생성될 때 반환되는 도구 메시지의 커스텀 콘텐츠입니다. 제공되지 않으면 structured response 데이터를 표시하는 기본 메시지가 사용됩니다. -
handle_errors: Structured output 검증 실패에 대한 오류 처리 전략입니다. 기본값은True입니다.True: 기본 오류 템플릿으로 모든 오류 처리str: 이 커스텀 메시지로 모든 오류 처리type[Exception]: 기본 메시지로 이 예외 타입만 처리tuple[type[Exception], ...]: 기본 메시지로 이러한 예외 타입만 처리Callable[[Exception], str]: 오류 메시지를 반환하는 커스텀 함수False: 재시도 없음, 예외 전파
Pydantic Model
from typing import Literal
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductReview(BaseModel):
"""제품 리뷰 분석입니다."""
rating: int | None = Field(description="제품 평점", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="리뷰의 감성")
key_points: list[str] = Field(
description="리뷰의 핵심 포인트. 소문자, 각 1-3 단어."
)
agent = create_agent(
model="openai:gpt-4.1-nano", response_format=ToolStrategy(ProductReview)
)inputs: dict = {
"messages": [
{
"role": "user",
"content": "이 리뷰를 분석해 보세요: '훌륭한 제품: 별점 5점 만점에 5점. 배송은 빠르지만 비쌉니다.'",
}
]
}
result = agent.invoke(inputs)
result["structured_response"]ProductReview(rating=5, sentiment='positive', key_points=['훌륭한 제품'])
Dataclass
from dataclasses import dataclass
@dataclass
class ProductReview:
"""제품 리뷰 분석입니다."""
rating: int | None # 제품 평점 (1-5)
sentiment: Literal["positive", "negative"] # 리뷰의 감성
key_points: list[str] # 리뷰의 핵심 포인트
agent = create_agent(
model="openai:gpt-4.1-nano", response_format=ToolStrategy(ProductReview)
)
result = agent.invoke(inputs)
result["structured_response"]ProductReview(rating=5, sentiment='positive', key_points=['훌륭한 제품', '배송은 빠름', '비쌈'])
from typing_extensions import TypedDict
class ProductReview(TypedDict):
"""제품 리뷰 분석입니다."""
rating: int | None # 제품 평점 (1-5)
sentiment: Literal["positive", "negative"] # 리뷰의 감성
key_points: list[str] # 리뷰의 핵심 포인트
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ToolStrategy(ProductReview),
)
result = agent.invoke(inputs)
result["structured_response"]{'rating': 5, 'sentiment': 'positive', 'key_points': ['훌륭한 제품', '빠른 배송']}
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
product_review_schema = {
"type": "object",
"description": "제품 리뷰 분석입니다.",
"properties": {
"rating": {
"type": ["integer", "null"],
"description": "제품 평점 (1-5)",
"minimum": 1,
"maximum": 5,
},
"sentiment": {
"type": "string",
"enum": ["positive", "negative"],
"description": "리뷰의 감성",
},
"key_points": {
"type": "array",
"items": {"type": "string"},
"description": "리뷰의 핵심 포인트",
},
},
"required": ["sentiment", "key_points"],
}
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ToolStrategy(product_review_schema),
)
result = agent.invoke(inputs)
result["structured_response"]{'rating': 5,
'sentiment': 'positive',
'key_points': ['훌륭한 제품', '빠른 배송', '비싸다']}
from typing import Literal, Union
class ProductReview(BaseModel):
"""제품 리뷰 분석입니다."""
rating: int | None = Field(description="제품 평점", ge=1, le=5)
sentiment: Literal["positive", "negative"] = Field(description="리뷰의 감성")
key_points: list[str] = Field(
description="리뷰의 핵심 포인트. 소문자, 각 1-3 단어."
)
class CustomerComplaint(BaseModel):
"""제품 또는 서비스에 대한 고객 불만입니다."""
issue_type: Literal["product", "service", "shipping", "billing"] = Field(
description="문제 유형"
)
severity: Literal["low", "medium", "high"] = Field(description="불만의 심각도")
description: str = Field(description="불만에 대한 간단한 설명")
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ToolStrategy(Union[ProductReview, CustomerComplaint]),
)
result = agent.invoke(inputs)
result["structured_response"]ProductReview(rating=5, sentiment='positive', key_points=['훌륭한 제품', '배송은 빠름', '비쌈'])
Custom tool message content
tool_message_content 매개변수를 사용하면 structured output이 생성될 때 대화 기록에 나타나는 메시지를 사용자 정의할 수 있습니다:
from typing import Literal
from libs.helpers import pretty_print
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class MeetingAction(BaseModel):
"""회의록에서 추출한 액션 아이템입니다."""
task: str = Field(description="완료해야 할 구체적인 작업")
assignee: str = Field(description="작업 담당자")
priority: Literal["low", "medium", "high"] = Field(description="우선순위 레벨")
inputs: dict = {
"messages": [
{
"role": "user",
"content": "회의 내용: 사라가 가능한 한 빨리 프로젝트 일정을 업데이트해야 합니다.",
}
]
}tool_message_content 없이는 최종 ToolMessage가 다음과 같이 표시됩니다:
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ToolStrategy(MeetingAction),
)
response = agent.invoke(inputs)
pretty_print(response)================================[1m Human Message [0m=================================
회의 내용: 사라가 가능한 한 빨리 프로젝트 일정을 업데이트해야 합니다.
==================================[1m Ai Message [0m==================================
Tool Calls:
MeetingAction (call_VTouxQPEoLjTfalpzwEUIXvv)
Call ID: call_VTouxQPEoLjTfalpzwEUIXvv
Args:
task: 프로젝트 일정 업데이트
assignee: 사라
priority: high
=================================[1m Tool Message [0m=================================
Name: MeetingAction
Returning structured response: task='프로젝트 일정 업데이트' assignee='사라' priority='high'
tool_message_content에 사용자 정의 메시지를 작성합니다.
agent = create_agent(
model="openai:gpt-4.1-nano",
response_format=ToolStrategy(
schema=MeetingAction,
tool_message_content="액션 아이템이 캡처되어 회의록에 추가되었습니다!",
),
)
response = agent.invoke(inputs)
pretty_print(response)================================[1m Human Message [0m=================================
회의 내용: 사라가 가능한 한 빨리 프로젝트 일정을 업데이트해야 합니다.
==================================[1m Ai Message [0m==================================
Tool Calls:
MeetingAction (call_wjHOuC1pMnUbJvEsmmst59AG)
Call ID: call_wjHOuC1pMnUbJvEsmmst59AG
Args:
task: 프로젝트 일정 업데이트
assignee: 사라
priority: high
=================================[1m Tool Message [0m=================================
Name: MeetingAction
액션 아이템이 캡처되어 회의록에 추가되었습니다!
Error handling
모델은 도구 호출을 통해 구조화된 출력(structured output)을 생성할 때 실수를 할 수 있습니다. LangChain은 이러한 오류를 자동으로 처리하기 위한 지능형 재시도 메커니즘을 제공합니다.
Multiple structured outputs error
모델이 여러 구조화된 출력 도구를 잘못 호출하면, 에이전트는 ToolMessage에 오류 피드백을 제공하고 모델에 재시도를 요청합니다:
from typing import Union
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ContactInfo(BaseModel):
name: str = Field(description="사람의 이름")
email: str = Field(description="이메일 주소")
class EventDetails(BaseModel):
event_name: str = Field(description="이벤트 이름")
date: str = Field(description="이벤트 날짜")
agent = create_agent(
model="openai:gpt-4.1-mini",
tools=[],
response_format=ToolStrategy(
Union[ContactInfo, EventDetails]
), # 기본값: handle_errors=True
)response = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "정보 발췌: John Doe ([email protected])가 3월 15일에 테크 컨퍼런스를 주최합니다.",
}
]
}
)
pretty_print(response)================================[1m Human Message [0m=================================
정보 발췌: John Doe ([email protected])가 3월 15일에 테크 컨퍼런스를 주최합니다.
==================================[1m Ai Message [0m==================================
Tool Calls:
ContactInfo (call_vHYS7oTWekLWYno6b6NRKJ5Z)
Call ID: call_vHYS7oTWekLWYno6b6NRKJ5Z
Args:
name: John Doe
email: [email protected]
EventDetails (call_7VExWfV1b1I6chgYQjVB0xBo)
Call ID: call_7VExWfV1b1I6chgYQjVB0xBo
Args:
event_name: 테크 컨퍼런스
date: 3월 15일
=================================[1m Tool Message [0m=================================
Name: ContactInfo
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
=================================[1m Tool Message [0m=================================
Name: EventDetails
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
==================================[1m Ai Message [0m==================================
Tool Calls:
ContactInfo (call_wY675785jrdk4MXubIhJKUF5)
Call ID: call_wY675785jrdk4MXubIhJKUF5
Args:
name: John Doe
email: [email protected]
EventDetails (call_MMWVLC1WXRMMkISSSKubJM7L)
Call ID: call_MMWVLC1WXRMMkISSSKubJM7L
Args:
event_name: 테크 컨퍼런스
date: 2023-03-15
=================================[1m Tool Message [0m=================================
Name: ContactInfo
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
=================================[1m Tool Message [0m=================================
Name: EventDetails
Error: Model incorrectly returned multiple structured responses (ContactInfo, EventDetails) when only one is expected.
Please fix your mistakes.
==================================[1m Ai Message [0m==================================
Tool Calls:
ContactInfo (call_zi7H1TIPmL13vuTQaATXjVg0)
Call ID: call_zi7H1TIPmL13vuTQaATXjVg0
Args:
name: John Doe
email: [email protected]
=================================[1m Tool Message [0m=================================
Name: ContactInfo
Returning structured response: name='John Doe' email='[email protected]'
Schema validation error
구조화된 출력이 예상 스키마와 일치하지 않으면 에이전트는 특정 오류 피드백을 제공합니다.
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
class ProductRating(BaseModel):
rating: int | None = Field(description="1-5 사이의 평점", ge=1, le=5)
comment: str = Field(description="리뷰 코멘트")
agent = create_agent(
model="openai:gpt-4.1-mini",
response_format=ToolStrategy(ProductRating), # 기본값: handle_errors=True
system_prompt="당신은 제품 리뷰를 분석하는 유용한 보조 도구입니다. 어떤 필드나 값도 임의로 생성하지 마십시오.",
)response = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "이걸 해석해 봐: 놀라운 제품, 10점 만점에 10점!",
}
]
}
)
pretty_print(response)================================[1m Human Message [0m=================================
이걸 해석해 봐: 놀라운 제품, 10점 만점에 10점!
==================================[1m Ai Message [0m==================================
Tool Calls:
ProductRating (call_zpARjuhdlPdPdtZCPgk7HhyJ)
Call ID: call_zpARjuhdlPdPdtZCPgk7HhyJ
Args:
rating: 5
comment: 놀라운 제품, 10점 만점에 10점!
=================================[1m Tool Message [0m=================================
Name: ProductRating
Returning structured response: rating=5 comment='놀라운 제품, 10점 만점에 10점!'
# TODO: rating 유효성 검사 실패 케이스(10점 입력 시 5점으로 제한되고 피드백 출력)를 재현하려 했으나,
# 코드가 정상 작동하여 재현이 어려움.Error handling strategies
handle_errors 매개변수를 사용하여 오류 처리 방법을 사용자 정의할 수 있습니다:
커스텀 오류 메시지:
ToolStrategy(
schema=ProductRating,
handle_errors="1-5 사이의 유효한 평점과 코멘트를 제공해주세요."
)handle_errors가 문자열인 경우, 에이전트는 항상 고정된 도구 메시지로 모델에 재시도를 요청합니다:
================================= Tool Message =================================
Name: ProductRating
1-5 사이의 유효한 평점과 코멘트를 제공해주세요.
특정 예외만 처리:
ToolStrategy(
schema=ProductRating,
handle_errors=ValueError # ValueError에서만 재시도, 나머지는 raise
)handle_errors가 예외 타입인 경우, 발생한 예외가 지정된 타입인 경우에만 에이전트가 재시도합니다(기본 오류 메시지 사용). 다른 모든 경우에는 예외가 발생합니다.
여러 예외 타입 처리:
ToolStrategy(
schema=ProductRating,
handle_errors=(ValueError, TypeError) # ValueError와 TypeError에서 재시도
)handle_errors가 예외 튜플인 경우, 발생한 예외가 지정된 타입 중 하나인 경우에만 에이전트가 재시도합니다(기본 오류 메시지 사용). 다른 모든 경우에는 예외가 발생합니다.
커스텀 오류 핸들러 함수:
def custom_error_handler(error: Exception) -> str:
if isinstance(error, StructuredOutputValidationError):
return "형식에 문제가 있습니다. 다시 시도해주세요."
elif isinstance(error, MultipleStructuredOutputsError):
return "여러 구조화된 출력이 반환되었습니다. 가장 관련성 높은 것을 선택하세요."
else:
return f"오류: {str(error)}"
ToolStrategy(
schema=ToolStrategy(Union[ContactInfo, EventDetails]),
handle_errors=custom_error_handler
)StructuredOutputValidationError 발생 시:
================================= Tool Message =================================
Name: ToolStrategy
형식에 문제가 있습니다. 다시 시도해주세요.
MultipleStructuredOutputsError 발생 시:
================================= Tool Message =================================
Name: ToolStrategy
여러 구조화된 출력이 반환되었습니다. 가장 관련성 높은 것을 선택하세요.
기타 오류 발생 시:
================================= Tool Message =================================
Name: ToolStrategy
오류: <error message>
오류 처리 없음:
response_format = ToolStrategy(
schema=ProductRating,
handle_errors=False # 모든 오류 발생
)