LangSmith Engine 웹훅 이벤트
LangSmith Engine 웹훅 이벤트
LangSmith Engine이 이슈를 만들거나 새 트레이스를 기존 이슈에 연결할 때 보내는 웹훅 이벤트에 대한 참조예요.
LangSmith가 감지한 에이전트 이슈를 인시던트 관리, 페이징 또는 채팅 도구로 전달하세요. LangSmith Engine은 새 이슈를 열 때, 또는 새 트레이스를 이미 연 이슈에 연결할 때 웹훅 이벤트를 엔드포인트로 보내요.
웹훅 구독을 구성하려면 추적 프로젝트의 Engine 탭에서 Engine Settings 패널을 여세요. Engine 구성을 참고하세요.
대상은 웹훅 URL 또는 Slack 채널로 전달돼요. 둘 다 이 페이지에서 설명하는 동일한 이벤트 유형과 최소 심각도 필터링을 사용해요. Slack 대상은 아래 JSON 페이로드를 보내는 대신 LangSmith의 관리형 Slack 앱을 통해 게시하므로, 서명 시크릿과 커스텀 헤더가 적용되지 않아요.
Slack 전달을 설정하려면 Slack 채널에 알림을 참고하세요. 이 페이지의 나머지는 웹훅 URL 대상을 문서화해요.
출처: 문서
본문
전달
LangSmith는 JSON 본문이 포함된 POST 요청을 웹훅 URL로 보내요. 요청은 Content-Type: application/json을 사용하고 구독에 연결한 커스텀 헤더를 포함해요.
| 속성 | 값 |
|---|---|
| 메서드 | POST |
| 본문 | JSON, 아래 공통 엔벨로프 |
| 스킴 | http:// 및 https:// 허용. https://를 강력히 권장 |
| 서명 | X-LangSmith-Signature 헤더, 구독의 서명 시크릿으로 서명됨 |
| 타임아웃 | 시도당 20초 |
| 시도 | 전송 오류, HTTP 408, 425, 429 및 모든 HTTP 5xx에 대해 최대 4회 시도(초기 1회 + 지수 백오프로 3회 재시도). 기타 4xx 응답은 영구적인 것으로 간주되어 재시도되지 않음 |
| 응답 | 성공은 상태 코드로만 결정됨. 응답 본문은 무시됨. |
재시도는
id를 포함해 바이트 단위로 동일한 페이로드를 전달해요.id로 중복 제거해서 재시도 전달이 중복 다운스트림 효과를 내지 않게 하세요.
커스텀 헤더
각 구독에 임의의 헤더를 붙일 수 있어요 (예: 엔드포인트에서 호출자를 인증하기 위한 Authorization: Bearer <token>). Content-Type은 항상 LangSmith가 설정하며 재정의할 수 없어요.
서명 시크릿
각 구독에는 서명 시크릿이 있어요. LangSmith는 이 시크릿으로 원시 웹훅 요청 본문에 서명하고 결과를 X-LangSmith-Signature 헤더로 보내요.
헤더 값 형식은 다음과 같아요:
sha256=<hex-encoded HMAC-SHA256 digest>
페이로드를 파싱하거나 처리하기 전에 서명을 검증하세요. HMAC 입력은 정확한 원시 요청 본문 바이트이고, HMAC 키는 구독의 서명 시크릿이에요. 검증 전에 JSON 본문을 파싱하고 재직렬화하지 마세요.
import hashlib
import hmac
from typing import Optional
def verify_langsmith_signature(
*,
body: bytes,
signing_secret: str,
signature_header: Optional[str],
) -> bool:
if not signature_header or not signature_header.startswith("sha256="):
return False
expected = "sha256=" + hmac.new(
signing_secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature_header)
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyLangSmithSignature({
body,
signingSecret,
signatureHeader,
}: {
body: Buffer;
signingSecret: string;
signatureHeader: string | undefined;
}) {
if (!signatureHeader?.startsWith("sha256=")) {
return false;
}
const expected = `sha256=${createHmac("sha256", signingSecret)
.update(body)
.digest("hex")}`;
const expectedBytes = Buffer.from(expected);
const actualBytes = Buffer.from(signatureHeader);
return (
expectedBytes.length === actualBytes.length &&
timingSafeEqual(expectedBytes, actualBytes)
);
}
서명 시크릿 교체
서명 시크릿이 노출됐을 수 있거나 조직의 자격 증명 교체 정책이 새 시크릿을 요구할 때 서명 시크릿을 교체하세요.
시크릿을 교체하려면 Engine Settings에서 구독 행을 열고 Roll signing secret을 클릭한 다음 확인하세요. LangSmith는 새 서명 시크릿을 생성하고 즉시 향후 웹훅 전달에 사용해요. 교체가 완료되는 즉시 이전 시크릿은 전달 서명을 중단해요.
시크릿 교체 후 X-LangSmith-Signature를 검증하는 모든 소비자를 새 값으로 업데이트하세요.
심각도 필터링
각 구독에는 0부터 3까지의 severity_threshold가 있어요. 이슈 이벤트의 경우, 이슈의 severity가 임계값보다 작거나 같을 때만 이벤트가 전달돼요. 숫자가 낮을수록 더 긴급해요.
| 심각도 | 의미 |
|---|---|
0 |
긴급(Urgent) |
1 |
높음(High) |
2 |
중간(Medium) |
3 |
낮음(Low) |
예를 들어 severity_threshold: 1인 구독은 URGENT(0)와 HIGH(1) 이슈의 이벤트만 받아요.
심각도 임계값은 issue.agent_run.failed에는 적용되지 않아요. 런 실패 이벤트는 특정 이슈가 아니라 Engine 세션에 범위가 지정되기 때문이에요.
이벤트 유형 필터링
각 구독은 받고 싶은 이벤트 유형을 지정해요. 명시적 목록 없이 만들어진 구독은 기본적으로 ["issue.created"]로 설정돼요.
이벤트 엔벨로프
엔드포인트로 전달되는 모든 이벤트는 동일한 외부 JSON 모양을 사용해요.
| 필드 | 유형 | 설명 |
|---|---|---|
id |
UUID | 이 전달의 고유 식별자. 재시도 간에 안정적. 중복 제거에 사용. |
type |
string | 이벤트 유형. issue.created, issue.trace.added 또는 issue.agent_run.failed 중 하나. |
created |
integer | 이벤트가 대기열에 추가된 Unix 초(UTC). |
request_id |
UUID | 같은 업스트림 동작에서 발생한 모든 이벤트가 공유. 배치 병합 참고. |
data |
object | 이벤트 페이로드. 항상 data.object를 포함. issue.trace.added 이벤트에서만 data.trace를 포함. |
이슈 data.object
issue.created 및 issue.trace.added의 경우 data.object는 이슈의 스냅샷이에요. 이벤트가 생성된 시점의 이슈 권위 있는 상태로 취급하세요.
| 필드 | 유형 | 설명 |
|---|---|---|
id |
UUID | 이슈 ID. |
name |
string | 이슈의 짧은 제목. |
description |
string | 사람이 읽을 수 있는 설명. |
severity |
integer | 0(긴급)부터 3(낮음)까지. 심각도 필터링 참고. |
tenant_id |
UUID | 이슈가 속한 워크스페이스. |
tenant_name |
string | 워크스페이스 표시 이름. |
session_id |
UUID | 이슈가 속한 추적 프로젝트. |
session_name |
string | 추적 프로젝트 이름. |
url |
string | LangSmith UI에서 이슈로의 딥 링크. |
런 실패 data.object
issue.agent_run.failed의 경우 data.object는 실패한 Engine 런을 설명해요.
| 필드 | 유형 | 설명 |
|---|---|---|
tenant_id |
UUID | 런이 속한 워크스페이스. |
tenant_name |
string | 워크스페이스 표시 이름. |
session_id |
UUID | 런이 속한 추적 프로젝트. |
session_name |
string | 추적 프로젝트 이름. |
url |
string | UI에서 LangSmith 프로젝트로의 딥 링크. |
thread_id |
string | Engine 스레드 ID. |
run_id |
string | Engine 런 ID. 사용할 수 없으면 생략. |
status |
string | 최종 런 상태. |
error_message |
string | 실패한 런의 오류 텍스트. 사용할 수 없으면 생략. |
occurred_at |
string | 실패가 발생한 RFC 3339 타임스탬프. |
data.trace
data.trace는 issue.trace.added 이벤트에서만 포함돼요.
| 필드 | 유형 | 설명 |
|---|---|---|
run_id |
UUID | 이슈에 연결된 런의 ID. |
trace_id |
UUID | 런을 포함하는 트레이스의 ID. |
start_time |
string | 런이 시작된 RFC 3339 타임스탬프. |
comment |
string | null | 트레이스가 연결됐을 때 기록된 선택 메모. 비어 있으면 생략. |
배치 병합
단일 업스트림 동작이 여러 웹훅 이벤트를 생성할 수 있어요. Engine이 새 이슈를 열고 다섯 개의 트레이스를 연결하면, 같은 request_id를 공유하는 하나의 issue.created 이벤트와 다섯 개의 issue.trace.added 이벤트를 받아요. request_id를 사용해 이것들을 단일 다운스트림 알림으로 그룹화하세요.
이벤트 유형
아래 이벤트 유형은 오늘 LangSmith Engine이 보내는 전체 집합이에요. 나중에 새 유형이 추가될 수 있으므로, 핸들러는 실패 대신 알 수 없는 type 값을 무시해야 해요.
issue.created
LangSmith Engine이 새 이슈를 만들 때 전송돼요. data.trace는 생략돼요.
{
"id": "b91c1f0e-7c4a-4f53-9d3e-9f1c8e7a2b10",
"type": "issue.created",
"created": 1747238400,
"request_id": "0d2f4f6a-2a3a-4b6e-9b87-5d5b6e8c9a01",
"data": {
"object": {
"id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"name": "Tool selection inconsistency",
"description": "Agent repeatedly calls the search tool with identical arguments before terminating.",
"severity": 1,
"tenant_id": "11111111-2222-3333-4444-555555555555",
"tenant_name": "Acme Workspace",
"session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
"session_name": "prod-api",
"url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa?tab=5&issue=9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
}
}
}
issue.trace.added
새 트레이스가 기존 이슈에 연결될 때 전송돼요. data.trace는 연결된 트레이스를 설명해요.
{
"id": "c02e3a4b-5c6d-7e8f-9a0b-1c2d3e4f5a6b",
"type": "issue.trace.added",
"created": 1747238410,
"request_id": "0d2f4f6a-2a3a-4b6e-9b87-5d5b6e8c9a01",
"data": {
"object": {
"id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",
"name": "Tool selection inconsistency",
"description": "Agent repeatedly calls the search tool with identical arguments before terminating.",
"severity": 1,
"tenant_id": "11111111-2222-3333-4444-555555555555",
"tenant_name": "Acme Workspace",
"session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
"session_name": "prod-api",
"url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa?tab=5&issue=9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"
},
"trace": {
"run_id": "f1e2d3c4-b5a6-9788-6655-44332211ffee",
"trace_id": "abcdefab-1234-5678-9abc-def012345678",
"start_time": "2026-05-14T12:30:00Z",
"comment": "Reproduces the same tool-loop pattern."
}
}
}
issue.agent_run.failed
LangSmith Engine이 런을 완료하지 못했을 때 전송돼요. 이 이벤트는 세션 범위이므로 data.trace를 포함하지 않고 심각도 필터링을 사용하지 않아요.
{
"id": "4d0e8db2-81e6-4491-b8e5-b13a8f5afc0d",
"type": "issue.agent_run.failed",
"created": 1747238500,
"request_id": "f6bbd48a-0386-403d-9344-31051264b45f",
"data": {
"object": {
"tenant_id": "11111111-2222-3333-4444-555555555555",
"tenant_name": "Acme Workspace",
"session_id": "66666666-7777-8888-9999-aaaaaaaaaaaa",
"session_name": "prod-api",
"url": "https://smith.langchain.com/o/11111111-2222-3333-4444-555555555555/projects/p/66666666-7777-8888-9999-aaaaaaaaaaaa",
"thread_id": "thread-123",
"run_id": "run-456",
"status": "error",
"error_message": "RuntimeError: missing API key",
"occurred_at": "2026-05-14T12:45:00Z"
}
}
}
엔드포인트 테스트
실제 구독을 엔드포인트에 연결하기 전에 샘플 페이로드를 보내 20초 타임아웃 안에 수락하고 승인하는지 확인하세요:
curl -X POST https://your-endpoint.example.com/webhook \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d @sample-issue-created.json
issue.created의 예시 본문을 sample-issue-created.json으로 사용하세요. 다음을 확인하세요:
- 커스텀
Authorization헤더가 도착하고 구독에 구성한 시크릿과 일치하는지. - 핸들러가 이벤트를
id키로 영구화해서 재시도가 중복 제거되는지. - 핸들러가 느린 다운스트림 작업을 시작하기 전에
2xx를 반환하는지.
보안
- 웹훅 URL은 구독 생성 시점과 전달 시점에 검증돼요. 개인 및 메타데이터 IP 범위는 SaaS에서 차단돼요.
http://와https://모두 허용되며, 페이로드와 커스텀 헤더가 평문으로 전송되지 않도록https://를 사용하세요. - LangSmith는 구독의 서명 시크릿으로 웹훅 본문에 서명해요. 페이로드를 처리하기 전에
X-LangSmith-Signature를 검증하세요. - 구독에
Authorization: Bearer <token>같은 커스텀 헤더를 설정해 라우팅 또는 엔드포인트에서 추가 인증을 할 수도 있어요. - 재시도 전달이 중복 알림을 일으키지 않도록 이벤트
id로 중복 제거하세요.
모범 사례
- 빠르게 승인하기. 이벤트를 영구화하는 즉시
2xx로 응답하세요. 느린 작업(팬아웃, 페이징, 다운스트림 API 호출)은 큐로 옮겨 핸들러가 20초 타임아웃 안에 머물게 하세요. - 알 수 없는 이벤트 유형 허용하기. 핸들러가 인식하지 못하는
type값은 무시하세요. 새 이벤트 유형이 통지 없이 추가될 수 있어요. - 새 필드 허용하기. 허용적 스키마로 페이로드를 파싱하세요. 기존 이벤트 유형에 통지 없이 새 필드가 추가될 수 있어요.