Recent Posts
Recent Comments
반응형
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Archives
Today
Total
관리 메뉴

오늘도 공부

5. Provider 선택자 만들기 본문

AI/Agent

5. Provider 선택자 만들기

행복한 수지아빠 2026. 2. 23. 11:36
반응형

 

 

GitHub - MiniMax-AI/Mini-Agent: A minimal yet professional single agent demo project that showcases the core execution pipeline

A minimal yet professional single agent demo project that showcases the core execution pipeline and production-grade features of agents. - MiniMax-AI/Mini-Agent

github.com

 

"""Example: Using LLMClient with different providers.

This example demonstrates how to use the LLMClient wrapper with different
LLM providers (Anthropic or OpenAI) through the provider parameter.
"""

import asyncio
from pathlib import Path

import yaml

from mini_agent import LLMClient, LLMProvider, Message


async def demo_anthropic_provider():
    """Demo using LLMClient with Anthropic provider."""
    print("\n" + "=" * 60)
    print("DEMO: LLMClient with Anthropic Provider")
    print("=" * 60)

    # Load config
    config_path = Path("mini_agent/config/config.yaml")
    with open(config_path, encoding="utf-8") as f:
        config = yaml.safe_load(f)

    # Initialize client with Anthropic provider
    client = LLMClient(
        api_key=config["api_key"],
        provider=LLMProvider.ANTHROPIC,  # Specify Anthropic provider
        model=config.get("model", "MiniMax-M2.5"),
    )

    print(f"Provider: {client.provider}")
    print(f"API Base: {client.api_base}")

    # Simple question
    messages = [Message(role="user", content="Say 'Hello from Anthropic!'")]
    print(f"\n👤 User: {messages[0].content}")

    try:
        response = await client.generate(messages)
        if response.thinking:
            print(f"💭 Thinking: {response.thinking}")
        print(f"💬 Model: {response.content}")
        print("✅ Anthropic provider demo completed")
    except Exception as e:
        print(f"❌ Error: {e}")


async def demo_openai_provider():
    """Demo using LLMClient with OpenAI provider."""
    print("\n" + "=" * 60)
    print("DEMO: LLMClient with OpenAI Provider")
    print("=" * 60)

    # Load config
    config_path = Path("mini_agent/config/config.yaml")
    with open(config_path, encoding="utf-8") as f:
        config = yaml.safe_load(f)

    # Initialize client with OpenAI provider
    client = LLMClient(
        api_key=config["api_key"],
        provider=LLMProvider.OPENAI,  # Specify OpenAI provider
        model=config.get("model", "MiniMax-M2.5"),
    )

    print(f"Provider: {client.provider}")
    print(f"API Base: {client.api_base}")

    # Simple question
    messages = [Message(role="user", content="Say 'Hello from OpenAI!'")]
    print(f"\n👤 User: {messages[0].content}")

    try:
        response = await client.generate(messages)
        if response.thinking:
            print(f"💭 Thinking: {response.thinking}")
        print(f"💬 Model: {response.content}")
        print("✅ OpenAI provider demo completed")
    except Exception as e:
        print(f"❌ Error: {e}")


async def demo_default_provider():
    """Demo using LLMClient with default provider."""
    print("\n" + "=" * 60)
    print("DEMO: LLMClient with Default Provider (Anthropic)")
    print("=" * 60)

    # Load config
    config_path = Path("mini_agent/config/config.yaml")
    with open(config_path, encoding="utf-8") as f:
        config = yaml.safe_load(f)

    # Initialize client without specifying provider (defaults to Anthropic)
    client = LLMClient(
        api_key=config["api_key"],
        model=config.get("model", "MiniMax-M2.5"),
    )

    print(f"Provider (default): {client.provider}")
    print(f"API Base: {client.api_base}")

    # Simple question
    messages = [Message(role="user", content="Say 'Hello with default provider!'")]
    print(f"\n👤 User: {messages[0].content}")

    try:
        response = await client.generate(messages)
        print(f"💬 Model: {response.content}")
        print("✅ Default provider demo completed")
    except Exception as e:
        print(f"❌ Error: {e}")


async def demo_provider_comparison():
    """Compare responses from both providers."""
    print("\n" + "=" * 60)
    print("DEMO: Provider Comparison")
    print("=" * 60)

    # Load config
    config_path = Path("mini_agent/config/config.yaml")
    with open(config_path, encoding="utf-8") as f:
        config = yaml.safe_load(f)

    # Create clients for both providers
    anthropic_client = LLMClient(
        api_key=config["api_key"],
        provider=LLMProvider.ANTHROPIC,
        model=config.get("model", "MiniMax-M2.5"),
    )

    openai_client = LLMClient(
        api_key=config["api_key"],
        provider=LLMProvider.OPENAI,
        model=config.get("model", "MiniMax-M2.5"),
    )

    # Same question for both
    messages = [Message(role="user", content="What is 2+2?")]
    print(f"\n👤 Question: {messages[0].content}\n")

    try:
        # Get response from Anthropic
        anthropic_response = await anthropic_client.generate(messages)
        print(f"🔵 Anthropic: {anthropic_response.content}")

        # Get response from OpenAI
        openai_response = await openai_client.generate(messages)
        print(f"🟢 OpenAI: {openai_response.content}")

        print("\n✅ Provider comparison completed")
    except Exception as e:
        print(f"❌ Error: {e}")


