AI·News
뒤로

LangSmith 엔진 구축 방법: 에이전트 개선을 위한 우리의 에이전트

https://www.langchain.com/blog/how-we-built-langsmith-engine-our-agent-for-improving-agents

지난주 LangSmith Engine을 출시했습니다. Engine은 에이전트 추적 위에 위치하여 반복되는 문제를 감지하고 다음 작업을 제안하는 에이전트입니다.

이 글은 우리가 Engine을 구축한 방식에 대한 기술적 세부사항을 다룹니다: Engine을 구축한 이유, 어떤 입출력으로 작동하는지, 그리고 대량의 추적 데이터를 분석할 수 있게 해주는 아키텍처 결정사항들입니다.

Engine을 구축한 이유

LangSmith는 에이전트 개선 루프의 중심입니다. 빌드, 테스트, 배포, 모니터링이 에이전트 개발을 강화하는 이 루프의 4가지 기둥입니다.

배포하는 에이전트의 수가 증가하면서 생성되는 추적 데이터도 함께 늘어납니다. 결과적으로 추적 데이터를 분류하고 에이전트가 어디서 잘못되었는지 파악하는 데 더 많은 시간을 소비하게 됩니다.

기본 도구 오류는 상대적으로 쉽게 발견됩니다. 전반적인 궤적도 추적 보기에서 보입니다. 하지만 많은 에이전트 문제는 각 추적을 세밀한 수준에서 검사하지 않으면 감지하기 훨씬 어렵습니다:

  • 에이전트가 같은 도구 호출을 반복합니다
  • 잘못된 도구 인자를 사용합니다
  • 비효율적으로 실행합니다
  • 사용해야 할 도구를 놓칩니다
  • 다양한 실행들에서 같은 종류의 요청에 반복해서 실패합니다

LangChain에서 내부적으로 이 문제에 직면한 후, 우리는 LangSmith Engine을 구축하기로 결정했습니다.

Engine의 역할은 세 가지입니다:

  1. 추적 데이터에서 반복되는 실패를 찾습니다.
  2. 이 실패들을 실행 가능한 문제로 변환합니다.
  3. 이 문제들을 지속적인 개선으로 변환합니다: 평가기, 데이터셋 예제, 그리고 수정사항입니다.

Engine은 그 자체로 에이전트이며, 전문화된 컴포넌트를 사용하여 개선 루프를 처음부터 끝까지 실행하는 오케스트레이터입니다. 추적을 가져오고, 리포지토리가 연결되어 있을 때 코드를 읽으며, 실패를 문제로 그룹화하고, 평가기와 데이터셋 예제를 제안하며, 시간에 따라 에이전트에 대한 이해를 업데이트합니다.

Engine이 생성하는 것: 문제

핵심적으로 Engine은 문제를 식별합니다.

문제는 반복되는 실패 패턴이며, 증거 추적으로 지원되고, 제안된 후속 조치가 따릅니다. 문제는 사용자에게 Issue Board에 표시됩니다: Engine이 추적 프로젝트에서 찾은 문제의 목록입니다.

문제는 다음을 포함합니다:

  • 이름: 문제의 제목
  • 설명: 문제에 대한 단락 설명
  • 카테고리: 미리 정의된 에이전트 실패 카테고리 중 하나
  • 심각도: 낮음, 중간, 또는 높음
  • 추적: 문제가 발생하는 위치에 대한 증거를 제공하는 관련 추적
  • 제안된 조치: 문제가 반복되지 않도록 하기 위한 제안된 다음 단계
  • 태그: needs_fix와 같은 후속 워크플로우를 구동하는 메타데이터

제안된 조치에는 다음이 포함될 수 있습니다:

  • 제안된 온라인 평가기: 문제가 다시 발생하면 플래그를 지정할 평가기
  • 제안된 데이터셋 예제: 문제를 나타내는 오프라인 데이터셋에 추가
  • 제안된 수정: 기본 문제를 해결하기 위한 코드 또는 프롬프트 변경

중요한 점은 Engine이 단지 나쁜 추적을 지적하지 않는다는 것입니다. 이것은 프로덕션 실패를 팀이 행동하고 나중에 테스트할 수 있는 무언가로 바꾸려고 시도합니다.

Engine이 소비하는 것

Engine은 4가지 주요 입력을 받거나 가져올 수 있습니다.

지침

Engine은 Agent Overview에 의해 안내됩니다. 이는 AGENTS.md 파일과 유사합니다: 에이전트가 수행하는 작업, 예상할 추적 구조, 주의할 실패 모드, 그리고 팀이 표현한 선호사항에 대한 살아있는 설명입니다.

첫 번째 실행은 온보딩 답변 및 프로젝트 컨텍스트에서 부트스트랩됩니다. 이 초기 실행 동안 Engine은 추적을 분석하고 학습한 것을 사용하여 Agent Overview의 첫 번째 버전을 생성합니다. 후속 실행에서 Agent Overview는 Engine이 읽고 업데이트하는 지속적인 입력이 됩니다.

언제든지 Agent Overview를 수동으로 편집할 수도 있습니다.

추적

Engine은 LangSmith CLI를 통해 관련 LangSmith 추적 프로젝트에서 추적을 가져옵니다.

전체 추적은 에이전트 실행의 메시지와 궤적을 포함합니다. 규모의 경우 Engine은 항상 모든 추적의 전체 내용을 로드하는 것으로 시작하지 않습니다. 종종 컴팩트한 궤적 요약에서 시작하고, 추적이 더 깊은 조사가 필요할 때만 선택적으로 전체 추적 내용을 로드합니다.

기존 문제

Engine은 현재 Issue Board를 가져옵니다. 개방된 문제와 이전에 종료된 문제를 포함합니다.

이는 Engine에 프로젝트의 현재 상태를 제공합니다. 알려진 문제를 중복으로 생성하지 않고, 기존 문제에 증거를 추가하며, 이미 해결되거나 종료된 것이 무엇인지 이해할 수 있습니다.

