LangChain Agent - 3. Context Engineering편

선인장과 빨간씨앗·2026년 1월 3일

LangChain Agent

목록 보기
5/7
post-thumbnail

Intro

최근 Agent를 빌드할 때 가장 중요하게 고려되는 사항은 '최소한의 토큰/컨텍스트 윈도우를 활용하여 최대한의 컨텍스트를 모델에 주입하는 방법'입니다.

이번 포스팅에서는 LangChain Agent에서 효율적으로 Context Engineering을 수행하는 방법에 대해 소개합니다.
특히, context, State, checkpointer, Command에 대해서 LangChain Academy 강의에서 다루지 않은 구체적인 내용을 위주로 소개합니다.

이번 포스팅의 내용은 기본적으로 LangGraph의 컴포넌트 (Node, Edge, State) 및 작동 원리에 대한 이해가 필요합니다.


0. 기본 Context Engineering 코드 예시

from dotenv import load_dotenv
from typing import Optional

from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import InMemorySaver
from pydantic import BaseModel

load_dotenv()
chat_model = init_chat_model(
    "openai:gpt-4.1",
    temperature=0
)

class AgentContext(BaseModel):
    user_role: str
    language: str = "en"

class AgentState(BaseModel):
    visit_count: int = 0

checkpointer = InMemorySaver()

agent = create_agent(
    model=chat_model,
    system_prompt="You are a helpful assistant. Answer briefly.",
    context_schema=AgentContext,   # ✅ context
    state_schema=AgentState,       # ✅ custom state
    checkpointer=checkpointer,     # ✅ checkpoint
)

config = {
    "configurable": {
        "thread_id": "context-state-demo"
    }
}

response = agent.invoke(
    {
        "messages": [
            HumanMessage(content="What is LangChain?")
        ],
        # context 입력
        "context": {
            "user_role": "developer",
            "language": "en"
        }
    },
    config=config
)

1. Context

LangChain에서의 Context는 Agent 실행에 앞서 외부에서 주어지는 입력 정보입니다.
사용자가 입력하여 Agent 실행 전 이미 결정되고, 그래프 실행 도중에는 변경되지 않는 것을 전제로 합니다.

context를 아래와 같은 방식으로 활용할 수 있습니다.

  • 해당 내용을 모델에 직접 전달하지 않으면서 tool의 작동을 제어
  • 같은 내용을 content에 전달해서 모델에 의해 변형되는 것을 방지
  • 모델에 전달하더라도 필수 인자/타입 등을 강제

따라서, 아래와 같은 내용을 주로 context로 표현하면 좋습니다.

  • 실행 중 바뀌면 안 됨
  • 외부 시스템의 책임 (Graph나 모델에서 생성 X)
  • checkpoint로 저장할 필요가 없는 내용

(사실 엄밀히 말하면 코드 단계에서는 context_schema로 초기화해도 그래프 State에 포함됩니다.
따라서 context가 '불변'이라는 표현은 '기술적인 제약'이 아니라 '안 바꾸라고 설계된 입력'으로 이해하면 좋을 것 같습니다.)

2. State

State는 그래프 내부에서 실제로 흐르고, 변경되고, 저장되는 유일한 데이터입니다.
LangGraph의 모든 노드에서 공유되며, 주로 Agent의 기억과 실행 기록, 중간 결과 등을 포함합니다.

state는 모든 그래프의 노드를 통과하며 노드의 내용을 구성하는데 활용됩니다.
Agent에서는 messages를 default로 포함하며, 대화 내용이 저장됩니다.
필요에 따라 Custom State를 정의해 활용할 수 있고, context와 달리 그래프 내부에서 업데이트 되는 정보를 담고 있습니다.

Custom State를 사용할 경우, 아래와 같은 내용을 주로 표현하면 좋습니다.

  • 실행하면서 계속 변함
  • Tool 호출 결과 저장
  • 그래프 실행 '상태'를 표현하는 내용 (retry_count, error 등)

3. Command

Command는 state를 직접 반환하지 않고, 실행 흐름을 제어하는 객체입니다.
Langgraph의 노드는 일반적으로 state 자체를 출력하지만, Command를 사용하여 다음에 무엇을 할지 지시하는 제어 신호를 출력할 수 있습니다.

Command의 argumment와 역할은 아래와 같습니다.

  • goto: 다음 실행할 노드 지정
  • update: state 일부 수정
  • resume: (특수 케이스) Human-in-the-Loop 등 중단 후 재개 정보 제공

4. Checkpoint

