Claude 3.7 소네 평가: 성능, 추론, 비용 최적화
Anthropic의 최신 플래그십 LLM, Claude 3.7 Sonnet 실험기! 이 글은 AI 번역본입니다. 오역이 있을 경우 댓글로 자유롭게 알려주세요.
Created on September 12|Last edited on September 12
Comment
Claude 3.7 Sonnet 는 Anthropic의 최신 AI 모델로, 추론, 코딩, 멀티모달 역량에서 큰 도약을 이뤘습니다. Claude 3.5의 업그레이드로 설계된 이번 버전은 Extended Thinking 모드를 도입해, 답변을 제공하기 전에 AI가 얼마나 “생각”할지 사용자가 제어할 수 있게 합니다. 200K 토큰 컨텍스트 윈도우, 향상된 문제 해결 능력, 강화된 코딩 기능을 갖춘 Claude 3.7은 현재 사용 가능한 가장 강력한 LLM 중 하나로 자리매김합니다.
이 모델은 특히 소프트웨어 개발, 연구, 비즈니스 자동화에 적합합니다. AI 분야에서 최고 수준의 추론 성능을 제공하며, 이전 Claude 버전들을 능가하고 일부 벤치마크에서는 GPT-4o 같은 경쟁 모델과 견줄 만큼 우수한 결과를 보입니다.
그러나 이러한 기능 향상에는 효율적인 예산 관리가 필수적으로 따라옵니다. 즉, 쿼리마다 사용자가 얼마만큼의 계산 자원과 토큰 소모를 할당할지 관리해야 합니다. 이 글에서는 Claude 3.7을 활용하는 방법을 보여주고, AIME 2024 수학 벤치마크에서 더 많은 추론 노력이 전체 성능에 어떤 영향을 미치는지 평가하겠습니다.

목차
목차Claude 3.7 Sonnet의 새로운 점은 무엇인가요?성능 벤치마크멀티모달 기능과 대용량 컨텍스트코딩 기능 향상예산 책정과 비용Claude 3.7에서의 예산 책정이란 무엇인가?예산 책정이 중요한 이유OpenAI의 o3-mini와의 예산 책정 비교Claude 3.7 Sonnet에서 추론 실행하기Claude 3.7 Sonnet에서의 도구 사용Weave Evaluations로 Claude 3.7의 추론 능력 평가Weave로 결과 해석 Weave 비교 보기 결론
Claude 3.7 Sonnet의 새로운 점은 무엇인가요?
Claude 3.7 Sonnet은 이전 버전인 Claude 3.5 대비 여러 가지 주요 업그레이드를 도입했습니다. 그중 가장 중요한 변화는 Extended Thinking 모드로, 속도와 더 깊은 추론 사이의 균형을 사용자가 조절할 수 있게 합니다. 표준 모드에서는 Claude가 일반 지식 기반의 응답을 빠르게 제공하지만, Extended 모드에서는 단계적 사고 과정을 거쳐 코딩, 수학, 논리 추론 같은 복잡한 작업에서 정확도를 높입니다. 빠른 처리와 느린 처리를 위해 별도의 아키텍처를 사용하는 일부 모델과 달리, Claude 3.7은 두 모드를 모두 단일 하이브리드 모델로 구현해 성능 제어권을 더 많이 제공합니다.
성능 벤치마크
특정 벤치마크 전반에서 이들 최전선 AI 모델을 비교해 보면, 각자의 뚜렷한 역량이 드러납니다:

지시 수행(IFEval): Claude 3.7 Sonnet은 확장 추론 모드에서 93.2%, 표준 모드에서 90.8%로 DeepSeek R1의 83.3%를 능가합니다. Grok 3 Beta와 o3-mini는 이 항목의 보고된 점수가 없습니다.
수학 문제 해결(MATH 500)DeepSeek R1가 이 벤치마크에서 97.3%로 최상위를 기록했으며, o3-mini의 97.9%와 Claude 3.7 Sonnet의 96.2%(확장 추론)를 간발의 차로 앞섰습니다.
대학원 수준 추론(GPQA Diamond): Claude 3.7 Sonnet은 84.8%를 기록하여 84.6%를 달성한 Grok 3 Beta를 소폭 앞섰습니다. Claude 3.7 Sonnet의 높은 점수는 병렬 테스트 시간 연산과 함께 “internal scoring”을 사용한 것이고, Grok 3의 결과는 N=64 샘플에 대한 다수결 투표를 사용한 것입니다. o3-mini는 79.7%에 도달했습니다.
에이전틱 코딩(SWE-bench Verified): Claude 3.7 Sonnet은 고연산 모드에서 70.3%로 o3-mini의 49.3%, DeepSeek R1의 49.2%를 앞서며, Grok 3의 점수는 보고되지 않았습니다.
고등학교 수학(AIME 2024): Grok 3 Beta는 93.3%를 기록했고 Claude 3.7 Sonnet은 80.0%에 도달했습니다. GPQA와 마찬가지로 Claude의 점수는 병렬 테스트 시간 연산을 동반한 internal scoring을 사용한 반면, Grok 3의 높은 결과는 N=64 샘플에 대한 majority voting을 사용했습니다. o3-mini는 83.3%를 달성했습니다.
시각적 추론(MMMU): Grok 3 Beta가 78.0%로 선두이며, o3-mini의 78.2%와 Claude 3.7 Sonnet의 75%를 약간 앞섭니다.
멀티모달 기능과 대용량 컨텍스트
Claude 3.7은 이제 멀티모달을 지원하여 텍스트뿐 아니라 이미지도 처리할 수 있습니다. 이를 통해 스크린샷, 다이어그램, 스캔한 문서를 업로드해 분석할 수 있습니다. 또한 200K 토큰 컨텍스트 윈도우는 업계에서 가장 큰 수준 중 하나로, 한 번의 프롬프트로 전체 책, 대규모 연구 문서, 혹은 전체 코드 저장소를 다룰 수 있게 해줍니다.
코딩 기능 향상
Claude 3.7은 소프트웨어 엔지니어링에 맞춰 특별히 최적화되었습니다. 개발자 워크플로와 통합되는 커맨드라인 도구인 Claude Code라는 새로운 기능을 지원하여, Claude가 파일을 수정하고 테스트를 실행하며 GitHub에 코드를 커밋하는 것까지 수행할 수 있습니다. 이를 통해 가장 자율성이 높은 AI 코딩 어시스턴트 중 하나로 자리매김합니다. 벤치마크 결과는 실제 환경의 코딩 과제를 해결하는 데서 최첨단 성능을 보여 주며, 대규모 프로젝트를 진행하는 개발자들이 선호하는 도구임을 입증합니다.
예산 책정과 비용
Claude 3.7의 가격은 Claude 3.5와 동일합니다. 입력 토큰 100만 개당 $3, ���력 토큰 100만 개당 $15입니다. 다만 Extended Thinking 모드가 도입되면서 예산 관리의 중요성이 그 어느 때보다 커졌습니다. 표준 응답과 달리 확장 추론은 추가적인 “thinking tokens”를 소모하며, 이는 전체 비용에 포함됩니다. 각 질의에 얼마나 많은 계산 자원을 배정할지 결정해, 추론의 깊이와 비용, 응답 시간을 균형 있게 조절해야 합니다.
다음 섹션에서는 Claude 3.7의 예산 책정 방식, OpenAI의 접근법과의 비교, 그리고 비용·정확도·지연 시간 간의 균형점과 트레이드오프를 살펴보겠습니다.
Claude 3.7에서의 예산 책정이란 무엇인가?
Claude 3.7에서의 예산 책정은 비용을 통제하면서 성능을 최적화하기 위해 토큰 사용량을 관리하는 것을 의미합니다. 이 모델은 빠른 추론과 확장 추론을 모두 제공하므로, 답변을 제시하기 전에 Claude가 사고에 사용할 토큰 수를 세밀하게 조정할 수 있습니다. 이는 정확도가 중요한 동시에 과도한 토큰 소모로 비용 증가나 응답 지연이 발생할 수 있는 상황에서 특히 유용합니다.
예산 책정이 중요한 이유
Claude 3.7의 모든 응답은 토큰을 소모하지만, Extended Thinking 모드에서는 이 소모량이 크게 증가합니다. 표준 응답은 수백에서 수천 토큰을 사용할 수 있는 반면, 확장 추론 응답은—특히 복잡한 코딩이나 논리 문제의 경우—수만 토큰을 소모할 수 있습니다. 가격이 입력/출력 토큰 사용량을 기준으로 책정되므로, 더 긴 추론은 곧바로 더 높은 비용으로 이어집니다.
예산 책정은 과도한 지출 없이 모델의 성능을 최대한 끌어내도록 해 줍니다. 또한 속도가 중요한 지연 시간 민감형 애플리케이션에서도 도움이 됩니다. 예를 들어, AI 기반 지원 챗봇이 Claude를 사용 중일 때 모든 질의에서 확장 추론을 활성화하면 응답이 느려지고 운영 비용이 불필요하게 증가할 수 있습니다. 이런 경우에는 가장 복잡한 질의에만 선택적으로 확장 추론을 적용하는 편이 더 나은 균형을 제공합니다.
OpenAI의 o3-mini와의 예산 책정 비교
Claude 3.7은 수동 예산 책정을 제공하여 추론 깊이를 직접 제어할 수 있습니다. 더 높은 예산은 정확도를 높여 주며, 특히 코딩·수학·논리 과제에서 효과적이지만 비용이 증가하고 응답 속도가 느려집니다. 반대로 낮은 예산은 더 빠르고 저렴하지만 추론 성능이 약화될 수 있습니다. Claude의 큰 장점 중 하나는 전체 추론 트레이스를 제공해 투명성과 해석 가능성을 확보한다는 점입니다.
OpenAI의 o3-mini는 추론 노력 파라미터로 예산 책정을 자동화합니다. 높은 모드는 수동 튜닝 없이 정확도를 높이지만 더 많은 토큰을 소모하고, 낮은 모드는 비용 효율적이지만 추론 성능이 약화될 수 있습니다. Claude와 달리 o3는 전체 추론 과정을 투명하게 제공하지 않으므로, 통제력과 투명성이 필요한 사용자에게는 Claude가 훌륭한 선택입니다.
Claude 3.7 Sonnet에서 추론 실행하기
Claude 3.7 Sonnet은 응답 속도와 추론 깊이의 균형을 조절할 수 있는 유연한 추론 시스템을 제공합니다. 기본 추론 모드에서는 확장된 다단계 추론 없이 질의를 처리하므로, 빠르고 효율적인 응답이 필요한 작업에 적합합니다. 이 모드는 추가적인 계산이 필요하지 않은 직관적 문제 해결, 논리 퍼즐, 일반 지식 질의에 특히 유용합니다.
이 구현에서는 추론 요청이 다음을 기반으로 동적으로 조정됩니다 THINKING_BUDGET 파라미터입니다. 예산이 0보다 크면 Claude는 답을 제공하기 전에 단계별 추론을 수행할 수 있습니다. 그렇지 않으면 최소한의 계산만으로 표준 응답을 반환합니다. 이를 통해 작업의 복잡도에 따라 정확도와 토큰 소모를 모두 최적화할 수 있습니다.
import osimport jsonfrom anthropic import Anthropicimport weave; weave.init("claude37_inference")# ConfigurationAPI_KEY = "your claude key" # Replace with your API keyMODEL = "claude-3-7-sonnet-20250219"THINKING_BUDGET = 2000ANSWER_TOKENS = 4000MAX_TOKENS = ANSWER_TOKENS + THINKING_TOKENSENABLE_STREAMING = False # Set to True to enable streaming# Default prompt if none is providedDEFAULT_PROMPT = """Solve this puzzle: Three people check into a hotel. They pay $30 to the manager.The manager finds out that the room only costs $25 so he gives $5 to the bellboy to returnto the three people. The bellboy, however, decides to keep $2 and gives $1 back to each person.Now, each person paid $10 and got back $1, so they paid $9 each, totaling $27.The bellboy kept $2, which makes $29. Where is the missing $1?"""# Initialize clientclient = Anthropic(api_key=API_KEY)@weave.opdef run_inference(prompt=None):"""Run inference with the given prompt or default prompt if none provided.Args:prompt (str, optional): The prompt to send to Claude. Defaults to None.Returns:dict: The complete response object when using basic_inferenceor the final text response when using stream_inference"""# Use the provided prompt or fall back to the defaultactual_prompt = prompt if prompt is not None else DEFAULT_PROMPTif ENABLE_STREAMING:return stream_inference(actual_prompt)else:return basic_inference(actual_prompt)def basic_inference(prompt):"""Basic non-streaming inference.Args:prompt (str): The prompt to send to ClaudeReturns:dict: The complete response object from the API"""try:# Prepare request parametersrequest_params = {"model": MODEL,"max_tokens": MAX_TOKENS,"messages": [{"role": "user", "content": prompt}]}# Only include thinking if budget is greater than 0if THINKING_BUDGET > 0:request_params["thinking"] = {"type": "enabled", "budget_tokens": THINKING_BUDGET}response = client.messages.create(**request_params)# Print thinking blocks and answerfor block in response.content:if block.type == "thinking":print("\n=== THINKING ===")print(block.thinking)print("===============")elif block.type == "text":print("\n=== ANSWER ===")print(block.text)print("==============")return responseexcept Exception as e:print(f"Error: {e}")return Nonedef stream_inference(prompt):"""Streaming inference.Args:prompt (str): The prompt to send to ClaudeReturns:str: The combined text response from the stream"""try:# Prepare streaming request parametersstream_params = {"model": MODEL,"max_tokens": MAX_TOKENS,"messages": [{"role": "user", "content": prompt}]}# Only include thinking if budget is greater than 0if THINKING_BUDGET > 0:stream_params["thinking"] = {"type": "enabled", "budget_tokens": THINKING_BUDGET}full_text_response = ""with client.messages.stream(**stream_params) as stream:print("\n=== STREAMING RESPONSE ===")current_block_type = Nonefor event in stream:if event.type == "content_block_start":current_block_type = event.content_block.typeprint(f"\n--- Starting {current_block_type} block ---")elif event.type == "content_block_delta":if event.delta.type == "thinking_delta":print(event.delta.thinking, end="", flush=True)elif event.delta.type == "text_delta":print(event.delta.text, end="", flush=True)full_text_response += event.delta.textelif event.type == "content_block_stop":print(f"\n--- End {current_block_type} block ---")elif event.type == "message_stop":print("\n--- Message complete ---")print("\n==========================")return full_text_responseexcept Exception as e:print(f"Error in streaming: {e}")return Noneif __name__ == "__main__":# Example 1: Using the default promptdefault_response = run_inference()print("\nInference with default prompt complete.")# Example 2: Using a custom promptcustom_prompt = "Explain how quantum computing differs from classical computing in simple terms."custom_response = run_inference(custom_prompt)print("\nInference with custom prompt complete.")
요청을 동적으로 구성하면, 이 방식은 명시적으로 필요할 때에만 모델이 확장 추론을 수행하도록 보장합니다. 이런 수준의 제어를 통해 비용과 지연 시간 고려 사항에 따라 실시간으로 조정할 수 있습니다.
수학 증명처럼 단계가 많은 문제나 코딩 작업처럼 더 복잡한 문제에서는 확장 추론 모드로 전환하여, Claude가 더 깊은 분석을 위해 추가 토큰을 할당하도록 할 수 있습니다. 다음 섹션에서는 이것이 실제로 정확도와 문제 해결 성능에 어떤 영향을 미치는지 살펴보겠습니다.
스크립트를 실행한 뒤에는 Weave 내부의 run_inference 함수에 전달된 입력과 생성된 출력 값을 확인할 수 있습니다.

Claude 3.7 Sonnet에서의 도구 사용
Claude 3.7 Sonnet은 외부 도구를 통합해 기능을 확장하며, 실시간 데이터를 조회하고 계산을 수행하며 구조화된 소스와 상호작용할 수 있습니다. 내부 추론에만 의존하는 대신, 모델은 API를 호출하고 함수를 실행하며 그 결과를 응답에 반영할 수 있습니다. 이를 통해 정확도가 향상되고 실제 과제를 처리하는 능력이 확대됩니다.
이 구현에서는 날씨 API, 계산기, 사전 조회 함수 등 여러 도구 중에서 Claude가 적절한 도구를 선택합니다. 모델은 먼저 사용자의 질의를 평가해 도구 필요 여부를 판단하고, 해당하는 도구 호출을 생성합니다. 필요할 경우, 올바른 결과를 가져와 응답에 반영할 때까지 시스템이 반복 실행됩니다.
import osimport jsonfrom anthropic import Anthropicimport weave; weave.init("claude37_inference")# ConfigurationAPI_KEY = "your claude key" # Replace with your API keyMODEL = "claude-3-7-sonnet-20250219"ENABLE_STREAMING = False # Set to True to enable streaming output# Initialize clientclient = Anthropic(api_key=API_KEY)# Tools definitionsTOOLS = [{"name": "weather","description": "Get current weather information for a location.","input_schema": {"type": "object","properties": {"location": {"type": "string", "description": "The location to get weather for."}},"required": ["location"]}},{"name": "calculator","description": "Perform mathematical calculations.","input_schema": {"type": "object","properties": {"expression": {"type": "string", "description": "The mathematical expression to evaluate."}},"required": ["expression"]}},{"name": "dictionary","description": "Get definitions of words.","input_schema": {"type": "object","properties": {"word": {"type": "string", "description": "The word to look up."}},"required": ["word"]}}]# Mock data for toolsWEATHER_DATA = {"New York": {"temperature": 72, "condition": "Sunny", "humidity": "45%"},"London": {"temperature": 62, "condition": "Cloudy", "humidity": "78%"},"Tokyo": {"temperature": 80, "condition": "Partly cloudy", "humidity": "65%"},"Paris": {"temperature": 65, "condition": "Rainy", "humidity": "82%"},"Sydney": {"temperature": 85, "condition": "Clear", "humidity": "55%"},}DICTIONARY_DATA = {"ephemeral": {"definition": "Lasting for a very short time.","part_of_speech": "adjective","example": "Ephemeral snowflakes melted on her hand."},"ubiquitous": {"definition": "Present, appearing, or found everywhere.","part_of_speech": "adjective","example": "Smartphones have become ubiquitous in modern society."},"serendipity": {"definition": "The occurrence of events by chance in a happy or beneficial way.","part_of_speech": "noun","example": "The discovery was a perfect example of serendipity."},"algorithm": {"definition": "A process or set of rules to be followed in calculations or other problem-solving operations.","part_of_speech": "noun","example": "A search algorithm"}}def get_weather(location):"""Mock weather data function."""return WEATHER_DATA.get(location, {"error": f"No weather data available for {location}"})def calculate(expression):"""Simple calculator function."""try:# Warning: eval can be dangerous in production code# Use a proper math expression parser in real applicationsresult = eval(expression, {"__builtins__": {}})return {"result": result}except Exception as e:return {"error": f"Could not evaluate expression: {str(e)}"}def get_definition(word):"""Mock dictionary function."""return DICTIONARY_DATA.get(word.lower(), {"error": f"No definition found for '{word}'"})def execute_tool(tool_name, tool_input):"""Execute the appropriate tool based on the name and input."""if tool_name == "weather":return get_weather(tool_input["location"])elif tool_name == "calculator":return calculate(tool_input["expression"])elif tool_name == "dictionary":return get_definition(tool_input["word"])else:return {"error": "Unknown tool requested"}@weave.opdef multi_tool_example(prompt):"""Example showing Claude choosing between multiple tools.Args:prompt (str): The user's input promptReturns:dict: The final response object from the API"""# Initial request with the user's promptprint(f"\n=== QUESTION: {prompt} ===")response = client.messages.create(model=MODEL,max_tokens=4000,thinking={"type": "enabled", "budget_tokens": 2000},tools=TOOLS,messages=[{"role": "user", "content": prompt}])# Display thinking and tool selectionthinking_blocks = [b for b in response.content if b.type == "thinking"]for block in thinking_blocks:print("\n🧠 THINKING:")print(block.thinking[:300] + "..." if len(block.thinking) > 300 else block.thinking)# Process tool use if neededconversation = [{"role": "user", "content": prompt}]# We might need multiple tool calls, so loop until we get a final answerwhile response.stop_reason == "tool_use":tool_block = next((b for b in response.content if b.type == "tool_use"), None)if tool_block:# Show which tool was selectedprint(f"\n🔧 SELECTED TOOL: {tool_block.name}")print(f"Tool input: {tool_block.input}")# Execute the appropriate tooltool_result = execute_tool(tool_block.name, tool_block.input)print(f"Tool result: {json.dumps(tool_result, indent=2)}")# Save assistant's response (thinking + tool use)assistant_blocks = thinking_blocks + [tool_block]conversation.append({"role": "assistant", "content": assistant_blocks})# Add tool result to conversationconversation.append({"role": "user","content": [{"type": "tool_result","tool_use_id": tool_block.id,"content": json.dumps(tool_result)}]})# Get next responseresponse = client.messages.create(model=MODEL,max_tokens=4000,thinking={"type": "enabled", "budget_tokens": 2000},tools=TOOLS,messages=conversation)# Update thinking blocks for next iterationthinking_blocks = [b for b in response.content if b.type == "thinking"]for block in thinking_blocks:print("\n🧠 ADDITIONAL THINKING:")print(block.thinking[:300] + "..." if len(block.thinking) > 300 else block.thinking)# Print final answerprint("\n✓ FINAL ANSWER:")final_text = ""for block in response.content:if block.type == "text":print(block.text)final_text += block.text# Collect all tools used throughout the conversationtools_used = []for msg in conversation:if msg["role"] == "assistant" and isinstance(msg["content"], list):for block in msg["content"]:if hasattr(block, "type") and block.type == "tool_use":tools_used.append({"name": block.name,"input": block.input,"id": block.id})# Return the complete response object for further processingreturn {"response_object": response,"final_text": final_text,"conversation_history": conversation,"tools_used": tools_used}if __name__ == "__main__":# Example 1: Weather questionresult1 = multi_tool_example("What's the current weather in Tokyo?")# Example 2: Math calculationresult2 = multi_tool_example("Calculate the square root of 144 plus 25")# Example 3: Dictionary definitionresult3 = multi_tool_example("Define the word 'algorithm' for me")# Example 4: Complex question requiring multiple toolsresult4 = multi_tool_example("If it's 62°F in London, what's that in Celsius?")# You can now use the returned results for further processingprint("\n=== Example of accessing returned data ===")print(f"Example 4 final text: {result4['final_text']}")# Print tools used in the complex query exampleprint("\n=== Tools used in Example 4 ===")if result4['tools_used']:for i, tool in enumerate(result4['tools_used'], 1):print(f"Tool {i}: {tool['name']} (ID: {tool['id']})")print(f"Input: {json.dumps(tool['input'], indent=2)}")else:print("No tools were used in this example.")
이 방식은 입력에 따라 Claude가 외부 도구를 언제 사용할지 결정할 수 있게 합니다. 모델은 질의를 분석하고, 필요하면 적절한 도구를 호출한 뒤 결과를 처리하여 응답에 통합합니다.
이 접근 방식은 자동화 과정을 투명하게 유지하여 Claude가 어떻게 추론하고 도구와 상호작용하는지 확인할 수 있게 합니다. 실시간 데이터 조회나 정밀 계산 해결이 더 정확하고 효율적이 됩니다.
도구 사용을 추론 과정에 통합함으로써 Claude 3.7 Sonnet은 속도와 안정성을 유지하면서 문제 해결 능력을 확장합니다. W&B Weave를 사용하면 도구 사용 현황도 추적할 수 있어, 모델이 각 도구를 어떻게 사용하는지 모니터링할 수 있습니다:

Weave Evaluations로 Claude 3.7의 추론 능력 평가
이제 표준 모드부터 최대 24K 토큰의 Extended Thinking까지 다양한 추론 예산을 설정해 Claude 3.7의 추론 능력을 평가하겠습니다. Weave Evaluations를 사용해 모델의 입력, 출력, 성능 지표를 추적함으로써, 예산 설정이 정확도, 지연 시간, 비용에 어떤 영향을 미치는지 명확하고 구조적인 방식으로 분석할 수 있습니다. 이 과정을 마치면 Claude 3.7의 기능을 효율적으로 탐색하고, 서로 다른 추론 예산의 트레이드오프를 이해하는 간소화된 절차를 갖추게 됩니다.
이번 평가에 사용된 데이터셋은 AIME 2024 데이터셋으로, 난도 높은 수학적 추론 문제들로 구성되어 있습니다. 각 항목은 입력으로 문제 설명, 출력으로 해당 해답을 포함합니다. 이 데이터셋은 대수, 미적분, 정수론 등 다양한 과제를 통해 대규모 언어 모델의 추론 능력을 검증하도록 설계되었습니다. 이를 활용해 Claude 3.7이 문맥적 이해와 정밀한 논리 추론을 모두 요구하는 문제를 이해하고 해결하는 능력을 벤치마크하는 것을 목표로 합니다.
제 평가에 사용한 코드는 다음과 같습니다:
import osimport asyncioimport jsonfrom datasets import load_datasetfrom anthropic import Anthropicfrom litellm import completion # Using litellm instead of Azure OpenAIimport weaveweave.init("aime_evaluation") # Initialize Weave# Initialize Anthropic clientANTHROPIC_API_KEY = os.environ.get("ANTHROPIC_API_KEY", "your key")client = Anthropic(api_key=ANTHROPIC_API_KEY)# Set OpenAI API key for litellmos.environ["OPENAI_API_KEY"] = "your key" # Replace with your actual OpenAI API key# ConstantsMODEL_NAME = "claude-3-7-sonnet-20250219"MAX_ANSWER_TOKENS = 8000 # Maximum tokens for the answer# Define different reasoning setups to test with much larger thinking budgetsREASONING_CONFIGS = [{"name": "standard","thinking": None, # Standard Claude (no extended thinking)"max_tokens": MAX_ANSWER_TOKENS},{"name": "thinking_4k","thinking": {"type": "enabled", "budget_tokens": 4000},"max_tokens": MAX_ANSWER_TOKENS + 4000},{"name": "thinking_8k","thinking": {"type": "enabled", "budget_tokens": 8000},"max_tokens": MAX_ANSWER_TOKENS + 8000},{"name": "thinking_16k","thinking": {"type": "enabled", "budget_tokens": 16000},"max_tokens": MAX_ANSWER_TOKENS + 16000},{"name": "thinking_24k","thinking": {"type": "enabled", "budget_tokens": 24000},"max_tokens": MAX_ANSWER_TOKENS + 24000}]# Consistent system message for all modelssystem_message = "Solve the following problem. put your final answer within \\boxed{}: "# Function to perform inference using litellm (for the scorer)def run_inference(prompt, model_id="gpt-4o-2024-08-06"):try:response = completion(model=model_id,temperature=0.0,messages=[{"role": "user", "content": prompt}])# Extract content from litellm responseif response and hasattr(response, 'choices') and len(response.choices) > 0:content = response.choices[0].message.contentreturn contentelse:print("No content found in response")return Noneexcept Exception as e:print(f"Failed to get response: {e}")return None# Claude inference function with streamingasync def claude_inference(prompt: str, config: dict) -> str:"""Generate solution using Claude with specified reasoning config and streaming."""try:kwargs = {"model": MODEL_NAME,"max_tokens": config["max_tokens"],"messages": [{"role": "user", "content": system_message + prompt}]}# Add thinking parameters if specifiedif config["thinking"]:kwargs["thinking"] = config["thinking"]print(f"\n--- Starting inference with {config['name']} config ---")print(f"Max tokens: {config['max_tokens']}")if config["thinking"]:print(f"Thinking budget: {config['thinking']['budget_tokens']} tokens")thinking_content = ""text_content = ""# Use streamingwith client.messages.stream(**kwargs) as stream:for event in stream:if event.type == "content_block_start":if event.content_block.type == "thinking":print(f"\n[Starting thinking block...]")elif event.content_block.type == "text":print(f"\n[Starting response...]")elif event.content_block.type == "redacted_thinking":print(f"\n[Redacted thinking block]")elif event.type == "content_block_delta":if event.delta.type == "thinking_delta":# Just print dots to show progress without flooding consoleprint(".", end="", flush=True)thinking_content += event.delta.thinkingelif event.delta.type == "text_delta":print(event.delta.text, end="", flush=True)text_content += event.delta.textelif event.type == "content_block_stop":if hasattr(event, 'content_block') and event.content_block.type == "thinking":print(f"\n[Thinking block complete - {len(thinking_content)} chars]")elif hasattr(event, 'content_block') and event.content_block.type == "text":print(f"\n[Response complete - {len(text_content)} chars]")else:print(f"\n[Block complete]")print(f"\n--- Inference complete ---")# If no text was generated, return an error messageif not text_content:return "No response generated"return text_contentexcept Exception as e:print(f"Error in inference: {e}")return f"Error: {str(e)}"# Define model classes for each reasoning configurationclass ClaudeStandardModel(weave.Model):@weave.opasync def predict(self, text: str) -> str:return await claude_inference(text, REASONING_CONFIGS[0])class ClaudeThinking4kModel(weave.Model):@weave.opasync def predict(self, text: str) -> str:return await claude_inference(text, REASONING_CONFIGS[1])class ClaudeThinking8kModel(weave.Model):@weave.opasync def predict(self, text: str) -> str:return await claude_inference(text, REASONING_CONFIGS[2])class ClaudeThinking16kModel(weave.Model):@weave.opasync def predict(self, text: str) -> str:return await claude_inference(text, REASONING_CONFIGS[3])class ClaudeThinking24kModel(weave.Model):@weave.opasync def predict(self, text: str) -> str:return await claude_inference(text, REASONING_CONFIGS[4])# Score function using GPT-4o via litellm@weave.opasync def gpt4o_scorer(label: str, model_output: str) -> dict:"""Score the model's output by comparing it with the ground truth."""query = f"""YOU ARE A LLM JUDGE DETERMINING IF THE FOLLOWING MODEL GENERATED ANSWER IS THE SAME AS THE CORRECT ANSWERI WILL GIVE YOU THE LAST 100 CHARS OF THE MODEL'S REASONING PATH, WHICH WILL CONTAIN THE FINAL ANSWER ->Model's Answer (last 100 chars): {str(model_output)[-100:]}Correct Answer: {label}Your task:1. State the model's predicted answer (answer only).2. State the ground truth (answer only).3. Determine if the model's final answer is correct (ignore formatting differences, etc.). RESPOND with the predicted and ground truth answer, followed with a JSON object containing the correctness encapsulated within the following delimiters:```json{{ "correctness": true/false }}```"""# Perform inference using litellmresponse = run_inference(query, "gpt-4o-2024-08-06")if response is None:return {"correctness": False, "reasoning": "Inference failed."}try:# Extract correctness JSON object from the responsejson_start = response.index("```json") + 7json_end = response.index("```", json_start)correctness = json.loads(response[json_start:json_end].strip()).get("correctness", False)except (ValueError, IndexError):correctness = Falsereturn {"correctness": correctness, "reasoning": response}# Load and preprocess datasetdef load_ds():print("Loading AIME dataset...")try:dataset = load_dataset("Maxwell-Jia/AIME_2024")["train"] # No test set herereturn [{"text": row["Problem"], "label": row["Answer"]} for row in dataset]except Exception as e:print(f"Error loading AIME dataset: {e}")return []# Run evaluations for each modelasync def run_evaluations():print("Loading dataset...")dataset = load_ds()print(f"Loaded {len(dataset)} problems")print("Initializing models...")models = {"standard": ClaudeStandardModel(),"thinking_4k": ClaudeThinking4kModel(),"thinking_8k": ClaudeThinking8kModel(),"thinking_16k": ClaudeThinking16kModel(),"thinking_24k": ClaudeThinking24kModel(),}print("Preparing dataset for evaluation...")# Take a subset for faster testing - adjust as needed# test_size = 5 # Number of problems to evaluate per modeldataset_prepared = [{"text": row["text"], "label": row["label"]} for row in dataset]print("Running evaluations...")scorers = [gpt4o_scorer]results = {}for model_name, model in models.items():print(f"\n\n=== EVALUATING {model_name.upper()} ===")evaluation = weave.Evaluation(dataset=dataset_prepared,scorers=scorers,name=f"{model_name} Evaluation")await evaluation.evaluate(model)if __name__ == "__main__":# Set your API keys here or in environment variablesif not os.environ.get("ANTHROPIC_API_KEY"):os.environ["ANTHROPIC_API_KEY"] = "YOUR_ANTHROPIC_API_KEY"if not os.environ.get("OPENAI_API_KEY"):os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY" # Required for litellm# Run evaluationsasyncio.run(run_evaluations())
AIME 2024 데이터셋은 Hugging Face의 load_dataset 함수를 사용해 로드하며, 각 문제를 정답과 매칭하여 Claude 3.7의 추론 정확도를 객관적으로 평가할 수 있게 합니다. 이 데이터셋은 복잡한 수학적·논리적 추론 과제를 처리하는 모델의 능력을 평가하는 강력한 벤치마크로 활용됩니다.
이번 평가는 Claude 3.7의 여러 구성(표준 모드와 4K, 8K, 16K, 24K 토큰의 extended reasoning 예산)을 대상으로 진행합니다. 각 모델 변형은 공정하고 일관된 비교를 보장하기 위해 Weave의 Model 클래스를 사용해 동일하게 초기화합니다. 또한 모든 실행에 표준화된 시스템 메시지를 적용하여 평가의 공정성을 유지합니다.
Claude 3.7은 스트리밍 추론을 지원하여 응답을 실시간으로 확인할 수 있습니다. Extended Thinking을 켜면 최종 답변을 제시하기 전에 모델의 가시적인 사고 과정을 표시합니다. 각 추론 구성은 reasoning token 예산을 정의하며, 이를 통해 서로 다른 계산 노력 수준을 통제된 방식으로 실험할 수 있습니다.
Weave로 결과 해석
이번 평가는 Claude 3.7에서 서로 다른 reasoning budget을 사용할 때 비용, 정확도, 응답 시간 간의 트레이드오프를 강조합니다. 더 높은 예산은 일반적으로 복잡한 문제에서 정답률을 높이지만, 토큰 사용량과 지연 시간도 증가시킵니다. 제 평가 결과는 다음과 같습니다:

정확도는 채점 모델로 GPT-4o를 사용해, 각 모델이 정답 대비 얼마나 정확하게 해법을 생성하는지로 평가합니다. 응답 지연 시간과 총 토큰 사용량도 추적하여, 더 빠른 응답과 더 깊은 추론 사이의 효율성 트레이드오프를 파악합니다.
결과에 따르면 추론 예산을 높이면 정확도는 향상되지만 지연 시간은 크게 증가합니다. 16K와 24K 토큰을 사용하는 모델이 가장 높은 정답률(0.500)을 기록했으며, 예산이 낮을수록 점수가 점진적으로 하락했고 표준 모드는 0.200으로 가장 낮은 성능을 보였습니다.
Anthropic이 공개한 Claude 3.7 Sonnet의 벤치마크는 확장 추론에서의 성능 향상을 강조하지만, 병렬 테스트 시간 계산 방식의 구체적인 세부사항은 완전히 공개하지 않았습니다. 그들은 Claude가 Extended Thinking에 최대 64K tokens까지 사용할 수 있다고 밝혔으나, 이 과정에서 언제 추론 경로 생성을 중단하는지, 혹은 여러 가능한 해법을 어떻게 평가하는지에 대한 방식은 상세히 설명하지 않았습니다. 이 접근법은 문제 복잡도에 따라 추론 시간을 동적으로 할당하는 별도의 내부 모델이나 휴리스틱을 활용할 가능성이 있습니다.


