Tree-of-Thought
ToT / 생각의 나무
여러 추론 경로를 트리 구조로 탐색하는 고급 프롬프팅 기법. 복잡한 문제 해결에서 LLM 성능을 크게 향상시킵니다.
ToT / 생각의 나무
여러 추론 경로를 트리 구조로 탐색하는 고급 프롬프팅 기법. 복잡한 문제 해결에서 LLM 성능을 크게 향상시킵니다.
Tree-of-Thought(ToT, 생각의 나무)는 Chain-of-Thought(CoT)를 확장한 프롬프팅 기법으로, LLM이 단일 추론 경로가 아닌 여러 추론 경로를 탐색하고 평가하여 최적의 답을 찾게 합니다. 인간이 복잡한 문제를 풀 때 여러 가능성을 고려하고 가장 유망한 경로를 선택하는 것과 유사합니다.
ToT는 2023년 Princeton과 Google DeepMind 연구팀이 발표했습니다. Chain-of-Thought가 직선적 추론만 가능한 반면, ToT는 BFS(너비 우선 탐색)나 DFS(깊이 우선 탐색) 같은 탐색 알고리즘을 적용하여 더 나은 해결책을 찾습니다. "Game of 24", 창작 글쓰기 등에서 CoT 대비 큰 성능 향상을 보였습니다.
ToT의 핵심 구성요소는 세 가지입니다. 첫째, "생각(Thought)"을 중간 단계로 분해합니다. 둘째, 각 생각의 가치를 LLM이 자체 평가(Self-evaluation)합니다. 셋째, 유망한 경로를 우선 탐색하고 막다른 경로는 백트래킹합니다. 이를 통해 greedy decoding의 한계를 극복합니다.
실무에서 ToT는 수학 문제 풀이, 코드 생성, 전략 게임, 계획 수립 등 복잡한 추론이 필요한 작업에 효과적입니다. 다만 여러 경로를 탐색하므로 API 호출 비용이 증가하는 점을 고려해야 합니다. Claude 3.5 Sonnet의 extended thinking과 같은 기능이 ToT 개념을 내재화하고 있습니다.
LangChain을 활용한 Tree-of-Thought 구현 예제입니다.
from openai import OpenAI
import json
from typing import List, Dict
client = OpenAI()
def generate_thoughts(problem: str, current_state: str, n_thoughts: int = 3) -> List[str]:
"""현재 상태에서 가능한 다음 생각들을 생성"""
prompt = f"""문제: {problem}
현재 진행 상황: {current_state}
가능한 다음 단계를 {n_thoughts}가지 제안하세요.
각 단계는 한 줄로 간결하게 작성하세요.
형식: 1. [단계] 2. [단계] 3. [단계]"""
response = client.chat.completions.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}],
temperature=0.7
)
# 생각들 파싱
text = response.choices[0].message.content
thoughts = [t.strip() for t in text.split("\n") if t.strip()]
return thoughts[:n_thoughts]
def evaluate_thought(problem: str, thought: str) -> float:
"""각 생각의 유망도를 0-1 사이로 평가"""
prompt = f"""문제: {problem}
제안된 접근법: {thought}
이 접근법이 문제 해결에 얼마나 유망한지 0에서 10 사이 점수로 평가하세요.
점수만 숫자로 답하세요."""
response = client.chat.completions.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}],
temperature=0.1
)
try:
score = float(response.choices[0].message.content.strip()) / 10
return min(max(score, 0), 1)
except:
return 0.5
def tree_of_thought_solve(problem: str, max_depth: int = 3, beam_width: int = 2) -> str:
"""Tree-of-Thought로 문제 해결 (Beam Search 방식)"""
# 초기 상태
beams = [{"path": [], "score": 1.0}]
for depth in range(max_depth):
new_beams = []
for beam in beams:
current_state = " -> ".join(beam["path"]) if beam["path"] else "시작"
# 다음 가능한 생각들 생성
thoughts = generate_thoughts(problem, current_state)
for thought in thoughts:
# 각 생각 평가
score = evaluate_thought(problem, thought)
new_beams.append({
"path": beam["path"] + [thought],
"score": beam["score"] * score
})
# 상위 beam_width개만 유지 (가지치기)
new_beams.sort(key=lambda x: x["score"], reverse=True)
beams = new_beams[:beam_width]
print(f"Depth {depth + 1}: Top beam score = {beams[0]['score']:.3f}")
# 최종 답 생성
best_path = " -> ".join(beams[0]["path"])
final_prompt = f"""문제: {problem}
추론 과정: {best_path}
위 추론을 바탕으로 최종 답을 제시하세요."""
response = client.chat.completions.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": final_prompt}]
)
return response.choices[0].message.content
# 사용 예시
problem = "8, 3, 1, 6 네 숫자를 사칙연산으로 조합하여 24를 만드세요."
solution = tree_of_thought_solve(problem)
print(f"\n최종 답:\n{solution}")
깊이와 너비를 크게 설정하면 비용이 기하급수로 증가합니다. beam_width=2~3, depth=3~5 정도로 시작하세요.
간단한 QA에는 ToT가 오버헤드만 증가시킵니다. 정말 복잡한 추론이 필요한 작업에만 사용하세요.
문제 복잡도에 따라 CoT/ToT 선택, 캐싱으로 중복 호출 방지, 적절한 가지치기(pruning) 전략을 적용하세요.