LangServe로 배포하기

LangServe로 배포하기#

LangServe는 LangChain 런너블(runnable) 및 체인을 REST API로 배포할 수 있도록 돕는 도구입니다. 이 라이브러리는 FastAPI와 통합되어 있으며, 데이터 검증을 위해 Pydantic을 사용합니다. 또한, 서버에 배포된 런너블을 호출할 수 있는 클라이언트와 LangChainJS에서 사용할 수 있는 JavaScript 클라이언트를 제공합니다.

특징#

  • 입력 및 출력 스키마가 LangChain 객체에서 자동으로 추론되어 각 API 호출 시 엄격하게 적용됩니다. 이와 함께 풍부한 에러 메시지가 제공됩니다.

  • JSONSchema와 Swagger를 포함한 API 문서 페이지가 제공됩니다.

  • /invoke, /batch, /stream 엔드포인트는 단일 서버에서 많은 동시 요청을 처리할 수 있도록 효율적으로 설계되었습니다.

  • /stream_log 엔드포인트를 통해 체인/에이전트의 모든 중간 단계를 스트리밍할 수 있습니다.

  • /playground 페이지에서 스트리밍 출력과 중간 단계를 사용하여 런너블을 구성하고 호출할 수 있습니다.

  • (선택 사항으로) LangSmith로의 내장 추적 기능을 추가할 수 있습니다. API 키만 추가하면 됩니다.

  • FastAPI, Pydantic, uvloop, asyncio와 같은 오픈 소스 Python 라이브러리를 사용하여 안정성이 보장됩니다.

한계#

  • 서버에서 발생하는 이벤트에 대해 클라이언트 콜백을 지원하지 않습니다.

  • Pydantic V2를 사용할 때 OpenAPI 문서가 생성되지 않습니다. FastAPI는 pydantic v1과 v2 네임스페이스를 혼용하는 것을 지원하지 않습니다. 자세한 내용은 아래 섹션을 참조하십시오.

LangServe 프로젝트 부트스트랩#

LangChain CLI를 사용하여 LangServe 프로젝트를 빠르게 부트스트랩할 수 있습니다. 이를 위해 최신 버전의 langchain-cli가 설치되어 있는지 확인하십시오. 설치는 다음 명령어를 사용합니다:

pip install -U langchain-cli

LangChain CLI를 사용하여 LangServe 인스턴스를 빠르게 시작할 수 있습니다. 다음 명령어를 사용하여 새로운 LangServe 프로젝트를 생성합니다:

langchain app new ./path/to/directory

다음 예제는 OpenAI 채팅 모델, Anthropic 채팅 모델, 그리고 주제에 대해 농담을 하는 체인을 배포하는 서버입니다.

#!/usr/bin/env python
from fastapi import FastAPI
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatAnthropic, ChatOpenAI
from langserve import add_routes

app = FastAPI(
  title="LangChain Server",
  version="1.0",
  description="A simple api server using Langchain's Runnable interfaces",
)

add_routes(
    app,
    ChatOpenAI(),
    path="/openai",
)

add_routes(
    app,
    ChatAnthropic(),
    path="/anthropic",
)

model = ChatAnthropic()
prompt = ChatPromptTemplate.from_template("tell me a joke about {topic}")
add_routes(
    app,
    prompt | model,
    path="/chain",
)

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="localhost", port=8000)

서버를 배포한 후, 생성된 OpenAPI 문서를 다음 명령어로 확인할 수 있습니다:

curl localhost:8000/docs

/docs 접미사를 추가해야 합니다.

다음은 다양한 엔드포인트를 사용하는 Python 코드입니다:

from langchain.schema import SystemMessage, HumanMessage
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnableMap
from langserve import RemoteRunnable

openai = RemoteRunnable("http://localhost:8000/openai/")
anthropic = RemoteRunnable("http://localhost:8000/anthropic/")
joke_chain = RemoteRunnable("http://localhost:8000/chain/")

joke_chain.invoke({"topic": "parrots"})

# or async
await joke_chain.ainvoke({"topic": "parrots"})

prompt = [
    SystemMessage(content='Act like either a cat or a parrot.'),
    HumanMessage(content='Hello!')
]

# Supports astream
async for msg in anthropic.astream(prompt):
    print(msg, end="", flush=True)

prompt = ChatPromptTemplate.from_messages(
    [("system", "Tell me a long story about {topic}")]
)

# Can define custom chains
chain = prompt | RunnableMap({
    "openai": openai,
    "anthropic": anthropic,
})

chain.batch([{"topic": "parrots"}, {"topic": "cats"}])

