LangChain v1 마이그레이션 가이드

LangChain v1 마이그레이션 가이드 (LangChain v1 migration guide)

LangChain v1과 이전 버전 사이의 주요 변경 사항을 다루는 가이드예요. v1은 langchain 패키지의 네임스페이스를 크게 줄여 에이전트의 핵심 구성 요소에 집중했고, 기존 코드는 langchain-classic으로 옮겼어요. 마이그레이션에는 langchain>=1.0.0, langchain-core>=1.0.0, 그리고 Python 3.10 이상이 필요해요.

출처: 공식문서

핵심 변경 사항을 먼저 정리하면:

  1. 패키지 네임스페이스 축소: langchain 패키지는 에이전트, 메시지, 툴, chat 모델, 임베딩에 집중해요. 레거시 체인, retriever, index, hub, CacheBackedEmbeddings 같은 임베딩 헬퍼, 커뮤니티 재수출은 langchain-classic으로 옮겨졌어요. langchain-classic을 설치하고 해당 import를 langchain_classic.*로 바꾸세요. langchain의 커뮤니티 통합 재수출 대신 프로바이더 패키지를 직접 설치해요(예: langchain-openai).
  2. create_react_agentcreate_agent: from langgraph.prebuilt import create_react_agentfrom langchain.agents import create_agent로 바꾸세요. prompt=system_prompt=로 이름이 바뀌었어요. agent.invoke({"messages": [...]}) / agent.stream(...)으로 호출해요. deprecated된 langgraph.prebuilt 에이전트 상태 헬퍼보다 langchain.agents.AgentState를 선호해요.
  3. 훅이 미들웨어가 됨: pre_model_hook=, post_model_hook=, state_modifier= 커스터마이징을 create_agent 미들웨어(before_model, after_model, @wrap_model_call, @wrap_tool_call)로 대체해요. 툴 승인용 human-in-the-loop은 langchain.agents.middlewareHumanInTheLoopMiddleware(interrupt_on={...})를 사용해요.
  4. 구조화 출력: response_format=은 유지하되 langchain.agents.structured_outputToolStrategy / ProviderStrategy를 사용해요. response_format=("please generate ...", Schema) 같은 프롬프트 기반 출력 형식은 제거됐어요.
  5. 스트리밍 노드 이름: 스트리밍 이벤트를 노드 이름으로 필터링·매칭할 때 "agent""model"로 바꾸세요.
  6. 런타임 컨텍스트: config["configurable"]만 쓰는 대신 invoke / streamcontext= 인자로 정적 컨텍스트를 전달해요(create_agent에선 context_schema=).
  7. 표준 콘텐츠 블록: 프로바이더 무관 콘텐츠에는 message.content_blocks를 선호해요. 기존 message.content도 여전히 유효해요.

create_react_agent, pre_model_hook, post_model_hook, state_modifier, langgraph.prebuilt의 import, 그리고 langchain.chains, langchain.retrievers, langchain.indexes, langchain.hub 같은 레거시 langchain 모듈을 코드베이스에서 검색해 필요한 변경을 적용하세요.

단순화된 패키지 (Simplified package)

v1에서 langchain 패키지 네임스페이스는 에이전트의 필수 빌딩 블록에 집중하도록 크게 줄었어요. 이렇게 정리된 패키지는 핵심 기능을 더 쉽게 발견하고 사용하게 해줘요.

네임스페이스

모듈 제공 내용 비고
langchain.agents create_agent, AgentState 핵심 에이전트 생성 기능
langchain.messages 메시지 유형, content blocks, trim_messages langchain-core에서 재수출
langchain.tools @tool, BaseTool, 주입 헬퍼 langchain-core에서 재수출
langchain.chat_models init_chat_model, BaseChatModel 통합 모델 초기화
langchain.embeddings init_embeddings, Embeddings 임베딩 모델

langchain-classic

langchain 패키지에서 다음 중 하나라도 썼다면 langchain-classic을 설치하고 import를 업데이트해야 해요.

  • 레거시 체인 (LLMChain, ConversationChain 등)
  • Retriever (예: MultiQueryRetriever 또는 이전 langchain.retrievers 모듈의 것)
  • Indexing API
  • Hub 모듈 (프롬프트를 프로그래밍 방식으로 관리)
  • 임베딩 모듈 (예: CacheBackedEmbeddings, 커뮤니티 임베딩)
  • langchain-community 재수출
  • 기타 deprecated 기능
