인공지능 개발자 수다(유튜브 바로가기) 자세히보기

자연어처리/Langchain

[Langchain] Langchain v0.3 패치노트

Suda_777 2024. 9. 28. 15:26

목차

    반응형

    1. 변경 사항

    • 내부적으로 모든 패키지가 Pydantic 1에서 Pydantic 2로 업그레이드됨. 이제 Pydantic 2를 완전히 지원
    • Pydantic 1은 2024년 6월에 지원 종료
    • Python 3.8은 2024년 10월에 지원이 종료

    2. 새롭게 추가된 기능

    2.1. the latest integration packages 패치,

    • 기존 langchain-community 의 내용이 integration packages로 많이 넘어감 
      •  langchain-openai
      •  langchain-anthropic
      •  langchain-google-vertexai
      •  langchain-aws
      •  langchain-huggingface
      •  langchain-mistralai

    2.2. tool의 정의와 사용이 단순화됨

    2.2.1. Tool 정의 간소화

    • bind_tools() 함수는 일반적인 Python 함수를 LangChain 모델이 호출할 수 있는 도구(tool)로 변환합니다.
    • 예를 들어, 이 예시에서 validate_user 함수는 원래 단순한 Python 함수이지만, bind_tools()를 통해 LangChain의 모델이 해당 함수를 직접 호출할 수 있게 된 것입니다.
    from typing import List
    from typing_extensions import TypedDict
    
    from langchain_anthropic import ChatAnthropic
    
    # 사용자의 주소 정보를 저장하는 데이터 타입을 정의 (TypedDict 사용)
    class Address(TypedDict):
        street: str  # 거리 정보
        city: str    # 도시 정보
        state: str   # 주 정보
    
    # validate_user 함수 정의: 사용자 ID와 이전 주소 목록을 받아 유효성 검사를 수행하는 도구
    def validate_user(user_id: int, addresses: List[Address]) -> bool:
        """
        사용자의 이전 주소를 기반으로 유효성을 검사하는 함수.
        
        Args:
            user_id: (int) 유저 ID
            addresses: (List[Address]) 유저가 거주했던 이전 주소 목록
        
        Returns:
            bool: 유효성 검사 결과 (유효하면 True, 유효하지 않으면 False)
        """
        # 간단한 유효성 검사 로직 (여기서는 항상 True 반환)
        return True
    
    # Claude-3 모델을 사용한 ChatAnthropic 객체 생성
    llm = ChatAnthropic(
        model="claude-3-sonnet-20240229"
    ).bind_tools([validate_user])  # validate_user 함수를 도구로 바인딩하여 LLM에서 호출 가능하게 함
    
    # 모델에 입력을 전달하여 validate_user 도구 호출
    result = llm.invoke(
        "Could you validate user 123? They previously lived at "
        "123 Fake St in Boston MA and 234 Pretend Boulevard in "
        "Houston TX."
    )
    
    # 모델이 호출한 도구의 결과를 확인
    result.tool_calls
    [{'name': 'validate_user',
      'args': {'user_id': 123,  # user_id는 입력에서 추출됨
       'addresses': [  # addresses는 입력에서 추출된 주소 목록
         {'street': '123 Fake St', 'city': 'Boston', 'state': 'MA'},
         {'street': '234 Pretend Boulevard', 'city': 'Houston', 'state': 'TX'}]},
      'id': 'toolu_011KnPwWqKuyQ3kMy6McdcYJ',  # 도구 호출에 대한 고유 ID
      'type': 'tool_call'}]  # 호출된 도구의 타입은 'tool_call'로 명시됨

     

     

    2.2.2. 유연해진 Tool 의 input

    • 모델에서 생성한 입력을 도구에 직접 전달 할 수 있음
    tool_call = ai_msg.tool_calls[0]
    # -> ToolCall(args={...}, id=..., ...)
    
    tool_message = tool.invoke(tool_call)
    # -> ToolMessage(
    #    content="도구 결과 예시...",
    #    tool_call_id=...,
    #    name="tool_name"
    #)
    • Tool에 LangGraph 상태를 전달 (상세 내용)
      • 내용이 길기 때문에 요약하자면, 먼저 tool을 정의한다.
      • 모델에 tool을 입력해준다.
      • 그래프를 정의할 때, node에 tool을 넣어준다.

    2.2.3. 풍부한 도구(Tool) 출력

    • 도구는 모델에서 호출할 수 있는 유틸리티이며, 그 출력은 모델에 피드백되도록 설계되었습니다.
    • 도구는 모델에 전달되지 않을 추가 데이터를 포함하여 결과를 반환할 수 있으며, 일부만 모델에 반환할 수 있습니다.
    • 예를 들어, 도구가 사용자 지정 개체, 데이터 프레임 또는 이미지를 반환하는 경우 실제 출력을 모델에 전달하지 않고 이 출력에 대한 일부 메타데이터를 모델에 전달하고 싶을 수 있습니다.
    • 이를 위해, ToolMessage는 모델에 전달될 메시지(content)와 모델 외부에서 사용할 수 있는 아티팩트(artifact)를 구분합니다.

    Tool 정의 예시

    import random
    from typing import List, Tuple
    
    from langchain_core.tools import tool
    
    
    @tool(response_format="content_and_artifact")
    def generate_random_ints(min: int, max: int, size: int) -> Tuple[str, List[int]]:
        """Generate size random ints in the range [min, max]."""
        array = [random.randint(min, max) for _ in range(size)]
        content = f"Successfully generated array of {size} random ints in [{min}, {max}]."
        return content, array

     

    도구 호출

    • 이 코드는 generate_random_ints 도구를 호출하지만, 단순히 함수의 인수만 전달하기 때문에 content만 반환합니다.
    generate_random_ints.invoke({"min": 0, "max": 9, "size": 10})

     

    ToolCall을 이용한 호출

    • ToolCall을 사용하여 도구를 호출할 때, 추가적으로 ToolMessage를 반환받을 수 있습니다.
    • 여기서는 name, args, id, type을 포함하는 ToolCall을 전달합니다. 이 호출은 도구 호출 결과로 content와 artifact를 모두 반환합니다.
    generate_random_ints.invoke(
        {
            "name": "generate_random_ints",
            "args": {"min": 0, "max": 9, "size": 10},
            "id": "123",  # 도구 호출에 필요한 고유 ID
            "type": "tool_call",  # 호출 유형
        }
    )

     

    모델과 도구 통합

    import getpass
    import os
    
    os.environ["OPENAI_API_KEY"] = getpass.getpass()
    
    from langchain_openai import ChatOpenAI
    
    llm = ChatOpenAI(model="gpt-4o-mini")
    llm_with_tools = llm.bind_tools([generate_random_ints])

     

    모델 실행

    • 반환된 ai_msg.tool_calls에는 generate_random_ints 도구를 호출한 기록이 포함됩니다. 이 호출은 도구의 이름, 인수(min, max, size), 호출 ID 등이 포함된 구조입니다.
    ai_msg = llm_with_tools.invoke("generate 6 positive ints less than 25")
    ai_msg.tool_calls

     

    2.2.4. 도구(Tool)의 오류 처리

    1. tool에 try / except 로 처리해준다.

    2. Fallbacks을 이용한다.

    • 더 나은 체인을 하나 더 만들어서, 사용하도록 한다.
    • 이는 chain에 with_fallbacks 함수를 사용해준다.
    chain = llm_with_tools | (lambda msg: msg.tool_calls[0]["args"]) | complex_tool
    
    better_model = ChatOpenAI(model="gpt-4-1106-preview", temperature=0).bind_tools(
        [complex_tool], tool_choice="complex_tool"
    )
    
    better_chain = better_model | (lambda msg: msg.tool_calls[0]["args"]) | complex_tool
    
    chain_with_fallback = chain.with_fallbacks([better_chain])
    
    chain_with_fallback.invoke(
        "use complex tool. the args are 5, 2.1, empty dictionary. don't forget dict_arg"
    )

     

    2.3. 채팅 모델과 상호작용할 수 있는 유틸리티 추가

    2.3.1. 범용 모델 생성자

    최종 사용자가 애플리케이션에 어떤 모델 공급자와 모델을 제공할지 지정할 수 있도록 합니다. init_chat_model() 메서드로 여러 다른 모델을 통합할 수 있습니다.

     

    다양한 모델을 테스트하거나 동적으로 선택할 필요가 있는 경우 적합합니다.

    from langchain.chat_models import init_chat_model
    
    # 기본적으로 특정 모델을 지정하지 않으면, 설정을 바꾸는 옵션 없이 모델을 사용 가능
    configurable_model = init_chat_model(temperature=0)  # 모델의 temperature 설정
    
    # GPT-4o 모델을 사용하여 질문
    response_gpt4o = configurable_model.invoke(
        "What's your name?",
        config={"configurable": {"model": "gpt-4o"}}  # 모델을 GPT-4o로 설정
    )
    print(f"GPT-4o response: {response_gpt4o}")
    
    # Claude 3.5 Sonnet 모델을 사용하여 질문
    response_claude = configurable_model.invoke(
        "What's your name?",
        config={"configurable": {"model": "claude-3-5-sonnet-20240620"}}  # Claude 3.5 Sonnet으로 설정
    )
    print(f"Claude-3.5 Sonnet response: {response_claude}")

     

    2.3.2. 속도 제한기

    API 제공자의 제한으로 인해 너무 많은 요청을 보내면 속도 제한(rate limit)에 걸릴 수 있습니다. 예를 들어, 테스트 데이터셋에서 채팅 모델의 성능을 벤치마킹하기 위해 여러 병렬 쿼리를 실행할 때 이런 상황이 발생할 수 있습니다. 이러한 상황에서는 속도 제한기를 사용하여 요청 빈도를 API에서 허용하는 속도에 맞출 수 있습니다.

    from langchain_core.rate_limiters import InMemoryRateLimiter
    
    rate_limiter = InMemoryRateLimiter(
        requests_per_second=0.1,  # <-- 매우 느림! 10초에 한 번씩만 요청을 보낼 수 있음!!
        check_every_n_seconds=0.1,  # 100ms마다 요청을 보낼 수 있는지 확인하기 위해 깨어남,
        max_bucket_size=10,  # 최대 버스트 크기를 제어.
    )

     

    모델 선택 및 타임 리미터 적용

    from langchain_anthropic import ChatAnthropic
    
    model = ChatAnthropic(model_name="claude-3-opus-20240229", rate_limiter=rate_limiter)

     

     

    2.3.3. 메시지 유틸리티

    • 메세지(Message)는 chat models의 인풋과 아웃풋이다.
    • content, role로 구성되어 있다.

     

    2.4. 사용자 지정 이벤트 전송

     

    사용자 지정 이벤트는 어디에 사용하는가? 커스텀 이벤트는 주로 특정 작업의 상태 추적, 실시간 모니터링, 워크플로우 관리 등 다양한 상황에서 사용됩니다. 특히, 비동기 환경이나 이벤트 기반 시스템에서 작업의 흐름을 제어하거나 중요한 정보를 수집하는 데 유용합니다. 커스텀 이벤트는 다음과 같은 주요 용도에서 많이 활용됩니다.

     

    2.4.1. Astream Events API

    adispatch_custom_event 함수는 이벤트를 발행하는 역할을 담당합니다.

     

    커스텀 이벤트를 비동기 환경에서 처리하기 위한 API입니다. adispatch_custom_event를 사용해 비동기 방식으로 이벤트를 발행할 수 있습니다.

     

    Astream Events API는 모델 실행 중 발생하는 중요한 이벤트를 실시간으로 기록하고 추적하는 데 사용됩니다. 이를 통해 모델의 상태, 성능, 오류, 상호작용 등을 상세하게 모니터링할 수 있으며, 비동기 작업을 효율적으로 관리하는 데 도움을 줍니다.

    from langchain_core.callbacks.manager import (
        adispatch_custom_event,
    )
    from langchain_core.runnables import RunnableLambda
    from langchain_core.runnables.config import RunnableConfig
    
    
    @RunnableLambda
    async def foo(x: str) -> str:
        await adispatch_custom_event("event1", {"x": x})
        await adispatch_custom_event("event2", 5)
        return x
    
    
    async for event in foo.astream_events("hello world", version="v2"):
        print(event) # 이벤트 정보를 출력

     

    2.4.2. Async Callback Handler (비동기 콜백 핸들러)

    AsyncCallbackHandler는 비동기적으로 커스텀 이벤트를 처리하는 핸들러 클래스입니다.

     

    on_custom_event 메서드는 커스텀 이벤트가 발생할 때 호출되며, 이벤트 이름, 데이터, 실행 ID(run_id), 태그(tags), 메타데이터(metadata)를 인수로 받아 처리합니다.

    class AsyncCustomCallbackHandler(AsyncCallbackHandler):
        async def on_custom_event(
            self,
            name: str,
            data: Any,
            *,
            run_id: UUID,
            tags: Optional[List[str]] = None,
            metadata: Optional[Dict[str, Any]] = None,
            **kwargs: Any,
        ) -> None:
            print(
                f"Received event {name} with data: {data}, with tags: {tags}, with metadata: {metadata} and run_id: {run_id}"
            )
            
    @RunnableLambda
    async def bar(x: str, config: RunnableConfig) -> str:
        """An example that shows how to manually propagate config.
    
        You must do this if you're running python<=3.10.
        """
        await adispatch_custom_event("event1", {"x": x}, config=config)
        await adispatch_custom_event("event2", 5, config=config)
        return x
    
    
    async_handler = AsyncCustomCallbackHandler()
    await foo.ainvoke(1, {"callbacks": [async_handler], "tags": ["foo", "bar"]})

     

    2.4.3. Sync Callback Handler (동기 콜백 핸들러)

    동기적으로 작업하는 것은 예만 보고 넘어갑시다.

    from typing import Any, Dict, List, Optional
    from uuid import UUID
    
    from langchain_core.callbacks import BaseCallbackHandler
    from langchain_core.callbacks.manager import (
        dispatch_custom_event,
    )
    from langchain_core.runnables import RunnableLambda
    from langchain_core.runnables.config import RunnableConfig
    
    
    class CustomHandler(BaseCallbackHandler):
        def on_custom_event(
            self,
            name: str,
            data: Any,
            *,
            run_id: UUID,
            tags: Optional[List[str]] = None,
            metadata: Optional[Dict[str, Any]] = None,
            **kwargs: Any,
        ) -> None:
            print(
                f"Received event {name} with data: {data}, with tags: {tags}, with metadata: {metadata} and run_id: {run_id}"
            )
    
    
    @RunnableLambda
    def foo(x: int, config: RunnableConfig) -> int:
        dispatch_custom_event("event1", {"x": x})
        dispatch_custom_event("event2", {"x": x})
        return x
    
    
    handler = CustomHandler()
    foo.invoke(1, {"callbacks": [handler], "tags": ["foo", "bar"]})

     

    2.5. 통합 문서 및 API 참조가 개선됨

    • 문서를 새로 만듦

    2.6. 마이그레이션 가이드

    • 생략

    3. 코드 업데이트 방법

    • langchain, langchain-community, langchain-core 버전 0.2에서 0.3으로 업그레이드 권장.
    • langgraph는 0.2.20 버전 이상으로 업그레이드해야 함.

    4. Pydantic 2

    더이상 pydancit v1을 사용하지 않습니다. 그리고 pydantic을 직접적으로 사용합니다.

     

    예를들어 아래와 같은 코드는

    from langchain_core.pydantic_v1 import BaseModel

     

    아래와 같이 변형해 사용합니다.

    from pydantic import BaseModel
    반응형