3. 고쳐도 안 깨지는 코드

아홉 영역 규칙표

이 차시를 마치면

아홉 개 영역별로 무엇을 지키고 무엇을 바꿔도 되는지 찾아봅니다.

아홉 개 영역마다 A, B, C 를 정리했습니다. 코드를 고치기 전에 해당 영역 표를 먼저 봅니다.

1. State

State 는 그래프의 현재 스냅샷입니다. 공식 Graph API 의 핵심 구성요소는 State, Nodes, Edges 입니다.

구분내용
ANode 와 routing 이 참조하는 key 이름 일치 · Reducer 를 붙인 필드는 의미를 이해하고 변경 · 운영 시스템에서는 기존 Checkpoint 와의 호환을 신중히 검토
B목적에 맞는 field 추가 · field 이름 변경(단, 전체 참조를 함께 수정) · TypedDict 대신 dataclass 나 Pydantic 사용 여부
C한 파일에서 query, 다른 파일에서 question 처럼 이름만 바꾸기 · Reducer 를 이해하지 않고 삭제 · 병렬 업데이트되는 필드를 단순 overwrite 로 변경 · Checkpoint 호환성 검토 없이 type 변경

설계 질문 세 가지 — ① 다음 Node 가 정말 이 값을 필요로 하는가? ② 계산해서 다시 만들 수 있는 값을 굳이 State 에 저장하는가? ③ 대용량 원문 대신 ID 나 reference 만 저장할 수 있는가?

2. Tools

ToolLLMAgent 가 외부 기능을 사용하도록 연결하는 함수입니다. 검색, 계산, DB 조회, API 요청, 파일 읽기가 여기 해당합니다.

from langchain.tools import tool


@tool
def multiply(a: int, b: int) -> int:
    """Multiply two integers."""
    return a * b
구분내용
ATool 입출력 contract · 실제 side effect 가 있는지 명확하게 구분 · Tool description 을 실제 기능과 일치
BTool 이름 · description · 내부 API · return shape(단, caller 와 schema 를 함께 변경)
Cread-only Tool 을 write Tool 로 몰래 변경 · 설명은 '조회'인데 내부에서 삭제나 수정 수행 · Agent 가 의존하는 argument 이름을 다른 곳 수정 없이 변경

실무 권장 — Tool 에 business logic 전체를 넣지 않고 얇게 유지하면 테스트가 쉬워집니다.

3. Nodes

구분내용
A입력으로 State schema 를 따름 · 반환 키가 State schema 와 일치 · Node 의 side effect 를 명확히 함
B내부 알고리즘 · 호출 model · prompt · helper function
C반환해야 하는 키를 제거하면서 downstream Node 를 수정하지 않음 · 하나의 Node 에 여러 책임을 계속 추가 · Interrupt 이전에 되돌릴 수 없는 side effect 추가

좋은 Node 이름retrieve_documents, grade_documents, rewrite_query, generate_answer, approve_answer, save_answer

피해야 할 Noderetrieve_grade_rewrite_generate_save_send()

4. Routing

구분내용
A반환되는 route 이름이 실제 등록된 Node 이름과 일치 · 모든 가능한 branch 처리 · loop 에는 종료 조건이 존재
Bthreshold · routing criteria · branch 추가와 삭제(그래프와 test 를 함께 수정)
Crouter 가 "retry" 를 반환하는데 그래프에 retry Node 가 없는 상태 · max retry 제거 · 정상 Edge 와 dynamic routing 을 같은 Node 에서 무분별하게 혼합

공식 Graph API 문서는 한 Node 에서 routing mechanism 을 명확히 선택하도록 권고하며, 정적 Edge 와 dynamic routing 을 섞으면 둘 다 실행될 수 있어 추론이 어려워질 수 있다고 설명합니다. 확인 필요 해당 문서는 이번 작업에서 접속 확인하지 않았습니다.

5. Graph

구분내용
A등록된 Node 이름과 Edge 및 routing 반환값 일치 · START 와 종료 조건 존재 · PersistenceHITL 이 필요하면 compile 시 Checkpointer 설정
BNode 순서 · branch · subgraph 구성 · Checkpointer implementation
CNode 함수 이름만 바꾸고 graph 등록을 그대로 둠 · HITL 그래프에서 Checkpointer 제거 · 실제 사용 중인 Thread semantics 를 검토하지 않고 Persistence 제거

6. Schemas / Structured Output

구분내용
Adownstream code 가 기대하는 schema · validation 범위
Bfield description · optional field · Pydantic, TypedDict, dataclass 선택
CDB 나 API 계약에 쓰는 필드를 migration 없이 이름 변경 · validation 을 이유 없이 제거 · Structured Output 을 갑자기 자유형 text 로 바꿔 downstream parsing 을 깨뜨림

7. Prompts

Prompt 는 graph topology 보다 자주 수정되는 영역입니다. 그래서 이 영역만 3단으로 나눕니다.

구분내용
B — 바꿔도 되는 것문체 · 설명 수준 · few-shot example · 역할 설명
주의해서 바꿀 것classification label · Structured Output 필드와 연결되는 지시 · Tool 사용 조건 · safety rule
C시스템이 의존하는 label 을 설명 없이 변경(예: PASS/FAILYES/NO) · prompt 에서는 JSON 을 요구하면서 schema 는 다른 필드를 요구

safety rule 이 "주의" 칸에 있다는 점이 중요합니다. 앞 차시의 A-2 와 함께 읽으면, prompt 안의 safety rule 은 문체가 아니라 계약으로 다뤄야 합니다. 분석

8. Config / Environment

{
  "dependencies": ["."],
  "graphs": {
    "main": "./src/my_agent/graph.py:graph"
  },
  "env": ".env"
}
구분내용
A — 절대 하면 안 되는 것API 키를 Git 에 commit · production 키를 예제 코드에 하드코딩 · 실제 graph export path 와 langgraph.json 경로 불일치
Bgraph 이름 · env file 위치 · dependency 방식 — 단, 실제 파일 구조와 함께 수정

9. Tests

LLM app 은 prompt 만 바꿔도 다른 부분이 깨질 수 있습니다.

대상
Unit testTool, router, helper
Graph test특정 입력이 예상 경로를 타는지
Integration testLLM, DB, API 와 실제 연결
Evaluation test정답 품질, groundedness, Tool 선택
def test_router_sends_high_score_to_generate():
    state = {"relevance_score": 0.9}
    assert route_after_grade(state) == "generate"
구분내용
A핵심 business rule 을 검증하는 regression test
B테스트 프레임워크 · fixture 구조
C실패하는 테스트를 원인 수정 없이 삭제해서 'PASS' 로 만드는 것

이 차시의 파이썬 코드는 개념을 보이려고 떼어 놓은 예시 조각입니다. 문법 검사는 통과하지만 그대로 돌려서 결과를 확인한 코드는 아닙니다. 실행까지 검증한 코드는 4번과 6번 트랙에 있으며, 그 차시에는 무엇을 어떻게 확인했는지 따로 적어 두었습니다.

이해도 확인

문항을 불러오는 중입니다.