Plan-and-Execute: 계획 → 실행 → 재계획 루프
planner가 요청을 단계 리스트로 쪼개고, execute가 맨 앞 단계 하나만 실행한 뒤
replan이 남은 계획을 유지·수정·종료 중에서 고릅니다. plan이 빌 때까지 execute ↔ replan을 돌고, 비면 finalize로 나갑니다.
그래프 구조
노드 4개, 고정 엣지 4개, 조건부 엣지 1개입니다. 순환은 replan → execute 한 군데뿐입니다.
고정 엣지
add_edge
조건부: 루프 계속
조건부: 루프 탈출
노드 아래 회색 글씨 = 그 노드가 State에 쓰는 값
State: 누가 무엇을 쓰나
| 키 | 리듀서 | 쓰는 노드 | 동작 |
|---|---|---|---|
user_request | 없음 | 입력 | 처음 invoke에서 한 번 들어오고 이후 읽기 전용입니다. |
plan | 없음 (덮어쓰기) | planner execute replan | 남은 할 일 큐입니다. execute가 plan[1:]로 맨 앞을 떼어내고, replan이 통째로 교체하거나 비웁니다. |
past_steps | operator.add |
execute | (단계, 결과) 튜플 1개짜리 리스트를 반환하면 리듀서가 기존 리스트 뒤에 이어 붙입니다. |
final_response | 없음 | finalize | past_steps 전체를 근거로 만든 최종 답변입니다. |
실행 순서 예시
계획이 3단계이고 replan이 계속 continue를 고른 경우입니다. 마지막 단계를 실행하면 plan이 비므로 replan이 무엇을 고르든 finalize로 갑니다.
START→
planner→
execute ①→
replan→
execute ②→
replan→
execute ③→
replan→
finalize→
END
슈퍼스텝 수 = planner 1 + (execute + replan) × N + finalize 1 = 2N + 2. 7단계 계획이면 16스텝, LLM 호출도 16번입니다.
주의할 점
- 빈 계획이면 바로 터집니다.
planner → execute는 고정 엣지라서planner가steps=[]를 돌려줘도execute가 실행되고,state["plan"][0]에서IndexError가 납니다. recursion_limit=40은 execute 약 19회까지입니다.(40 − 2) / 2 = 19.revise가 계획을 계속 늘리면 한도에 걸려GraphRecursionError로 끝나고finalize는 실행되지 않습니다.after_replan은action이 아니라plan만 봅니다.finish는plan=[]로 번역되어 전달됩니다. 그래서revise인데revised_plan이 빈 리스트여도 종료로 처리됩니다.continue는{}를 반환합니다. 빈 dict는 State를 건드리지 않으므로plan이 그대로 유지됩니다.- 실행 결과는 잘려서 전달됩니다. 다음 노드 프롬프트에는
r[:60],r[:80],r[:100]만 들어가므로 긴 결과의 뒷부분은 이후 판단에 쓰이지 않습니다.