# v1 (new) — Chains
from langchain_classic.chains import LLMChain

# Retrievers
from langchain_classic.retrievers import ...

# Indexing
from langchain_classic.indexes import ...

# Hub
from langchain_classic import hub
# v0 (old) — Chains
from langchain_classic.chains import LLMChain

# Retrievers
from langchain.retrievers import ...

# Indexing
from langchain.indexes import ...

# Hub
from langchain import hub

설치:

pip install langchain-classic
uv add langchain-classic

create_agent로 마이그레이션 (Migrate to create_agent)

v1.0 이전에는 에이전트를 만들 때 langgraph.prebuilt.create_react_agent를 권장했어요. 이제는 langchain.agents.create_agent를 권장해요.

섹션 TL;DR — 무엇이 바뀌었나
Import 경로 패키지가 langgraph.prebuilt에서 langchain.agents로 이동
프롬프트 파라미터가 system_prompt로 이름 변경, 동적 프롬프트는 미들웨어 사용
Pre-model hook before_model 메서드를 가진 미들웨어로 대체
Post-model hook after_model 메서드를 가진 미들웨어로 대체
커스텀 상태 TypedDict만 지원, state_schema 또는 미들웨어로 정의
모델 미들웨어를 통한 동적 선택, 사전 바인딩 모델 미지원
툴 오류 처리가 wrap_tool_call 미들웨어로 이동
구조화 출력 프롬프트 기반 출력 제거, ToolStrategy/ProviderStrategy 사용
스트리밍 노드 이름 노드 이름이 "agent"에서 "model"로 변경
런타임 컨텍스트 config["configurable"] 대신 context 인자로 의존성 주입
네임스페이스 에이전트 빌딩 블록에 집중하도록 정리, 레거시는 langchain-classic으로 이동

Import 경로 (Import path)

에이전트 prebuilt의 import 경로가 langgraph.prebuilt에서 langchain.agents로 바뀌었고, 함수 이름도 create_react_agent에서 create_agent로 바뀌었어요.

from langgraph.prebuilt import create_react_agent # [!code --]
from langchain.agents import create_agent # [!code ++]

자세한 내용은 Agents를 참고해요.

프롬프트 (Prompts)

정적 프롬프트 이름 변경

prompt 파라미터가 system_prompt으로 이름이 바뀌었어요.

# v1 (new)
from langchain.agents import create_agent

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather],
    system_prompt="You are a helpful assistant"  # [!code highlight]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather],
    prompt="You are a helpful assistant"  # [!code highlight]
)

SystemMessage를 문자열로

시스템 프롬프트에 SystemMessage 객체를 썼다면 문자열 콘텐츠를 추출하세요.

# v1 (new)
from langchain.agents import create_agent

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather],
    system_prompt="You are a helpful assistant"  # [!code highlight]
)
# v0 (old)
from langchain.messages import SystemMessage
from langgraph.prebuilt import create_react_agent

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather],
    prompt=SystemMessage(content="You are a helpful assistant")  # [!code highlight]
)

동적 프롬프트

동적 프롬프트는 핵심 컨텍스트 엔지니어링 패턴이에요. 현재 대화 상태에 따라 모델에게 알려주는 내용을 바꾸는 거죠. 이를 위해 @dynamic_prompt 데코레이터를 사용해요.

# v1 (new)
from dataclasses import dataclass

from langchain.agents import create_agent
from langchain.agents.middleware import dynamic_prompt, ModelRequest
from langgraph.runtime import Runtime


@dataclass
class Context:  # [!code highlight]
    user_role: str = "user"

@dynamic_prompt  # [!code highlight]
def dynamic_prompt(request: ModelRequest) -> str:  # [!code highlight]
    user_role = request.runtime.context.user_role
    base_prompt = "You are a helpful assistant."

    if user_role == "expert":
        prompt = (
            f"{base_prompt} Provide detailed technical responses."
        )
    elif user_role == "beginner":
        prompt = (
            f"{base_prompt} Explain concepts simply and avoid jargon."
        )
    else:
        prompt = base_prompt

    return prompt  # [!code highlight]

