다음을 통해 공유


OpenAI SDK를 사용하여 Azure AI Foundry에서 DeepSeek 모델을 사용하여 추론 앱 개발

Python용 OpenAI SDK를 사용하여 Azure OpenAI의 DeepSeek과 같은 추론 모델을 사용하는 방법을 알아봅니다.

이 문서에서는 추론 모델을 통합하기 위한 몇 가지 모범 사례를 보여 줍니다.

  • 키 없는 인증: API 키 대신 관리 ID 또는 개발자 자격 증명을 사용합니다.
  • 비동기 작업: 성능을 향상시키려면 비동기 기능을 사용합니다.
  • 스트리밍 응답: 사용자에게 즉각적인 피드백을 제공합니다.
  • 추론 분리: 최종 출력과 추론 단계를 구분합니다.
  • 리소스 관리: 사용 후 리소스를 정리합니다.

DeepSeek 구성 요소

DeepSeek 구성 요소 샘플을 살펴봅니다. OpenAI 클라이언트 라이브러리를 사용하여 DeepSeek-R1 모델을 호출하고 사용자 메시지에 대한 응답을 생성하는 방법을 보여 줍니다.

아키텍처 개요

다음 다이어그램에서는 샘플 앱의 간단한 아키텍처를 보여 줍니다 . 클라이언트에서 백 엔드 앱으로의 아키텍처를 보여 주는 다이어그램.

채팅 앱은 Azure Container App으로 실행됩니다. 앱은 Microsoft Entra ID와 함께 관리 ID를 사용하여 API 키 대신 Azure OpenAI로 인증합니다. 앱은 Azure OpenAI를 사용하여 사용자 메시지에 대한 응답을 생성합니다.

앱은 다음과 같은 서비스 및 구성 요소를 사용합니다.

  • OpenAI 클라이언트 라이브러리 패키지를 사용하여 사용자 메시지에 대한 응답을 생성하는 Python Quart
  • ReadableStream을 통해 JSON 선을 사용하여 백 엔드에서 응답을 스트리밍하는 기본 HTML/JS 프런트 엔드
  • Azure AI Services, Azure Container Apps, Azure Container Registry, Azure Log Analytics 및 RBAC 역할을 포함하여 Azure 리소스를 프로비전하기 위한 Bicep 파일입니다.

비용

비용을 낮게 유지하기 위해 이 샘플은 대부분의 리소스에 대해 기본 또는 소비 가격 책정 계층을 사용합니다. 필요에 따라 계층을 조정하고 요금이 부과되지 않도록 완료되면 리소스를 삭제합니다.

샘플 리포의 비용에 대해 자세히 알아보십시오.

필수 조건

개발 컨테이너에는 이 문서에 필요한 모든 종속성이 포함됩니다. GitHub Codespaces(브라우저에서) 또는 Visual Studio Code를 사용하여 로컬로 실행할 수 있습니다.

이 문서를 따르려면 다음 필수 구성 요소를 충족하는지 확인합니다.

  • Azure 구독 – 무료로 구독 만들기
  • Azure 계정 권한 – Azure 계정에 Microsoft.Authorization/roleAssignments/write, 사용자 액세스 관리자 또는 소유자와 같은 권한이 있어야 합니다. 구독 수준 권한이 없는 경우 기존 리소스 그룹에 대한 RBAC 를 부여하고 해당 그룹에 배포해야 합니다.
    • Azure 계정에는 구독 수준의 권한도 필요합니다 Microsoft.Resources/deployments/write .
  • GitHub 계정

개방형 개발 환경

필요한 모든 종속성을 사용하여 미리 구성된 개발 환경을 설정하려면 다음 단계를 수행합니다.

GitHub Codespaces는웹용 Visual Studio Code 를 인터페이스로 사용하여 GitHub에서 관리하는 개발 컨테이너를 실행합니다. 이 문서에 미리 설치된 필수 도구 및 종속성과 함께 제공되는 가장 간단한 설정에는 GitHub Codespaces를 사용합니다.

