Azure OpenAI LLM

컨텍스트 기반 질의응답 생성 및 프롬프트 최적화

Azure OpenAI LLM을 활용한 RAG 질의응답 시스템 구축 및 프롬프트 엔지니어링 방법을 다룬다.

AI
RAG
Azure
저자

Kwangmin Kim

공개

2025년 11월 07일

1 Azure OpenAI LLM이란?

Azure OpenAI Service는 OpenAI의 강력한 언어 모델을 Azure 클라우드에서 제공하는 엔터프라이즈급 서비스다.

RAG 시스템에서의 역할:
- 검색된 문서를 컨텍스트로 활용
- 자연어 답변 생성
- 컨텍스트 기반 추론
- 한국어 지원

엔터프라이즈 장점:
- 99.9% SLA 보장
- 데이터 주권 (한국 리전)
- Private Endpoint 지원
- RBAC 기반 접근 제어

2 Azure OpenAI 모델

2.1 GPT-4 Turbo (권장)

출시: 2024년 4월

사양:
- 컨텍스트 윈도우: 128K 토큰
- 출력 토큰: 4,096
- 가격: $10 / 1M 입력 토큰, $30 / 1M 출력 토큰

특징:
- 최고 성능 (복잡한 추론)
- JSON 모드 지원
- 함수 호출
- 비전 (이미지 이해)

RAG 권장: ✅ 높은 정확도 필요 시

2.2 GPT-4o (최신)

출시: 2024년 5월

사양:
- 컨텍스트 윈도우: 128K 토큰
- 출력 토큰: 4,096
- 가격: $5 / 1M 입력 토큰, $15 / 1M 출력 토큰 (GPT-4 대비 50% 저렴)

특징:
- GPT-4 수준 성능
- 2배 빠른 속도
- 멀티모달 (텍스트, 이미지, 오디오)
- 향상된 한국어 지원

RAG 권장: ✅ 최선의 선택 (성능 + 비용)

2.3 GPT-3.5 Turbo

사양:
- 컨텍스트 윈도우: 16K 토큰
- 가격: $0.50 / 1M 입력 토큰, $1.50 / 1M 출력 토큰

특징:
- 빠른 응답 속도
- 낮은 비용
- 간단한 질문에 적합

RAG 권장: 단순 FAQ 또는 개발/테스트 환경

2.4 모델 비교

모델 컨텍스트 입력 가격 출력 가격 성능 속도 RAG 권장
GPT-4o 128K $5/1M $15/1M ⭐⭐⭐⭐⭐ ⚡⚡⚡ 최고
GPT-4 Turbo 128K $10/1M $30/1M ⭐⭐⭐⭐⭐ ⚡⚡ 고정밀도
GPT-3.5 Turbo 16K $0.50/1M $1.50/1M ⭐⭐⭐ ⚡⚡⚡ 개발용

3 환경 설정

3.1 Azure OpenAI 리소스 생성

# Azure OpenAI 리소스 생성  
az cognitiveservices account create \  
    --name openai-rag-prod \  
    --resource-group rg-rag-prod \  
    --kind OpenAI \  
    --sku S0 \  
    --location eastus  

# 키 조회  
az cognitiveservices account keys list \  
    --name openai-rag-prod \  
    --resource-group rg-rag-prod  

3.2 모델 배포

Azure OpenAI Studio에서:
1. “배포” → “+ 배포 만들기”
2. 모델 선택: gpt-4o
3. 배포 이름: gpt-4o
4. TPM: 80K (분당 토큰 제한)

3.3 환경 변수

.env 파일:

AZURE_OPENAI_ENDPOINT=https://openai-rag-prod.openai.azure.com/  
AZURE_OPENAI_API_KEY=your-key-here  
AZURE_OPENAI_DEPLOYMENT=gpt-4o  
AZURE_OPENAI_API_VERSION=2024-02-01  

4 기본 사용법

4.1 AzureChatOpenAI 초기화

from langchain_openai import AzureChatOpenAI  
from dotenv import load_dotenv  
import os  