코드베이스(선택사항)

Engine을 코드베이스에 선택적으로 연결할 수 있습니다. 이를 통해 Engine은 문제를 더 정확하게 진단할 수 있으며 별도의 수정 에이전트가 변경사항을 제안할 수 있습니다.

리포지토리가 연결되면 리포는 샌드박스에 설치됩니다. 설정 중에 Engine이 사용해야 할 브랜치 또는 하위 디렉토리를 지정할 수 있습니다.

Engine이 업데이트하는 것

Engine은 실행 중에 여러 출력을 업데이트할 수 있습니다.

Issue Board

Engine의 주요 역할은 Issue Board를 업데이트하는 것입니다. 새 문제를 생성하고, 기존 문제를 업데이트하며, 증거 추적을 첨부하고, 문제 메타데이터를 변경할 수 있습니다.

각 문제에 대해 Engine은 동일한 패턴을 향후 추적에서 포착할 평가기를 제안할 수 있습니다. 증거 추적에서 회귀 예제를 제안할 수도 있으므로 프로덕션에서 관찰된 실패가 오프라인 테스트 커버리지가 될 수 있습니다. 기본 문제를 해결하기 위해 프롬프트 또는 코드 변경을 제안할 수도 있습니다.

Agent Overview

Engine은 발견한 내용에 대한 메모를 작성하고 향후 실행을 위해 Agent Overview를 업데이트할 수 있습니다.

이것이 Engine이 시간에 따라 프로젝트 특화 정보를 기억하는 방법입니다: 일반적인 실패 모드, 추적 패턴, 도구 동작, 그리고 사용자 선호사항입니다.

고수준 아키텍처

Engine은 Deep Agents 위에 구축되고 파일을 작성하고, 추적을 검사하며, 코드를 실행하고, 체크아웃된 리포지토리로 작업할 수 있는 샌드박스에 연결됩니다.

고수준에서 Engine은 다음에 의해 주도됩니다:

  • 시스템 프롬프트 및 지침: Agent Overview를 포함합니다
  • 샌드박스: Engine이 작업하는 환경
  • LangSmith CLI: Engine이 데이터를 가져오고 업데이트를 다시 LangSmith로 푸시하기 위해 사용하는 주요 인터페이스
  • 사용자 정의 도구: 특히 평가기를 테스트하고 회귀 예제를 제안하기 위한 도구
  • 서브에이전트: 주 에이전트 컨텍스트가 오버플로우되지 않도록 추적을 선별하고 가능성 있는 문제를 조사하는 데 사용됩니다
  • 메모리: Agent Overview를 통해 유지되고 사용자 작업에 따라 업데이트됩니다

이 글의 나머지는 핵심 루프를 따라갑니다:

  1. 에이전트의 컨텍스트를 준비합니다.
  2. 규모에 맞게 추적을 선별합니다.
  3. 가능성 있는 문제를 조사합니다.
  4. 문제, 평가기, 데이터셋 예제를 생성합니다.
  5. 필요할 때 수정을 별도의 에이전트로 전달합니다.
  6. 다음 실행을 위해 메모리를 업데이트합니다.

1. 에이전트의 컨텍스트 준비

Engine이 추적을 분석하기 전에 작업할 환경과 검사하는 에이전트를 이해할 충분한 컨텍스트가 필요합니다.

샌드박스 설정

Engine은 샌드박스에 연결되어 실행됩니다. 이를 위해 우리는 LangSmith Sandboxes를 사용합니다.

Engine을 실행하기 전에 에이전트의 환경을 설정합니다. 먼저 기본 Engine Docker 이미지를 가져옵니다. 이 이미지는 필요한 라이브러리와 LangSmith CLI를 포함하며, Engine이 이를 사용하여 LangSmith 데이터와 상호작용합니다.

Engine이 GitHub 리포지토리에 연결되어 있으면 관련 코드 아티팩트도 다운로드합니다. 사용자는 설정 중에 사용할 브랜치 또는 하위 디렉토리를 지정할 수 있습니다.

샌드박스가 중요한 이유는 Engine이 종종 추적 데이터를 검사하고, 중간 파일을 작성하며, 평가기 코드를 테스트하고, 제안된 출력을 반복해야 하기 때문입니다. 에이전트에 제어된 작업 환경을 제공하면 그 워크플로우가 훨씬 더 안정적이 됩니다.

Agent Overview

Agent Overview는 명령어 파일이자 메모리 계층입니다.

Engine을 설정할 때 기본 온보딩 질문 세트에 답합니다. Engine은 이 답변을 첫 번째 실행 중에 발견한 것과 함께 사용하여 초기 Agent Overview를 생성합니다.

개요는 Engine이 다음에 대한 지속적인 기록을 유지하도록 도와줍니다:

  • 에이전트가 수행하는 작업
  • 예상할 추적 구조
  • 주의할 일반적인 함정
  • 프로젝트 특화 컨텍스트
  • 사용자 선호사항

Engine은 연속적인 실행에서 이 파일을 읽고 업데이트합니다.

LangSmith CLI

Engine이 LangSmith와 상호작용하는 주요 방법은 LangSmith CLI를 통한 것입니다.

우리는 모든 LangSmith 작업에 대해 사용자 정의 도구를 생성하는 대신 이를 선호합니다. CLI는 추적을 가져오고, 문제를 쿼리하며, 문제를 생성하고, 추적을 첨부하고, 문제 메타데이터를 업데이트하며, 아티팩트를 제안하기 위한 범용 인터페이스를 제공합니다.

또한 Engine을 디버그하고 재현하기 쉽게 만듭니다. CLI는 다운로드할 수 있는 동일한 인터페이스이며 코딩 에이전트에 로컬로 제공할 수 있습니다. Engine이 CLI를 통해 뭔가를 수행하면 일반적으로 Engine 외부에서도 해당 작업을 이해하고 재현할 수 있습니다.

