Claude API 도구 사용과 프롬프트 체이닝으로 다단계 계약서 분석 파이프라인 구축하기
개요: 왜 다단계 파이프라인인가?
계약서 분석은 단순한 단일 프롬프트로 해결하기 어렵습니다. 조항 추출, 리스크 평가, 법률 용어 검증, 최종 보고서 생성 등 여러 전문 단계가 필요합니다. Claude API의 Tool Use와 **프롬프트 체이닝(Prompt Chaining)**을 결합하면, 각 단계에서 외부 도구를 호출하고 그 결과를 다음 단계로 전달하는 강력한 자동화 파이프라인을 구축할 수 있습니다. 이 가이드에서는 Python으로 4단계 계약서 리뷰 파이프라인을 처음부터 끝까지 구현합니다.
Step 1: 환경 설정 및 SDK 설치
먼저 필요한 패키지를 설치하고 API 키를 설정합니다.
# Python 3.9 이상 필요
pip install anthropic>=0.39.0
pip install python-dotenv
.env 파일을 생성하여 API 키를 안전하게 관리합니다.
# .env
ANTHROPIC_API_KEY=YOUR_API_KEY
기본 클라이언트를 초기화합니다.
import anthropic
import json
from dotenv import load_dotenv
load_dotenv()
client = anthropic.Anthropic()
MODEL = “claude-sonnet-4-6”
Step 2: 도구(Tool) 스키마 정의
파이프라인에서 사용할 세 가지 도구를 정의합니다. 각 도구는 Claude가 호출할 수 있는 함수 인터페이스입니다.
tools = [
{
"name": "extract_clauses",
"description": "계약서 텍스트에서 주요 조항을 구조화된 형태로 추출합니다.",
"input_schema": {
"type": "object",
"properties": {
"contract_text": {
"type": "string",
"description": "분석할 계약서 전문"
},
"clause_types": {
"type": "array",
"items": {"type": "string"},
"description": "추출할 조항 유형 목록 (예: 해지, 배상, 기밀유지)"
}
},
"required": ["contract_text", "clause_types"]
}
},
{
"name": "assess_risk",
"description": "추출된 조항의 법적 리스크를 평가합니다.",
"input_schema": {
"type": "object",
"properties": {
"clauses": {
"type": "array",
"items": {"type": "object"},
"description": "평가할 조항 목록"
}
},
"required": ["clauses"]
}
},
{
"name": "generate_report",
"description": "분석 결과를 기반으로 최종 리뷰 보고서를 생성합니다.",
"input_schema": {
"type": "object",
"properties": {
"risk_assessment": {"type": "object"},
"format": {"type": "string", "enum": ["summary", "detailed"]}
},
"required": ["risk_assessment", "format"]
}
}
]
## Step 3: 도구 실행 핸들러 구현
Claude가 도구를 호출하면 실제로 실행될 로직을 정의합니다.
def handle_tool_call(tool_name, tool_input):
if tool_name == "extract_clauses":
# 실제로는 NLP 파싱 또는 DB 조회 로직 구현
return {
"extracted": [
{"type": "해지 조항", "content": tool_input["contract_text"][:200], "section": "7조"},
{"type": "배상 책임", "content": "손해배상 한도 계약금의 100%", "section": "12조"}
],
"total_found": 2
}
elif tool_name == "assess_risk":
results = []
for clause in tool_input["clauses"]:
results.append({
"clause_type": clause.get("type", "unknown"),
"risk_level": "high" if "배상" in clause.get("type", "") else "medium",
"recommendation": "법률 검토 권장"
})
return {"assessments": results}
elif tool_name == "generate_report":
return {"report": "계약서 리뷰 완료", "status": "generated"}
return {"error": "Unknown tool"}
## Step 4: 프롬프트 체이닝 오케스트레이터 구축
핵심 부분입니다. 각 단계의 출력을 다음 단계의 입력으로 자동 연결하며, Tool Use 루프를 처리합니다.
def run_pipeline(contract_text):
"""다단계 계약서 분석 파이프라인 실행"""
# === 1단계: 조항 추출 ===
messages = [{
"role": "user",
"content": f"다음 계약서에서 해지, 배상, 기밀유지 조항을 추출해주세요. "
f"extract_clauses 도구를 사용하세요.\n\n{contract_text}"
}]
step1_result = execute_with_tools(messages, "조항 추출")
# === 2단계: 리스크 평가 (이전 결과를 컨텍스트로 전달) ===
messages = [{
"role": "user",
"content": f"추출된 조항을 분석하여 리스크를 평가해주세요. "
f"assess_risk 도구를 사용하세요.\n\n"
f"추출 결과: {json.dumps(step1_result, ensure_ascii=False)}"
}]
step2_result = execute_with_tools(messages, "리스크 평가")
# === 3단계: 보고서 생성 ===
messages = [{
"role": "user",
"content": f"리스크 평가 결과를 바탕으로 상세 보고서를 생성해주세요. "
f"generate_report 도구를 사용하세요.\n\n"
f"평가 결과: {json.dumps(step2_result, ensure_ascii=False)}"
}]
final_result = execute_with_tools(messages, "보고서 생성")
return final_result
def execute_with_tools(messages, step_name):
"""Tool Use 루프를 처리하는 핵심 함수"""
print(f”\n▶ {step_name} 단계 실행 중…”)
response = client.messages.create(
model=MODEL,
max_tokens=4096,
tools=tools,
messages=messages
)
# Tool Use 루프: Claude가 도구 호출을 멈출 때까지 반복
while response.stop_reason == "tool_use":
tool_block = next(b for b in response.content if b.type == "tool_use")
tool_result = handle_tool_call(tool_block.name, tool_block.input)
print(f" ↳ 도구 호출: {tool_block.name}")
messages.append({"role": "assistant", "content": response.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_block.id,
"content": json.dumps(tool_result, ensure_ascii=False)
}]
})
response = client.messages.create(
model=MODEL,
max_tokens=4096,
tools=tools,
messages=messages
)
final_text = next((b.text for b in response.content if hasattr(b, "text")), "")
print(f" ✓ {step_name} 완료")
return {"step": step_name, "output": final_text, "raw": tool_result}</code></pre>
Step 5: 파이프라인 실행
# 실행
contract = """소프트웨어 라이선스 계약서
제7조(계약 해지) 일방 당사자가 본 계약을 위반할 경우 30일 서면 통지 후 해지 가능...
제12조(배상 책임) 손해배상 한도는 계약금의 100%로 한다...
"""
result = run_pipeline(contract)
print(json.dumps(result, indent=2, ensure_ascii=False))
파이프라인 아키텍처 요약
단계 역할 사용 도구 출력 1단계 조항 추출 extract_clauses 구조화된 조항 목록 2단계 리스크 평가 assess_risk 리스크 레벨 및 권고사항 3단계 보고서 생성 generate_report 최종 분석 보고서
## Pro Tips: 파워 유저를 위한 고급 기법
- **병렬 도구 호출:** Claude는 한 번의 응답에서 여러 도구를 동시에 호출할 수 있습니다. 독립적인 조항 분석은 병렬로 처리하여 속도를 높이세요.- **시스템 프롬프트 활용:** 각 단계마다 system 파라미터에 전문가 페르소나를 지정하면 분석 품질이 향상됩니다. 예: "당신은 15년 경력의 기업법 전문 변호사입니다."- **스트리밍 응답:** 긴 보고서 생성 시 client.messages.stream()을 사용하면 실시간으로 결과를 출력할 수 있습니다.- **컨텍스트 윈도우 관리:** 계약서가 길 경우 각 단계에서 필요한 정보만 전달하세요. 전체 원문을 매 단계마다 넘기면 토큰을 낭비합니다.- **에러 경계(Error Boundary) 설정:** 각 단계를 try-except로 감싸고, 실패 시 해당 단계만 재시도하는 로직을 추가하세요.
## Troubleshooting: 자주 발생하는 오류와 해결법
오류 원인 해결 방법 400 invalid_request_errortool_result의 tool_use_id가 불일치 응답에서 받은 tool_block.id를 정확히 사용하세요 overloaded_errorAPI 서버 과부하 지수 백오프(exponential backoff)로 재시도 구현 도구가 호출되지 않음 프롬프트에서 도구 사용을 명시하지 않음 프롬프트에 도구 이름을 직접 언급하거나 tool_choice={"type": "tool", "name": "도구명"} 사용 무한 루프 tool_result 형식 오류로 Claude가 반복 호출 while 루프에 최대 반복 횟수(예: 10회) 제한 추가토큰 한도 초과 계약서 원문이 너무 긺 계약서를 섹션별로 분할하여 단계별로 처리
## FAQ
Q1: Tool Use와 프롬프트 체이닝의 차이점은 무엇인가요?
**Tool Use**는 Claude가 외부 함수를 호출할 수 있게 하는 기능으로, 하나의 대화 안에서 작동합니다. **프롬프트 체이닝**은 여러 개의 독립적인 API 호출을 순차적으로 연결하여 이전 단계의 출력을 다음 단계의 입력으로 전달하는 패턴입니다. 이 가이드에서는 두 가지를 결합하여, 각 체인 단계 내에서 도구를 호출하고, 단계 간에는 결과를 넘겨주는 방식을 사용합니다.
Q2: 실제 프로덕션 환경에서 비용을 최적화하려면 어떻게 해야 하나요?
세 가지 전략을 권장합니다. 첫째, 1단계(조항 추출)에는 비용이 낮은 claude-haiku-4-5-20251001 모델을 사용하고, 리스크 평가처럼 판단이 중요한 단계에만 claude-sonnet-4-6을 사용하세요. 둘째, 이전에 분석한 계약서 결과를 캐싱하여 동일 문서의 재분석을 방지하세요. 셋째, Prompt Caching 기능을 활용하면 반복되는 시스템 프롬프트와 도구 정의에 대한 토큰 비용을 크게 줄일 수 있습니다.
Q3: PDF 형태의 계약서도 처리할 수 있나요?
네, Claude API는 PDF 파일을 직접 지원합니다. messages API에서 source 타입을 base64로 설정하고 media_type을 application/pdf로 지정하면 됩니다. PDF에서 텍스트와 표를 자동으로 인식하므로 별도의 OCR 전처리 없이 파이프라인에 바로 연결할 수 있습니다. 다만, 스캔된 이미지 PDF의 경우 별도 OCR 처리 후 텍스트를 전달하는 것이 정확도가 높습니다.