JavaScript에서는 다음과 같이 사용할 수 있습니다(버전 0.0.166 이상의 LangChain.js 필요):

import { RemoteRunnable } from "langchain/runnables/remote";

const chain = new RemoteRunnable({
  url: `http://localhost:8000/chain/invoke/`,
});
const result = await chain.invoke({
  topic: "cats",
});

Python의 requests를 사용하여 호출할 수도 있습니다:

import requests
response = requests.post(
    "http://localhost:8000/chain/invoke/",
    json={'input': {'topic': 'cats'}}
)
response.json()

curl을 사용한 예제:

curl --location --request POST 'http://localhost:8000/chain/invoke/' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "input": {
            "topic": "cats"
        }
    }'

다음 코드는 서버에 엔드포인트를 추가합니다:

...
add_routes(
  app,
  runnable,
  path="/my_runnable",
)

이 코드는 다음 엔드포인트를 서버에 추가합니다:

  • POST /my_runnable/invoke - 단일 입력에 대해 런너블 호출

  • POST /my_runnable/batch - 입력 배치에 대해 런너블 호출

  • POST /my_runnable/stream - 단일 입력에 대해 호출하고 출력 스트리밍

  • POST /my_runnable/stream_log - 단일 입력에 대해 호출하고 생성된 모든 중간 단계를 포함하여 출력 스트리밍

  • GET /my_runnable/input_schema - 런너블의 입력에 대한 JSON 스키마

  • GET /my_runnable/output_schema - 런너블의 출력에 대한 JSON 스키마

  • GET /my_runnable/config_schema - 런너블의 설정에 대한 JSON 스키마

런너블에 대한 playground 페이지는 /my_runnable/playground에서 확인할 수 있습니다. 이 페이지에서는 스트리밍 출력과 중간 단계를 사용하여 런너블을 구성하고 호출할 수 있습니다.

클라이언트 및 서버 모두 다음 명령어로 설치할 수 있습니다:

pip install "langserve[all]"

또는 클라이언트 코드용으로는 pip install "langserve[client]", 서버 코드용으로는 pip install "langserve[server]"를 사용합니다.

서버에 인증을 추가해야 하는 경우, FastAPI의 보안 문서와 미들웨어 문서를 참조하십시오.

GCP Cloud Run에 배포하려면 다음 명령어를 사용하십시오:

gcloud run deploy [your-service-name] --source . --port 8001 --allow-unauthenticated --region us-central1 --set-env-vars=OPENAI_API_KEY=your_key

LangServe는 Pydantic 2를 일부 제한 사항과 함께 지원합니다. Pydantic V2를 사용할 때 invoke/batch/stream/stream_log에 대한 OpenAPI 문서가 생성되지 않습니다. FastAPI는 pydantic v1과 v2 네임스페이스를 혼용하는 것을 지원하지 않습니다. LangChain은 Pydantic v2에서 v1 네임스페이스를 사용합니다. 이 제한 사항 외에는 API 엔드포인트, playground, 기타 기능이 예상대로 작동해야 합니다.

LLM 애플리케이션은 종종 파일을 처리합니다. 파일 처리를 구현하기 위한 다양한 아키텍처가 있습니다. 높은 수준에서 다음과 같은 방법이 있습니다:

  • 파일을 전용 엔드포인트를 통해 서버에 업로드하고 별도의 엔드포인트를 사용하여 처리합니다.

  • 파일을 값(파일의 바이트) 또는 참조(e.g., s3 URL로 파일 내용)로 업로드할 수 있습니다.

  • 처리 엔드포인트는 블로킹 또는 논블로킹일 수 있습니다.

  • 상당한 처리가 필요한 경우 전용 프로세스 풀로 처리를 오프로드할 수 있습니다.

애플리케이션에 적합한 아키텍처를 결정해야 합니다. 현재 값으로 파일을 업로드하려면 base64 인코딩을 사용하여 런너블로 업로드합니다(multipart/form-data는 아직 지원되지 않습니다).

다음은 base64 인코딩을 사용하여 파일을 원격 런너블에 보내는 방법을 보여주는 예제입니다:

try:
    from pydantic.v1 import Field
except ImportError:
    from pydantic import Field

from langserve import CustomUserType

# CustomUserType을 상속받지 않고 BaseModel을 상속받으면 서버는 이를 dict로 디코딩합니다.
class FileProcessingRequest(CustomUserType):
    """base64로 인코딩된 파일을 포함하는 요청입니다."""

    # 추가 필드는 playground UI용 위젯을 지정하는 데 사용됩니다.
    file: str = Field(..., extra={"widget": {"type": "base64file"}})
    num_chars: int = 100