먼저 용어 정리부터 하겠습니다.
LangChain 모델은 단순히 텍스트 생성만 하는 게 아니라, 보통 아래 기능들을 함께 지원합니다. ([LangChain Docs][1])
그리고 이 모델이 바로 에이전트의 “두뇌” 역할을 합니다.
에이전트는 “어떤 툴을 쓸까? 언제 멈출까?” 같은 결정을 모델의 출력에 따라 내립니다. ([LangChain Docs][1])
간단히 흐름을 그림으로 보면 아래 느낌입니다.

여기서 오늘 이야기의 주인공은 가운데 있는 Model(M) 입니다.
에이전트 없이도, 이 모델만 직접 호출해서 쓸 수 있습니다.
문서에서는 모델 사용 방식을 크게 두 가지로 나눕니다. ([LangChain Docs][1])
에이전트와 함께 사용
단독으로 사용(standalone)
model.invoke("질문") 이런 식으로 바로 호출해서LangChain의 인터페이스는 에이전트 안에서 쓸 때나, 단독으로 쓸 때나 거의 동일해서
“간단한 스크립트 → 복잡한 에이전트”로 점진적으로 확장하기 좋게 설계되어 있습니다. ([LangChain Docs][1])
init_chat_model 한 방으로 시작하기가장 쉽게 시작하는 방법은 init_chat_model 함수를 사용하는 것입니다. ([LangChain Docs][1])
지원하는 대표 프로바이더 예시:
import os
from langchain.chat_models import init_chat_model
os.environ["OPENAI_API_KEY"] = "sk-..." # 실제 키로 교체
# 모델 초기화
model = init_chat_model("gpt-4.1")
# 한 줄 질문
response = model.invoke("Why do parrots talk?")
print(response)
여기서 init_chat_model("gpt-4.1") 이 한 줄이
를 한 번에 지정해 줍니다. ([LangChain Docs][1])
invoke, stream, batchLangChain 문서에서는 모델의 핵심 메서드를 이렇게 정리합니다. ([LangChain Docs][1])
| 메서드 | 용도 | 언제 쓰면 좋은지 |
|---|---|---|
invoke | 한 번 질문 → 한 번 완성된 답변 | 간단한 호출, API 서버 백엔드, 테스트 |
stream | 토큰이 생성되는 동안 “조각조각” 받기 | 챗 UI에서 실시간으로 글자 나오는 느낌 줄 때 |
batch | 여러 입력을 한 번에 병렬 처리 | 대량 오프라인 작업(요약 수백 개, 분류 수천 개 등) |
invoke: 가장 기본적인 호출response = model.invoke("Why do parrots have colorful feathers?")
print(response.text) # AIMessage의 text만 보기
AIMessage (역할/텍스트/메타데이터를 포함한 객체) ([LangChain Docs][1])문서에서는 두 가지 형식을 보여줍니다. ([LangChain Docs][1])
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)
from langchain_core.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)
둘 다 결국 역할(role) + 내용(content) 을 명시해서
“지금 어떤 대화 상황인지”를 모델에 알려주는 방식입니다. ([LangChain Docs][2])
stream: 답변을 조금씩 흘려 받기stream() 은 토큰이 생성되는 대로 조각(chunk) 을 계속 보내 줍니다.
문서 예시와 비슷하게 쓰면: ([LangChain Docs][1])
for chunk in model.stream("Why do parrots have colorful feathers?"):
print(chunk.text, end="|", flush=True)
invoke() → 한 번에 완성된 AIMessagestream() → 여러 개의 AIMessageChunk 를 순서대로 받음 ([LangChain Docs][1])이 chunk들을 더해서 하나의 메시지로 만드는 것도 가능합니다.
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) # 점점 길어지는 문장
print(full.content_blocks)
# [{"type": "text", "text": "The sky is typically blue..."}]
이렇게 모아서 만든 full은 invoke() 로 받은 AIMessage 처럼
그냥 대화 히스토리에 추가해서 다시 모델에게 넘길 수 있습니다. ([LangChain Docs][1])
batch: 여러 요청을 한 번에 보내기여러 질문을 병렬로 처리하고 싶을 때 batch() 를 사용합니다. ([LangChain Docs][1])
responses = model.batch([
"Why do parrots have colorful feathers?",
"How do airplanes fly?",
"What is quantum computing?"
])
for r in responses:
print(r.text)
대량 처리 시에는 max_concurrency 로 동시 호출 수를 조절할 수도 있습니다. ([LangChain Docs][1])
init_chat_model() 에서 다양한 파라미터를 같이 넘길 수 있습니다. ([LangChain Docs][1])
대표적인 것만 정리하면:
| 파라미터 | 역할 |
|---|---|
model | 사용할 모델 이름 (예: "gpt-4.1", "claude-sonnet-4-5-20250929") |
api_key | 프로바이더 인증 키 (보통 환경변수에 저장) |
temperature | 창의성/랜덤성 조절 (높을수록 다양, 낮을수록 일정한 답) |
timeout | 한 요청에서 기다릴 최대 시간(초) |
max_tokens | 응답 길이 제한 (토큰 개수 기준) |
max_retries | 네트워크 오류/레이트 리밋 발생 시 재시도 횟수 |
문서 예시는 이런 식입니다. ([LangChain Docs][1])
model = init_chat_model(
"claude-sonnet-4-5-20250929",
temperature=0.7,
timeout=30,
max_tokens=1000,
)
각 프로바이더별로 여기서 더 많은 추가 옵션이 있습니다
(예: OpenAI의 use_responses_api, logprobs, prompt_cache_key 등). ([LangChain Docs][1])
문서의 예시는 get_weather 라는 간단한 툴을 만들어 모델에 바인딩하는 코드입니다. ([LangChain Docs][1])
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])
response = model_with_tools.invoke("What's the weather like in Boston?")
print(response.tool_calls) # 모델이 어떤 툴을 어떻게 호출하라고 했는지