2. 규모에 맞게 추적 선별

Engine을 구축할 때 가장 큰 아키텍처 과제는 추적 볼륨이었습니다.

에이전트가 한 번에 50개의 추적을 조사하고 분류하도록 하는 것은 상대적으로 쉬웠습니다. 하지만 시스템을 프로덕션 에이전트에 연결하면 그 볼륨에서 작동하던 기술들이 무너지기 시작했습니다. 프로덕션 프로젝트는 룩백 윈도우에 수천 또는 수만 개의 추적을 가질 수 있습니다.

모든 전체 추적 내용을 메인 에이전트의 컨텍스트에 로드하는 것은 불가능합니다. 장시간 실행되는 에이전트의 10개 추적만 해도 수백 개의 도구 호출과 메시지를 포함할 수 있습니다.

따라서 문제를 두 단계로 나누었습니다:

  1. 의심스러운 추적을 빠르게 식별하는 광범위한 선별 단계.
  2. 중요할 가능성이 있는 추적에만 전체 컨텍스트를 로드하는 더 깊은 조사 단계.

궤적 포맷

선별을 가능하게 하려면 각 추적의 압축된 표현이 필요했습니다.

질문은 다음과 같았습니다:

추적으로 다시 돌아갈 수 있는 정보를 유지하면서 추적의 정보를 어떻게 압축합니까?

답은 에이전트 궤적이었습니다: 추적의 컴팩트한 뼈대입니다.

궤적은 차례마다 한 항목을 가지며, 역할, 선택적 도구 이름, 지연시간, 및 콘텐츠 크기를 포함합니다. 전체 콘텐츠는 포함하지 않습니다.

{ role: "human", chars: 142 }
{ role: "ai", latency_ms: 1820, chars: 89 }
{ role: "tool", tool_name: "search_db", latency_ms: 340, chars: 2100 }
{ role: "tool", tool_name: "search_db", latency_ms: 312, chars: 1980 }
{ role: "tool", tool_name: "search_db", latency_ms: 298, chars: 2040 }
{ role: "ai", latency_ms: 2100, chars: 210 }

궤적은 네비게이션 도구 역할을 합니다. 선별기가 의심스러운 형태를 빠르게 발견하고, 전체 추적을 grep으로 검색한 다음 필요한 정보만 컨텍스트에 로드할 수 있게 해줍니다.

피드백으로 추적 우선순위 지정

추적에는 이미 피드백이 연결되어 있을 수 있습니다. 이는 인간의 주석, LLM-as-a-judge 점수, 또는 최종 사용자 피드백(엄지손가락 올림/내림)에서 올 수 있습니다.

Engine은 추적에 대한 피드백을 문제가 존재할 수 있다는 높은 우선순위 신호로 소비합니다.

Engine이 가져오는 초기 추적 세트는 피드백 통계를 포함합니다. Engine은 피드백이 있는 추적을 찾고 분류를 위해 우선순위를 지정하도록 지시됩니다.

피드백이 자동으로 문제가 되지는 않습니다. 선별 및 조사의 우선순위를 높입니다. 에이전트는 여전히 추적이 실제 문제의 일부인지 여부를 결정해야 합니다.

선별기 서브에이전트

핵심 선별 질문은:

이 추적을 고려할 때 더 깊은 조사를 보증할 문제가 있습니까?

Engine은 이를 위해 전용 선별기 서브에이전트를 사용합니다. 선별기는 메인 에이전트가 대략 20개의 추적 그룹에 걸쳐 배포하는 Haiku 기반 서브에이전트입니다.

선별기의 작업은 의도적으로 좁습니다. 문제를 생성하지 않습니다. 근본 원인을 진단하지 않습니다. 추적이 깨끗한지 또는 문제를 포함할 수 있는지만 표면 수준에서 결정합니다.

선별기는 메인 에이전트에 구조화된 응답을 반환합니다. 응답은 플래그된 각 추적당 한 줄을 포함하며, 추적 ID, 카테고리, 그리고 짧은 이유, 그리고 깨끗한 추적의 수를 포함합니다.

<trace_id> | <category> | <one-line reason>
CLEAN: 47

이 단계는 검색 공간을 줄입니다. 메인 에이전트에게 전체 추적에서 모든 추적을 추론하도록 요청하는 대신 병렬 선별기를 사용하여 더 깊은 주의가 필요한 추적을 식별합니다.

3. 가능성 있는 문제 조사

선별 후 메인 에이전트는 선별기 출력을 읽고 더 깊은 조사를 배포합니다.

조사자 서브에이전트

조사자는 플래그된 추적을 취하고, 전체 추적 내용을 가져오며, 가능할 때 코드베이스를 읽고, 잠재적인 문제에 대한 더 깊은 분석을 수행합니다.

우리는 메인 에이전트가 이를 위해 서브에이전트를 사용하도록 권장합니다. 전체 추적 내용은 클 수 있으며, 메인 에이전트의 컨텍스트 윈도우에 여러 개의 전체 추적과 관련 코드를 로드하면 빠르게 오버플로우됩니다.

선별기와 달리 조사자는 고정된 시스템 프롬프트를 가진 전용 서브에이전트가 아닙니다. 메인 에이전트에 의해 특정 조사를 위해 프롬프트된 범용 서브에이전트입니다. 이는 메인 에이전트에 유연성을 제공합니다: 다양한 문제 유형은 다양한 조사 전략이 필요할 수 있습니다.

조사자의 작업은 플래그된 추적이 실제 문제를 나타내는지, 추적을 함께 그룹화해야 하는지, 그리고 문제에서 무엇을 기록해야 하는지 결정하는 것입니다.

문제 카테고리

Engine은 각 문제에 대한 카테고리 개념을 가집니다.