async def main():
    """Run all demos."""
    print("\n🚀 LLM Provider Selection Demo")
    print("This demo shows how to use LLMClient with different providers.")
    print("Make sure you have configured API key in config.yaml.")

    try:
        # Demo default provider
        await demo_default_provider()

        # Demo Anthropic provider
        await demo_anthropic_provider()

        # Demo OpenAI provider
        await demo_openai_provider()

        # Demo provider comparison
        await demo_provider_comparison()

        print("\n✅ All demos completed successfully!")

    except Exception as e:
        print(f"\n❌ Error: {e}")
        import traceback

        traceback.print_exc()


if __name__ == "__main__":
    asyncio.run(main())

05_provider_selection.py 해설: “provider 선택”은 모델이 아니라 ‘프로토콜’ 선택이다

이 글은 examples/05_provider_selection.py를 기반으로, Mini Agent의 LLMClient가 두 가지 API 프로토콜(Anthropic 호환 / OpenAI 호환) 을 어떻게 감싸는지 설명합니다.

여기서 provider는 “모델 공급자”라기보다 요청/응답 포맷(프로토콜) 선택에 가깝습니다.

TL;DR

  • LLMClient(provider=...)는 내부적으로 AnthropicClient 또는 OpenAIClient를 선택합니다.
  • MiniMax 엔드포인트를 쓸 경우, api_base에 provider에 맞는 suffix(/anthropic 또는 /v1)를 자동으로 붙여줍니다.
  • 이 예제 코드는 현재 api_base를 config에서 읽어도 LLMClient에 전달하지 않아 기본값을 사용한다는 점이 중요합니다(실습 시 수정 권장).

실행 전 준비

이 예제는 실제로 API 호출을 합니다. mini_agent/config/config.yaml이 필요합니다.

cp mini_agent/config/config-example.yaml mini_agent/config/config.yaml

실행 방법

uv run python examples/05_provider_selection.py

예제 구성(무엇을 보여주나)

이 파일은 네 가지 데모를 순서대로 실행합니다.

  1. 기본 provider(지정하지 않으면 Anthropic)
  2. provider=LLMProvider.ANTHROPIC
  3. provider=LLMProvider.OPENAI
  4. 같은 질문을 두 provider로 비교

각 데모는 공통적으로:

  • 간단한 messages=[Message(role="user", ...)]를 만들고
  • await client.generate(messages)를 호출해
  • 응답(content, thinking)을 출력합니다.

provider 선택의 의미: 내부 구현 관점

핵심은 mini_agent/llm/llm_wrapper.py입니다.

  • LLMClient(provider=anthropic)
    • Anthropic SDK 기반의 AnthropicClient를 사용
    • thinking/tool_use/tool_result 블록 기반으로 변환
  • LLMClient(provider=openai)
    • OpenAI SDK 기반의 OpenAIClient를 사용
    • reasoning_split / reasoning_details 등을 사용해 thinking을 분리(호환 구현)

즉, 같은 “모델 이름”을 쓰더라도 요청 포맷과 응답 파싱이 달라집니다.

MiniMax 엔드포인트 suffix 자동 처리

MiniMax API 도메인(api.minimax.io, api.minimaxi.com)을 쓰면 LLMClient가 아래처럼 api_base를 정규화합니다.

  • provider=anthropic: <api_base>/anthropic
  • provider=openai: <api_base>/v1

이 로직 덕분에 사용자는 “global/china 도메인”만 맞추고, suffix는 provider에 맞겨도 됩니다.

이 예제에서 꼭 알아야 할 함정: api_base를 넘기지 않는다

05_provider_selection.py는 config.yaml을 읽지만, 아래처럼 api_base를 LLMClient에 전달하지 않습니다.

client = LLMClient(api_key=config["api_key"], provider=..., model=...)

따라서 실제 호출은 LLMClient의 기본 api_base로 진행됩니다.
만약 당신의 키가 글로벌 플랫폼(https://api.minimax.io) 기준이라면, 다음처럼 config의 api_base를 전달하는 쪽이 안전합니다.

client = LLMClient(
  api_key=config["api_key"],
  api_base=config.get("api_base"),
  provider=...,
  model=config.get("model", "MiniMax-M2.5"),
)

기술 블로그 관점에서 이 포인트는 중요합니다.

  • “provider 선택”을 이해하려면,
  • “어느 base URL로 가는지”도 함께 통제해야
  • 관찰이 정확해집니다.

실습 과제(공부용)

  1. api_base를 넘기도록 수정하고, provider별로 client.api_base 출력 비교하기
    Anthropic면 /anthropic, OpenAI면 /v1이 붙는지 확인하세요.
  2. 같은 질문을 두 provider로 여러 번 던져 응답 특성 차이를 관찰하기
    thinking 출력 유무/형식, tool calling 응답 형태 등을 눈여겨보세요.
  3. provider를 config.yaml과 연동하기
    CLI(mini-agent)는 config의 provider를 읽어 반영합니다. 이 예제도 같은 패턴으로 바꿔보세요.

다음 글 안내

다음 문서인 examples/docs/06_tool_schema_demo.md에서는 provider가 달라도 일관되게 동작하도록 Tool 스키마를 어떻게 설계하는지가 핵심입니다.
즉, “프로토콜이 달라도 도구 호출이 잘 되게 만드는 설계”로 이어집니다.

반응형