중요합니다

모든 GitHub 계정은 두 개의 핵심 인스턴스를 사용하여 매월 최대 60시간 동안 Codespaces를 무료로 사용할 수 있습니다. 자세한 내용은 GitHub Codespaces 월별 포함된 스토리지 및 코어 시간을 참조하세요.

다음 단계를 사용하여 GitHub 리포지토리의 분기에 새 GitHub Codespace mainAzure-Samples/deepseek-python 만듭니다.

  1. 다음 단추를 마우스 오른쪽 단추로 클릭하고 새 창에서 링크 열기를 선택합니다. 이 작업을 통해 개발 환경과 설명서를 나란히 열 수 있습니다.

    GitHub Codespaces에서 열기

  2. 코드스페이 스 만들기 페이지에서 새 코드스페이 스 만들기를 검토하고 선택합니다 .

  3. 코드스페이스가 시작될 때까지 기다립니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.

  4. 화면 맨 아래에 있는 터미널에서 Azure 개발자 CLI를 사용하여 Azure에 로그인합니다.

    azd auth login
    
  5. 터미널에서 코드를 복사한 다음 브라우저에 붙여넣습니다. 지침에 따라 Azure 계정으로 인증합니다.

이 개발 컨테이너에서 나머지 작업을 수행합니다.

배포 및 실행

샘플 리포지토리에는 채팅 앱을 Azure에 배포하는 데 필요한 모든 코드 및 구성 파일이 있습니다. 다음 단계에 따라 Azure에 채팅 앱을 배포합니다.

Azure에 채팅 앱 배포

중요합니다

이 섹션에서 만든 Azure 리소스는 즉시 비용 청구를 시작합니다. 이러한 리소스는 명령이 완료되기 전에 중지하더라도 비용이 계속 발생할 수 있습니다.

  1. Azure 리소스 프로비저닝 및 소스 코드 배포에 대해 다음 Azure Developer CLI 명령을 실행합니다.

    azd up
    
  2. 다음 표를 사용하여 프롬프트에 응답합니다.

    프롬프트 답변
    환경 이름 짧게 하고 소문자로 유지하세요. 이름 또는 별칭을 추가합니다. 예: chat-app. 리소스 그룹 이름의 일부로 사용됩니다.
    구독 구독을 선택하여 리소스를 생성합니다.
    위치(호스팅용) 목록에서 가까운 위치를 선택합니다.
    DeepSeek 모델의 위치 목록에서 가까운 위치를 선택합니다. 첫 번째 위치와 동일한 위치를 사용할 수 있는 경우 해당 위치를 선택합니다.
  3. 앱이 배포되기를 기다립니다. 배포에는 일반적으로 5~10분이 걸립니다.

채팅 앱을 사용하여 큰 언어 모델에 질문하기

  1. 배포 후 터미널에 URL이 표시됩니다.

  2. 레이블이 지정된 URL을 Deploying service web 선택하여 브라우저에서 채팅 앱을 엽니다.

    응답과 함께 채팅 텍스트 상자에 질문이 있는 브라우저의 채팅 앱 스크린샷

  3. 브라우저에서 업로드된 이미지에 대해 "누가 모나리자를 그렸는가?"와 같은 질문을 합니다.

  4. Azure OpenAI는 모델 유추를 통해 답변을 제공하며 결과는 앱에 표시됩니다.

샘플 코드 탐색

OpenAI 및 Azure OpenAI Service는 모두 공통 Python 클라이언트 라이브러리를 사용하지만 Azure OpenAI 엔드포인트에 대해 몇 가지 작은 코드를 변경해야 합니다. 이 샘플에서는 DeepSeek-R1 추론 모델을 사용하여 간단한 채팅 앱에서 응답을 생성합니다.

설정 및 인증

파일은 src\quartapp\chat.py 키 없는 인증 설정 및 구성으로 시작합니다.