우리는 일반적인 에이전트 실패 모드의 미리 정의된 세트를 식별하고 Engine이 주로 이러한 유형의 문제를 찾도록 프롬프트합니다. 이 목록에는 다음과 같은 것들이 포함됩니다:

  • pii_leak
  • agent_looping
  • incorrect_tool_args
  • missing_tool

Engine을 알려진 카테고리로 제한하면 어떤 문제를 찾는지 제어하고 이러한 문제 유형을 고객에게 소개하기 전에 평가할 수 있습니다. 또한 사용자가 출력을 이해하기 쉽게 합니다.

사용자는 여전히 Agent Overview를 통해 Engine이 초점을 맞춰야 할 것을 사용자 정의할 수 있습니다. 팀이 특정 문제 유형에 관심이 있다면 그 우선순위를 그곳에 설명할 수 있습니다.

우리는 새로운 반복되는 에이전트 실패 모드를 식별하고 검증하면서 시간에 따라 이 카테고리 목록을 적극적으로 확대하고 있습니다.

4. 문제, 평가기, 데이터셋 예제 생성

Engine이 실제 문제를 식별하면 메인 에이전트는 문제를 생성하거나 업데이트하고 증거 추적을 첨부합니다.

메인 에이전트는 문제 생성 및 문제 주변의 평가 아티팩트에 대한 책임이 있습니다. 기본 코드나 프롬프트를 직접 수정하는 책임은 없습니다.

각 문제에 대해 Engine은 생성할 수 있습니다:

  1. 증거 추적이 있는 문제 자체.
  2. 제안된 평가기.
  3. 제안된 회귀 예제.
  4. 별도의 수정 에이전트가 시작되어야 할 경우 needs_fix 태그.

평가기

Engine은 모든 문제에 대해 평가기를 제안하도록 프롬프트됩니다.

아이디어는 간단합니다: 실패 패턴을 찾으면 향후 추적에서 동일한 패턴을 포착하는 검사를 원합니다.

Engine은 두 가지 평가기 유형을 지원합니다.

코드 평가기는 추적의 구조를 검사하는 JavaScript 함수입니다 — 필드 값, 도구 출력, 단계 수, 오류 패턴. 실패가 콘텐츠를 읽지 않고 감지될 수 있을 때 올바른 선택입니다.

LLM-as-judge 평가기는 이해가 필요한 경우를 처리합니다: 환각, 근거 실패, 도움이 되지 않는 거부, 잘못된 조언.

에이전트는 문제를 기반으로 평가기 유형을 선택합니다. 구조적 실패는 코드 평가기를 얻습니다. 의미론적 실패는 judge 평가기를 얻습니다.

Engine이 제안된 평가기를 표시하기 전에 test_evaluator 도구를 호출합니다.

test_evaluator 도구

test_evaluator 도구를 통해 Engine은 사용자에게 제안하기 전에 증거 추적에 대해 제안된 평가기를 테스트할 수 있습니다.

평가기가 합리적으로 보일 수 있지만 실제 문제를 포착하지 못할 수 있으므로 이는 중요합니다. Engine은 평가기 정의 및 실행하려는 추적과 함께 도구를 호출합니다. 도구는 평가기를 실행하고 추적 ID에서 결과로의 매핑을 반환합니다.

def test_evaluator(evaluator, traces) -> {run_id: PASS | FAIL | SKIPPED}

여기서:

PASS 포착된 문제
FAIL 누락된 문제, 또는 평가기 오류
SKIPPED 평가기가 이 추적에 적용되지 않음

평가기가 올바른 추적을 포착하지 못하면 Engine은 코드나 프롬프트를 반복할 수 있습니다. 목표는 증거 추적으로 표현된 실패 패턴을 가장 잘 캡처하는 버전을 배포하는 것입니다.

회귀 예제 및 주장

문제가 생성되거나 새 추적이 문제에 추가될 때마다 Engine은 증거 추적당 한 번씩 propose_regression_example을 호출하도록 지시됩니다.

이는 해당 추적에 대해 제안된 회귀 예제를 생성합니다. 예제는 에이전트에 대한 원본 입력과 예상 출력에 대한 주장으로 구성됩니다.

우리는 전체 정답 출력보다는 주장을 제안합니다. 왜냐하면 주장이 더 간단하고 유연하기 때문입니다. 올바른 응답은 여러 가지 방식으로 표현될 수 있습니다. 중요한 것은 추적이 암시하는 주요 주장을 만족하는지 여부입니다.

각 주장은 다음을 가집니다:

  • key: 피드백 식별자, 짧은 슬러그로 작성됨
  • comment: 올바른 응답이 무엇을 만족하는지에 대한 한 문장 인간이 읽을 수 있는 주장

{
"key": "must_cite_max_connections_4096",
"comment": "응답은 get_config 도구 호출에서 반환된 max_connections 값 4096을 인용합니다."
}
{
"key": "must_not_reference_strict_mode_flag",
"comment": "응답은 이 버전에서 더 이상 지원되지 않는 strict_mode를 활성화하도록 제안하지 않아야 합니다."
}

제안된 예제는 프론트엔드의 문제에 표시됩니다. 검토자는 이를 데이터셋으로 승격할 수 있습니다.

이는 프로덕션 실패에서 오프라인 테스트 커버리지로의 루프를 닫습니다.

5. 별도의 에이전트에 수정 전달

핵심 설계 결정은 문제 생성과 수정 생성을 분리하는 것이었습니다.

초기 버전에서는 메인 에이전트가 문제를 식별하고 프롬프트나 코드 수정을 제안하도록 시도했습니다. 이는 에이전트의 작업을 너무 광범위하게 만들었습니다. 추적을 스캔하고, 무엇이 중요한지 결정하며, 실패를 그룹화하고, 문제를 생성하고, 평가기를 생성하고, 데이터셋 예제를 제안하고, 또한 올바른 수정이 무엇인지 추론해야 했습니다.