load_dotenv()  

llm = AzureChatOpenAI(  
    azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT"),  
    openai_api_version=os.getenv("AZURE_OPENAI_API_VERSION"),  
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),  
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    temperature=0,  # 결정적 출력  
    max_tokens=1000  # 최대 출력 길이  
)  

# 테스트  
response = llm.invoke("Azure OpenAI란 무엇인가?")  
print(response.content)  

4.2 메시지 형식

from langchain_core.messages import HumanMessage, SystemMessage  

messages = [  
    SystemMessage(content="당신은 Azure 전문가입니다."),  
    HumanMessage(content="Azure AI Search를 설명해주세요.")  
]  

response = llm.invoke(messages)  
print(response.content)  

5 RAG 프롬프트 엔지니어링

5.1 기본 RAG 프롬프트

from langchain_core.prompts import ChatPromptTemplate  

basic_prompt = ChatPromptTemplate.from_template(  
    """다음 컨텍스트를 참고하여 질문에 답변하세요.  

컨텍스트:  
{context}  

질문: {question}  

답변:"""  
)  

5.2 한국어 최적화 프롬프트

korean_prompt = ChatPromptTemplate.from_template(  
    """당신은 친절한 AI 어시스턴트입니다.  
주어진 컨텍스트를 바탕으로 사용자의 질문에 정확하고 상세하게 답변하세요.  

## 지침:  
1. 컨텍스트에 있는 정보만 사용하세요  
2. 확실하지 않으면 "잘 모르겠습니다"라고 답하세요  
3. 답변은 한국어로 작성하세요  
4. 전문 용어는 쉽게 설명하세요  

## 컨텍스트:  
{context}  

## 질문:  
{question}  

## 답변:"""  
)  

5.3 구조화된 답변 프롬프트

structured_prompt = ChatPromptTemplate.from_template(  
    """다음 컨텍스트를 참고하여 질문에 답변하세요.  

컨텍스트:  
{context}  

질문: {question}  

답변은 다음 형식으로 작성하세요:  

**요약:** (한 문장 요약)  

**상세 설명:**  
- 핵심 포인트 1  
- 핵심 포인트 2  
- 핵심 포인트 3  

**출처:** (컨텍스트에서 인용)  

답변:"""  
)  

5.4 Few-Shot 프롬프트

few_shot_prompt = ChatPromptTemplate.from_template(  
    """다음 예시를 참고하여 질문에 답변하세요.  

예시 1:  
질문: Azure Blob Storage란?  
답변: Azure Blob Storage는 Microsoft의 클라우드 객체 스토리지 서비스입니다.   
대량의 비구조화 데이터를 저장할 수 있으며, Hot/Cool/Archive 티어를 제공합니다.  

예시 2:  
질문: Document Intelligence의 용도는?  
답변: Document Intelligence는 OCR 및 문서 분석 서비스입니다.  
PDF, 이미지에서 텍스트, 표, 레이아웃을 추출하여 RAG 시스템의 입력으로 사용합니다.  

이제 실제 질문에 답변하세요:  

컨텍스트:  
{context}  

질문: {question}  

답변:"""  
)  

6 컨텍스트 관리

6.1 컨텍스트 압축

def compress_context(docs, max_tokens=3000):  
    """컨텍스트를 토큰 제한 내로 압축"""  
    import tiktoken  
    
    encoding = tiktoken.encoding_for_model("gpt-4")  
    
    compressed = []  
    total_tokens = 0  
    
    for doc in docs:  
        tokens = encoding.encode(doc.page_content)  
        doc_tokens = len(tokens)  
        
        if total_tokens + doc_tokens <= max_tokens:  
            compressed.append(doc.page_content)  
            total_tokens += doc_tokens  
        else:  
            # 남은 공간에 맞춰 자르기  
            remaining = max_tokens - total_tokens  
            truncated = encoding.decode(tokens[:remaining])  
            compressed.append(truncated + "...")  
            break  
    
    return "\n\n".join(compressed)  

# 사용  
# context = compress_context(retrieved_docs, max_tokens=3000)  

