planner가 요청을 단계 리스트로 쪼개고, execute가 맨 앞 단계 하나만 실행한 뒤
replan이 남은 계획을 유지·수정·종료 중에서 고릅니다. plan이 빌 때까지 execute ↔ replan을 돌고, 비면 finalize로 나갑니다.
노드 4개, 고정 엣지 4개, 조건부 엣지 1개입니다. 순환은 replan → execute 한 군데뿐입니다.
add_edge
조건부: 루프 계속
조건부: 루프 탈출
노드 아래 회색 글씨 = 그 노드가 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로 갑니다.
슈퍼스텝 수 = 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]만 들어가므로 긴 결과의 뒷부분은 이후 판단에 쓰이지 않습니다.