Skip to content
LangSmith 文档版本 跟随上游 源快照 508ff0c9 更新于 2026-10-18

LangSmith Engine 通知

把 LangSmith Engine 的问题和运行失败通知发送到 Slack 频道和 webhook 端点。

LangSmith Engine 能在它新开一个问题、把一条新追踪关联到现有问题,或未能完成一次运行时通知你。把这些通知投递到一个 Slack 频道、一个 HTTP webhook 端点,或两者。每个目的地都有它自己的事件类型和最低优先级,因此你可以把紧急问题路由到一个寻呼 webhook,同时把每个问题发送到一个 Slack 频道。

添加一个目的地

通知目的地按追踪项目配置。在 Engine 页上,点击 Configure Engine,然后在 Notifications 下点击 + Add destination。对每个目的地,选择:

要在一个被关注的问题复发时收到提醒,在问题上点击 Alert me via Slack,它会打开同一个 Notifications 部分。

事件类型

事件何时发送
issue.createdEngine 新开一个问题。
issue.trace.addedEngine 把一条新追踪关联到现有问题。
issue.agent_run.failed一次 Engine 运行未能完成。

这是 Engine 今天发送的完整事件类型集。未来可能新增类型。一个未带显式事件类型列表创建的目的地,只接收 issue.created。

严重度过滤

Minimum priority 设置被存为一个从 0 到 3 的 severity_threshold。对于问题事件,只有当问题的 severity 小于或等于该阈值时才投递通知。数字越低越紧急。

严重度含义
0紧急
1高
2中
3低

例如,一个带 severity_threshold: 1 的目的地只为 URGENT(0)和 HIGH(1)问题接收事件。

严重度阈值不适用于 issue.agent_run.failed,因为运行失败事件的作用域是一个 Engine 会话而非某个特定问题。

通知一个 Slack 频道

Connect a Slack workspace

连接一个 Slack 工作区是一次性的组织级操作,而非按项目。连接或断开一个工作区需要 organization:manage 权限。在 LangSmith 控制台 中,打开 Settings,进入你组织的 General 设置,在 Slack 下点击 Connect Slack。在 Slack 中授权 LangSmith 应用。你可以为一个组织连接多个 Slack 工作区。

Add a Slack destination

在 Engine 页上,点击 Configure Engine,然后点击 Add destination。把 Deliver to 字段设为 Slack,然后在 Channel 下选择工作区和频道。

Choose events and priority

在 Notify when 下,选择哪些事件类型向频道发消息。在 Minimum priority 下,选择触发通知的最低严重度。点击 Add destination 保存。

LangSmith 会自动加入你选择的公共频道。要向一个私有频道发消息,请先在 Slack 中把 LangSmith 应用邀请到那个频道。

每条 Slack 消息都包含问题标题、描述和严重度,一个指回 LangSmith 的 View issue 链接,以及(对问题事件)一张问题随时间复发的图表。如果一个工作区的连接变得无效,例如该应用被从 Slack 移除,它的目的地会停止投递,直到你从你组织的 General 设置重新连接它。

Slack 目的地通过 LangSmith 托管的 Slack 应用发消息,而非发送 webhook 载荷,因此签名秘密和自定义标头不适用。

发送到 webhook

把 Engine 事件转发给你自己的事件管理、寻呼或聊天工具。添加一个目的地并把 Deliver to 字段设为 Webhook。输入一个 URL,以及可选的自定义标头。每次投递都是签名的,以便你验证其真实性。

投递

LangSmith 向你的 webhook URL 发送一个带 JSON 请求体的 POST 请求。该请求使用 Content-Type: application/json,并包含你附加到该目的地的任何自定义标头。

属性值
方法POST
请求体JSON,下面的是通用信封
方案接受 http:// 和 https://。强烈建议 https://
签名X-LangSmith-Signature 标头,用目的地的签名秘密签名
超时每次尝试 20 秒
尝试在传输错误、HTTP 408、425、429 和任何 HTTP 5xx 上最多 4 次尝试(1 次初始加 3 次指数退避重试)。其他 4xx 响应被视为永久性,不重试
响应成功仅由状态码决定。响应体被忽略。

NOTE

重试投递一个字节完全相同的载荷,包括同样的 id。按 id 去重,使一次重试的投递不会产生重复的下游效应。

自定义标头

你可以为每个目的地附加任意标头(例如 Authorization: Bearer …),以在你的端点处对调用者进行身份验证。Content-Type 始终由 LangSmith 设置,无法被覆盖。

签名秘密

每个目的地都有一个签名秘密。LangSmith 用这个秘密签名原始 webhook 请求体,并在 X-LangSmith-Signature 标头中发送结果。

该标头值有这样的格式:

text
sha256=<hex-encoded HMAC-SHA256 digest>

在解析或依据载荷采取行动之前验证签名。HMAC 输入是确切的原始请求体字节,HMAC 密钥是目的地的签名秘密。不要在验证之前解析并重新序列化 JSON 请求体。

python
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)
typescript
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 生成一个新的签名秘密并立即用它进行未来的 webhook 投递。前一个秘密在轮换完成的那一刻起停止为投递签名。

轮换秘密之后,用新值更新每个验证 X-LangSmith-Signature 的消费者。

测试你的端点

在把一个真实目的地指向你的端点之前,发送一个样例载荷以验证它能在 20 秒超时内接受并确认:

bash
curl -X POST https://your-endpoint.example.com/webhook \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $WEBHOOK_SECRET" \
  -d @sample-issue-created.json

使用来自 issue.created 的示例请求体作为 sample-issue-created.json。验证:

  • 自定义的 Authorization 标头到达且匹配你在目的地上配置的秘密。
  • 处理器按其 id 持久化该事件,以便重试被去重。
  • 处理器在开始缓慢的下游工作之前返回 2xx。

安全

  • webhook URL 在目的地创建时被验证,并在投递时再次验证。在 SaaS 中,私有和元数据 IP 范围被封锁。接受 http:// 和 https://;用 https:// 以使载荷和任何自定义标头不以明文发送。
  • LangSmith 用目的地的签名秘密签名 webhook 请求体。在处理载荷之前验证 X-LangSmith-Signature。
  • 你也可以在目的地上设置自定义标头,例如 Authorization: Bearer …,用于在你的端点处做路由或额外身份验证。
  • 按事件 id 去重,使一次重试的投递不会造成重复通知。

最佳实践

  • 快速确认。 一旦你持久化了事件就用 2xx 响应。把缓慢的工作(扇出、寻呼、下游 API 调用)移到队列上,使你的处理器保持在 20 秒超时内。
  • 容忍未知的事件类型。 忽略你的处理器不认识的 type 值。未来可能在不另行通知的情况下新增事件类型。
  • 容忍新字段。 用宽松的 schema 解析载荷。可能在不另行通知的情况下向现有事件类型添加新字段。

Webhook 载荷参考

Webhook 目的地接收下面的 JSON 载荷。Slack 目的地不接收。

事件信封

每个投递到你端点的事件都用同样的外层 JSON 形状。

字段类型描述
idUUID此次投递的唯一标识符。跨重试保持稳定。用它来去重。
typestring事件类型。issue.created、issue.trace.added 或 issue.agent_run.failed 之一。
createdinteger事件被入队时的 Unix 秒(UTC)。
request_idUUID由同一上游动作触发的每个事件共享。参阅批量合并。
dataobject事件载荷。总是包含 data.object。data.trace 仅在 issue.trace.added 事件上包含。

问题的 data.object

对于 issue.created 和 issue.trace.added,data.object 是问题的一个快照。把它当作事件生成时问题的权威状态。

字段类型描述
idUUID问题 ID。
namestring问题的短标题。
descriptionstring人类可读的描述。
severityinteger0(紧急)到 3(低)。参阅严重度过滤。
tenant_idUUID问题所属的工作区。
tenant_namestring工作区显示名。
session_idUUID问题所属的追踪项目。
session_namestring追踪项目名。
urlstring指向 LangSmith UI 中该问题的深链接。

运行失败的 data.object

对于 issue.agent_run.failed,data.object 描述失败的那个 Engine 运行。

字段类型描述
tenant_idUUID运行所属的工作区。
tenant_namestring工作区显示名。
session_idUUID运行所属的追踪项目。
session_namestring追踪项目名。
urlstring指向 UI 中 LangSmith 项目的深链接。
thread_idstringEngine 线程 ID。
run_idstringEngine 运行 ID。不可用时省略。
statusstring最终运行状态。
error_messagestring来自失败运行的错误文本。不可用时省略。
occurred_atstring失败发生时的 RFC 3339 时间戳。

data.trace

data.trace 仅在 issue.trace.added 事件上包含。

字段类型描述
run_idUUID被关联到该问题的运行的 ID。
trace_idUUID包含该运行的追踪的 ID。
start_timestring运行开始时的 RFC 3339 时间戳。
commentstring | null关联该追踪时记录的可选备注。为空时省略。

批量合并

单个上游动作可以产生多个 webhook 事件。当 Engine 新开一个问题并附加五条追踪到它时,你会收到一个 issue.created 事件和五个 issue.trace.added 事件,全部共享同样的 request_id。用 request_id 把这些归组为一个单一的下游通知。

issue.created

当 LangSmith Engine 创建一个新问题时发送。data.trace 被省略。

json
{
  "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 描述被关联的追踪。

json
{
  "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 也不使用严重度过滤。

json
{
  "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"
    }
  }
}

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。