agent = create_agent(
    model="gpt-5.5",
    tools=tools,
    middleware=[dynamic_prompt],  # [!code highlight]
    context_schema=Context
)

# Use with context
agent.invoke(
    {"messages": [{"role": "user", "content": "Explain async programming"}]},
    context=Context(user_role="expert")
)
# v0 (old)
from dataclasses import dataclass

from langgraph.prebuilt import create_react_agent, AgentState
from langgraph.runtime import get_runtime

@dataclass
class Context:
    user_role: str

def dynamic_prompt(state: AgentState) -> str:
    runtime = get_runtime(Context)  # [!code highlight]
    user_role = runtime.context.user_role
    base_prompt = "You are a helpful assistant."

    if user_role == "expert":
        return f"{base_prompt} Provide detailed technical responses."
    elif user_role == "beginner":
        return f"{base_prompt} Explain concepts simply and avoid jargon."
    return base_prompt

agent = create_react_agent(
    model="gpt-5.5",
    tools=tools,
    prompt=dynamic_prompt,
    context_schema=Context
)

# Use with context
agent.invoke(
    {"messages": [{"role": "user", "content": "Explain async programming"}]},
    context=Context(user_role="expert")
)

Pre-model hook

pre-model hook은 이제 before_model 메서드를 가진 미들웨어로 구현해요. 이 새 패턴은 더 확장 가능해요 — 모델 호출 전에 실행할 미들웨어를 여러 개 정의해 다양한 에이전트에서 공통 패턴을 재사용할 수 있죠.

흔한 사용 사례:

  • 대화 히스토리 요약
  • 메시지 정리(trimming)
  • PII 삭제 같은 입력 가드레일

v1에는 요약 미들웨어가 내장 옵션으로 들어 있어요.

# v1 (new)
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=tools,
    middleware=[
        SummarizationMiddleware(  # [!code highlight]
            model="claude-sonnet-4-6",  # [!code highlight]
            trigger={"tokens": 1000}  # [!code highlight]
        )  # [!code highlight]
    ]  # [!code highlight]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent, AgentState

def custom_summarization_function(state: AgentState):
    """Custom logic for message summarization."""
    ...

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=tools,
    pre_model_hook=custom_summarization_function
)

Post-model hook

post-model hook은 이제 after_model 메서드를 가진 미들웨어로 구현해요. 모델 호출 후 실행할 미들웨어를 여러 개 정의해 공통 패턴을 재사용할 수 있어요.

흔한 사용 사례:

v1에는 툴 호출 인간 승인용 내장 미들웨어가 있어요.

# v1 (new)
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[read_email, send_email],
    middleware=[
        HumanInTheLoopMiddleware(
            interrupt_on={
                "send_email": {
                    "description": "Please review this email before sending",
                    "allowed_decisions": ["approve", "reject"]
                }
            }
        )
    ]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent
from langgraph.prebuilt import AgentState

def custom_human_in_the_loop_hook(state: AgentState):
    """Custom logic for human in the loop approval."""
    ...

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=[read_email, send_email],
    post_model_hook=custom_human_in_the_loop_hook
)

커스텀 상태 (Custom state)

커스텀 상태는 기본 에이전트 상태에 추가 필드를 확장해요. 두 가지 방법으로 정의할 수 있어요.

  1. create_agentstate_schema — 툴에서 사용하는 상태에 적합
  2. 미들웨어로 — 특정 미들웨어 훅과 그 미들웨어에 붙은 툴이 관리하는 상태에 적합

미들웨어로 커스텀 상태를 정의하는 게 create_agentstate_schema로 정의하는 것보다 선호돼요. 상태 확장을 관련 미들웨어·툴에 개념적으로 범위를 한정할 수 있기 때문이죠. state_schemacreate_agent에서 역호환을 위해 여전히 지원해요.

state_schema로 상태 정의

커스텀 상태를 툴이 접근해야 할 때 state_schema 파라미터를 사용해요.

# v1 (new)
from langchain.tools import tool, ToolRuntime
from langchain.agents import create_agent, AgentState  # [!code highlight]


# Define custom state extending AgentState
class CustomState(AgentState):
    user_name: str

@tool  # [!code highlight]
def greet(
    runtime: ToolRuntime[None, CustomState]
) -> str:
    """Use this to greet the user by name."""
    user_name = runtime.state.get("user_name", "Unknown")  # [!code highlight]
    return f"Hello {user_name}!"

