다른 프레임워크 배포하기

다른 프레임워크 배포하기

Claude Agent SDK, Strands, CrewAI, AutoGen 및 다른 프레임워크로 만든 에이전트를 LangSmith Deployment에 배포해요.

이 가이드는 Functional API를 사용해 Strands AgentLangSmith Deployment에 배포하고 LangSmith Observability에 대한 추적을 설정하는 방법을 보여줘요. CrewAI, AutoGen, Google ADK 같은 다른 프레임워크에도 같은 접근 방식을 적용할 수 있어요.

LangSmith Deployment는 모든 프레임워크를 실행해요. Deep Agents, LangChain 또는 LangGraph로 만든 에이전트가 아닌 경우, deployments-wrap-sdk 패키지(Google ADK) 또는 LangGraph Functional API(Claude Agent SDK, Strands, CrewAI, AutoGen 및 기타 라이브러리) 중 하나를 사용해 배포하세요.

새로 만들 때는 Deep Agents를 고려하세요. 이는 계획하고, 도구를 사용하고, 하위 에이전트에 위임하고, 긴 기간 동안 작업하는 에이전트를 위한 오픈 소스 하네스예요. Deep Agents는 LangSmith Deployment에 직접 배포되며, 완전히 호스팅된 런타임용 Managed Deep Agents를 사용할 수 있어요.

출처: 문서

본문

지원되는 프레임워크

이 가이드에는 다음 프레임워크에 대한 종단 간 예시가 있어요. 각 예시는 Agent Server가 서빙할 수 있는 LangGraph 호환 그래프를 agent.py에서 내보내요:

프레임워크가 보이지 않나요? Functional API는 모든 callable을 허용하므로, 다음 예시에 보이는 것과 같은 패턴을 어떤 에이전트 라이브러리에도 적용할 수 있어요. 에이전트의 엔트리포인트를 @task@entrypoint로 래핑한 다음 배포하세요.

Functional API 작동 방식

Functional API로 래핑된 에이전트에 대해 Agent Server에 런이 도착하면:

  1. 플랫폼이 런 입력과 같은 스레드의 이전 턴에서 저장된 상태(previous 인수로 전달)로 @entrypoint 데코레이터가 붙은 agent 함수를 호출해요.
  2. 엔트리포인트가 @task 데코레이터가 붙은 함수를 호출하고, 그 함수가 프레임워크 에이전트(Claude Agent SDK, Strands, CrewAI, AutoGen 또는 다른 라이브러리)에 위임해요.
  3. 엔트리포인트는 entrypoint.final(value=..., save=...)을 반환해요. value는 이 턴의 응답이고, save는 다음 턴의 previous로 사용되는 체크포인트된 상태예요.
  4. Agent Server가 체크포인트를 영구화하고, 지원될 때 부분 출력을 스트리밍하며, 추적이 구성되면 트레이스를 기록해요.

이 패턴은 프레임워크의 실행 의미론을 보존하면서 표준 Agent Server 기능(내구성 있는 런, 멀티 스레드 영속성, 스트리밍 엔드포인트, LangSmith 관찰 가능성)을 제공해요.

사전 요구 사항

프레임워크와 관계없이 다음이 필요해요:

  • Functional API 프레임워크용 Python 3.10+ (Strands Agents는 Python 3.9+ 지원)
  • LangSmith API 키

일반 배포 패턴

각 프레임워크에 대해 같은 단계를 따르세요. 각 단계의 탭에서 스택을 선택하고, 스니펫을 하나의 모듈(예: agent.py)에 결합하고, @entrypoint 데코레이터가 붙은 함수를 agent라는 이름의 모듈 수준 변수로 내보내세요. 종단 간 예시 섹션에 복사할 수 있는 완전한 파일이 있어요.

1. 의존성 설치

프레임워크용 Python 패키지에 LangGraph와 LangSmith를 더해 설치하세요.

Claude Agent SDKClaude Agent SDK용:

pip install "langsmith[claude-agent-sdk]" langgraph "langgraph-cli[inmem]"

환경에 ANTHROPIC_API_KEY를 설정하세요. Anthropic API 키는 Claude 콘솔을 참고하세요.

Strands AgentsStrands Agents용:

pip install strands-agents strands-agents-tools langgraph "langsmith[strands-agents]" "langgraph-cli[inmem]"

모델 프로바이더로 Amazon Bedrock을 사용한다면 AWS 자격 증명을 구성하세요.

CrewAICrewAI용:

pip install crewai langgraph langsmith opentelemetry-instrumentation-crewai opentelemetry-instrumentation-openai "langgraph-cli[inmem]"

환경에 LLM 프로바이더 자격 증명을 설정하세요 (예: OpenAI 기반 모델을 사용한다면 OPENAI_API_KEY).

AutoGenAutoGen용:

pip install autogen-agentchat autogen-ext langgraph langsmith opentelemetry-instrumentation-openai "langgraph-cli[inmem]"

환경에 OPENAI_API_KEY(또는 모델 프로바이더 자격 증명)를 설정하세요.

2. 에이전트 정의

LangSmith 밖에서 하던 것과 정확히 같은 방식으로 선택한 프레임워크로 에이전트를 만드세요.

Claude Agent SDK:

from claude_agent_sdk import ClaudeAgentOptions

options = ClaudeAgentOptions(
    model="claude-sonnet-4-6",
    system_prompt="You are a helpful assistant.",
)

Strands Agents:

from strands import Agent

strands_agent = Agent(
    system_prompt="You are a helpful assistant.",
    model="us.anthropic.claude-sonnet-4-20250514-v1:0",
)

CrewAI:

from crewai import Agent as CrewAgent, Crew, Task

researcher = CrewAgent(role="Researcher", goal="Research a topic", backstory="Expert researcher.")
crew = Crew(
    agents=[researcher],
    tasks=[Task(description="{topic}", agent=researcher, expected_output="A short report.")],
)

AutoGen:

from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient

assistant = AssistantAgent(
    name="assistant",
    model_client=OpenAIChatCompletionClient(model="gpt-4o"),
)

3. Functional API로 래핑

에이전트를 @entrypoint 데코레이터가 붙은 agent라는 함수로 노출하세요. 내부에서 프레임워크를 호출하는 작업 단위에 @task를 사용하세요. entrypoint.final()을 사용해 응답을 반환하고 같은 스레드의 턴 간에 대화 기록을 영구화하세요.

Claude Agent SDK:

import operator

from claude_agent_sdk import ClaudeSDKClient
from langgraph.func import entrypoint, task

@task
async def invoke_claude(prompt: str) -> str:
    async with ClaudeSDKClient(options=options) as client:
        await client.query(prompt)
        chunks: list[str] = []
        async for message in client.receive_response():
            chunks.append(str(message))
        return "\n".join(chunks)

@entrypoint()
async def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    prompt = history[-1]["content"]
    response = await invoke_claude(prompt)
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(
        value=[new_message],
        save=operator.add(history, [new_message]),
    )

Strands Agents:

import operator

from langgraph.func import entrypoint, task
from strands.types.content import Message

@task
def invoke_strands(messages: list[Message]):
    result = strands_agent(messages)
    return [result.message]

@entrypoint()
def agent(messages: list[Message], previous: list[Message] | None = None):
    messages = operator.add(previous or [], messages)
    response = invoke_strands(messages).result()
    return entrypoint.final(value=response, save=operator.add(messages, response))

CrewAI:

import operator

from langgraph.func import entrypoint, task

@task
def run_crew(topic: str) -> str:
    return str(crew.kickoff(inputs={"topic": topic}))

@entrypoint()
def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    response = run_crew(history[-1]["content"]).result()
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(value=[new_message], save=operator.add(history, [new_message]))

AutoGen:

import operator

from langgraph.func import entrypoint, task

@task
async def invoke_autogen(prompt: str) -> str:
    result = await assistant.run(task=prompt)
    return result.messages[-1].content

@entrypoint()
async def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    response = await invoke_autogen(history[-1]["content"])
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(value=[new_message], save=operator.add(history, [new_message]))

4. 추적 구성

프레임워크의 네이티브 트레이스를 LangSmith로 전달하세요. 에이전트를 만들거나 호출하기 전에 애플리케이션 시작 시 추적 설정을 한 번 호출하세요.

Claude Agent SDK:

from langsmith.integrations.claude_agent_sdk import configure_claude_agent_sdk

configure_claude_agent_sdk()

전체 설정 세부 사항은 Claude Agent SDK 애플리케이션 추적을 참고하세요.

Strands Agents:

LangSmith API 키와 프로젝트 이름을 설정하세요. Amazon Bedrock을 사용한다면 AWS 자격 증명도 구성하세요.

from langsmith.integrations.strands_agents import setup_langsmith_telemetry

setup_langsmith_telemetry()

LangSmith 셀프 호스팅이라면 배포용 OpenTelemetry OTLP 엔드포인트와 헤더를 구성하세요. Strands Agents 애플리케이션 추적을 참고하세요.