6.2 컨텍스트 재정렬

from langchain.document_transformers import LongContextReorder  

def reorder_context(docs):  
    """중요한 문서를 양 끝에 배치"""  
    reorderer = LongContextReorder()  
    reordered = reorderer.transform_documents(docs)  
    return reordered  

# 사용: 첫 번째와 마지막 문서가 가장 중요  
# reordered_docs = reorder_context(retrieved_docs)  

7 고급 RAG 패턴

7.1 Chain-of-Thought (CoT)

cot_prompt = ChatPromptTemplate.from_template(  
    """다음 컨텍스트를 참고하여 질문에 단계적으로 답변하세요.  

컨텍스트:  
{context}  

질문: {question}  

단계별 추론:  
1. 먼저 질문의 핵심을 파악합니다  
2. 컨텍스트에서 관련 정보를 찾습니다  
3. 정보를 종합하여 답변을 구성합니다  

답변:"""  
)  

7.2 Self-Ask

self_ask_prompt = ChatPromptTemplate.from_template(  
    """질문에 답하기 위해 필요한 하위 질문을 먼저 생성하고 답변하세요.  

컨텍스트:  
{context}  

질문: {question}  

하위 질문 1: [질문]  
답변 1: [답변]  

하위 질문 2: [질문]  
답변 2: [답변]  

최종 답변: [종합 답변]"""  
)  

7.3 인용 포함 답변

citation_prompt = ChatPromptTemplate.from_template(  
    """컨텍스트를 참고하여 답변하고, 인용 출처를 표시하세요.  

컨텍스트:  
{context}  

질문: {question}  

답변 형식:  
답변 내용 [출처: 문서명 또는 페이지]  

답변:"""  
)  

8 스트리밍

8.1 실시간 응답 스트리밍

# 스트리밍 활성화  
streaming_llm = AzureChatOpenAI(  
    azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT"),  
    openai_api_version=os.getenv("AZURE_OPENAI_API_VERSION"),  
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),  
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    streaming=True,  
    temperature=0  
)  

# 스트리밍 실행  
for chunk in streaming_llm.stream("Azure AI Search란?"):  
    print(chunk.content, end="", flush=True)  

8.2 RAG 체인 스트리밍

from langchain_core.runnables import RunnablePassthrough  
from langchain_core.output_parsers import StrOutputParser  

# 스트리밍 RAG 체인  
streaming_chain = (  
    {"context": retriever | format_docs, "question": RunnablePassthrough()}  
    | korean_prompt  
    | streaming_llm  
    | StrOutputParser()  
)  

# 실행  
print("답변: ", end="")  
for chunk in streaming_chain.stream("Azure AI Search의 장점은?"):  
    print(chunk, end="", flush=True)  
print()  

9 JSON 모드

9.1 구조화된 출력

json_llm = AzureChatOpenAI(  
    azure_deployment=os.getenv("AZURE_OPENAI_DEPLOYMENT"),  
    openai_api_version=os.getenv("AZURE_OPENAI_API_VERSION"),  
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),  
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),  
    temperature=0,  
    model_kwargs={"response_format": {"type": "json_object"}}  
)  

json_prompt = ChatPromptTemplate.from_template(  
    """다음 컨텍스트를 참고하여 질문에 JSON 형식으로 답변하세요.  

컨텍스트:  
{context}  

질문: {question}  

JSON 형식:  
{{  
  "answer": "답변 내용",  
  "confidence": "high/medium/low",  
  "sources": ["출처1", "출처2"]  
}}  

JSON 응답:"""  
)  

# 사용  
response = json_llm.invoke(  
    json_prompt.invoke({  
        "context": "Azure AI Search는 벡터 검색을 지원합니다.",  
        "question": "Azure AI Search의 주요 기능은?"  
    })  
)  

import json  
result = json.loads(response.content)  
print(f"답변: {result['answer']}")  
print(f"신뢰도: {result['confidence']}")  

10 함수 호출

10.1 도구 정의

from langchain_core.tools import tool  