인프라 설정

스크립트는 비동기 웹 프레임워크인 Quart를 사용하여 Blueprint이라는 이름의 chat를 만듭니다. Blueprint는 앱의 경로를 정의하고 라이프사이클 후크를 관리합니다.

bp = Blueprint("chat", __name__, template_folder="templates", static_folder="static")

Blueprint//chat/stream 경로와 @bp.before_app_serving@bp.after_app_serving 수명 주기 후크를 정의합니다.

키 없는 인증으로 초기화

다음 코드 조각은 인증을 처리합니다.

비고

후크는 @bp.before_app_serving OpenAI 클라이언트를 초기화하고 인증을 처리합니다. 이 방법은 Azure 호스팅 DeepSeek-R1 모델에 안전하게 액세스하는 데 중요합니다.

인증 전략은 환경에 맞게 조정됩니다.

  • 프로덕션 환경에서: 중요한 키를 저장하지 않도록 Azure 클라이언트 ID와 함께 관리 ID 자격 증명 을 사용합니다. 이 방법은 클라우드 네이티브 앱에 대해 안전하고 확장 가능합니다.
  • 개발 중: Azure 테넌트 ID와 함께 Azure Developer CLI 자격 증명 을 사용하여 개발자의 Azure CLI 로그인 세션을 사용하여 로컬 테스트를 간소화합니다.
@bp.before_app_serving
async def configure_openai():
    if os.getenv("RUNNING_IN_PRODUCTION"):
        client_id = os.environ["AZURE_CLIENT_ID"]
        bp.azure_credential = ManagedIdentityCredential(client_id=client_id)
    else:
        tenant_id = os.environ["AZURE_TENANT_ID"]
        bp.azure_credential = AzureDeveloperCliCredential(tenant_id=tenant_id)

이 키 없는 인증 방법은 다음을 제공합니다.

  • 보안 향상: 코드 또는 환경 변수에 저장된 API 키가 없습니다.
  • 보다 간편한 관리: 키를 회전하거나 비밀을 관리할 필요가 없습니다.
  • 원활한 전환: 동일한 코드는 개발 및 프로덕션 모두에서 작동합니다.

토큰 공급자 설정

다음 코드 조각에서 토큰 공급자는 Azure OpenAI 서비스에 대한 요청을 인증하는 전달자 토큰을 만듭니다. 구성된 자격 증명을 사용하여 이러한 토큰을 자동으로 생성하고 새로 고칩니다.

bp.openai_token_provider = get_bearer_token_provider(
    bp.azure_credential, "https://cognitiveservices.azure.com/.default"
)

Azure OpenAI 클라이언트 구성

두 개의 가능한 클라이언트가 있습니다AzureOpenAI. AsyncAzureOpenAI 다음 코드 조각은 AsyncAzureOpenAI을 사용하여 동시 사용자의 성능을 향상하기 위해 비동기 Quart 프레임워크와 함께 사용합니다.

