사이클: 조건을 만족할 때까지 되돌아가기
LangGraph에는 루프 문법이 따로 없습니다. 조건부 엣지의 라우팅 함수가 이미 지나온 노드 이름을 반환하면 그게 사이클이 됩니다.
이 파일은 답변을 생성하고, 길이로 품질을 매긴 뒤, 기준 미달이면 generate로 돌아가 다시 생성합니다.
그래프 구조
route_quality가 END를 반환하면 종료, "generate"를 반환하면 한 바퀴 더 돕니다.
구성 요소
answer를 덮어쓰고, attempts를 1 올립니다.
len(answer) / 200을 1.0으로 자른 값을 quality_score에 기록합니다. 200자 이상이면 만점, 160자부터 통과입니다.
END 또는 "generate"를 반환합니다. 노드가 아니므로 State는 바꾸지 않습니다.
conditional.py와 달리 Literal 타입 힌트나 path_map이 없습니다. 그래서 get_graph()로 그리면 실제 갈 수 없는 경로까지 점선으로 표시될 수 있습니다. 실행 동작에는 영향이 없습니다.
종료 장치는 두 겹
| 장치 | 위치 | 동작 |
|---|---|---|
attempts >= 3 | 코드 (route_quality) | 3번 생성해도 미달이면 정상 종료합니다. 결과는 마지막 답변입니다. |
recursion_limit | LangGraph 프레임워크 | 노드 실행 횟수(step)가 한도(기본 25)를 넘으면 GraphRecursionError를 던집니다. invoke(..., config={"recursion_limit": N})으로 조정합니다. |
한 바퀴는 generate + check_quality로 2 step이라 3회 시도면 6 step입니다. 코드 쪽 한도가 먼저 걸리므로 recursion_limit은 라우팅 로직에 버그가 있을 때의 최후 방어선입니다. 사이클이 있는 그래프에는 코드 쪽 한도를 꼭 둡니다.
실행 결과
첫 시도에 541자가 나와 바로 종료됐습니다. 프롬프트가 "200자 이상"을 요구하고 Sonnet이 잘 따르기 때문에, 실제 실행으로는 사이클을 보기 어렵습니다.
사이클 확인용 시뮬레이션
그래프 구성은 그대로 두고 generate만 정해진 길이의 문자열을 돌려주는 가짜 함수로 바꿔 stream_mode="updates"로 돌린 결과입니다. check_quality와 route_quality는 cycle.py의 것을 import해 그대로 썼습니다.
품질은 끝내 미달이지만 3회에서 멈춥니다. 반환된 State는 quality_score=0.25인 마지막 답변입니다.
이 코드를 실서비스에 쓴다면
| 문제 | 지금 코드 | 개선 방향 |
|---|---|---|
| 재시도가 같은 요청 반복 | generate는 매번 같은 프롬프트를 보냅니다. 이전 답이 왜 떨어졌는지 모델은 모릅니다. |
이전 답변과 점수를 State에서 꺼내 "이전 답이 N자였으니 보강하라"처럼 피드백을 프롬프트에 넣습니다. |
| 평가 기준이 길이뿐 | 길면 통과입니다. 틀린 내용이어도 200자면 만점입니다. | LLM 평가자(with_structured_output로 점수와 이유 반환)나 규칙 기반 체크를 섞습니다. |
| 종료 이유를 구분 못 함 | 통과로 끝났는지 한도로 끝났는지 호출자가 quality_score를 다시 비교해야 압니다. |
status: "passed" | "exhausted" 같은 필드를 State에 남기거나, 한도 종료 시 별도 노드로 보내 처리합니다. |
| 이전 답변 유실 | answer는 리듀서 없이 덮어쓰기라 앞선 시도 결과가 사라집니다. |
시도 이력이 필요하면 Annotated[list, operator.add] 같은 리듀서로 누적합니다. |
정리
- 사이클은 별도 문법이 아니라, 조건부 엣지가 이전 노드 이름을 반환하는 것입니다.
- 종료 경로는 라우팅 함수가
END를 반환하는 것뿐입니다. 품질 조건과 별개로 시도 한도를 반드시 둡니다. - 코드 한도가 실패해도 LangGraph의
recursion_limit(기본 25 step)이 무한 루프를 막습니다. - 루프를 돌 때마다 같은 일을 반복하지 않도록, 재시도 노드가 이전 결과를 참고하게 설계합니다.