실제 코드에서는:
model.bind_tools([get_weather]) 로 툴을 모델에 바인딩invoke() 결과에서 tool_calls 를 확인invoke()에이전트를 쓰면 이 루프를 LangChain이 대신 돌려 줍니다.
“모델이 아무 말이나 하지 말고, 내가 정한 스키마대로만 답해 줘” 라고 강제하는 기능입니다.
문서에서 소개하는 대표적인 스키마 방식: ([LangChain Docs][1])
Pydantic 모델TypedDictJSON Schemafrom 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)
이 경우 response 는 그냥 텍스트가 아니라 검증된 Pydantic 객체가 됩니다. ([LangChain Docs][1])
또한 include_raw=True 로 원본 AIMessage + 파싱된 결과를 같이 돌려 받는 것도 가능합니다. ([LangChain Docs][1])
문서 뒤쪽에는 꽤 많은 고급 기능이 정리되어 있습니다. ([LangChain Docs][1])
각 항목을 아주 요약하면:
멀티모달(Multimodal)
content_blocks 에 “type: image” 같은 블록으로 들어감Reasoning(추론 노출)
reasoning 타입 블록으로 스트리밍해서 볼 수 있는 모델들로컬 모델(Local models)
프롬프트 캐싱(Prompt caching)
서버 사이드 툴 사용(Server-side tool use)
content_blocks 를 읽어 보면, 어떤 서버 툴이 어떻게 실행됐는지 알 수 있음Rate limiting
InMemoryRateLimiter 로 초당 요청 수를 제한해서 레이트 리밋 에러를 줄임Base URL / Proxy
base_url, openai_proxy 등의 옵션으로 설정Log probabilities
logprobs=True 로 토큰별 로그 확률을 받아서 “모델이 얼마나 확신하는지” 분석Token usage
AIMessage 의 usage_metadata 로 토큰 사용량을 추적하고,UsageMetadataCallbackHandler) 으로 여러 모델들의 토큰 사용량 집계Invocation config (RunnableConfig)
run_name, tags, metadata, callbacks, max_concurrency, recursion_limit 등Configurable models
configurable_fields 를 써서 “런타임에 모델 종류를 바꿔끼는” 구조config={"configurable": {"model": "gpt-5-nano"}} 처럼 바꿔 실행이 부분들은 “프로덕션에서 여러 모델, 여러 파이프라인을 운영할 때” 힘을 발휘하는 기능들입니다. ([LangChain Docs][1])
모델은 에이전트의 두뇌이고,
단독으로도, 에이전트 안에서도 동일한 인터페이스로 사용할 수 있다.
시작은 아주 간단하다
init_chat_model("모델이름")invoke, stream, batch 이 세 가지 메서드만 먼저 익히면 된다.그 다음으로
마지막으로
각 항목을 “기초 개념 → 코드 예시 → 간단 시각화/마인드셋” 순서로 풀어볼게요.
입력도, 출력도 content_blocks 안에 들어간다고 생각하면 편합니다.
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4.1") # 멀티모달 지원 가정
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"},
# ]
위처럼 type: "image" 블록이 나오면, 클라이언트(UI)에서 실제 이미지로 렌더링할 수 있습니다. ([LangChain Docs][1])