agent = create_agent(  # [!code highlight]
    model="claude-sonnet-4-6",
    tools=[greet],
    state_schema=CustomState  # [!code highlight]
)
# v0 (old)
from typing import Annotated
from langgraph.prebuilt import InjectedState, create_react_agent
from langgraph.prebuilt.chat_agent_executor import AgentState

class CustomState(AgentState):
    user_name: str

def greet(
    state: Annotated[CustomState, InjectedState]
) -> str:
    """Use this to greet the user by name."""
    user_name = state["user_name"]
    return f"Hello {user_name}!"

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=[greet],
    state_schema=CustomState
)

미들웨어로 상태 정의

미들웨어는 state_schema 속성을 설정해 커스텀 상태를 정의할 수도 있어요. 상태 확장을 관련 미들웨어·툴에 개념적으로 범위를 한정하는 데 도움돼요.

from langchain.agents.middleware import AgentState, AgentMiddleware
from typing_extensions import NotRequired
from typing import Any

class CustomState(AgentState):
    model_call_count: NotRequired[int]

class CallCounterMiddleware(AgentMiddleware[CustomState]):
    state_schema = CustomState  # [!code highlight]

    def before_model(self, state: CustomState, runtime) -> dict[str, Any] | None:
        count = state.get("model_call_count", 0)
        if count > 10:
            return {"jump_to": "end"}
        return None

    def after_model(self, state: CustomState, runtime) -> dict[str, Any] | None:
        return {"model_call_count": state.get("model_call_count", 0) + 1}

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[...],
    middleware=[CallCounterMiddleware()]  # [!code highlight]
)

미들웨어로 커스텀 상태를 정의하는 자세한 내용은 미들웨어 문서를 참고해요.

상태 타입 제한

create_agent는 상태 스키마에 TypedDict만 지원해요. Pydantic 모델과 dataclass는 더 이상 지원되지 않아요.

# v1 (new)
from langchain.agents import AgentState, create_agent

# AgentState is a TypedDict
class CustomAgentState(AgentState):  # [!code highlight]
    user_id: str

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=tools,
    state_schema=CustomAgentState  # [!code highlight]
)
# v0 (old)
from typing_extensions import Annotated

from pydantic import BaseModel
from langgraph.graph import StateGraph
from langgraph.graph.messages import add_messages
from langchain.messages import AnyMessage


class AgentState(BaseModel):  # [!code highlight]
    messages: Annotated[list[AnyMessage], add_messages]
    user_id: str

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=tools,
    state_schema=AgentState
)

BaseModel 상속이나 dataclass 데코레이터 대신 langchain.agents.AgentState에서 상속하면 돼요. 검증이 필요하면 미들웨어 훅에서 처리하세요.

모델 (Model)

동적 모델 선택은 런타임 컨텍스트(작업 복잡도, 비용 제약, 사용자 선호 등)에 따라 다른 모델을 고를 수 있게 해줘요. langgraph-prebuilt의 v0.6에서 릴리스된 create_react_agentmodel 파라미터에 전달된 콜러블로 동적 모델·툴 선택을 지원했어요. 이 기능은 v1에서 미들웨어 인터페이스로 포팅됐어요.

동적 모델 선택

# v1 (new)
from langchain.agents import create_agent
from langchain.agents.middleware import (
    AgentMiddleware, ModelRequest
)
from langchain.agents.middleware.types import ModelResponse
from langchain_openai import ChatOpenAI
from typing import Callable

basic_model = ChatOpenAI(model="gpt-5-nano")
advanced_model = ChatOpenAI(model="gpt-5.5")

class DynamicModelMiddleware(AgentMiddleware):

    def wrap_model_call(self, request: ModelRequest, handler: Callable[[ModelRequest], ModelResponse]) -> ModelResponse:
        if len(request.state.messages) > self.messages_threshold:
            model = advanced_model
        else:
            model = basic_model
        return handler(request.override(model=model))

    def __init__(self, messages_threshold: int) -> None:
        self.messages_threshold = messages_threshold