우리는 메인 에이전트가 모든 것이 한 번에 발생할 때 안정적으로 수정하기 위해 투쟁했다는 것을 보았습니다.

따라서 우리는 워크플로우를 분리했습니다:

  1. 메인 Engine 에이전트가 문제를 식별하고 기록합니다.
  2. 데이터셋 및 평가기 아티팩트를 생성합니다.
  3. 문제가 수정이 필요하면 needs_fix 태그를 남깁니다.
  4. 별도의 수정 에이전트가 시작되어 실제 코드나 프롬프트 변경을 제안합니다.

이는 메인 에이전트를 더 간단하게 만들고 수정 에이전트에 더 집중된 작업을 제공했습니다. 수정 에이전트는 전체 추적 선별 워크플로우를 수행할 필요 없이 문제, 증거 추적, 그리고 연결된 리포지토리 컨텍스트에서 시작할 수 있습니다.

6. 다음 실행을 위해 메모리 업데이트

Engine은 매번 처음부터 시작하지 않습니다.

Agent Overview는 Engine의 조사뿐만 아니라 사용자 작업에 의해서도 업데이트됩니다. 문제를 해결하거나, 문제를 종료하거나, 평가기를 생성할 때 그 작업이 신호가 됩니다.

Engine은 LangSmith CLI를 통해 최근 이벤트를 가져오고, 이러한 이벤트의 관찰을 일반화하며, Agent Overview를 업데이트할 수 있습니다.

특히 User Preferences 섹션을 유지하도록 프롬프트됩니다. 이 섹션은 Engine이 사용자가 문제와 상호작용하는 방식을 관찰하면서 배우는 것을 기록합니다.

모든 팀이 다양한 문제 집합을 신경씁니다. Agent Overview는 Engine이 시간에 따라 그 선호사항에 분석을 적응시키는 방법입니다.

아키텍처 결정사항 및 배운 교훈

몇 가지 아키텍처 결정이 특히 중요한 것으로 판명되었습니다.

CLI를 주요 LangSmith 인터페이스로 사용

Engine이 수행하는 대부분은 LangSmith CLI를 통해 합니다. 이는 모든 작업에 대해 좁은 사용자 정의 도구를 생성하는 것보다 시스템을 더 유연하게 만들었습니다. 또한 Engine 동작을 재현하고 디버그하기 쉽게 만들었습니다.

읽기 전에 추적 압축

전체 추적은 프로덕션 규모에서 선별하기에는 너무 큽니다. 궤적을 통해 Engine은 많은 추적의 형태를 추론하고, 그 다음 중요한 세부사항을 선택적으로 로드할 수 있습니다.

선별을 조사와 분리

선별기는 규모에 최적화됩니다. 조사자는 더 깊은 분석에 최적화됩니다.

이러한 분할을 통해 Engine은 모든 추적을 비용이 많이 드는 전체 조사를 강제하지 않고도 큰 추적 볼륨을 처리할 수 있습니다.

전용 선별기를 사용하되 유연한 조사자

선별기는 좁고 반복 가능한 작업을 가지고 있으므로 전용 프롬프트 및 구조로부터 이득을 얻습니다.

조사는 더 다양합니다. 일부는 코드 읽기가 필요하고, 일부는 평가기 실패 이해가 필요하며, 일부는 추적 비교가 필요합니다. 이 때문에 우리는 메인 에이전트가 동적으로 프롬프트하는 범용 조사자 서브에이전트를 사용합니다.

문제 카테고리 제한

에이전트가 임의의 문제 카테고리를 만들도록 하는 것은 출력을 평가하고 신뢰하기 어렵게 만듭니다. 미리 정의된 분류는 우리가 품질을 제어하고, 성능을 측정하며, 범위를 의도적으로 확대할 수 있게 해줍니다.

전체 예상 출력보다 주장을 선호합니다

회귀 예제의 경우 주장이 종종 전체 참조 답변보다 더 잘 맞습니다. 이들은 올바른 응답의 정확한 문구를 과도하게 제한하지 않고도 사실이어야 하는 것을 캡처합니다.

메인 에이전트를 문제에 집중하게 유지

메인 에이전트는 문제와 관련 데이터셋/평가기 아티팩트만 생성합니다. 프롬프트나 코드를 직접 수정하려고 시도하지 않습니다.

문제가 수정이 필요하면 메인 에이전트는 이를 needs_fix로 태그합니다. 별도의 수정 에이전트가 수정 제안을 처리합니다.

이 분할은 한 에이전트가 같은 실행에서 문제를 식별하고 수정을 제안해야 할 때 투쟁했다는 것을 관찰하면서 나왔습니다.

결론

Engine은 우리가 에이전트 개선 루프의 더 많은 부분을 자동화하려는 시도입니다.

에이전트 관찰성의 어려운 부분은 단지 하나의 추적에서 무엇이 일어났는지 보는 것이 아닙니다. 많은 추적에서 반복되는 패턴을 찾고, 어떤 것들이 중요한지 결정하며, 이러한 패턴을 문제, 평가기, 데이터셋 예제, 그리고 수정으로 변환하는 것입니다.

아키텍처는 해당 루프를 반영합니다. Engine은 컨텍스트를 준비하고, 규모에 맞게 추적을 선별하고, 가능성 있는 문제를 조사하며, 문제 아티팩트를 생성하고, 필요할 때 수정을 별도의 에이전트로 전달하며, 다음 실행을 위해 메모리를 업데이트합니다.

이는 이미 내부적으로 우리의 에이전트를 개선하는 방식을 바꾸었습니다. 추적을 수동으로 파고 평가기를 따로 작성하는 대신, 우리는 프로덕션 동작을 직접 문제, 수정, 그리고 테스트로 전환할 수 있습니다.

