TestForge | 📊 Plogger ✍️ Blog 📚 Docs
TestForge Blog

Python AI 가이드 · 05

LLM 개발

Claude API 기본 호출부터 스트리밍, Tool Use, LangChain, RAG 파이프라인까지. 실제 LLM 서비스를 만드는 과정을 다룹니다.

1. Claude API 기본 사용법

# pip install anthropic python-dotenv

import anthropic, os
from dotenv import load_dotenv

load_dotenv()
client = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[
        {"role": "user", "content": "Python으로 수급 분석 시스템을 만들려면?"}
    ],
)
print(message.content[0].text)

# 시스템 프롬프트 + 멀티턴

conversation = []

def chat(user_msg: str, system: str = "") -> str:
    conversation.append({"role": "user", "content": user_msg})
    kwargs = {"model": "claude-sonnet-4-6", "max_tokens": 2048, "messages": conversation}
    if system:
        kwargs["system"] = system
    resp = client.messages.create(**kwargs)
    text = resp.content[0].text
    conversation.append({"role": "assistant", "content": text})
    return text

system = "당신은 한국 주식 시장 전문 분석가입니다."
print(chat("삼성전자 외인 순매수 신호를 어떻게 해석하나요?", system))

2. 스트리밍 응답

with client.messages.stream(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "CQRS 패턴을 설명해주세요."}],
) as stream:
    for text in stream.text_stream:
        print(text, end="", flush=True)
    print()

final = stream.get_final_message()
print(f"\n토큰: {final.usage.input_tokens} in / {final.usage.output_tokens} out")

3. Tool Use (도구 사용)

import json

tools = [{
    "name": "get_stock_price",
    "description": "종목의 현재가와 외인 수급 정보를 가져옵니다.",
    "input_schema": {
        "type": "object",
        "properties": {"code": {"type": "string", "description": "종목코드"}},
        "required": ["code"],
    },
}]

def get_stock_price(code: str) -> dict:
    return {"code": code, "price": 78000, "foreign_net": 1500000}

messages = [{"role": "user", "content": "삼성전자(005930) 지금 사도 될까요?"}]

while True:
    resp = client.messages.create(
        model="claude-sonnet-4-6", max_tokens=1024,
        tools=tools, messages=messages,
    )
    messages.append({"role": "assistant", "content": resp.content})
    if resp.stop_reason == "end_turn":
        print(next(b.text for b in resp.content if hasattr(b, "text")))
        break
    tool_results = [
        {"type": "tool_result", "tool_use_id": b.id,
         "content": json.dumps(get_stock_price(**b.input), ensure_ascii=False)}
        for b in resp.content if b.type == "tool_use"
    ]
    messages.append({"role": "user", "content": tool_results})

4. LangChain — 체인 구성

# pip install langchain langchain-anthropic

from langchain_anthropic import ChatAnthropic
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatAnthropic(model="claude-sonnet-4-6", max_tokens=1024)

prompt = ChatPromptTemplate.from_messages([
    ("system", "당신은 {domain} 전문가입니다. 핵심만 간결하게 답변하세요."),
    ("human", "{question}"),
])

chain = prompt | llm | StrOutputParser()

result = chain.invoke({"domain": "백엔드 아키텍처", "question": "CQRS와 이벤트 소싱의 차이는?"})
print(result)

5. RAG 파이프라인

외부 문서를 검색해서 LLM 답변의 근거로 씁니다. 환각을 줄이고 최신 정보를 활용할 수 있습니다.

# pip install langchain-community chromadb sentence-transformers

from langchain_community.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

# 문서 인덱싱
chunks = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_documents(
    TextLoader("knowledge_base.txt", encoding="utf-8").load()
)
db = Chroma.from_documents(chunks, HuggingFaceEmbeddings(model_name="BAAI/bge-m3"), persist_directory="./db")
retriever = db.as_retriever(search_kwargs={"k": 4})

# RAG 체인
rag_prompt = ChatPromptTemplate.from_template("""
컨텍스트를 기반으로 답하세요. 없으면 "문서에 없습니다"라고 하세요.

컨텍스트:
{context}

질문: {question}
""")

rag_chain = (
    {"context": retriever | (lambda docs: "\n\n".join(d.page_content for d in docs)),
     "question": RunnablePassthrough()}
    | rag_prompt | llm | StrOutputParser()
)

print(rag_chain.invoke("t1702 API는 어떻게 사용하나요?"))

6. 프롬프트 설계 핵심 원칙

역할 부여

시스템 프롬프트에 명확한 역할을 줍니다. 구체적일수록 일관성이 높아집니다.

출력 형식 명시

JSON, 마크다운 표 등 원하는 형식을 명확히 지정합니다.

예시 포함 (Few-shot)

입력→출력 예시 1~3개를 제공하면 일관성이 크게 높아집니다.

사고 단계 유도

"단계별로 생각하세요"를 추가하면 복잡한 분석에서 정확도가 올라갑니다.