agent = create_agent(
    model=basic_model,
    tools=tools,
    middleware=[DynamicModelMiddleware(messages_threshold=10)]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent, AgentState
from langchain_openai import ChatOpenAI

basic_model = ChatOpenAI(model="gpt-5-nano")
advanced_model = ChatOpenAI(model="gpt-5.5")

def select_model(state: AgentState) -> BaseChatModel:
    # use a more advanced model for longer conversations
    if len(state.messages) > 10:
        return advanced_model
    return basic_model

agent = create_react_agent(
    model=select_model,
    tools=tools,
)

사전 바인딩 모델

구조화 출력을 더 잘 지원하기 위해 create_agent는 툴이나 구성이 바인딩된 사전 바인딩 모델을 더 이상 받지 않아요.

# No longer supported
model_with_tools = ChatOpenAI().bind_tools([some_tool])
agent = create_agent(model_with_tools, tools=[])

# Use instead
agent = create_agent("gpt-5.4-mini", tools=[some_tool])

구조화 출력을 사용하지 않으면 동적 모델 함수는 사전 바인딩 모델을 반환할 수 있어요.

툴 (Tools)

create_agenttools 인자는 다음 목록을 받아요.

  • LangChain BaseTool 인스턴스 (@tool 데코레이터를 쓴 함수)
  • 적절한 타입 힌트와 docstring을 가진 콜러블 객체(함수)
  • 내장 프로바이더 툴을 나타내는 dict

이 인자는 더 이상 ToolNode 인스턴스를 받지 않아요.

# v1 (new)
from langchain.agents import create_agent

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather, search_web]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent, ToolNode


agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=ToolNode([check_weather, search_web]) # [!code highlight]
)

툴 오류 처리

wrap_tool_call 메서드를 구현하는 미들웨어로 툴 오류 처리를 구성할 수 있어요.

# v1 (new)
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain.messages import ToolMessage


@wrap_tool_call
def handle_tool_errors(request, handler):
    """Handle tool execution errors with custom messages."""
    try:
        return handler(request)
    except Exception as e:
        # Only handle errors that occur during tool execution due to invalid inputs
        # that pass schema validation but fail at runtime (e.g., invalid SQL syntax).
        # Do NOT handle:
        # - Network failures (use tool retry middleware instead)
        # - Incorrect tool implementation errors (should bubble up)
        # - Schema mismatch errors (already auto-handled by the framework)
        #
        # Return a custom error message to the model
        return ToolMessage(
            content=f"Tool error: Please check your input and try again. ({str(e)})",
            tool_call_id=request.tool_call["id"]
        )

agent = create_agent(
    model="claude-sonnet-4-6",
    tools=[check_weather, search_web],
    middleware=[handle_tool_errors]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent, ToolNode
from langchain.messages import ToolMessage


def handle_tool_error(error: Exception) -> str:
    """Custom error handler function."""
    return f"Tool error: Please check your input and try again. ({str(error)})"

agent = create_react_agent(
    model="claude-sonnet-4-6",
    tools=ToolNode(
        [check_weather, search_web],
        handle_tool_errors=handle_tool_error  # [!code highlight]
    )
)

구조화 출력 (Structured output)

노드 변경

구조화 출력은 이전에 메인 에이전트와 별도 노드에서 생성됐어요. 이제는 아닙니다. 구조화 출력을 메인 루프에서 생성해 비용과 지연시간을 줄여요.

툴·프로바이더 전략

v1에는 두 가지 새 구조화 출력 전략이 있어요.

  • ToolStrategy — 인공 툴 호출로 구조화 출력 생성
  • ProviderStrategy — 프로바이더 네이티브 구조화 출력 생성
# v1 (new)
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy, ProviderStrategy
from pydantic import BaseModel


class OutputSchema(BaseModel):
    summary: str
    sentiment: str

# Using ToolStrategy
agent = create_agent(
    model="gpt-5.4-mini",
    tools=tools,
    # explicitly using tool strategy
    response_format=ToolStrategy(OutputSchema)  # [!code highlight]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent
from pydantic import BaseModel

class OutputSchema(BaseModel):
    summary: str
    sentiment: str

agent = create_react_agent(
    model="gpt-5.4-mini",
    tools=tools,
    # using tool strategy by default with no option for provider strategy
    response_format=OutputSchema  # [!code highlight]
)

# OR

agent = create_react_agent(
    model="gpt-5.4-mini",
    tools=tools,
    # using a custom prompt to instruct the model to generate the output schema
    response_format=("please generate ...", OutputSchema)  # [!code highlight]
)

프롬프트 기반 출력 제거

**프롬프트 기반 출력(Prompted output)**은 더 이상 response_format 인자를 통해 지원되지 않아요. 인공 툴 호출·프로바이더 네이티브 구조화 출력 같은 전략과 비교해, 프롬프트 기반 출력은 그다지 신뢰할 만하지 않다는 게 입증됐어요.

스트리밍 노드 이름 변경 (Streaming node name rename)

에이전트에서 이벤트를 스트리밍할 때 노드 이름이 node의 목적을 더 잘 반영하도록 "agent"에서 "model"로 바뀌었어요.

런타임 컨텍스트 (Runtime context)

에이전트를 호출할 때 두 종류의 데이터를 전달하고 싶은 경우가 많아요.

  • 대화 중 변하는 동적 상태 (예: 메시지 히스토리)
  • 대화 중 변하지 않는 정적 컨텍스트 (예: 사용자 메타데이터)

v1에서는 정적 컨텍스트를 invokestreamcontext 파라미터로 설정해 지원해요.

# v1 (new)
from dataclasses import dataclass

from langchain.agents import create_agent


@dataclass
class Context:
    user_id: str
    session_id: str

agent = create_agent(
    model=model,
    tools=tools,
    context_schema=Context  # [!code highlight]
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "Hello"}]},
    context=Context(user_id="123", session_id="abc")  # [!code highlight]
)
# v0 (old)
from langgraph.prebuilt import create_react_agent