@tool  
def search_documents(query: str) -> str:  
    """문서를 검색하는 도구"""  
    # 실제로는 retriever 사용  
    return f"'{query}'에 대한 검색 결과입니다."  

@tool  
def get_metadata(doc_id: str) -> dict:  
    """문서 메타데이터를 가져오는 도구"""  
    return {"id": doc_id, "title": "샘플 문서", "date": "2025-01-01"}  

tools = [search_documents, get_metadata]  

10.2 함수 호출 LLM

# 도구 바인딩  
llm_with_tools = llm.bind_tools(tools)  

# 실행  
response = llm_with_tools.invoke("Azure AI Search 문서를 검색해줘")  

# 도구 호출 확인  
if response.tool_calls:  
    for tool_call in response.tool_calls:  
        print(f"도구: {tool_call['name']}")  
        print(f"인자: {tool_call['args']}")  

11 토큰 최적화

11.1 토큰 계산

import tiktoken  

def count_tokens(text, model="gpt-4"):  
    """토큰 수 계산"""  
    encoding = tiktoken.encoding_for_model(model)  
    return len(encoding.encode(text))  

# 프롬프트 토큰 확인  
prompt_text = korean_prompt.invoke({  
    "context": "샘플 컨텍스트",  
    "question": "샘플 질문"  
}).to_string()  

tokens = count_tokens(prompt_text)  
print(f"프롬프트 토큰: {tokens}")  

11.2 비용 추정

def estimate_cost(input_tokens, output_tokens, model="gpt-4o"):  
    """비용 추정 (USD)"""  
    prices = {  
        "gpt-4o": {"input": 5.0, "output": 15.0},  # per 1M tokens  
        "gpt-4-turbo": {"input": 10.0, "output": 30.0},  
        "gpt-3.5-turbo": {"input": 0.5, "output": 1.5}  
    }  
    
    price = prices.get(model, prices["gpt-4o"])  
    
    input_cost = (input_tokens / 1_000_000) * price["input"]  
    output_cost = (output_tokens / 1_000_000) * price["output"]  
    
    return input_cost + output_cost  

# 예시: 1000개 질문 (각 3000 입력, 500 출력 토큰)  
total_cost = estimate_cost(3000 * 1000, 500 * 1000, "gpt-4o")  
print(f"예상 비용: ${total_cost:.2f}")  

12 콜백을 통한 모니터링

from langchain.callbacks import StdOutCallbackHandler  

# 콜백 핸들러  
callback = StdOutCallbackHandler()  

# LLM 호출 시 콜백 전달  
response = llm.invoke(  
    "Azure AI Search란?",  
    config={"callbacks": [callback]}  
)  

# 토큰 사용량 확인  
print(f"토큰 사용량: {response.response_metadata.get('token_usage')}")  

13 오류 처리

13.1 재시도 로직

from tenacity import retry, stop_after_attempt, wait_exponential  

@retry(  
    stop=stop_after_attempt(3),  
    wait=wait_exponential(multiplier=1, min=4, max=10)  
)  
def invoke_llm_with_retry(messages):  
    """재시도 가능한 LLM 호출"""  
    return llm.invoke(messages)  

# 사용  
try:  
    response = invoke_llm_with_retry("테스트 질문")  
    print(response.content)  
except Exception as e:  
    print(f"LLM 호출 실패: {e}")  

13.2 Rate Limit 처리

import time  

def invoke_with_rate_limit(query, requests_per_minute=60):  
    """Rate limit을 고려한 호출"""  
    sleep_time = 60 / requests_per_minute  
    
    response = llm.invoke(query)  
    time.sleep(sleep_time)  
    
    return response  

14 참고 자료

14.1 공식 문서

14.2 LangChain

15 다음 단계

Azure OpenAI LLM 최적화가 완료되었다면, 이제 서버리스 배포를 준비하자:

👉 07-Azure-Functions-Apps.qmd - Azure Functions를 활용한 서버리스 RAG 배포

Subscribe

Enjoy this blog? Get notified of new posts by email: