day5_practice/hook_trace.pyday5_practice/logging_middleware.pyday5_practice/masking_middleware.pyday5_practice/summarization_demo.pyday5_practice/middleware_order.py

미들웨어 훅 플레이어

create_agent(..., middleware=[...]) 가 만드는 루프(모델 호출 ↔ 도구 실행)에서 before_model / after_model / wrap_model_call / wrap_tool_call 이 각각 어디에 끼는지, 질문 하나가 지나가는 순서대로 보여 줍니다. 탭을 바꾸면 훅 관찰, 여러 개를 끼웠을 때의 순서, 로깅(읽기만)과 마스킹(State 고치기), 요약 미들웨어가 메시지를 줄이는 시점을 차례로 볼 수 있습니다. 모델이 만든 값은 (예시) 로 표시했습니다.

tool_calls 있음 없음 → END 도구 결과를 붙여 다시 START before_model wrap_model_call model LLM 호출 after_model wrap_tool_call tools 도구 실행 END

State["messages"]

agent_log.jsonl

추가 모드("a")로 한 줄씩

터미널 출력 (실행 타임라인)

훅 네 자리 한눈에

훅그래프 안의 모양불리는 때돌려주는 값의 뜻이 날의 예
before_model(state, runtime)노드 이름.before_model모델을 부르기 직전. 질문마다가 아니라 모델 호출마다None = State 그대로, dict = State 변경분HookTrace [1], Logging, Masking, Summarization
after_model(state, runtime)노드 이름.after_model모델 응답(AIMessage)이 State 에 붙은 직후위와 같음HookTrace [2], Logging
wrap_model_call(request, handler)노드가 아님. model 노드 안에서 호출을 감쌈모델 호출 하나하나handler(request) 를 불러야 모델이 호출됨. 안 부르고 다른 응답을 돌려주면 호출 생략이 묶음에는 없음 (guards.py 의 입력 가드)
wrap_tool_call(request, handler)노드가 아님. tools 노드 안에서 도구 실행을 감쌈도구 호출 하나하나handler(request) 의 결과(ToolMessage)를 돌려줘야 함HookTrace [3][4]

로깅과 마스킹: 같은 before_model, 다른 성격

로깅: 읽기만 한다

LoggingMiddleware 의 두 훅은 State 를 읽어 agent_log.jsonl 에 한 줄을 쓰고 None 을 돌려줍니다. 질문 하나에 도구를 한 번 쓰면 before / after 쌍이 두 번, 모두 네 줄이 쌓이고 messages 는 한 번도 바뀌지 않습니다.

마스킹: State 를 고친다

MaskingMiddleware 는 {"messages": [HumanMessage(content=masked, id=last_human.id)]} 를 돌려줍니다. reducer add_messages 가 같은 id 를 보고 그 자리의 메시지를 교체하므로 개수는 그대로이고 원문은 State 에서 사라집니다. id 를 빼면 교체가 아니라 추가가 됩니다.

미들웨어를 여러 개 끼울 때의 순서

리스트가 [A, B, C] 일 때입니다. 원고와 설치된 langchain 1.4.7 의 create_agent 구현을 기준으로 했습니다. before 노드는 리스트 순서대로 엣지가 이어지고, after 노드는 model 다음에 리스트의 마지막 것부터 이어집니다.

훅실행 순서기억하는 법
before_modelA → B → C앞에 적은 것이 먼저
after_modelC → B → A (A 가 루프의 출구: 여기서 tools / END 가 갈림)들어간 순서의 반대로 나옴
wrap_model_callA( B( C( 모델 호출 ) ) )앞에 적은 것이 가장 바깥 껍질 (docstring: "first in list as outermost layer")
wrap_tool_callA( B( C( 도구 실행 ) ) )같은 규칙 (docstring: "first = outermost")

훅의 종류가 다르면 리스트 순서만으로 실제 순서를 알 수 없습니다. 예를 들어 리스트 맨 앞의 wrap_model_call 미들웨어도 다른 미들웨어들의 before_model 노드가 모두 끝난 뒤에야 돕니다. 이 함정은 5일차 종합 실습 플레이어에서 여섯 겹으로 다시 볼 수 있습니다.

함정과 주의

관련 파일