content_blocks 로 통일해서 다루도록 도와줍니다. ([LangChain Docs][1])새로운 모델들(예: 일부 OpenAI/Anthropic 모델)은 중간 추론 과정을 별도 블록으로 뽑아낼 수 있습니다. ([LangChain Docs][1])
content_blocks 안에 {"type": "reasoning", ...} 같은 블록이 들어옴for chunk in model.stream("Why do parrots have colorful feathers?"):
reasoning_steps = [r for r in chunk.content_blocks
if r["type"] == "reasoning"]
# reasoning 블록이 있으면 그걸, 없으면 일반 텍스트 출력
print(reasoning_steps if reasoning_steps else chunk.text)
특정 모델은 reasoning 강도(예: "low", "high" 또는 토큰 budget)를 파라미터로 받기도 합니다. ([LangChain Docs][1])

Reasoning 기능은 이 S1~S3 과정 자체를 텍스트로 스트리밍해 준다고 보면 됩니다.
클라우드 API 대신 로컬에서 LLM을 돌리고 싶을 때 사용하는 옵션
장점:
LangChain 문서에서는 Ollama 를 대표 예로 소개합니다. ([LangChain Docs][1])
from langchain.chat_models import init_chat_model
# Ollama 같은 OpenAI 호환 서버를 띄워놓았다고 가정
model = init_chat_model(
model="llama3",
model_provider="openai",
base_url="http://localhost:11434/v1",
api_key="not-needed-or-dummy",
)
print(model.invoke("로컬에서 도는 거야?").text)
여기서 핵심은:
base_url 로 로컬 서버 주소를 넘겨주는 것프롬프트가 길 때(예: 50k 토큰짜리 지침 문서) 매번 전부 계산하면
그래서 프롬프트 캐싱을 지원하는 provider들이 많습니다. ([LangChain Docs][1])
종류:
Implicit
Explicit
prompt_cache_key 등으로 사용 (ChatOpenAI 등) ([LangChain Docs][1])system/developer 지침처럼 항상 동일한 긴 부분은user 메시지 정도만 남기기응답의 usage metadata 안에 “캐시 사용 여부”가 기록되기 때문에,
나중에 “얼마나 캐시가 먹혔는지” 모니터링 가능. ([LangChain Docs][1])
기본 tool-calling은 이렇게 동작했죠:
tool_calls를 내보내고Server-side tool use 는,
“이 루프를 클라우드 모델 제공자 쪽에서 한 번에 돌려버리는 기능” 입니다. ([LangChain Docs][1])
from langchain.chat_models import init_chat_model
model = init_chat_model("gpt-4.1-mini")
tool = {"type": "web_search"} # provider가 미리 정의한 server tool
model_with_tools = model.bind_tools([tool])
response = model_with_tools.invoke(
"What was a positive news story from today?"
)
print(response.content_blocks)
예상되는 content_blocks 예시: ([LangChain Docs][1])
[
{"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": [
{"type": "citation", "title": "article title", "url": "..."}
]
}
]
우리는 별도의 ToolMessage를 보낼 필요가 없습니다.
한 번의 invoke() 안에서 검색 → 분석 → 답변까지 끝난 상태로 와요. ([LangChain Docs][1])