LangSmith 추적 프로젝트의 Issues 탭에서 Engine을 찾을 수 있습니다. 코드 인식 수정 제안을 원하면 리포지토리를 연결하거나, 추적만으로 시작하여 Engine이 찾는 반복되는 패턴을 확인합니다.

Last week we launched LangSmith Engine. Engine is an agent that sits on top of your agent traces, spots recurring issues, and suggests what to do next.

This post goes into the technical details of how we built it: why we built Engine, what inputs and outputs it works with, and the architecture decisions that let it analyze large volumes of traces.

Why we built Engine

LangSmith is the home of the agent improvement loop. Build, test, deploy, and monitor are the four pillars of this loop that power agent development.

As the number of agents you deploy grows, the number of traces they generate grows as well. As a result, you spend more and more time sorting through traces and figuring out where your agent went wrong.

Basic tool errors are relatively easy to catch. Overall trajectories are also visible from the trace view. But many agent issues are much harder to detect unless you inspect each trace at a granular level:

  • the agent loops through the same tool calls
  • it uses incorrect tool arguments
  • it executes inefficiently
  • it misses a tool it should have used
  • it fails the same kind of request repeatedly across different runs

After running into this problem internally at LangChain, we set out to build LangSmith Engine.

Engine has three jobs:

  1. Find recurring failures in traces.
  2. Turn those failures into actionable issues.
  3. Convert those issues into durable improvements: evaluators, dataset examples, and fixes.

Engine is itself an agent: an orchestrator that uses specialized components to run the improvement loop end to end. It pulls traces, reads code when a repository is connected, groups failures into issues, proposes evaluators and dataset examples, and updates its understanding of your agent over time.

What Engine produces: issues

At its core, Engine identifies issues.

An issue is a recurring failure pattern, backed by evidence traces, with proposed follow-up actions. Issues are presented to the user in an Issue Board: a list of problems Engine has found in the tracing project.

An issue consists of:

  • Name: title of the issue
  • Description: paragraph description of the issue
  • Category: one of a predefined set of agent failure categories
  • Severity: low, medium, or high
  • Traces: associated traces that provide evidence for where the issue occurs
  • Proposed actions: suggested next steps for preventing the issue from recurring
  • Tags: metadata used to drive follow-up workflows, such as needs_fix

The proposed actions can include:

  • Proposed online evaluator: an evaluator that would flag the issue if it happens again
  • Proposed dataset examples: additions to an offline dataset that are representative of the issue
  • Proposed fix: code or prompt changes to fix the underlying issues.

The important point is that Engine does not just point at a bad trace. It tries to turn a production failure into something your team can act on and test against in the future.

What Engine consumes

Engine receives, or is able to fetch, four main inputs.

Instructions

Engine is guided by an Agent Overview. This is similar to an AGENTS.md file: a living description of what your agent does, what trace structures to expect, what failure modes to watch for, and what preferences your team has expressed.

The first run is bootstrapped from onboarding answers and project context. During that initial run, Engine analyzes traces and uses what it learns to create the first version of the Agent Overview. On subsequent runs, the Agent Overview becomes a persistent input that Engine reads and updates.

You can also edit the Agent Overview manually at any point.

Traces

Engine pulls traces from the relevant LangSmith tracing project through the LangSmith CLI.

A full trace includes the messages and trajectory of an agent run. For scale, Engine does not always start by loading the full content of every trace. It often starts from compact trajectory summaries, then selectively loads full trace contents when a trace needs deeper investigation.

Existing issues

Engine fetches the current Issue Board, including open issues and previously closed issues.

This gives Engine the current state of the project. It can avoid duplicating known issues, add evidence to existing ones, and understand what has already been resolved or closed.

Codebase, optionally

You can optionally connect Engine to your codebase. This lets Engine diagnose issues more precisely and enables a separate fix agent to propose changes.

If a repository is connected, the repo is installed into the sandbox. During setup, you can specify which branch or subdirectory Engine should use.

What Engine updates

Engine can update several outputs as it runs.

Issue Board

The main role of Engine is to update the Issue Board. It can create new issues, update existing issues, attach evidence traces, change issue metadata.

For each issue, Engine can propose an evaluator that catches the same pattern in future traces. It can also propose regression examples from evidence traces, so failures observed in production can become offline test coverage. It can also suggest prompt or code changes to fix the underlying issue.

Agent Overview

Engine can take notes on what it discovered and update the Agent Overview for future runs.

This is how Engine remembers project-specific information over time: common failure modes, trace patterns, tool behavior, and user preferences.

High-level architecture

Engine is built on top of Deep Agents and connects to a sandbox where it can write files, inspect traces, execute code, and work with a checked-out repository.

At a high level, Engine is driven by:

  • System prompt and instructions: including the Agent Overview
  • Sandbox: the environment where Engine works
  • LangSmith CLI: the main interface Engine uses to fetch data and push updates back to LangSmith
  • Custom tools: especially tools for testing evaluators and proposing regression examples
  • Subagents: used to screen traces and investigate likely issues without overflowing the main agent context
  • Memory: maintained through the Agent Overview and updated based on user actions

The rest of this post walks through the core loop:

  1. Prepare the agent’s context.
  2. Screen traces at scale.
  3. Investigate likely issues.
  4. Create issues, evaluators, and dataset examples.
  5. Hand off fixes to a separate agent when needed.
  6. Update memory for the next run.

1. Preparing the agent’s context

Before Engine can analyze traces, it needs an environment to work in and enough context to understand the agent it is inspecting.

Sandbox setup

Engine runs connected to a sandbox. We use LangSmith Sandboxes for this.

Before running Engine, we set up the agent’s environment. First, we pull the base Engine Docker image. This image includes the required libraries and the LangSmith CLI, which Engine uses to interact with LangSmith data.

If Engine is connected to a GitHub repository, we also pull down the relevant code artifacts. The user can specify which branch or subdirectory to use during setup.

The sandbox matters because Engine often needs to inspect trace data, write intermediate files, test evaluator code, and iterate on proposed outputs. Giving the agent a controlled working environment makes that workflow much more reliable.

Agent Overview

The Agent Overview is both an instruction file and a memory layer.

When you set up Engine, you answer a base set of onboarding questions. Engine uses those answers, along with what it discovers during its first run, to create the initial Agent Overview.

The overview helps Engine maintain a continuous record of:

  • what your agent does
  • what trace structures to expect
  • common pitfalls to watch for
  • project-specific context
  • user preferences

Engine reads and updates this file on consecutive runs.

LangSmith CLI

The main way Engine interacts with LangSmith is through the LangSmith CLI.

We prefer this, for the most part, over creating a custom tool for every LangSmith operation. The CLI gives Engine a general-purpose interface for pulling traces, querying issues, creating issues, attaching traces, updating issue metadata, and proposing artifacts.

It also makes Engine easier to debug and reproduce. The CLI is the same interface that is available for download and can be given to coding agents locally. If Engine does something through the CLI, it is usually possible to understand and reproduce that operation outside of Engine as well.

2. Screening traces at scale

The biggest architecture challenge in building Engine was trace volume.

It was relatively easy to get an agent to investigate and sort through 50 traces at a time. But once we connected the system to production agents, the techniques that worked at that volume started to fall apart. Production projects can have thousands or tens of thousands of traces in a lookback window.

Loading all full trace contents into the main agent’s context is not viable. Even 10 traces from long-running agents can contain hundreds of tool calls and messages.

So we split the problem into two phases:

  1. A broad screening phase that quickly identifies suspicious traces.
  2. A deeper investigation phase that only loads full context for traces that are likely to matter.

Trajectory format

To make screening possible, we needed a compressed representation of each trace.

The question was:

How do you compress the information in a trace while retaining the information needed to navigate back into it?

The answer was agent trajectories: compact skeletons of traces.

A trajectory has one entry per turn, with role, optional tool name, latency, and content size. It does not include full content.

{ role: "human", chars: 142 }
{ role: "ai", latency_ms: 1820, chars: 89 }
{ role: "tool", tool_name: "search_db", latency_ms: 340, chars: 2100 }
{ role: "tool", tool_name: "search_db", latency_ms: 312, chars: 1980 }
{ role: "tool", tool_name: "search_db", latency_ms: 298, chars: 2040 }
{ role: "ai", latency_ms: 2100, chars: 210 }

The trajectory acts as a navigation tool. It lets the screener spot suspicious shapes quickly, then grep around the full trace and load only the information it needs into context.

Prioritizing traces with feedback

Traces may already have feedback associated with them. This can come from human annotations, LLM-as-a-judge scores, or end-user feedback (thumbs up/down).

Engine consumes feedback on traces as a high-priority signal that an issue may exist.

The initial set of traces Engine pulls includes feedback stats. Engine is instructed to look for traces with feedback and prioritize them for triage.

Feedback does not automatically become an issue. It raises the priority for screening and investigation. The agent still needs to determine whether the trace is part of a real issue.

The screener subagent

The core screening question is:

Given this trace, is there an issue in it that warrants further investigation?

Engine uses a dedicated screener subagent for this. The screener is a Haiku-based subagent that the main agent dispatches across groups of roughly 20 traces.

The screener’s job is intentionally narrow. It does not create issues. It does not diagnose root cause. It only decides, at a surface level, whether a trace is clean or might contain an issue.

The screener returns a structured response to the main agent. The response contains one line per flagged trace, with the trace ID, category, and a short reason, followed by a count of clean traces.

<trace_id> | <category> | <one-line reason>
CLEAN: 47

This step reduces the search space. Instead of asking the main agent to reason over every trace in full, we use parallel screeners to identify the traces that deserve deeper attention.

3. Investigating likely issues

After screening, the main agent reads the screener outputs and dispatches deeper investigations.

Investigator subagents

The investigator takes flagged traces, pulls the full trace contents, reads the codebase when available, and does a deeper analysis of the potential issue.

We encourage the main agent to use subagents for this because full trace contents can be large. Loading several full traces and relevant code into the main agent’s context window would cause it to overflow quickly.

Unlike the screener, the investigator is not a dedicated subagent with a fixed system prompt. It is a general-purpose subagent prompted by the main agent for the specific investigation. This gives the main agent flexibility: different issue types may require different investigation strategies.

The investigator’s job is to determine whether the flagged traces represent a real issue, whether traces should be grouped together, and what should be recorded on the issue.

Issue categories

Engine has a concept of category for each issue.

We identified a predefined set of common agent failure modes and prompt Engine to primarily look for those types of issues. This list includes things like:

  • pii_leak
  • agent_looping
  • incorrect_tool_args
  • missing_tool

Constraining Engine to known categories helps us control what issues it finds and evaluate those issue types before introducing them to customers. It also makes the output easier for users to understand.

Users can still customize what Engine should focus on through the Agent Overview. If a team cares about particular issue types, they can describe those priorities there.

We are actively expanding this category list over time as we identify and validate new recurring agent failure modes.

4. Creating issues, evaluators, and dataset examples

Once Engine identifies a real issue, the main agent creates or updates the issue and attaches evidence traces.

The main agent is responsible for issue creation and the evaluation artifacts around the issue. It is not responsible for directly fixing the underlying code or prompt.

For each issue, Engine can produce:

  1. The issue itself, with evidence traces.
  2. A proposed evaluator.
  3. Proposed regression examples.
  4. A needs_fix tag, if a separate fix agent should be kicked off.

Evaluators

Engine is prompted to propose an evaluator for every issue.

The idea is simple: once you find a failure pattern, you want a check that catches the same pattern on future traces.

Engine supports two evaluator types.

Code evaluators are JavaScript functions that inspect the structure of a trace — field values, tool outputs, step counts, error patterns. They are the right choice when the failure is detectable without reading the content.