이들의 결과에 따르면 Claude 3.7 Sonnet의 점수는 61.3% / 80.0% AIME 2024에서는 첫 번째 숫자가 단일 추론 경로로 계산한 pass@1 점수를 의미하는 것으로 보이며(제 추정입니다), 두 번째 숫자는 내부 테스트 타임 컴퓨트 방식을 반영한 값입니다. 이는 작업을 검증하고 최적의 답안을 선택하기 위해 또 다른 언어 모델(예: 특화된 Claude 복제본)을 사용해 최종 추론 트레이스를 고르는 등 더 발전된 방법을 포함할 수 있습니다.
Weave 비교 보기
Weave의 비교 보기는 응답 트레이스를 직접 시각화할 수 있어 논리, 구조, 정답성의 불일치를 쉽게 파악하게 해 주므로 추론 모델을 평가할 때 특히 유용합니다. 여러 출력물을 나란히 제시함으로써 서로 다른 reasoning budget이 문제가 해결되는 단계별 접근 방식에 어떤 영향을 미치는지 명확히 보여줍니다.

이 형식은 토큰 할당의 변화가 추론의 깊이, 일관성, 명료성에 어떤 영향을 미치는지 이해하는 데 특히 유용합니다. 이를 통해 모델이 서로 다른 구성에서도 논리적 일관성을 유지하는지 분석하고, 더 짧거나 더 긴 추론 경로가 서로 다른 결론으로 이어지는 지점을 정확히 파악할 수 있습니다. 모델의 사고 과정을 투명하게 보여 줌으로써, Weave의 비교 도구는 특정 애플리케이션에 맞춰 추論 모델을 정교화하고 최적화하는 일을 한층 수월하게 만들어 줍니다.
Weave의 비교 보기는 개별 추론 트레이스에 직접 접근할 수 있게 해 주어, 각 응답의 지연 시간, 비용, 토큰 사용량을 손쉽게 확인할 수 있습니다. 이를 통해 서로 다른 reasoning budget이 정답성뿐만 아니라 효율성에 어떤 영향을 미치는지 정밀하게 평가할 수 있습니다.