rate limit exceeded 에러가 나죠.InMemoryRateLimiter 같은 도구로 요청 속도 자체를 제한할 수 있습니다. ([LangChain Docs][1])from langchain.chat_models import init_chat_model
from langchain_core.rate_limiters import InMemoryRateLimiter
rate_limiter = InMemoryRateLimiter(
requests_per_second=0.1, # 초당 0.1회 = 10초에 1번
check_every_n_seconds=0.1, # 0.1초마다 "지금 보내도 되나" 체크
max_bucket_size=10, # 버스트 허용량
)
model = init_chat_model(
model="gpt-5",
model_provider="openai",
rate_limiter=rate_limiter,
)
이 RateLimiter는 “시간당 요청 개수”만 관리하고,
“토큰 양(길이)”까지는 제한해 주지 않는다는 점에 주의. ([LangChain Docs][1])
많은 업체들이 OpenAI 호환 API를 제공합니다.
예: Together, vLLM 서버 등. ([LangChain Docs][1])
이때 init_chat_model 에서:
model = init_chat_model(
model="MODEL_NAME",
model_provider="openai",
base_url="https://my-openai-compatible-endpoint/v1",
api_key="YOUR_API_KEY",
)
사내망에서만 나갈 수 있도록 HTTP 프록시를 써야 할 때:
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-4o",
openai_proxy="http://proxy.example.com:8080",
)
프록시 지원 여부, 설정 방식은 provider마다 조금씩 다르니
각 provider 문서를 확인해야 합니다. ([LangChain Docs][1])
from langchain.chat_models import init_chat_model
model = init_chat_model(
model="gpt-4o",
model_provider="openai",
).bind(logprobs=True)
response = model.invoke("Why do parrots talk?")
print(response.response_metadata["logprobs"])
response_metadata["logprobs"] 안에
토큰별 확률 정보가 provider 형식으로 들어 있습니다. ([LangChain Docs][1])
AIMessage.usage_metadata 안에 들어있고,from langchain.chat_models import init_chat_model
from langchain_core.callbacks import UsageMetadataCallbackHandler
model_1 = init_chat_model(model="gpt-4o-mini")
model_2 = init_chat_model(model="claude-haiku-4-5-20251001")
callback = UsageMetadataCallbackHandler()
_ = model_1.invoke("Hello", config={"callbacks": [callback]})
_ = model_2.invoke("Hello", config={"callbacks": [callback]})
print(callback.usage_metadata)
usage_metadata 안에는 모델별로 input/output/total 토큰 수,
reasoning 토큰, cache 사용량 등 다양한 정보가 들어갑니다. ([LangChain Docs][1])
운영 관점에서:
같은 질문에 답할 수 있게 해 줍니다.
model.invoke(input, config=...) 의 config 파라미터에
RunnableConfig 딕셔너리를 넘겨서,
을 실행 시점에 컨트롤할 수 있습니다. ([LangChain Docs][1])
response = model.invoke(
"Tell me a joke",
config={
"run_name": "joke_generation",
"tags": ["humor", "demo"],
"metadata": {"user_id": "123"},
"callbacks": [my_callback_handler],
},
)
대표 속성들: ([LangChain Docs][1])
| 키 | 의미 |
|---|---|
run_name | 이 호출에 붙는 이름 (로그/트레이스에서 보임) |
tags | 하위 호출까지 상속되는 레이블 |
metadata | 임의의 key-value, 역시 상속됨 |
max_concurrency | batch() 호출 시 최대 병렬 개수 |
callbacks | 이벤트 모니터링용 핸들러 리스트 |
recursion_limit | 체인/에이전트가 재귀적으로 도는 깊이 제한 |
특히 LangSmith로 트레이싱할 때 run_name, tags, metadata 를 잘 써두면
디버깅이 훨씬 편해집니다. ([LangChain Docs][1])
init_chat_model("gpt-4o") 를 박아두면,config로 모델/파라미터를 바꿔끼는 방식. ([LangChain Docs][1])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"}},
)
configurable_model.invoke(
"what's your name",
config={"configurable": {"model": "claude-sonnet-4-5-20250929"}},
)
config={"configurable": {...}} 값만 바꿔서여러 모델이 섞여 있을 때, 각 모델별로 설정 키를 분리하고 싶다면: ([LangChain Docs][1])
first_model = init_chat_model(
model="gpt-4.1-mini",
temperature=0,
configurable_fields=("model", "model_provider", "temperature", "max_tokens"),
config_prefix="first", # 이 모델과 관련된 configurable 키에 prefix
)
first_model.invoke("what's your name") # 기본 설정 사용
first_model.invoke(
"what's your name",
config={
"configurable": {
"first_model": "claude-sonnet-4-5-20250929",
"first_temperature": 0.5,
"first_max_tokens": 100,
}
},
)
이 구조를 쓰면:
first_*, second_* 처럼 이름으로 구분하면서 설정을 바꿀 수 있습니다.configurable model에도 bind_tools, with_structured_output 등을 그대로 쓸 수 있습니다.
즉,
라는 패턴을 쉽게 만들 수 있습니다. ([LangChain Docs][1])