Strands의 OTel 추적에는 동기 코드가 포함돼 있어요. Agent Server에 배포할 때 BG_JOB_ISOLATED_LOOPS=true를 설정해야 할 수 있어요. BG_JOB_ISOLATED_LOOPS를 참고하세요.

전체 설정 세부 사항은 Strands Agents 애플리케이션 추적을 참고하세요.

CrewAI:

CrewAI 및 OpenAI 계측기로 LangSmith 스팬 프로세서를 등록하세요:

from langsmith.integrations.otel import OtelSpanProcessor
from opentelemetry import trace
from opentelemetry.instrumentation.crewai import CrewAIInstrumentor
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.sdk.trace import TracerProvider

current_provider = trace.get_tracer_provider()
if isinstance(current_provider, TracerProvider):
    tracer_provider = current_provider
else:
    tracer_provider = TracerProvider()
    trace.set_tracer_provider(tracer_provider)

tracer_provider.add_span_processor(OtelSpanProcessor())
CrewAIInstrumentor().instrument(tracer_provider=tracer_provider)
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

전체 설정 세부 사항은 CrewAI 애플리케이션 추적을 참고하세요.

AutoGen:

OpenAI 계측기로 LangSmith 스팬 프로세서를 등록하세요:

from langsmith.integrations.otel import OtelSpanProcessor
from opentelemetry import trace
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.sdk.trace import TracerProvider

tracer_provider = TracerProvider()
tracer_provider.add_span_processor(OtelSpanProcessor())
trace.set_tracer_provider(tracer_provider)
OpenAIInstrumentor().instrument()

전체 설정 세부 사항은 AutoGen 애플리케이션 추적을 참고하세요.

종단 간 예시

다음 예시는 에이전트 정의, Functional API 래핑, 추적 설정, agent 심볼 내보내기를 단일 agent.py 파일로 결합해요. 프레임워크 탭을 선택하세요.

import operator

from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient
from langgraph.func import entrypoint, task
from langsmith.integrations.claude_agent_sdk import configure_claude_agent_sdk

configure_claude_agent_sdk()

options = ClaudeAgentOptions(
    model="claude-sonnet-4-6",
    system_prompt="You are a helpful assistant.",
)

@task
async def invoke_claude(prompt: str) -> str:
    async with ClaudeSDKClient(options=options) as client:
        await client.query(prompt)
        chunks: list[str] = []
        async for message in client.receive_response():
            chunks.append(str(message))
        return "\n".join(chunks)

@entrypoint()
async def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    prompt = history[-1]["content"]
    response = await invoke_claude(prompt)
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(
        value=[new_message],
        save=operator.add(history, [new_message]),
    )

위는 Claude Agent SDK의 종단 간 예시입니다. 다음은 나머지 프레임워크의 완전한 agent.py입니다.

Strands Agents agent.py:

import operator

from langgraph.func import entrypoint, task
from langsmith.integrations.strands_agents import setup_langsmith_telemetry
from strands import Agent
from strands.types.content import Message

setup_langsmith_telemetry()

strands_agent = Agent(
    system_prompt="You are a helpful assistant.",
    model="us.anthropic.claude-sonnet-4-20250514-v1:0",
)

@task
def invoke_strands(messages: list[Message]):
    result = strands_agent(messages)
    return [result.message]

@entrypoint()
def agent(messages: list[Message], previous: list[Message] | None = None):
    messages = operator.add(previous or [], messages)
    response = invoke_strands(messages).result()
    return entrypoint.final(value=response, save=operator.add(messages, response))

CrewAI agent.py:

import operator

from crewai import Agent as CrewAgent, Crew, Task
from langgraph.func import entrypoint, task
from langsmith.integrations.otel import OtelSpanProcessor
from opentelemetry import trace
from opentelemetry.instrumentation.crewai import CrewAIInstrumentor
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.sdk.trace import TracerProvider

current_provider = trace.get_tracer_provider()
if isinstance(current_provider, TracerProvider):
    tracer_provider = current_provider
else:
    tracer_provider = TracerProvider()
    trace.set_tracer_provider(tracer_provider)

tracer_provider.add_span_processor(OtelSpanProcessor())
CrewAIInstrumentor().instrument(tracer_provider=tracer_provider)
OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

researcher = CrewAgent(role="Researcher", goal="Research a topic", backstory="Expert researcher.")
crew = Crew(
    agents=[researcher],
    tasks=[Task(description="{topic}", agent=researcher, expected_output="A short report.")],
)