agent = create_react_agent(model, tools)

# Pass context via configurable
result = agent.invoke(
    {"messages": [{"role": "user", "content": "Hello"}]},
    config={  # [!code highlight]
        "configurable": {  # [!code highlight]
            "user_id": "123",  # [!code highlight]
            "session_id": "abc"  # [!code highlight]
        }  # [!code highlight]
    }  # [!code highlight]
)

예전 config["configurable"] 패턴은 역호환을 위해 여전히 동작하지만, 새 애플리케이션이나 v1로 마이그레이션하는 애플리케이션에는 새 context 파라미터를 권장해요.

표준 콘텐츠 (Standard content)

v1에서 메시지는 프로바이더 무관 표준 콘텐츠 블록을 얻게 돼요. message.content_blocks로 접근해 프로바이더 전반에서 일관되고 타입이 지정된 뷰를 얻을 수 있어요. 기존 message.content 필드는 문자열이나 프로바이더 네이티브 구조에 그대로 유지돼요.

무엇이 바뀌었나

  • 메시지에 새 content_blocks 속성 추가 (정규화된 콘텐츠용)
  • Messages에 문서화된 표준화된 블록 형태
  • LC_OUTPUT_VERSION=v1 또는 output_version="v1"로 표준 블록을 content로 선택적 직렬화

표준화된 콘텐츠 읽기

# v1 (new)
from langchain.chat_models import init_chat_model

model = init_chat_model("gpt-5-nano")
response = model.invoke("Explain AI")

for block in response.content_blocks:
    if block["type"] == "reasoning":
        print(block.get("reasoning"))
    elif block["type"] == "text":
        print(block.get("text"))
# v0 (old)
# Provider-native formats vary; you needed per-provider handling
response = model.invoke("Explain AI")
for item in response.content:
    if item.get("type") == "reasoning":
        ...  # OpenAI-style reasoning
    elif item.get("type") == "thinking":
        ...  # Anthropic-style thinking
    elif item.get("type") == "text":
        ...  # Text

멀티모달 메시지 만들기

# v1 (new)
from langchain.messages import HumanMessage

message = HumanMessage(content_blocks=[
    {"type": "text", "text": "Describe this image."},
    {"type": "image", "url": "https://example.com/image.jpg"},
])
res = model.invoke([message])
# v0 (old)
from langchain.messages import HumanMessage