LLM-as-judge evaluators handle cases that require understanding: hallucinations, grounding failures, unhelpful refusals, wrong advice.

The agent picks the evaluator type based on the issue. Structural failures get code evaluators. Semantic failures get judge evaluators.

Before Engine surfaces a proposed evaluator, it calls the test_evaluator tool.

test_evaluator tool

The test_evaluator tool lets Engine test proposed evaluators on evidence traces before suggesting them to the user.

This matters because an evaluator can look reasonable while failing to catch the actual issue. Engine calls the tool with the evaluator definition and the traces it wants to run the evaluator on. The tool executes the evaluator and returns a mapping from trace ID to result.

def test_evaluator(evaluator, traces) -> {run_id: PASS | FAIL | SKIPPED}

Where:

PASS     caught issue
FAIL     missed issue, or evaluator errored
SKIPPED  evaluator did not apply to this trace

If the evaluator does not catch the right traces, Engine can iterate on the code or prompt. The goal is to ship the version that best captures the failure pattern represented by the evidence traces.

Regression examples and assertions

Whenever an issue is created or a new trace is added to an issue, Engine is instructed to call propose_regression_example once per evidence trace.

This creates a proposed regression example for that trace. The example consists of the original input to the agent along with assertions on the expected output.

We propose assertions rather than a full ground-truth output because assertions are simpler and more flexible. A correct response may be phrased in many different ways. What matters is whether it satisfies the key claims implied by the trace.

Each assertion has:

  • key: the feedback identifier, written as a short slug
  • comment: a one-sentence human-readable claim about what a correct response would satisfy

{
 "key": "must_cite_max_connections_4096",
 "comment": "Response cites the max_connections value of 4096 returned by the get_config tool call."
}
{
 "key": "must_not_reference_strict_mode_flag",
 "comment": "Response must not suggest enabling strict_mode, which was deprecated in this version."
}

The proposed examples show up on the issue in the frontend. A reviewer can then promote them into a dataset.

This closes the loop from production failure to offline test coverage.

5. Handing off fixes to a separate agent

A key design decision was separating issue creation from fix generation.

In earlier versions, we tried to have the main agent both identify issues and propose prompt or code fixes. This made the agent’s job too broad. It had to scan traces, decide what mattered, group failures, create issues, generate evaluators, propose dataset examples, and also reason about the right fix.

We saw that the main agent struggled to make fixes reliably when all of that happened in one pass.

So we split the workflow:

  1. The main Engine agent identifies and records the issue.
  2. It creates the dataset and evaluator artifacts.
  3. If the issue needs a fix, it leaves a needs_fix tag.
  4. A separate fix agent is kicked off to propose the actual code or prompt change.

This made the main agent simpler and gave the fix agent a more focused task. The fix agent can start from the issue, evidence traces, and connected repository context, without also needing to perform the full trace-screening workflow.

6. Updating memory for the next run

Engine does not start from scratch every time.

The Agent Overview is updated not only by Engine’s investigations, but also by user actions. When you resolve an issue, close an issue, or create an evaluator, that action becomes signal.

Engine can pull recent events through the LangSmith CLI, generalize observations from those events, and update the Agent Overview.

It is specifically prompted to maintain a User Preferences section. This section records what Engine learns from observing how users interact with issues.

Every team cares about a different set of problems. The Agent Overview is how Engine adapts its analysis to those preferences over time.

Architecture decisions and lessons learned

A few architecture decisions ended up being especially important.

Use the CLI as the main LangSmith interface

Most of what Engine does, it does through the LangSmith CLI. This made the system more flexible than creating a narrow custom tool for every operation. It also made Engine behavior easier to reproduce and debug.

Compress traces before reading them

Full traces are too large to screen at production scale. Trajectories let Engine reason over the shape of many traces, then selectively load the details that matter.

Split screening from investigation

The screener optimizes for scale. The investigator optimizes for deeper analysis.

This split lets Engine handle large trace volumes without forcing every trace through an expensive full investigation.

Use a dedicated screener but flexible investigators

The screener has a narrow, repeatable job, so it benefits from a dedicated prompt and structure.

Investigations are more varied. Some require reading code, some require understanding evaluator failures, and some require comparing traces. For that reason, we use general-purpose investigator subagents that the main agent prompts dynamically.

Constrain issue categories

Letting the agent invent arbitrary issue categories makes the output harder to evaluate and harder to trust. A predefined taxonomy lets us control quality, measure performance, and expand coverage deliberately.

Prefer assertions over full expected outputs

For regression examples, assertions are often a better fit than full reference answers. They capture what must be true without over-constraining the exact wording of a correct response.

Keep the main agent focused on issues

The main agent only creates issues and the associated dataset/evaluator artifacts. It does not try to fix prompt or code directly.

When an issue needs a fix, the main agent tags it with needs_fix. A separate fix agent then handles the fix proposal.

This split came from observing that one agent struggled when it had to both identify issues and propose fixes in the same pass.

Conclusion

Engine is our attempt to automate more of the agent improvement loop.

The hard part of agent observability is not just seeing what happened in one trace. It is finding recurring patterns across many traces, deciding which ones matter, and turning those patterns into issues, evaluators, dataset examples, and fixes.

The architecture reflects that loop. Engine prepares context, screens traces at scale, investigates likely issues, creates issue artifacts, hands off fixes to a separate agent when needed, and updates its memory for the next run.

It has already changed how we improve our own agents internally. Instead of manually digging through traces and separately writing evals, we can turn production behavior directly into issues, fixes, and tests.

You can find Engine in the Issues tab on LangSmith tracing projects. Connect a repository if you want code-aware fix proposals, or start with traces alone to see what recurring patterns Engine finds.

원문 보기 https://www.langchain.com/blog/how-we-built-langsmith-engine-our-agent-for-improving-agents