@task
def run_crew(topic: str) -> str:
    return str(crew.kickoff(inputs={"topic": topic}))

@entrypoint()
def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    response = run_crew(history[-1]["content"]).result()
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(value=[new_message], save=operator.add(history, [new_message]))

AutoGen agent.py:

import operator

from autogen_agentchat.agents import AssistantAgent
from autogen_ext.models.openai import OpenAIChatCompletionClient
from langgraph.func import entrypoint, task
from langsmith.integrations.otel import OtelSpanProcessor
from opentelemetry import trace
from opentelemetry.instrumentation.openai import OpenAIInstrumentor
from opentelemetry.sdk.trace import TracerProvider

tracer_provider = TracerProvider()
tracer_provider.add_span_processor(OtelSpanProcessor())
trace.set_tracer_provider(tracer_provider)
OpenAIInstrumentor().instrument()

assistant = AssistantAgent(
    name="assistant",
    model_client=OpenAIChatCompletionClient(model="gpt-4o"),
)

@task
async def invoke_autogen(prompt: str) -> str:
    result = await assistant.run(task=prompt)
    return result.messages[-1].content

@entrypoint()
async def agent(messages: list[dict], previous: list[dict] | None = None):
    history = operator.add(previous or [], messages)
    response = await invoke_autogen(history[-1]["content"])
    new_message = {"role": "assistant", "content": response}
    return entrypoint.final(value=[new_message], save=operator.add(history, [new_message]))

모든 예시에 필수적인 두 가지:

  1. @entrypoint 데코레이터가 붙은 함수를 모듈 범위에서 agent로 내보내세요. Agent Server는 그래프를 서빙할 때 이 심볼을 import해요.
  2. save 인수와 함께 entrypoint.final()을 반환하세요 그래야 대화 상태가 같은 스레드의 턴 간에 유지돼요.

프로젝트 구조

배포 가능한 프로젝트에는 다음 파일이 필요해요:

my-agent/
├── agent.py              # exports the agent graph
├── langgraph.json        # Agent Server config
├── pyproject.toml        # Python dependencies
└── .env                  # Provider credentials and LangSmith variables

langgraph.json은 Agent Server를 내보낸 심볼로 안내해요.

Claude Agent SDK:

{
  "$schema": "https://langgra.ph/schema.json",
  "dependencies": ["."],
  "graphs": {
    "claude_agent": "./agent.py:agent"
  },
  "env": ".env"
}
[project]
name = "my-claude-agent"
version = "0.0.1"
requires-python = ">=3.10"
dependencies = [
    "langsmith[claude-agent-sdk]>=0.3.0",
    "langgraph>=0.4.0",
]
LANGSMITH_API_KEY=your-langsmith-api-key
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=my-claude-agent
ANTHROPIC_API_KEY=your-anthropic-api-key

Strands Agents:

{
  "$schema": "https://langgra.ph/schema.json",
  "dependencies": ["."],
  "graphs": {
    "strands_agent": "./agent.py:agent"
  },
  "env": ".env"
}
[project]
name = "my-strands-agent"
version = "0.0.1"
requires-python = ">=3.9"
dependencies = [
    "strands-agents>=0.1.0",
    "strands-agents-tools>=0.1.0",
    "langsmith[strands-agents]>=0.3.0",
    "langgraph>=0.4.0",
]
LANGSMITH_API_KEY=your-langsmith-api-key
LANGSMITH_TRACING=true
LANGSMITH_PROJECT=my-strands-agent
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.smith.langchain.com/otel/v1/traces
OTEL_EXPORTER_OTLP_HEADERS=x-api-key=your-langsmith-api-key,Langsmith-Project=my-strands-agent
AWS_REGION=your-aws-region
AWS_PROFILE=your-aws-profile

CrewAI:

{
  "$schema": "https://langgra.ph/schema.json",
  "dependencies": ["."],
  "graphs": {
    "crewai_agent": "./agent.py:agent"
  },
  "env": ".env"
}
[project]
name = "my-crewai-agent"
version = "0.0.1"
requires-python = ">=3.10"
dependencies = [
    "crewai>=0.100.0",
    "langgraph>=0.4.0",
    "langsmith>=0.3.0",
    "opentelemetry-instrumentation-crewai>=0.1.0",
    "opentelemetry-instrumentation-openai>=0.1.0",
]
LANGSMITH_API_KEY=your-langsmith-api-key
LANGSMITH_PROJECT=my-crewai-agent
OPENAI_API_KEY=your-openai-api-key