결론
Claude 3.7 Sonnet은 AI 추론의 유연성을 크게 확장하여, 계산 깊이와 정확도를 사용자에게 더 직접적으로 제어할 수 있게 해 줍니다. 이 모델은 추론 노력의 강도를 동적으로 조정하고, 외부 도구를 통합하며, 대규모 컨텍스트를 처리할 수 있어 다양한 도메인에서 복잡한 문제 해결에 적합합니다. 다만 이러한 능력이 커진 만큼, 예산 편성과 응답 최적화에 대한 보다 의도적인 의사 결정이 필요해집니다.
Weave는 추론 모델을 평가하기에 뛰어난 도구로, 서로 다른 reasoning budget이 정확도, 효율성, 응답 품질에 어떤 영향을 미치는지 분석할 수 있는 체계적인 프레임워크를 제공합니다. 모델 출력, 토큰 소비량, 지연 시간을 체계적으로 추적할 수 있게 함으로써, Weave는 AI 성능을 정교화하는 데 필수적인 수준의 투명성을 제공합니다. AI 시스템이 계속 진화함에 따라 Weave와 같은 도구는 연구자와 개발자가 Claude 3.7 같은 모델을 실제 애플리케이션에 맞게 최적화하는 데 핵심적인 역할을 하게 될 것입니다.
Training GPT-4o to reason: Fine-tuning vs budget forcing
Can fine-tuning and budget forcing improve GPT-4o’s reasoning? We test structured datasets and inference-time techniques to boost multi-step problem-solving.
Budget forcing s1-32B: Waiting is all you need?
We test whether budget forcing - a simple test-time intervention - can significantly boost the reasoning accuracy of s1-32B, potentially enabling smaller models to rival closed-source giants like OpenAI's o1-preview.
o3-mini vs. DeepSeek-R1: API setup, performance testing & model evaluation
Learn how to set up and run OpenAI o3-mini via the API, explore its flexible reasoning effort settings, and compare its performance against DeepSeek-R1 using W&B Weave Evaluations.
DeepSeek-R1 vs OpenAI o1: A guide to reasoning model setup and evaluation
Discover the capabilities of DeepSeek-R1 and OpenAI o1 models for reasoning and decision-making. Includes setup guides, API usage, local deployment, and Weave-powered comparisons.
Add a comment