bp.openai_client = AsyncAzureOpenAI(
    azure_endpoint=os.environ["AZURE_INFERENCE_ENDPOINT"],
    azure_ad_token_provider=openai_token_provider,
    api_version="2024-10-21",
  • base_url: Azure 호스팅 DeepSeek 유추 엔드포인트를 가리킵니다.
  • api_key: 토큰 공급자에서 동적으로 생성된 API 키를 사용합니다.
  • api-version: DeepSeek 모델을 지원하는 API 버전을 지정합니다.

모델 배포 이름 구성

다음 코드 조각은 환경 구성에서 배포 이름을 가져오면 DeepSeek 모델 버전을 설정합니다. bp.model_deployment_name 변수에 이름을 할당하여, 이 변수가 앱 전체에서 사용할 수 있도록 합니다. 이 방법을 사용하면 코드를 업데이트하지 않고 모델 배포를 변경할 수 있습니다.

bp.model_deployment_name = os.getenv("AZURE_DEEPSEEK_DEPLOYMENT")

비고

Azure OpenAI에서는 모델 이름(예: gpt-4o 또는 deepseek-r1.)을 직접 사용하지 않습니다. 대신 Azure OpenAI 리소스에서 모델의 명명된 인스턴스인 배포를 만듭니다. 이 방법은 다음과 같은 이점을 제공합니다.

  • 추상화: 환경 변수를 사용하여 배포 이름을 코드에서 제외합니다.
  • 유연성: 코드를 변경하지 않고 여러 DeepSeek 배포 간에 전환할 수 있습니다.
  • 환경별 구성: 개발, 테스트 및 프로덕션에 다양한 배포를 사용할 수 있습니다.
  • 리소스 관리: 각 Azure 배포에는 자체 할당량, 제한 및 모니터링이 있습니다.

수명 주기 관리

다음 코드 조각은 애플리케이션이 종료될 때 비동기 Azure OpenAI 클라이언트를 닫아 리소스 누출을 방지합니다. @bp.after_app_serving 후크는 리소스의 적절한 정리를 보장합니다.

@bp.after_app_serving
async def shutdown_openai():
    await bp.openai_client.close()

채팅 처리기 스트리밍 함수

chat_handler() 함수는 DeepSeek-R1 경로를 통해 chat/stream 모델과의 사용자 상호 작용을 관리합니다. 실시간으로 클라이언트에 응답을 스트리밍하고 처리합니다. 이 함수는 JSON 페이로드에서 메시지를 추출합니다.

스트리밍 구현

  1. 이 함수는 response_stream 클라이언트의 메시지를 수락하여 시작합니다.

    • request_messages: 경로에는 사용자 메시지가 포함된 JSON 페이로드가 예상됩니다.
    @bp.post("/chat/stream")
    async def chat_handler():
       request_messages = (await request.get_json())["messages"]
    
  2. 다음으로, 함수는 OpenAI API의 응답을 스트리밍합니다. "시스템 메시지인 'You're a helpful assistant'를 사용자 제공 메시지와 결합합니다."

    @stream_with_context
    async def response_stream():
        all_messages = [
            {"role": "system", "content": "You are a helpful assistant."},
        ] + request_messages
    
  3. 다음으로, 이 함수는 스트리밍 채팅 완료 요청을 만듭니다.

    메서드는 chat.completions.create 모델에 메시지를 보냅니다 DeepSeek-R1 . 이 매개 변수는 stream=True 실시간 응답 스트리밍을 사용하도록 설정합니다.

      chat_coroutine = bp.openai_client.chat.completions.create(
          model=bp.openai_model,
          messages=all_messages,
          stream=True,
      )
    
  4. 다음 코드 조각은 DeepSeek-R1 모델의 스트리밍 응답을 처리하고 오류를 처리합니다. 업데이트를 반복하고, 유효한 선택을 확인하고, 각 응답 청크를 JSON 줄로 보냅니다. 오류가 발생하면 오류를 기록하고 스트림을 계속하는 동안 JSON 오류 메시지를 클라이언트에 보냅니다.

    try:
        async for update in await chat_coroutine:
            if update.choices:
                yield update.choices[0].model_dump_json() + "\n"
        except Exception as e:
            current_app.logger.error(e)
            yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
    
    return Response(response_stream())
    

추론적 콘텐츠 처리

기존 언어 모델은 최종 출력만 제공하지만, 추론 모델(예: DeepSeek-R1 중간 추론 단계)을 표시합니다. 이러한 단계는 다음과 같은 경우에 유용합니다.

  • 복잡한 문제 해결
  • 수학 계산 수행
  • 다단계 논리적 추론 처리
  • 투명한 의사 결정

submit 프런트 엔드에서 index.html 스트리밍 응답을 처리하는 이벤트 처리기입니다. 이 방법을 사용하면 최종 출력과 함께 모델의 추론 단계에 액세스하고 표시할 수 있습니다.

프런트 엔드는 백 엔드에서 스트리밍 응답을 처리하는 데 사용합니다 ReadableStream . 추론 콘텐츠를 일반 콘텐츠와 구분하여 확장 가능한 섹션의 추론과 기본 채팅 영역의 최종 답변을 보여 줍니다.

단계별 분석

  1. 스트리밍 요청 시작

    이 코드 조각은 JavaScript 프런트 엔드와 Python 백 엔드 간에 연결을 만들어 DeepSeek-R1의 Azure OpenAI와 키 없는 인증을 통합할 수 있도록 합니다.

    const response = await fetch("/chat/stream", {
        method: "POST",
        headers: {"Content-Type": "application/json"},
        body: JSON.stringify({messages: messages})
    });
    
  2. 변수 초기화

    다음 코드 조각은 변수를 초기화하여 답변과 생각을 별도로 저장합니다. 이러한 분리는 추론 콘텐츠를 효과적으로 처리하는 데 도움이 됩니다.

    let answer = "";
    let thoughts = "";    
    
  3. 각 업데이트 처리

    다음 코드 조각은 모델 응답의 청크를 비동기적으로 반복합니다.

    for await (const event of readNDJSONStream(response.body)) {
    
  4. 콘텐츠 형식 검색 및 라우팅

    스크립트는 이벤트에 필드가 포함되어 있는지 확인합니다 delta . 이 경우 추론 콘텐츠인지 일반 콘텐츠인지에 따라 콘텐츠를 처리합니다.

    if (!event.delta) {
         continue;
    }
    if (event.delta.reasoning_content) {
         thoughts += event.delta.reasoning_content;
         if (thoughts.trim().length > 0) {
             // Only show thoughts if they are more than just whitespace
             messageDiv.querySelector(".loading-bar").style.display = "none";
             messageDiv.querySelector(".thoughts").style.display = "block";
             messageDiv.querySelector(".thoughts-content").innerHTML = converter.makeHtml(thoughts);
         }
     } else if (event.delta.content) {
         messageDiv.querySelector(".loading-bar").style.display = "none";
         answer += event.delta.content;
         messageDiv.querySelector(".answer-content").innerHTML = converter.makeHtml(answer);
     }
    
    • 콘텐츠 형식이 reasoning_content인 경우, 콘텐츠가 thoughts에 추가되어 .thoughts-content 섹션에 표시됩니다.
    • 콘텐츠 형식이 content인 경우, 콘텐츠가 answer에 추가되어 .answer-content 섹션에 표시됩니다.
    • .loading-bar 콘텐츠가 스트리밍을 시작하면 숨겨지고, 생각이 있는 경우 .thoughts 섹션이 표시됩니다.
  5. 오류 처리:

    오류는 백 엔드에 기록되고 JSON 형식으로 클라이언트에 반환됩니다.

    except Exception as e:
        current_app.logger.error(e)
        yield json.dumps({"error": str(e)}, ensure_ascii=False) + "\n"
    

    이 프런트 엔드 코드 조각은 채팅 인터페이스에 오류 메시지를 표시합니다.

    messageDiv.scrollIntoView();
    if (event.error) {
        messageDiv.innerHTML = "Error: " + event.error;
    }
    

GitHub Codespaces 정리

GitHub Codespaces 환경을 삭제하여 코어당 무료 시간을 최대화합니다.

중요합니다

GitHub 계정의 무료 스토리지 및 코어 시간에 대한 자세한 내용은 GitHub Codespaces 월별 포함된 스토리지 및 코어 시간을 참조하세요.

  1. GitHub Codespaces 대시보드로그인합니다.

  2. Azure-Samples//deepseek-python GitHub 리포지토리에서 생성된 활성 Codespaces를 찾으세요.

  3. 코드스페이스에 대한 상황에 맞는 메뉴를 열고 삭제선택합니다.

도움받기

리포지토리의 문제에 문제를 기록합니다.

다음 단계