AutoGen:

{
  "$schema": "https://langgra.ph/schema.json",
  "dependencies": ["."],
  "graphs": {
    "autogen_agent": "./agent.py:agent"
  },
  "env": ".env"
}
[project]
name = "my-autogen-agent"
version = "0.0.1"
requires-python = ">=3.10"
dependencies = [
    "autogen-agentchat>=0.4.0",
    "autogen-ext>=0.4.0",
    "langgraph>=0.4.0",
    "langsmith>=0.3.0",
    "opentelemetry-instrumentation-openai>=0.1.0",
]
LANGSMITH_API_KEY=your-langsmith-api-key
LANGSMITH_PROJECT=my-autogen-agent
OPENAI_API_KEY=your-openai-api-key

의존성 설치

프로젝트 디렉터리에서:

pip install -e .

추적 활성화

프로젝트 구조의 프레임워크별 .env 템플릿을 사용하세요. langgraph.json"env": ".env"가 설정돼 있으면 Agent Server가 이 파일을 로드해요.

그 파일에 LANGSMITH_PROJECT와 프레임워크 프로바이더 자격 증명을 설정하세요. Claude Agent SDK와 Strands Agents는 LANGSMITH_TRACING=true도 설정하세요. CrewAI와 AutoGen의 경우 추적은 agent.py에서 OtelSpanProcessor()와 프레임워크 계측기를 통해 활성화되므로 LANGSMITH_API_KEYLANGSMITH_PROJECT만 설정하세요.

트레이스LangSmith UI에서 에이전트 호출, 도구 호출, LLM 상호작용을 보여줘요. 프레임워크별 추적 옵션은 추적 구성의 링크를 참고하세요.

로컬에서 실행

LangGraph CLI로 로컬 Agent Server를 시작하세요:

langgraph dev

langgraph devlanggraph-api가 없다고 보고하면 같은 환경에 langgraph-cli[inmem]을 설치하세요.

이 명령은 http://127.0.0.1:2024에서 에이전트를 서빙하고 LangSmith Studio를 열어요. curl로 요청을 보내세요:

langgraph dev는 다른 포트에서 서빙할 수 있어요. 터미널 출력의 URL을 확인하고 필요하면 아래 curl 명령을 업데이트하세요.

Claude Agent SDK, CrewAI, AutoGen:

# Create a thread
THREAD=$(curl -s -X POST http://127.0.0.1:2024/threads \
  -H "Content-Type: application/json" -d '{}' | python -c "import sys, json; print(json.load(sys.stdin)['thread_id'])")

# Run the agent and wait for the final response
curl -s -X POST "http://127.0.0.1:2024/threads/$THREAD/runs/wait" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "ASSISTANT_ID",
    "input": [{"role": "user", "content": "Hello"}]
  }'

Strands Agents:

# Create a thread
THREAD=$(curl -s -X POST http://127.0.0.1:2024/threads \
  -H "Content-Type: application/json" -d '{}' | python -c "import sys, json; print(json.load(sys.stdin)['thread_id'])")

# Run the agent and wait for the final response
curl -s -X POST "http://127.0.0.1:2024/threads/$THREAD/runs/wait" \
  -H "Content-Type: application/json" \
  -d '{
    "assistant_id": "ASSISTANT_ID",
    "input": [
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "Hello"}
        ]
      }
    ]
  }'

이 요청이 NoCredentialsError로 실패하면 모델 프로바이더용 AWS 자격 증명(예: AWS_PROFILE 또는 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY)을 구성하고 langgraph dev를 다시 시작하세요.

ASSISTANT_IDlanggraph.json graphs 객체의 그래프 키로 바꾸세요. 예를 들어 구성이 "graphs": {"claude_agent": "./agent.py:agent"}이면 claude_agent를 사용하고, "graphs": {"strands_agent": "./agent.py:agent"}이면 strands_agent를 사용하세요.

배포하기 전에 LangGraph API가 로컬에서 실행되는지 확인하세요. langgraph dev가 실패하면 LangSmith에 배포도 실패할 거예요.

LangSmith에 배포

에이전트가 로컬에서 실행되면 langgraph deploy로 배포하세요:

langgraph deploy --name my-agent

환경 구성, 배포 유형, 리비전 관리는 클라우드에 배포를 참고하세요. 셀프 호스팅 설정은 셀프 호스팅 배포를 참고하세요. 컨트롤 플레인 없이 Docker 전용 호스팅은 독립 실행형 배포를 참고하세요.

더 알아보기 (Learn more)