모델
모델 (Models · LangChain Python)
LLM은 사람처럼 텍스트를 해석하고 생성할 수 있는 강력한 AI 도구예요. 콘텐츠를 쓰고, 번역하고, 요약하고, 질문에 답하는 데 두루 쓸 수 있어요. 각 작업마다 별도의 전문 학습이 필요하지 않다는 게 장점이죠.
텍스트 생성 외에도 많은 모델이 다음을 지원해요.
- 도구 호출 — 데이터베이스 쿼리나 API 호출 같은 외부 도구를 호출하고, 그 결과를 응답에 활용한다
- 구조화된 출력 — 모델 응답을 정해진 형식을 따르도록 제약한다
- 멀티모달 — 텍스트 외에 이미지, 오디오, 비디오 같은 데이터를 처리하고 반환한다
- 추론 — 결론에 도달하기 위해 다단계 추론을 수행한다
모델은 에이전트의 추론 엔진이에요. 어떤 도구를 호출할지, 결과를 어떻게 해석할지, 언제 최종 답을 낼지 같은 의사결정 과정을 이끌어 주죠.
선택한 모델의 품질과 성능은 에이전트의 기본 신뢰성과 성능에 직접 영향을 줘요. 모델마다 잘하는 작업이 달라요 — 어떤 모델은 복잡한 지침을 잘 따르고, 어떤 모델은 구조화된 추론에 강하며, 어떤 모델은 더 큰 컨텍스트 윈도우를 지원해 더 많은 정보를 처리해요.
LangChain의 표준 모델 인터페이스는 다양한 프로바이더 통합에 접근할 수 있게 해 줘요. 덕분에 모델을 실험하고 전환하며 자신의 사용 사례에 가장 잘 맞는 걸 찾기 쉬워요.
프로바이더별 통합 정보와 기능은 해당 프로바이더의 채팅 모델 페이지를 보세요.
출처: 공식문서
💡 LangSmith는 모델 호출 하나하나를 추적하므로 프로바이더를 비교하고 도구 라우팅을 살펴보고 실패를 디버깅할 수 있어요. tracing quickstart를 따라 설정해 보세요. 추적을 모니터링하고 문제를 감지해 해결책을 제안하는 LangSmith Engine도 함께 구성하는 걸 권장해요.
기본 사용법
모델은 두 가지 방식으로 쓸 수 있어요.
- 에이전트와 함께 — 에이전트를 만들 때 모델을 동적으로 지정할 수 있어요.
- 단독(standalone)으로 — 에이전트 프레임워크 없이 텍스트 생성, 분류, 추출 같은 작업에 모델을 직접 호출할 수 있어요.
두 상황 모두 같은 모델 인터페이스가 동작해요. 그래서 간단하게 시작해 필요에 따라 더 복잡한 에이전트 기반 워크플로로 확장할 수 있는 유연함을 얻어요.
모델 초기화
LangChain에서 단독 모델을 시작하는 가장 쉬운 방법은 init_chat_model로 원하는 채팅 모델 프로바이더의 모델을 초기화하는 거예요 (아래 예시):
-
OpenAI — OpenAI 채팅 모델 통합 문서 참고
pip install -U "langchain[openai]"uv add "langchain[openai]"import os from langchain.chat_models import init_chat_model os.environ["OPENAI_API_KEY"] = "sk-..." model = init_chat_model("gpt-5.5")# Model Class import os from langchain_openai import ChatOpenAI os.environ["OPENAI_API_KEY"] = "sk-..." model = ChatOpenAI(model="gpt-5.5") -
Anthropic — Anthropic 채팅 모델 통합 문서 참고
pip install -U "langchain[anthropic]"uv add "langchain[anthropic]"import os from langchain.chat_models import init_chat_model os.environ["ANTHROPIC_API_KEY"] = "sk-..." model = init_chat_model("claude-sonnet-4-6")# Model Class import os from langchain_anthropic import ChatAnthropic os.environ["ANTHROPIC_API_KEY"] = "sk-..." model = ChatAnthropic(model="claude-sonnet-4-6") -
Azure — Azure 채팅 모델 통합 문서 참고
pip install -U "langchain[openai]"uv add "langchain[openai]"import os from langchain.chat_models import init_chat_model os.environ["AZURE_OPENAI_API_KEY"] = "..." os.environ["AZURE_OPENAI_ENDPOINT"] = "..." os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview" model = init_chat_model( "azure_openai:gpt-5.5", azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"], )# Model Class import os from langchain_openai import AzureChatOpenAI os.environ["AZURE_OPENAI_API_KEY"] = "..." os.environ["AZURE_OPENAI_ENDPOINT"] = "..." os.environ["OPENAI_API_VERSION"] = "2025-03-01-preview" model = AzureChatOpenAI( model="gpt-5.5", azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"] ) -
Google Gemini — Google GenAI 채팅 모델 통합 문서 참고
pip install -U "langchain[google-genai]"uv add "langchain[google-genai]"import os from langchain.chat_models import init_chat_model os.environ["GOOGLE_API_KEY"] = "..." model = init_chat_model("google_genai:gemini-3.7-flash")# Model Class import os from langchain_google_genai import ChatGoogleGenerativeAI os.environ["GOOGLE_API_KEY"] = "..." model = ChatGoogleGenerativeAI(model="gemini-3.7-flash") -
AWS Bedrock — AWS Bedrock 채팅 모델 통합 문서 참고
pip install -U "langchain[aws]"uv add "langchain[aws]"from langchain.chat_models import init_chat_model # Follow the steps here to configure your credentials: # https://docs.aws.amazon.com/bedrock/latest/userguide/getting-started.html model = init_chat_model( "us.anthropic.claude-sonnet-4-6", model_provider="bedrock_converse", )# Model Class from langchain_aws import ChatBedrock model = ChatBedrock(model="us.anthropic.claude-sonnet-4-6") -
HuggingFace — HuggingFace 채팅 모델 통합 문서 참고
pip install -U "langchain[huggingface]"uv add "langchain[huggingface]"import os from langchain.chat_models import init_chat_model os.environ["HUGGINGFACEHUB_API_TOKEN"] = "hf_..." model = init_chat_model( "microsoft/Phi-3-mini-4k-instruct", model_provider="huggingface", temperature=0.7, max_tokens=1024, )# Model Class import os from langchain_huggingface import ChatHuggingFace, HuggingFaceEndpoint os.environ["HUGGINGFACEHUB_API_TOKEN"] = "hf_..." llm = HuggingFaceEndpoint( repo_id="microsoft/Phi-3-mini-4k-instruct", temperature=0.7, max_length=1024, ) model = ChatHuggingFace(llm=llm) -
OpenRouter — OpenRouter 채팅 모델 통합 문서 참고
pip install -U "langchain-openrouter"uv add "langchain-openrouter"import os from langchain.chat_models import init_chat_model os.environ["OPENROUTER_API_KEY"] = "sk-..." model = init_chat_model( "auto", model_provider="openrouter", )# Model Class import os from langchain_openrouter import ChatOpenRouter os.environ["OPENROUTER_API_KEY"] = "sk-..." model = ChatOpenRouter(model="auto")
response = model.invoke("Why do parrots talk?")
모델 파라미터를 넘기는 방법을 포함해 더 자세한 내용은 init_chat_model을 보세요.
지원되는 프로바이더와 모델
LangChain은 전용 통합 패키지를 통해 주요 모델 프로바이더를 모두 지원해요. 각 프로바이더 패키지는 같은 표준 인터페이스를 구현하므로, 애플리케이션 로직을 다시 쓰지 않고도 프로바이더를 바꿀 수 있어요. 새 모델 이름은 즉시 동작해요 — LangChain 업데이트가 필요 없어요. 프로바이더 패키지가 모델 이름을 그대로 프로바이더 API에 넘기기 때문이에요.
지원 프로바이더 전체 목록을 둘러보거나, 프로바이더와 모델에서 프로바이더, 패키지, 모델 이름이 LangChain에서 어떻게 함께 동작하는지 개념적으로 살펴보세요.
핵심 메서드
- Invoke — 모델이 메시지를 입력받고 완전한 응답을 생성한 뒤 메시지를 출력해요.
- Stream — 모델을 호출하되, 출력이 생성되는 대로 실시간으로 스트리밍해요.
- Batch — 여러 요청을 배치로 모델에 보내 더 효율적으로 처리해요.
ℹ️ 채팅 모델 외에도 LangChain은 임베딩 모델, 벡터 스토어 같은 인접 기술도 지원해요. 자세한 내용은 통합 페이지를 보세요.
파라미터
채팅 모델은 동작을 설정하는 파라미터를 받아요. 지원되는 파라미터 전체 집합은 모델과 프로바이더마다 다르지만, 표준적인 것들은 이렇습니다.
model(string, 필수) — 프로바이더에서 사용할 특정 모델의 이름이나 식별자.{model_provider}:{model}형식으로 한 인자에 모델과 프로바이더를 함께 지정할 수도 있어요 (예:'openai:o1').api_key(string) — 모델 프로바이더 인증에 필요한 키. 보통 모델 접근에 가입할 때 발급돼요. 주로 <환경변수>를 설정해 접근해요.temperature(number) — 모델 출력의 무작위성을 조절해요. 숫자가 높을수록 응답이 창의적이고, 낮을수록 결정적이에요.max_tokens(number) — 응답의 총 <토큰> 수를 제한해 출력 길이를 실질적으로 통제해요.timeout(number) — 모델 응답을 기다리는 최대 시간(초). 이 시간이 지나면 요청을 취소해요.max_retries(number, 기본 6) — 네트워크 타임아웃이나 레이트 제한 같은 문제로 요청이 실패했을 때 재전송하는 최대 횟수. 재시도는 지터가 있는 지수 백오프를 사용해요. 네트워크 오류, 레이트 제한(429), 서버 오류(5xx)는 자동으로 재시도돼요. 401(인증 실패)이나 404 같은 클라이언트 오류는 재시도하지 않아요. 불안정한 네트워크에서 오래 실행되는 에이전트 작업은 10–15로 늘리는 걸 고려하세요.
init_chat_model을 쓰면 이 파라미터들을 인라인 **kwargs로 넘겨요.
model = init_chat_model(
"claude-sonnet-4-6",
# Kwargs passed to the model:
temperature=0.7,
timeout=30,
max_tokens=1000,
max_retries=6, # Default; increase for unreliable networks
)
연결 복원력 (Connection resilience)
LangChain 채팅 모델은 실패한 API 요청을 지수 백오프로 자동 재시도해요. 기본적으로 네트워크 오류, 레이트 제한(429), 서버 오류(5xx)에 대해 최대 6회 재시도해요. 401(인증 실패)이나 404 같은 클라이언트 오류는 재시도하지 않아요.
max_retries와 timeout은 모델을 만들 때 조정하고, 그 인스턴스를 create_agent, create_deep_agent에 넘기거나 단독으로 호출하면 돼요.
from langchain.chat_models import init_chat_model
model = init_chat_model(
"google_genai:gemini-3.6-flash",
max_retries=10, # Increase for unreliable networks (default: 6)
timeout=120, # Seconds; increase for slow connections
)
💡 불안정한 네트워크에서 오래 실행되는 에이전트 그래프라면 더 높은
max_retries(예: 10–15)와 체크포인터를 써서 실패가 나도 진행 상태가 보존되도록 하는 걸 고려해 보세요.
ℹ️ 각 채팅 모델 통합에는 프로바이더별 기능을 제어하는 추가 파라미터가 있을 수 있어요. 예를 들어
ChatOpenAI는 OpenAI Responses API를 쓸지 Completions API를 쓸지 정하는use_responses_api가 있어요. 특정 채팅 모델이 지원하는 모든 파라미터를 보려면 채팅 모델 통합 페이지를 보세요.
호출 (Invocation)
채팅 모델은 출력을 생성하려면 반드시 호출해야 해요. 호출 메서드는 크게 세 가지이며, 각각 다른 사용 사례에 맞아요.
Invoke
모델을 호출하는 가장 직관적인 방법은 단일 메시지나 메시지 리스트로 invoke()를 쓰는 거예요.
# Single message
response = model.invoke("Why do parrots have colorful feathers?")
print(response)
메시지 리스트를 채팅 모델에 넘기면 대화 기록을 나타낼 수 있어요. 각 메시지에는 모델이 대화에서 누가 보냈는지 나타내는 역할(role)이 있어요.
역할, 유형, 콘텐츠에 대한 자세한 내용은 메시지 가이드를 보세요.
# Dictionary format
conversation = [
{"role": "system", "content": "You are a helpful assistant that translates English to French."},
{"role": "user", "content": "Translate: I love programming."},
{"role": "assistant", "content": "J'adore la programmation."},
{"role": "user", "content": "Translate: I love building applications."}
]
response = model.invoke(conversation)
print(response) # AIMessage("J'adore créer des applications.")
# Message objects
from langchain.messages import HumanMessage, AIMessage, SystemMessage
conversation = [
SystemMessage("You are a helpful assistant that translates English to French."),
HumanMessage("Translate: I love programming."),
AIMessage("J'adore la programmation."),
HumanMessage("Translate: I love building applications.")
]
response = model.invoke(conversation)
print(response) # AIMessage("J'adore créer des applications.")
ℹ️ 호출의 반환 타입이 문자열이라면, LLM이 아니라 채팅 모델을 쓰고 있는지 확인하세요. 레거시 텍스트 완성(text-completion) LLM은 문자열을 직접 반환해요. LangChain 채팅 모델은 "Chat" 접두사가 붙어 있어요 (예:
ChatOpenAIor /oss/integrations/chat/openai).
Stream
대부분의 모델은 출력을 생성하면서 스트리밍할 수 있어요. 출력을 점진적으로 보여 주는 스트리밍은 특히 긴 응답에서 사용자 경험을 크게 개선해요.
stream()을 호출하면 출력 청크가 만들어지는 대로 순차적으로 접근할 수 있는 <이터레이터>를 반환해요. 루프로 각 청크를 실시간 처리할 수 있어요.
# Basic text streaming
for chunk in model.stream("Why do parrots have colorful feathers?"):
print(chunk.text, end="|", flush=True)
# Stream tool calls, reasoning, and other content
for chunk in model.stream("What color is the sky?"):
for block in chunk.content_blocks:
if block["type"] == "reasoning" and (reasoning := block.get("reasoning")):
print(f"Reasoning: {reasoning}")
elif block["type"] == "tool_call_chunk":
print(f"Tool call chunk: {block}")
elif block["type"] == "text":
print(block["text"])
else:
...
모델이 전체 응답을 생성한 뒤 단일 AIMessage를 반환하는 invoke()와 달리, stream()은 출력 텍스트의 일부씩 담은 여러 AIMessageChunk 객체를 반환해요. 중요한 점은, 스트림의 각 청크는 합산(summation)을 통해 완전한 메시지로 모을 수 있도록 설계됐다는 거예요.
# Construct an AIMessage
full = None # None | AIMessageChunk
for chunk in model.stream("What color is the sky?"):
full = chunk if full is None else full + chunk
print(full.text)
# The
# The sky
# The sky is
# The sky is typically
# The sky is typically blue
# ...
print(full.content_blocks)
# [{"type": "text", "text": "The sky is typically blue..."}]
결과 메시지는 invoke()로 생성된 메시지와 똑같이 취급할 수 있어요 — 예를 들어 메시지 기록에 모아서 대화 문맥으로 모델에 다시 넘겨줄 수 있죠.
⚠️ 스트리밍은 프로그램의 모든 단계가 청크 스트림을 처리하는 방법을 알 때만 동작해요. 예를 들어 전체 출력을 메모리에 저장한 뒤에야 처리할 수 있는 애플리케이션은 스트리밍이 불가능한 경우예요.
스트리밍 이벤트 — LangChain 채팅 모델은 astream_events()로 의미적(semantic) 이벤트도 스트리밍할 수 있어요. 이벤트 유형과 기타 메타데이터를 기준으로 필터링하기 쉬워지고, 그 사이 전체 메시지를 백그라운드에서 모아 줘요.
async for event in model.astream_events("Hello"):
if event["event"] == "on_chat_model_start":
print(f"Input: {event['data']['input']}")
elif event["event"] == "on_chat_model_stream":
print(f"Token: {event['data']['chunk'].text}")
elif event["event"] == "on_chat_model_end":
print(f"Full message: {event['data']['output'].text}")
else:
pass
Input: Hello
Token: Hi
Token: there
Token: !
Token: How
Token: can
Token: I
...
Full message: Hi there! How can I help today?
이벤트 유형과 기타 자세한 내용은 astream_events() 레퍼런스를 보세요.
"자동 스트리밍" 채팅 모델 — LangChain은 어떤 경우에는 명시적으로 스트리밍 메서드를 호출하지 않아도 스트리밍 모드를 자동으로 켜서 채팅 모델의 스트리밍을 단순화해요. 스트리밍이 아닌 invoke 메서드를 쓰면서도 앱 전체(채팅 모델의 중간 결과 포함)를 스트리밍하고 싶을 때 특히 유용해요.
예를 들어 LangGraph 에이전트에서 노드 안에서 model.invoke()를 호출하지만, 스트리밍 모드로 실행 중이면 LangChain이 자동으로 스트리밍에 위임해요. invoke()로 채팅 모델을 호출할 때 LangChain이 전체 애플리케이션을 스트리밍하려는 것임을 감지하면 내부 스트리밍 모드로 자동 전환해요. invoke를 쓰던 코드 입장에서 결과는 같아요. 다만 채팅 모델이 스트리밍되는 동안 LangChain이 콜백 시스템의 on_llm_new_token 이벤트를 호출해 준다는 게 다르죠. 이 콜백 이벤트 덕분에 LangGraph의 stream()과 astream_events()가 채팅 모델 출력을 실시간으로 드러낼 수 있어요.
Batch
독립적인 요청들을 모아 배치로 처리하면 병렬 처리 덕분에 성능이 크게 좋아지고 비용도 줄어요.
# Batch
responses = model.batch([
"Why do parrots have colorful feathers?",
"How do airplanes fly?",
"What is quantum computing?"
])
for response in responses:
print(response)
📝 이 섹션은 모델 호출을 클라이언트 측에서 병렬화하는 채팅 모델 메서드
batch()를 설명해요. OpenAI나 Anthropic 같은 추론 프로바이더가 지원하는 배치 API와는 다른 것이에요.
기본적으로 batch()는 전체 배치의 최종 출력만 반환해요. 각 개별 입력에 대한 출력이 생성되는 대로 받고 싶다면, batch_as_completed()로 결과를 스트리밍할 수 있어요.
# Yield batch responses upon completion
for response in model.batch_as_completed([
"Why do parrots have colorful feathers?",
"How do airplanes fly?",
"What is quantum computing?"
]):
print(response)
📝
batch_as_completed()를 쓰면 결과가 순서에 어긋나 도착할 수 있어요. 각 결과는 입력 인덱스를 포함하므로 필요하면 원래 순서를 재구성할 수 있어요.
💡
batch()나batch_as_completed()로 많은 입력을 처리할 때는 동시 호출 최대 수를 제어하고 싶을 거예요.RunnableConfig딕셔너리에서max_concurrency속성을 설정하면 돼요.# Batch with max concurrency model.batch( list_of_inputs, config={ 'max_concurrency': 5, # Limit to 5 parallel calls } )
지원되는 속성 전체 목록은 RunnableConfig 레퍼런스를 보세요. 배칭에 대한 자세한 내용은 레퍼런스를 참고하세요.
도구 호출 (Tool calling)
모델은 데이터베이스에서 데이터를 가져오거나, 웹을 검색하거나, 코드를 실행하는 등의 작업을 수행하는 도구를 호출하도록 요청할 수 있어요. 도구는 다음 두 가지의 짝이에요.
- 도구 이름, 설명, 인자 정의(종종 JSON 스키마)를 포함한 스키마
- 실행할 함수 또는 <코루틴>
📝 "함수 호출(function calling)"이라는 말을 들을 수도 있는데, 우리는 "도구 호출(tool calling)"과 같은 의미로 씁니다.
사용자와 모델 사이의 기본 도구 호출 흐름은 이렇게 생겼어요.
- 사용자 → 모델: "서울과 뉴욕 날씨 어때?"
- 모델 → 모델: 요청 분석 & 필요한 도구 결정
- (병렬 도구 호출) 모델 → 도구: get_weather("San Francisco"), get_weather("New York")
- (도구 실행) 도구 → 모델: SF 날씨 데이터, NYC 날씨 데이터
- 모델 → 모델: 결과 처리 & 응답 생성
- 모델 → 사용자: "SF: 72°F sunny, NYC: 68°F cloudy"
정의한 도구를 모델이 사용할 수 있게 하려면 bind_tools로 바인딩해야 해요. 이후 호출에서 모델은 필요에 따라 바인딩된 도구 중 아무거나 호출할 수 있어요.
일부 모델 프로바이더는 모델이나 호출 파라미터로 켤 수 있는 <내장 도구>(예: ChatOpenAI, ChatAnthropic)를 제공해요. 자세한 내용은 해당 프로바이더 레퍼런스를 확인하세요.
💡 도구를 만드는 방법과 다른 옵션은 도구 가이드를 보세요.
# Binding user tools
from langchain.tools import tool
@tool
def get_weather(location: str) -> str:
"""Get the weather at a location."""
return f"It's sunny in {location}."
model_with_tools = model.bind_tools([get_weather]) # [!code highlight]
response = model_with_tools.invoke("What's the weather like in Boston?")
for tool_call in response.tool_calls:
# View tool calls made by the model
print(f"Tool: {tool_call['name']}")
print(f"Args: {tool_call['args']}")
사용자 정의 도구를 바인딩하면 모델 응답에는 도구를 실행하라는 요청이 담겨요. 모델을 에이전트와 분리해서 쓸 때는, 요청된 도구를 직접 실행하고 결과를 후속 추론에 쓰도록 모델에 다시 돌려줘야 해요. 에이전트를 쓰면 에이전트 루프가 도구 실행 루프를 대신 처리해 줘요.
아래에 도구 호출을 쓰는 일반적인 방법 몇 가지를 보여 드릴게요.
도구 실행 루프 (Tool execution loop) — 모델이 도구 호출을 반환하면, 도구를 실행하고 결과를 모델에 다시 전달해야 해요. 이렇게 하면 모델이 도구 결과를 써서 최종 응답을 만들 수 있는 대화 루프가 생기죠. LangChain에는 이런 조율을 대신 처리해 주는 에이전트 추상화가 있어요.
# Tool execution loop
# Bind (potentially multiple) tools to the model
model_with_tools = model.bind_tools([get_weather])
# Step 1: Model generates tool calls
messages = [{"role": "user", "content": "What's the weather in Boston?"}]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
# Step 2: Execute tools and collect results
for tool_call in ai_msg.tool_calls:
# Execute the tool with the generated arguments
tool_result = get_weather.invoke(tool_call)
messages.append(tool_result)
# Step 3: Pass results back to model for final response
final_response = model_with_tools.invoke(messages)
print(final_response.text)
# "The current weather in Boston is 72°F and sunny."
도구가 반환하는 각 ToolMessage는 원래 도구 호출과 일치하는 tool_call_id를 포함하므로, 모델이 결과를 요청과 연결하기 쉬워요.
도구 호출 강제하기 (Forcing tool calls) — 기본적으로 모델은 사용자 입력에 따라 바인딩된 도구 중 무엇을 쓸지 자유롭게 골라요. 하지만 특정 도구나 주어진 목록의 아무 도구를 쓰도록 강제하고 싶을 때가 있어요.
# Force use of any tool
model_with_tools = model.bind_tools([tool_1], tool_choice="any")
# Force use of specific tools
model_with_tools = model.bind_tools([tool_1], tool_choice="tool_1")
병렬 도구 호출 (Parallel tool calls) — 많은 모델이 적절할 때 여러 도구를 병렬로 호출하는 것을 지원해요. 덕분에 모델이 서로 다른 소스에서 동시에 정보를 모을 수 있어요.
# Parallel tool calls
model_with_tools = model.bind_tools([get_weather])
response = model_with_tools.invoke(
"What's the weather in Boston and Tokyo?"
)
# The model may generate multiple tool calls
print(response.tool_calls)
# [
# {'name': 'get_weather', 'args': {'location': 'Boston'}, 'id': 'call_1'},
# {'name': 'get_weather', 'args': {'location': 'Tokyo'}, 'id': 'call_2'},
# ]
# Execute all tools (can be done in parallel with async)
results = []
for tool_call in response.tool_calls:
if tool_call['name'] == 'get_weather':
result = get_weather.invoke(tool_call)
...
results.append(result)
모델은 요청된 작업의 독립성에 따라 병렬 실행이 적절한 때를 지능적으로 판단해요.
💡 도구 호출을 지원하는 대부분의 모델은 병렬 도구 호출을 기본으로 켜요. 일부(OpenAI, Anthropic 포함)는 이 기능을 끌 수 있게 해 줘요. 이렇게 하려면
parallel_tool_calls=False를 설정하세요.model.bind_tools([get_weather], parallel_tool_calls=False)
스트리밍 도구 호출 (Streaming tool calls) — 응답을 스트리밍할 때 도구 호출은 ToolCallChunk를 통해 점진적으로 만들어져요. 완전한 응답을 기다리는 대신 도구 호출이 생성되는 과정을 볼 수 있게 해 주죠.
# Streaming tool calls
for chunk in model_with_tools.stream(
"What's the weather in Boston and Tokyo?"
):
# Tool call chunks arrive progressively
for tool_chunk in chunk.tool_call_chunks:
if name := tool_chunk.get("name"):
print(f"Tool: {name}")
if id_ := tool_chunk.get("id"):
print(f"ID: {id_}")
if args := tool_chunk.get("args"):
print(f"Args: {args}")
# Output:
# Tool: get_weather
# ID: call_SvMlU1TVIZugrFLckFE2ceRE
# Args: {"lo
# Args: catio
# Args: n": "B
# Args: osto
# Args: n"}
# Tool: get_weather
# ID: call_QMZdy6qInx13oWKE7KhuhOLR
# Args: {"lo
# Args: catio
# Args: n": "T
# Args: okyo
# Args: "}
청크를 누적해 완전한 도구 호출을 만들 수도 있어요.
# Accumulate tool calls
gathered = None
for chunk in model_with_tools.stream("What's the weather in Boston?"):
gathered = chunk if gathered is None else gathered + chunk
print(gathered.tool_calls)
구조화된 출력 (Structured output)
모델에 주어진 스키마에 맞는 형식으로 응답을 제공하도록 요청할 수 있어요. 출력을 쉽게 파싱하고 후속 처리에 쓰는 데 유용하죠. LangChain은 구조화된 출력을 강제하기 위한 여러 스키마 타입과 방법을 지원해요.
💡 구조화된 출력을 배우려면 구조화된 출력을 보세요.
Pydantic — Pydantic 모델은 필드 검증, 설명, 중첩 구조를 갖춘 가장 풍부한 기능을 제공해요.
from pydantic import BaseModel, Field
class Movie(BaseModel):
"""A movie with details."""
title: str = Field(description="The title of the movie")
year: int = Field(description="The year the movie was released")
director: str = Field(description="The director of the movie")
rating: float = Field(description="The movie's rating out of 10")
model_with_structure = model.with_structured_output(Movie)
response = model_with_structure.invoke("Provide details about the movie Inception")
print(response) # Movie(title="Inception", year=2010, director="Christopher Nolan", rating=8.8)
TypedDict — Python의 TypedDict는 Pydantic 모델보다 간단한 대안이에요. 런타임 검증이 필요 없을 때 이상적이죠.
from typing_extensions import TypedDict, Annotated
class MovieDict(TypedDict):
"""A movie with details."""
title: Annotated[str, ..., "The title of the movie"]
year: Annotated[int, ..., "The year the movie was released"]
director: Annotated[str, ..., "The director of the movie"]
rating: Annotated[float, ..., "The movie's rating out of 10"]
model_with_structure = model.with_structured_output(MovieDict)
response = model_with_structure.invoke("Provide details about the movie Inception")
print(response) # {'title': 'Inception', 'year': 2010, 'director': 'Christopher Nolan', 'rating': 8.8}
JSON Schema — 최대한의 제어와 상호운용성을 위해 JSON Schema를 제공할 수 있어요.
import json
json_schema = {
"title": "Movie",
"description": "A movie with details",
"type": "object",
"properties": {
"title": {
"type": "string",
"description": "The title of the movie"
},
"year": {
"type": "integer",
"description": "The year the movie was released"
},
"director": {
"type": "string",
"description": "The director of the movie"
},
"rating": {
"type": "number",
"description": "The movie's rating out of 10"
}
},
"required": ["title", "year", "director", "rating"]
}
model_with_structure = model.with_structured_output(
json_schema,
method="json_schema",
)
response = model_with_structure.invoke("Provide details about the movie Inception")
print(response) # {'title': 'Inception', 'year': 2010, ...}
📝 구조화된 출력의 핵심 고려 사항
- Method 파라미터: 일부 프로바이더는 구조화된 출력에 다른 메서드를 지원해요.
'json_schema': 프로바이더가 제공하는 전용 구조화된 출력 기능을 사용한다.'function_calling': 주어진 스키마를 따르는 도구 호출을 강제해 구조화된 출력을 유도한다.'json_mode': 일부 프로바이더가 제공하는'json_schema'의 전신. 유효한 JSON을 생성하지만 스키마를 프롬프트에 기술해야 한다.- Include raw:
include_raw=True로 설정하면 파싱된 출력과 원시 AI 메시지를 모두 얻을 수 있어요.- 검증: Pydantic 모델은 자동 검증을 제공해요.
TypedDict와 JSON Schema는 수동 검증이 필요해요.지원되는 메서드와 설정 옵션은 프로바이더 통합 페이지를 보세요.
예시: 파싱된 구조와 함께 메시지 출력 — 파싱된 표현과 함께 원시 AIMessage 객체를 반환하면 토큰 수 같은 응답 메타데이터에 접근할 수 있어 유용해요. 이렇게 하려면 with_structured_output를 호출할 때 include_raw=True로 설정하면 돼요.
from pydantic import BaseModel, Field
class Movie(BaseModel):
"""A movie with details."""
title: str = Field(description="The title of the movie")
year: int = Field(description="The year the movie was released")
director: str = Field(description="The director of the movie")
rating: float = Field(description="The movie's rating out of 10")
model_with_structure = model.with_structured_output(Movie, include_raw=True) # [!code highlight]
response = model_with_structure.invoke("Provide details about the movie Inception")
response
# {
# "raw": AIMessage(...),
# "parsed": Movie(title=..., year=..., ...),
# "parsing_error": None,
# }
예시: 중첩 구조 — 스키마는 중첩될 수 있어요.
# Pydantic BaseModel
from pydantic import BaseModel, Field
class Actor(BaseModel):
name: str
role: str
class MovieDetails(BaseModel):
title: str
year: int
cast: list[Actor]
genres: list[str]
budget: float | None = Field(None, description="Budget in millions USD")
model_with_structure = model.with_structured_output(MovieDetails)
# TypedDict
from typing_extensions import Annotated, TypedDict
class Actor(TypedDict):
name: str
role: str
class MovieDetails(TypedDict):
title: str
year: int
cast: list[Actor]
genres: list[str]
budget: Annotated[float | None, ..., "Budget in millions USD"]
model_with_structure = model.with_structured_output(MovieDetails)
고급 주제 (Advanced topics)
모델 프로파일 (Model profiles)
ℹ️ 모델 프로파일은
langchain>=1.1이 필요해요.
LangChain 채팅 모델은 profile 속성을 통해 지원되는 기능과 역량의 딕셔너리를 노출할 수 있어요.
model.profile
# {
# "max_input_tokens": 400000,
# "image_inputs": True,
# "reasoning_output": True,
# "tool_calling": True,
# ...
# }
필드 전체 집합은 API 레퍼런스를 참고하세요.
모델 프로파일 데이터의 상당 부분은 모델 역량 데이터를 제공하는 오픈소스 프로젝트인 models.dev에서 나와요. 이 데이터는 LangChain과 함께 쓰기 위한 추가 필드로 보강돼요. 이런 보강은 상위 프로젝트가 발전함에 따라 그에 맞춰 유지돼요.
모델 프로파일 데이터는 애플리케이션이 모델 역량에 맞춰 동적으로 동작하도록 해 줘요. 예를 들어:
- 요약 미들웨어가 모델의 컨텍스트 윈도우 크기를 기준으로 요약을 트리거할 수 있어요.
create_agent의 구조화된 출력 전략을 자동으로 유추할 수 있어요 (예: 고유 구조화된 출력 기능 지원 여부 확인).- 모델 입력을 지원되는 모달리티와 최대 입력 토큰에 따라 게이팅할 수 있어요.
- Deep Agents Code는 대화형 모델 스위처를 프로파일이
tool_calling지원과 텍스트 I/O를 보고하는 모델로 필터링하고, 선택기 세부 보기에 컨텍스트 윈도우 크기와 역량 플래그를 표시해요.
프로파일 데이터 업데이트·덮어쓰기 — 모델 프로파일 데이터가 없거나, 낡았거나, 정확하지 않다면 바꿀 수 있어요.
옵션 1 (빠른 수정): 유효한 프로파일로 채팅 모델을 인스턴스화할 수 있어요.
custom_profile = {
"max_input_tokens": 100_000,
"tool_calling": True,
"structured_output": True,
# ...
}
model = init_chat_model("...", profile=custom_profile)
profile은 일반 dict이기도 해서 제자리에서 업데이트할 수 있어요. 모델 인스턴스가 공유된다면 공유 상태를 바꾸지 않도록 model_copy 사용을 고려하세요.
new_profile = model.profile | {"key": "value"}
model.model_copy(update={"profile": new_profile})
옵션 2 (데이터를 업스트림에서 수정): 데이터의 기본 출처는 models.dev 프로젝트예요. 이 데이터는 LangChain 통합 패키지의 추가 필드·오버라이드와 병합되어 그 패키지들과 함께 배포돼요. 모델 프로파일 데이터는 다음 과정으로 업데이트할 수 있어요.
- (필요 시) models.dev의 GitHub 저장소에 pull request를 보내 원본 데이터를 업데이트한다.
- (필요 시) LangChain 통합 패키지의
langchain_<package>/data/profile_augmentations.toml에 추가 필드·오버라이드를 pull request로 업데이트한다. langchain-model-profilesCLI 도구로 models.dev에서 최신 데이터를 받아 보강분을 병합하고 프로파일 데이터를 업데이트한다.
pip install -U langchain-model-profiles
uv add langchain-model-profiles
langchain-profiles refresh --provider <provider> --data-dir <data_dir>
이 명령은: <provider>의 최신 데이터를 models.dev에서 내려받고, <data_dir>의 profile_augmentations.toml에서 보강분을 병합하며, 병합된 프로파일을 <data_dir>의 profiles.py에 씁니다. 예를 들어 LangChain 모노레포의 libs/partners/anthropic에서:
uv run --with langchain-model-profiles --provider anthropic --data-dir langchain_anthropic/data
⚠️ 모델 프로파일은 베타 기능이에요. 프로파일 형식은 바뀔 수 있어요.
멀티모달 (Multimodal)
특정 모델은 이미지, 오디오, 비디오 같은 비텍스트 데이터를 처리하고 반환할 수 있어요. 비텍스트 데이터는 콘텐츠 블록으로 모델에 넘길 수 있어요.
💡 멀티모달 역량이 있는 모든 LangChain 채팅 모델은 다음을 지원해요.
- 프로바이더 간 표준 형식의 데이터 (우리 메시지 가이드 참고)
- OpenAI chat completions 형식
- 해당 프로바이더 고유의 형식 (예: Anthropic 모델은 Anthropic 고유 형식을 받음)
자세한 내용은 메시지 가이드의 멀티모달 섹션을 보세요.
<일부 모델>은 응답의 일부로 멀티모달 데이터를 반환할 수 있어요. 그렇게 호출하면 결과 AIMessage는 멀티모달 타입의 콘텐츠 블록을 갖게 돼요.
# Multimodal output
response = model.invoke("Create a picture of a cat")
print(response.content_blocks)
# [
# {"type": "text", "text": "Here's a picture of a cat"},
# {"type": "image", "base64": "...", "mime_type": "image/jpeg"},
# ]
특정 프로바이더의 자세한 내용은 통합 페이지를 보세요.
추론 (Reasoning)
많은 모델이 결론에 도달하기 위해 다단계 추론을 수행할 수 있어요. 복잡한 문제를 더 작고 다루기 쉬운 단계로 쪼개는 작업을 포함하죠.
기반 모델이 지원한다면, 이 추론 과정을 드러내 모델이 최종 답에 어떻게 도달했는지 더 잘 이해할 수 있어요.
# Stream reasoning output
for chunk in model.stream("Why do parrots have colorful feathers?"):
reasoning_steps = [r for r in chunk.content_blocks if r["type"] == "reasoning"]
print(reasoning_steps if reasoning_steps else chunk.text)
# Complete reasoning output
response = model.invoke("Why do parrots have colorful feathers?")
reasoning_steps = [b for b in response.content_blocks if b["type"] == "reasoning"]
print(" ".join(step["reasoning"] for step in reasoning_steps))
모델에 따라 추론에 들일 노력의 수준을 지정할 수 있어요. 비슷하게 추론을 완전히 끄도록 요청할 수도 있어요. 이는 범주형 "단계" (예: 'low'나 'high') 또는 정수 토큰 예산의 형태를 띠는 경우가 많아요.
📝 표준 파라미터
reasoning_effort는langchain-core>=1.5.2와 해당 파트너 패키지 버전이 필요해요:langchain-anthropic>=1.5.3,langchain-openai>=1.4.1,langchain-fireworks>=1.5.2,langchain-xai>=1.3.0,langchain-google-genai>=4.3.1,langchain-aws>=1.6.5.
ChatOpenAI, ChatAnthropic, ChatFireworks, ChatXAI, ChatGoogleGenerativeAI, ChatBedrockConverse는 표준 reasoning_effort 파라미터를 지원해요. temperature처럼 모델 생성 시나 호출 시에 설정할 수 있고, 각 프로바이더가 자신의 API 형식으로 변환해요.
from langchain_anthropic import ChatAnthropic
model = ChatAnthropic(model="claude-sonnet-4-6")
response = model.invoke(
"Why do parrots have colorful feathers?",
reasoning_effort="high",
)
지원되는 노력 수준과 프로바이더의 문서화된 기본값은 모델마다 달라요. 모델의 프로파일에서 지원 수준과 기본값을 확인할 수 있어요.
model.profile["reasoning_effort_levels"] # e.g. ['low', 'medium', 'high']
model.profile["reasoning_effort_default"] # e.g. 'high'
일부 프로바이더는 reasoning_effort의 고유 별칭도 받아들여요 (예: ChatAnthropic은 effort, ChatGoogleGenerativeAI는 thinking_level). 프로바이더별 자세한 내용은 채팅 모델 통합 페이지를 보세요.
자세한 내용은 통합 페이지나 해당 채팅 모델의 레퍼런스를 보세요.
로컬 모델 (Local models)
LangChain은 자체 하드웨어에서 모델을 로컬로 실행하는 것을 지원해요. 데이터 프라이버시가 중요한 시나리오, 커스텀 모델을 호출하고 싶을 때, 또는 클라우드 기반 모델 사용 비용을 피하고 싶을 때 유용해요.
Ollama는 채팅·임베딩 모델을 로컬에서 실행하는 가장 쉬운 방법 중 하나예요.
프롬프트 캐싱 (Prompt caching)
많은 프로바이더가 같은 토큰을 반복 처리할 때 지연 시간과 비용을 줄이는 프롬프트 캐싱 기능을 제공해요. 캐싱은 세 가지 수준에서 활용할 수 있어요.
- 암시적 프로바이더 캐싱: 프로바이더가 요청이 캐시에 맞으면 별도 설정 없이 비용 절감을 자동으로 넘겨 줘요. 예: OpenAI, Gemini.
- 프로바이더 수준 명시적 제어: 캐시 지점을 수동으로 표시해 더 큰 제어나 비용 절감을 보장할 수 있어요. 기반 프로바이더/API 동작을 그대로 따르죠. 예:
ChatOpenAI(prompt_cache_key사용)- Anthropic 콘텐츠 블록
cache_control - Gemini
- AWS Bedrock
cachePoint블록
- LangChain 미들웨어: 에이전트에서 미들웨어로 LangChain이 안정적인 시스템 프롬프트·도구 콘텐츠의 캐싱을 최적화할 수 있어요. 예:
- Anthropic의
AnthropicPromptCachingMiddleware - AWS Bedrock의
BedrockPromptCachingMiddleware
- Anthropic의
⚠️ 프롬프트 캐싱은 보통 최소 입력 토큰 임계값 이상에서만 동작해요. 자세한 내용은 프로바이더 페이지를 보세요.
캐시 사용은 모델 응답의 사용량 메타데이터에 반영돼요.
서버 측 도구 사용 (Server-side tool use)
일부 프로바이더는 서버 측 도구 호출 루프를 지원해요. 모델이 웹 검색, 코드 인터프리터 같은 도구와 상호작용하고 그 결과를 단일 대화 턴 안에서 분석할 수 있게 해 주죠.
모델이 서버 측에서 도구를 호출하면, 응답 메시지 콘텐츠에 도구의 호출과 결과를 나타내는 콘텐츠가 포함돼요. 응답의 콘텐츠 블록에 접근하면 서버 측 도구 호출과 결과를 프로바이더 무관한 형식으로 얻을 수 있어요.
# Invoke with server-side tool use
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-5.4-mini")
tool = {"type": "web_search"}
model_with_tools = model.bind_tools([tool])
response = model_with_tools.invoke("What was a positive news story from today?")
print(response.content_blocks)
# Result
[
{
"type": "server_tool_call",
"name": "web_search",
"args": {
"query": "positive news stories today",
"type": "search"
},
"id": "ws_abc123"
},
{
"type": "server_tool_result",
"tool_call_id": "ws_abc123",
"status": "success"
},
{
"type": "text",
"text": "Here are some positive news stories from today...",
"annotations": [
{
"end_index": 410,
"start_index": 337,
"title": "article title",
"type": "citation",
"url": "..."
}
]
}
]
이것은 단일 대화 턴을 나타내요. 클라이언트 측 도구 호출처럼 넘겨줘야 할 연관 ToolMessage 객체가 없어요.
사용 가능한 도구와 사용법은 해당 프로바이더의 통합 페이지를 보세요.
모델 예외 (Model exceptions)
주요 통합 패키지는 인증 오류, 레이트 제한, 타임아웃 같은 일반적인 모델 실패에 대해 langchain_core.exceptions의 표준 예외 타입을 발생시켜요. 이 예외들은 LangChain 기본 타입과 프로바이더 SDK의 자체 예외 타입을 모두 상속하므로, 둘 중 아무거나 잡을 수 있어요.
from langchain.chat_models import init_chat_model
from langchain_core.exceptions import ModelTimeoutError
model = init_chat_model("openai:gpt-5.6-luna", timeout=0.0001)
try:
response = model.invoke("Hello")
except ModelTimeoutError:
print("caught")
각 타입은 재시도 미들웨어가 기본으로 존중하는 is_retryable 속성을 가져요.
ModelAuthenticationError— API 키가 없거나, 잘못됐거나, 만료됨 (재시도 불가)ModelPermissionDeniedError— 자격 증명에 권한이 없음 (재시도 불가)ModelInvalidRequestError— 프로바이더가 요청을 거부함 (재시도 불가)ModelNotFoundError— 요청한 모델을 찾을 수 없음 (재시도 불가)ModelRateLimitError— 프로바이더 레이트 제한 초과 (재시도 가능)ModelAPIError— 프로바이더 서버 실패 (재시도 가능)ModelConnectionError— 프로바이더에 접근 불가 (재시도 가능)ModelTimeoutError— 요청 타임아웃 (재시도 가능)ContextOverflowError— 입력이 모델 컨텍스트 제한을 초과 (재시도 불가)
레이트 리밋 (Rate limiting)
많은 채팅 모델 프로바이더가 주어진 시간 동안 호출할 수 있는 횟수를 제한해요. 레이트 리밋에 걸리면 보통 프로바이더로부터 레이트 리밋 오류 응답을 받고, 더 요청하기 전에 기다려야 해요.
레이트 리밋을 관리하기 위해 채팅 모델 통합은 초기화 시 제공할 수 있는 rate_limiter 파라미터를 받아들여, 요청을 보내는 속도를 제어합니다.
LangChain에는 (선택적) 내장 InMemoryRateLimiter가 있어요. 이 리미터는 스레드 안전하며 같은 프로세스의 여러 스레드가 공유할 수 있어요.
# Define a rate limiter
from langchain.rate_limiters import InMemoryRateLimiter
rate_limiter = InMemoryRateLimiter(
requests_per_second=0.1, # 1 request every 10s
check_every_n_seconds=0.1, # Check every 100ms whether allowed to make a request
max_bucket_size=10, # Controls the maximum burst size.
)
model = init_chat_model(
model="gpt-5.5",
model_provider="openai",
rate_limiter=rate_limiter # [!code highlight]
)
⚠️ 제공된 레이트 리미터는 단위 시간당 요청 수만 제한할 수 있어요. 요청 크기를 기준으로도 제한해야 한다면 도움이 되지 않아요.
Base URL과 프록시 설정
OpenAI Chat Completions API를 구현하는 프로바이더에는 커스텀 base URL을 구성할 수 있어요.
⚠️
model_provider="openai"(또는 직접ChatOpenAI사용)는 공식 OpenAI API 스펙을 대상으로 해요. 라우터·프록시의 프로바이더별 필드는 추출되거나 보존되지 않을 수 있어요.OpenRouter와 LiteLLM에는 전용 통합을 권장해요.
- OpenRouter via
ChatOpenRouter(langchain-openrouter)- LiteLLM via
ChatLiteLLM/ChatLiteLLMRouter(langchain-litellm)
커스텀 base URL — 많은 모델 프로바이더가 OpenAI 호환 API를 제공해요 (예: Together AI, vLLM). 이런 프로바이더에는 init_chat_model과 함께 적절한 base_url 파라미터를 지정해 쓸 수 있어요.
model = init_chat_model(
model="MODEL_NAME",
model_provider="openai",
base_url="BASE_URL",
api_key="YOUR_API_KEY",
)
📝 직접 채팅 모델 클래스 인스턴스화를 쓸 때는 파라미터 이름이 프로바이더마다 다를 수 있어요. 자세한 내용은 해당 레퍼런스를 확인하세요.
HTTP 프록시 설정 — HTTP 프록시가 필요한 배포에서 일부 모델 통합은 프록시 설정을 지원해요.
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-5.5",
openai_proxy="http://proxy.example.com:8080"
)
📝 프록시 지원은 통합마다 달라요. 프록시 설정 옵션은 특정 모델 프로바이더의 레퍼런스를 확인하세요.
로그 확률 (Log probabilities)
특정 모델은 초기화 시 logprobs 파라미터를 설정하면 특정 토큰의 확률을 나타내는 토큰 단위 로그 확률을 반환하도록 구성할 수 있어요.
model = init_chat_model(
model="gpt-5.5",
model_provider="openai"
).bind(logprobs=True)
response = model.invoke("Why do parrots talk?")
print(response.response_metadata["logprobs"])
토큰 사용량 (Token usage)
여러 모델 프로바이더가 호출 응답의 일부로 토큰 사용량 정보를 반환해요. 가능할 때 이 정보는 해당 모델이 만든 AIMessage 객체에 포함돼요. 자세한 내용은 메시지 가이드를 보세요.
📝 일부 프로바이더 API, 특히 OpenAI와 Azure OpenAI chat completions는 사용자가 스트리밍 컨텍스트에서 토큰 사용량 데이터를 받도록 옵트인해야 해요. 자세한 내용은 통합 가이드의 스트리밍 사용량 메타데이터 섹션을 보세요.
콜백이나 컨텍스트 관리자로 애플리케이션 전반의 모델 토큰 총계를 추적할 수 있어요 (아래 참고).
# Callback handler
from langchain.chat_models import init_chat_model
from langchain_core.callbacks import UsageMetadataCallbackHandler
model_1 = init_chat_model(model="gpt-5.4-mini")
model_2 = init_chat_model(model="claude-haiku-4-5-20251001")
callback = UsageMetadataCallbackHandler()
result_1 = model_1.invoke("Hello", config={"callbacks": [callback]})
result_2 = model_2.invoke("Hello", config={"callbacks": [callback]})
print(callback.usage_metadata)
{
'gpt-5.4-mini': {
'input_tokens': 8,
'output_tokens': 10,
'total_tokens': 18,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
},
'claude-haiku-4-5-20251001': {
'input_tokens': 8,
'output_tokens': 21,
'total_tokens': 29,
'input_token_details': {'cache_read': 0, 'cache_creation': 0}
}
}
# Context manager
from langchain.chat_models import init_chat_model
from langchain_core.callbacks import get_usage_metadata_callback
model_1 = init_chat_model(model="gpt-5.4-mini")
model_2 = init_chat_model(model="claude-haiku-4-5-20251001")
with get_usage_metadata_callback() as cb:
model_1.invoke("Hello")
model_2.invoke("Hello")
print(cb.usage_metadata)
{
'gpt-5.4-mini': {
'input_tokens': 8,
'output_tokens': 10,
'total_tokens': 18,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
},
'claude-haiku-4-5-20251001': {
'input_tokens': 8,
'output_tokens': 21,
'total_tokens': 29,
'input_token_details': {'cache_read': 0, 'cache_creation': 0}
}
}
호출 설정 (Invocation config)
모델을 호출할 때 RunnableConfig 딕셔너리로 config 파라미터에 추가 설정을 넘길 수 있어요. 실행 동작, 콜백, 메타데이터 추적을 런타임에 제어할 수 있게 해 주죠.
# Invocation with config
response = model.invoke(
"Tell me a joke",
config={
"run_name": "joke_generation", # Custom name for this run
"tags": ["humor", "demo"], # Tags for categorization
"metadata": {"user_id": "123"}, # Custom metadata
"callbacks": [my_callback_handler], # Callback handlers
}
)
이 설정 값들은 특히 다음 상황에서 유용해요.
- LangSmith 추적으로 디버깅할 때
- 커스텀 로깅이나 모니터링을 구현할 때
- 프로덕션에서 리소스 사용량을 제어할 때
- 복잡한 파이프라인에서 호출을 추적할 때
핵심 설정 속성:
run_name(string) — 로그와 추적에서 이 특정 호출을 식별해요. 하위 호출에는 상속되지 않아요.tags(string[]) — 모든 하위 호출에 상속되는 라벨. 디버깅 도구에서 필터링·조직화에 써요.metadata(object) — 추가 컨텍스트 추적용 커스텀 키-값 쌍. 모든 하위 호출에 상속돼요.max_concurrency(number) —batch()나batch_as_completed()를 쓸 때 동시 호출 최대 수를 제어해요.callbacks(array) — 실행 중 이벤트를 모니터링·대응하는 핸들러.recursion_limit(number) — 복잡한 파이프라인의 무한 루프를 막기 위한 최대 재귀 깊이.
지원되는 모든 속성은 RunnableConfig 레퍼런스를 보세요.
설정 가능한 모델 (Configurable models)
configurable_fields를 지정해 런타임에 설정 가능한 모델을 만들 수도 있어요. 모델 값을 지정하지 않으면 'model'과 'model_provider'가 기본으로 설정 가능해져요.
from langchain.chat_models import init_chat_model
configurable_model = init_chat_model(temperature=0)
configurable_model.invoke(
"what's your name",
config={"configurable": {"model": "gpt-5-nano"}}, # Run with GPT-5-Nano
)
configurable_model.invoke(
"what's your name",
config={"configurable": {"model": "claude-sonnet-4-6"}}, # Run with Claude
)
기본값이 있는 설정 가능한 모델 — 기본 모델 값이 있는 설정 가능한 모델을 만들고, 어떤 파라미터가 설정 가능할지 지정하고, 설정 가능한 파라미터에 접두사를 붙일 수 있어요.
first_model = init_chat_model(
model="gpt-5.4-mini",
temperature=0,
configurable_fields=("model", "model_provider", "temperature", "max_tokens"),
config_prefix="first", # Useful when you have a chain with multiple models
)
first_model.invoke("what's your name")
first_model.invoke(
"what's your name",
config={
"configurable": {
"first_model": "claude-sonnet-4-6",
"first_temperature": 0.5,
"first_max_tokens": 100,
}
},
)
configurable_fields와 config_prefix에 대한 자세한 내용은 init_chat_model 레퍼런스를 보세요.
설정 가능한 모델을 선언적으로 쓰기 — 설정 가능한 모델에 bind_tools, with_structured_output, with_configurable 같은 선언적 연산을 호출하고, 일반적으로 인스턴스화한 채팅 모델 객체와 같은 방식으로 체이닝할 수 있어요.
from pydantic import BaseModel, Field
class GetWeather(BaseModel):
"""Get the current weather in a given location"""
location: str = Field(description="The city and state, e.g. San Francisco, CA")
class GetPopulation(BaseModel):
"""Get the current population in a given location"""
location: str = Field(description="The city and state, e.g. San Francisco, CA")
model = init_chat_model(temperature=0)
model_with_tools = model.bind_tools([GetWeather, GetPopulation])
model_with_tools.invoke(
"what's bigger in 2024 LA or NYC", config={"configurable": {"model": "gpt-5.4-mini"}}
).tool_calls
[
{
'name': 'GetPopulation',
'args': {'location': 'Los Angeles, CA'},
'id': 'call_Ga9m8FAArIyEjItHmztPYA22',
'type': 'tool_call'
},
{
'name': 'GetPopulation',
'args': {'location': 'New York, NY'},
'id': 'call_jh2dEvBaAHRaw5JUDthOs7rt',
'type': 'tool_call'
}
]
model_with_tools.invoke(
"what's bigger in 2024 LA or NYC",
config={"configurable": {"model": "claude-sonnet-4-6"}},
).tool_calls
[
{
'name': 'GetPopulation',
'args': {'location': 'Los Angeles, CA'},
'id': 'toolu_01JMufPf4F4t2zLj7miFeqXp',
'type': 'tool_call'
},
{
'name': 'GetPopulation',
'args': {'location': 'New York City, NY'},
'id': 'toolu_01RQBHcE8kEEbYTuuS8WqY1u',
'type': 'tool_call'
}
]
동적 모델 선택 (Dynamic model selection)
동적 모델은 현재 <상태>와 문맥을 바탕으로 <런타임>에 선택돼요. 이를 통해 정교한 라우팅 로직과 비용 최적화가 가능해져요.
동적 모델을 쓰려면 요청의 모델을 수정하는 @wrap_model_call 데코레이터로 미들웨어를 만들어요.
from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
basic_model = ChatOpenAI(model="gpt-5.4-mini")
advanced_model = ChatOpenAI(model="gpt-5.5")
@wrap_model_call
def dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse:
"""Choose model based on conversation complexity."""
message_count = len(request.state["messages"])
if message_count > 10:
# Use an advanced model for longer conversations
model = advanced_model
else:
model = basic_model
return handler(request.override(model=model))
agent = create_agent(
model=basic_model, # Default model
tools=[],
middleware=[dynamic_model_selection]
)
⚠️ 구조화된 출력을 쓸 때는 미리 바인딩된 모델(
bind_tools이 이미 호출된 모델)을 지원하지 않아요. 구조화된 출력과 함께 동적 모델 선택이 필요하다면, 미들웨어에 넘기는 모델이 미리 바인딩되지 않았는지 확인하세요.
💡 모델 설정에 대한 자세한 내용은 모델을, 동적 모델 선택 패턴은 미들웨어의 동적 모델을 보세요.