Checkpoint는 특정 시점의 state를 저장하는 매커니즘입니다.
이를 통해 Memory, 디버깅, Human-in-the-Loop 등의 기능을 추가할 수 있습니다.
그 시점에 어떤 노드를 지나고 있고, 어떤 내용이 입/출력 되고 있는지 등에 대한 정보가 담깁니다.

4.1. Checkpoint 데이터 구조

  • State: 상태 데이터
  • Config: 설정 정보 (thread_id, checkpoint_id 등)
  • Next: 다음 노드
  • Metadata: state에 대한 메타데이터 (몇 번째 단계인지 등)
  • Parent ID: 이전 단계의 체크포인트 ID

4.2. 프로세스
1. 노드 실행
2. 상태 업데이트
3. checkpoint 저장

(checkpoint의 데이터 값을 실제로 확인해보고 싶은 분들은 Appendix를 참고하세요.)

4.3. Memory 종류
1. MemorySaver: RAM에 저장 (주로 테스트/데모 개발용)
2. SqliteSaver: .sqlite 단일 파일에 저장 (주로 개인 프로젝트용; 여기부터 휘발성X)
3. PostgresSaver: PostgreSQL DB 사용 (주로 실서비스/대규모 운영용)
4. RedisSaver: Redis DB 사용 (주로 실서비스/대규모 운영용)


Outro

이번 포스팅에서 다룬 component들은 에이전트를 초기화할 때, 해당 에이전트가 가지고 있어야 할 기본적인 상태와 기능을 정의하는 용도라고 볼 수 있습니다.

Langchain에서는 이를 조금 더 동적으로 사용하기 위해서 Middleware라는 추상화 모듈을 제공합니다. 실제로 대부분의 에이전트에 사용되면 좋을 기능들은 pre-built Middleware 형태로 langchain/langgraph library에 함께 공유되어 있고, 데코레이터를 활용해 쉽게 custom Middleware를 만들어 사용할 수도 있습니다.

따라서 다음 포스팅(3. Middleware편)에서는 Middleware의 개념과 종류, 구현 방법에 대해서 자세히 다뤄보도록 하겠습니다.


Appendix

Checkpoint 확인 코드

from typing import TypedDict, List

from langgraph.checkpoint.memory import MemorySaver
from langchain.agents import create_agent

from langchain_openai import ChatOpenAI
from langchain_core.messages import BaseMessage, HumanMessage

from dotenv import load_dotenv

load_dotenv()

# =========================
# 1. State (create_agent 기본형)
# =========================
class AgentState(TypedDict):
    messages: List[BaseMessage]


# =========================
# 2. LLM
# =========================
llm = ChatOpenAI(model="gpt-4o-mini")


# =========================
# 3. In-Memory Checkpointer
# =========================
checkpointer = MemorySaver()


# =========================
# 4. create_agent (tool 없이 순수 agent)
# =========================
agent = create_agent(
    model=llm,
    tools=[],
    state_schema=AgentState,
    checkpointer=checkpointer,
)


# =========================
# 5. 실행 config (중요)
# =========================
config = {
    "configurable": {
        "thread_id": "create-agent-demo"
    }
}


# =========================
# 6. Agent 실행 (checkpoint 저장됨)
# =========================
agent.invoke(
    {
        "messages": [
            HumanMessage(content="LangGraph에서 checkpoint는 뭐야?")
        ]
    },
    config=config
)


# =========================
# 7. 🔥 checkpoint 실제 값 확인
# =========================

from pprint import pprint

print("\n===== RAW CHECKPOINT =====")
checkpoint = checkpointer.get(config)
pprint(checkpoint)

snapshot = agent.get_state(config)

print("\n===== RAW SNAPSHOT =====")
pprint(snapshot)

결과 (pprint)

StateSnapshot(
    values={
        "messages": [
            AIMessage(
                content=(
                    'LangGraph에서 "checkpoint"는 그래프 실행 중 특정 시점의 '
                    'state와 실행 위치를 저장하는 스냅샷입니다.\n\n'
                    '이를 통해 다음과 같은 작업이 가능합니다:\n'
                    '1. 실행 중단 후 재개\n'
                    '2. Long-running Agent 복구\n'
                    '3. Human-in-the-loop 워크플로우 구현'
                )
            )
        ]
    },
    next=(),
    config={
        "configurable": {
            "thread_id": "create-agent-demo",
            "checkpoint_id": "..."
        }
    },
    metadata={
        "source": "loop",
        "step": 1
    },
    created_at="2026-01-03T15:00:07Z",
    parent_config={
        "configurable": {
            "thread_id": "create-agent-demo",
            "checkpoint_id": "..."
        }
    },
    tasks=(),
    interrupts=()
)

0개의 댓글