message = HumanMessage(content=[
    # Provider-native structure
    {"type": "text", "text": "Describe this image."},
    {"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}},
])
res = model.invoke([message])

예제 블록 형태

# Text block
text_block = {
    "type": "text",
    "text": "Hello world",
}

# Image block
image_block = {
    "type": "image",
    "url": "https://example.com/image.png",
    "mime_type": "image/png",
}

콘텐츠 블록 레퍼런스를 더 자세히 보세요.

표준 콘텐츠 직렬화

표준 콘텐츠 블록은 기본적으로 content 속성으로 직렬화되지 않아요. content 속성에서 표준 콘텐츠 블록에 접근해야 한다면(예: 메시지를 클라이언트에 보낼 때) content로 직렬화하도록 옵트인할 수 있어요.

export LC_OUTPUT_VERSION=v1
from langchain.chat_models import init_chat_model

model = init_chat_model(
    "gpt-5-nano",
    output_version="v1",
)

더 보기: Messages, Standard content blocks, Multimodal.

파괴적 변경 (Breaking changes)

Python 3.9 지원 중단

모든 LangChain 패키지는 이제 Python 3.10 이상이 필요해요. Python 3.9는 2025년 10월에 수명 종료됩니다.

chat 모델 반환 타입 업데이트

chat 모델 호출의 반환 타입 시그니처가 BaseMessage에서 AIMessage로 고쳐졌어요. bind_tools를 구현하는 커스텀 chat 모델은 반환 시그니처를 업데이트해야 해요.

# v1 (new)
def bind_tools(
        ...
    ) -> Runnable[LanguageModelInput, AIMessage]:
# v0 (old)
def bind_tools(
        ...
    ) -> Runnable[LanguageModelInput, BaseMessage]:

OpenAI Responses API의 기본 메시지 형식

Responses API와 상호작용할 때 langchain-openai는 이제 기본적으로 응답 항목을 메시지 content에 저장해요. 이전 동작을 복원하려면 LC_OUTPUT_VERSION 환경 변수를 v0으로 설정하거나, ChatOpenAI를 인스턴스화할 때 output_version="v0"을 지정하세요.

# Enforce previous behavior with output_version flag
model = ChatOpenAI(model="gpt-5.4-mini", output_version="v0")

langchain-anthropic의 기본 max_tokens

langchain-anthropicmax_tokens 파라미터는 이제 이전 기본값 1024 대신 선택한 모델에 따라 더 높은 값이 기본이 돼요. 예전 기본값에 의존했다면 명시적으로 max_tokens=1024를 설정하세요.

레거시 코드가 langchain-classic으로 이동

표준 인터페이스와 에이전트의 초점 밖에 있던 기존 기능은 langchain-classic 패키지로 옮겨졌어요. 핵심 langchain 패키지에 무엇이 있고 무엇이 langchain-classic으로 옮겨갔는지는 단순화된 네임스페이스 섹션을 참고해요.

deprecated API 제거

이미 deprecated이고 1.0에서 제거 예정이었던 메서드·함수·기타 객체는 삭제됐어요. 대체 API는 이전 버전의 deprecation 공지를 확인하세요.

Text 속성

메시지 객체의 .text() 메서드는 이제 속성이므로 괄호를 빼야 해요.

# Property access
text = response.text

# Deprecated method call
text = response.text()

기존 사용 패턴(즉, .text())은 계속 동작하지만 경고를 내보내요. 메서드 형태는 v2에서 제거됩니다.

AIMessage에서 example 파라미터 제거

AIMessage 객체에서 example 파라미터가 제거됐어요. 필요한 경우 추가 메타데이터는 additional_kwargs로 전달하는 것을 권장해요.

사소한 변경 (Minor changes)

  • AIMessageChunk 객체는 이제 스트림의 최종 청크를 나타내기 위해 chunk_position 속성('last' 값)을 포함해요. 스트리밍 메시지 처리를 더 명확하게 해주죠. 청크가 최종이 아니면 chunk_positionNone이에요.
  • LanguageModelOutputVarBaseMessage 대신 AIMessage로 타입이 지정됐어요.
  • 메시지 청크 병합(AIMessageChunk.add) 로직이 병합된 청크의 최종 id에 대한 더 정교한 선택 처리를 갖도록 업데이트됐어요. LangChain 생성 ID보다 프로바이더 지정 ID를 우선해요.
  • 이제 기본적으로 utf-8 인코딩으로 파일을 열어요.
  • 표준 테스트는 이제 멀티모달 콘텐츠 블록을 사용해요.

보관된 문서 (Archived docs)

구식 문서는 참고용으로 보관돼 있어요.

더 알아보기 (Learn more)