본문으로 건너뛰기
AIPida
채택됨중급AI 개발

LangChain으로 만든 에이전트가 느리고 디버깅이 안 됩니다. 직접 짜는 게 나을까요?

처음 LLM 앱을 LangChain으로 시작했는데, 체인이 좀만 복잡해져도 내부에서 뭐가 어떻게 호출되는지 추적이 안 됩니다. 에러가 나면 스택트레이스가 추상화 계층 깊숙한 데서 터져서 원인 찾는 데 시간을 다 씁니다. 프롬프트가 실제로 뭐가 들어가는지도 한눈에 안 보이고요.

주변에서 "그냥 SDK로 직접 짜라"는 얘기도 듣는데, 그러면 메모리/툴/리트리버 같은 걸 다시 다 만들어야 하니 망설여집니다.

실무에서 LangChain 계속 쓰시는 분들은 이 문제 어떻게 푸시나요? 아니면 정말 걷어내는 게 답인가요?

답변 3

  • 채택된 답변에디터 검증

    "느리고 디버깅 안 된다"의 절반은 트레이싱을 안 붙여서입니다. LangChain이면 LangSmith(또는 OTel 기반 트레이싱) 먼저 꽂으세요. 호출별 실제 입력 프롬프트·토큰 수·지연·툴 순서가 트리로 다 보입니다. "프롬프트에 뭐 들어가는지 안 보인다"는 이걸로 거의 끝나요.

    그래도 느리면 라이브러리 탓이 아니라 설계인 경우가 많습니다. 직렬일 필요 없는 리트리버/LLM 호출 병렬화 됐는지, 매 턴 전체 히스토리를 통째로 다시 넣고 있진 않은지부터 보세요. 후자가 의외로 흔한 토큰 폭탄 원인입니다.

    프레임워크 자체는, 저는 "얇게 쓰자" 쪽입니다. 리트리버·도큐먼트 로더·텍스트 스플리터 같은 유틸은 가져다 쓰되, 에이전트 제어 흐름(루프·분기·종료 조건)은 직접 코드로 들고 가세요. 제어 흐름을 추상화에 다 맡기는 순간 스택트레이스가 라이브러리 안쪽에서 터지고 추적이 지옥 됩니다. 분기 많은 멀티스텝이면 LangGraph처럼 상태를 명시적으로 들고 가는 구조가 "마법 에이전트" 추상화보다 훨씬 디버깅 잘 돼요. 전부 걷어낼 건 없지만 제어 흐름만큼은 본인 손에.

  • 작년에 LangChain 에이전트를 SDK 직접 호출로 걷어낸 적 있는데, 막상 해보니 핵심 루프가 허무할 정도로 단순했습니다. "메시지 배열 만들기 → 모델 호출 → tool_use 있으면 실행하고 결과 append → 반복 → 종료조건" 이게 사실상 전부예요. 메모리는 그냥 메시지 리스트, 툴은 함수 dict 매핑. 제어 흐름 한 200줄 직접 짠 게 추상화 미궁에서 헤매던 것보다 훨씬 빨랐습니다.

    다만 리트리버/스플리터/문서 로더처럼 이미 잘 만들어진 유틸까지 직접 짜는 건 낭비라, 거기만 가져다 쓰는 게 맞아요. 그리고 팀 크고 표준화가 더 중요한 상황이면 프레임워크 유지의 이점도 있으니 케바케긴 합니다. 우리 팀은 작아서 걷어내는 쪽이 이득이었어요.

  • 뭘 쓰든 모든 LLM 호출의 raw 요청/응답을 통째로 로깅해두세요. 프레임워크가 내부에서 합쳐서 보내는 최종 프롬프트를 파일이든 트레이스든 남겨두면, 나중에 "왜 이렇게 답했지?"의 한 90%는 그 로그만 보면 풀립니다. 이거 안 되어 있으면 어떤 프레임워크 써도 디버깅 안 돼요.