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

通过 HTTP 连接 Managed Deep Agent

从任何可发送 webhook 的外部服务启动 Managed Deep Agents 运行,并通过你自己的传输方式返回响应。

HTTP 渠道将托管 deep agent 变成一个任何外部服务都能调用的 HTTP 端点。用它来接入 Managed Deep Agents 不直接支持的提供商,例如订单系统、支持工具或你自己的应用。

你提供两个回调:一个对请求进行身份验证,另一个将其转换为一条消息,并命名调用者及其所属的对话。Managed Deep Agents 负责可信交接、agent 运行和回复。有关提供商托管的替代方案,请参阅 Slack。

NOTE

Managed Deep Agents 处于公开 beta 阶段,仅在 US 区域的 LangSmith Cloud 上可用。

NOTE

HTTP 渠道需要 managed-deepagents>=0.8.0。

项目结构

一个 HTTP 渠道声明位于 channels/ 下,与其他任何渠道一样:

text
my-agent/
  agent.py
  channels/
    orders.py

文件名成为渠道名和端点路径。一个项目可以声明多个 HTTP 渠道,且名称必须唯一。有关完整的项目布局,参阅项目结构。

添加 HTTP 渠道

Declare the channel

python
from managed_deepagents import channels

from lib.orders import parse, verify

channel = channels.http(
    provider="orders",
    verify=verify,
    parse=parse,
)

provider 命名外部服务,且它与渠道名是分开的。Managed Deep Agents 将其连同调用者的 ID 一起发送到 Agent Auth,解析出该运行可能使用其凭据的主体。因此提供商为调用者 ID 提供命名空间。

在服务同一渠道之间共享一个提供商。两个 Shopify 渠道 channels/orders 和 channels/refunds 都声明 provider: "shopify",会将同一个 Shopify 用户解析为一个主体。一个声明 provider: "slack" 的渠道会解析为不同的主体,即使调用者 ID 相同。

声明渠道会注册该提供商,因此任何非空名称都可以。

Verify the request

Managed Deep Agents 首先调用 verify。返回 True 以继续到 parse,或返回 False 以用 401 拒绝请求。verify 可以是 async def。

verify 接收一个 HttpChannelRequest,其中带有 Starlette request 和原始的 raw_body 字节。请针对字节校验签名,而不是针对一个重新序列化的负载:

python
import hashlib
import hmac
import os

from managed_deepagents import HttpChannelRequest

def verify(context: HttpChannelRequest) -> bool:
    secret = os.environ["ORDERS_WEBHOOK_SECRET"]
    digest = hmac.new(secret.encode(), context.raw_body, hashlib.sha256).hexdigest()
    received = context.request.headers.get("x-orders-signature", "")
    return hmac.compare_digest(f"sha256={digest}", received)

WARNING

该端点没有平台身份验证。渠道事件路由在适配器内部进行身份验证,而不是通过 Managed Deep Agents 入口。这使得 verify 成为横亘在公共互联网与一次 agent 运行之间的唯一防线。始终校验一个签名或共享密钥,切勿无条件接受一个请求。

使用部署机密来保存签名密钥。一个抛出异常的 verify 回调会用 500 拒绝请求。

从环境解析渠道凭据,而不要用 connections.get,后者需要一次运行。有关 agent 工具在一次运行期间使用的凭据,参阅管理连接。

Parse the request into a message

parse 将一个已验证的请求转换为启动一次运行的消息,或忽略该事件。它接收与 verify 相同的 HttpChannelRequest,因此请求标头在此处也可用,且它可以是异步的。返回两种形态之一:

  • {"type": "message", "message": {...} 启动一次运行。
  • {"type": "ignore"} 跳过该事件。

两者都接受一个可选的 response,即一个 Starlette Response。

该消息带有四个必需字段:

  • user_id:调用者在外部服务中的 ID,作为字符串,从已验证的事件解析而来。Agent Auth 将其映射为该运行可能使用其凭据的主体,因此请从已验证的数据派生它,而不是从一个未经验证的字段派生。使用 str() 转换一个数值型提供商 ID。
  • thread_id:要在其中运行的对话,作为 UUID。Managed Deep Agents 会将其转为小写并映射到一个持久 agent 线程,因此同一个值会继续同一个对话。
  • target:一个 JSON 值,在提供商中标识回复目的地。它与 agent 线程 UUID 是分开的。即使渠道只启动运行,也要包含它。
  • content:消息文本,或一个 LangChain 内容块列表。

WARNING

thread_id 必须是一个 UUID。Managed Deep Agents 会用 400 拒绝任何其他值,因此在返回外部对话 ID 之前先将其映射为一个 UUID,例如使用 uuid.uuid5。

将 parse 添加到包含 verify 的 lib/orders 文件中。合并两个示例顶部的导入,只保留一份 HttpChannelRequest 的导入:

python
import json
import uuid

from managed_deepagents import HttpChannelParseResult, HttpChannelRequest

ORDERS_NAMESPACE = uuid.UUID("6f0c9a3e-8f1a-4f5e-9c2b-7d4e1a2b3c4d")

def parse(context: HttpChannelRequest) -> HttpChannelParseResult:
    event = json.loads(context.raw_body)
    if event.get("type") != "order.comment":
        return {"type": "ignore"}
    return {
        "type": "message",
        "message": {
            "user_id": str(event["actor"]["id"]),
            "thread_id": str(uuid.uuid5(ORDERS_NAMESPACE, str(event["order"]["id"]))),
            "content": event["comment"]["body"],
            "target": str(event["order"]["id"]),
        },
        "raw_event": event,
    }

返回一个 response 以控制提供商接收到什么。对于消息,Managed Deep Agents 会在运行被接受后发送它;对于被忽略的事件,则立即发送。用它来响应提供商的验证挑战而不启动一次运行:

此示例使用 Starlette 的 JSONResponse。在使用它之前,请将 Starlette 添加到你的项目依赖中:

bash
uv add "starlette>=1.6.0"
python
from starlette.responses import JSONResponse

return {
    "type": "ignore",
    "response": JSONResponse({"challenge": event["challenge"]}),
}

Send replies with post

添加一个异步的 post 回调,以将 agent 的最终响应交付给外部服务。对于一个只启动运行的渠道,省略它。

post 接收一个带有已验证 target 和一条消息的输入。自动回复使用带 content 字段的 type: "content"。显式的原样消息使用带 native 字段的 type: "native"。返回所发布消息的 id 和一个可选的 url。

这些示例支持内容消息并拒绝原样消息。target 是 parse 返回的订单 ID。

python
import os

import httpx
from managed_deepagents import HttpChannelPostInput, HttpPostedMessage, channels

from lib.orders import parse, verify

async def post(input: HttpChannelPostInput) -> HttpPostedMessage:
    if input["type"] != "content":
        raise ValueError("This adapter supports content messages only")
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://orders.example.com/api/comments",
            headers={"authorization": f"Bearer {os.environ['ORDERS_API_TOKEN']}"},
            json={"order_id": input["target"], "body": input["content"]},
        )
        response.raise_for_status()
    return {"id": str(response.json()["id"])}

channel = channels.http(
    provider="orders",
    verify=verify,
    parse=parse,
    post=post,
)

Managed Deep Agents 在运行结束后发布回复,与提供商已经接收到的响应分开。两种情况不产生回复:一次在中断上暂停的运行,以及一次其 agent 已经自己交付了最终消息的运行。失败的 post 会作为交付失败记录日志。

添加一个可选的异步 on_error(error, target) 回调来处理运行或交付失败。它替换默认的错误回复。它接收已验证的回复目标。

调用端点

一个已部署的 HTTP 渠道在渠道名处接受请求,而不是在提供商名处:

text
POST https://<deployment-url>/channels/<name>/events

channels/orders.py 中的一个声明可在 /channels/orders/events 访问到。

你的 parse 回调解码请求负载。它可以读取 JSON、表单数据、文本或字节。上面的示例期望 JSON。

除非 parse 返回了它自己的 response,Managed Deep Agents 会用以下之一响应:

状态负载原因
202{"status": "accepted", "deliveryId": "..."}运行已启动。
202{"status": "ignored"}parse 返回了一个忽略结果。
400{"error": "invalid channel payload"}parse 抛出异常,例如其解码器拒绝了负载。
400{"error": "invalid channel parse result"}parse 返回了一个无法识别的形态。
400{"error": "invalid channel message"}该消息含有无效的调用者、线程 UUID、内容或 JSON target。
401{"error": "invalid channel signature"}verify 拒绝了该请求。
500{"error": "channel verification failed"}verify 抛出异常。
500{"error": "channel runtime is not configured"}托管运行时缺少必需的配置。
500{"error": "channel run could not be started"}agent 运行启动失败。

202 意味着运行被接受,而不是它已完成。agent 的答案稍后通过 post(若已配置)到达。

在 agent 中读取事件

每次运行都在运行上下文中携带渠道数据。解析器示例显式包含了提供商事件。这些字段出现在渠道上下文内部:

json
{
  "channel": {
    "provider": "orders",
    "target": "A-1024",
    "raw_event": { "type": "order.comment", "order": { "id": "A-1024" } }
  }
}

从 parse 返回 raw_event,以在 runtime.channel.raw_event 和 runtime.context.channel["raw_event"] 处暴露 JSON 提供商数据。如果省略,上下文就没有 raw_event 键,且 runtime.channel.raw_event 为 None。

Managed Deep Agents 从解析后的消息构建 runtime.channel.event。它不会再次解码负载,也不会自动保留提供商事件。

部署 agent

HTTP 渠道不需要提供商授权,因此部署用的是标准命令。Managed Deep Agents 从声明挂载该端点。

bash
uv run mda deploy

将签名密钥和任何回复凭据放入项目 .env,以便 mda deploy 将它们作为部署机密转发。然后将 https://<deployment-url>/channels/<name>/events 作为其 webhook 目标注册到外部服务。

另请参阅

  • 渠道概览:了解渠道如何将消息服务连接到 agent。
  • Slack:改用提供商托管的 Slack 渠道。
  • 身份:对调用者进行身份验证,并将渠道运行限定到解析出的用户。
  • 部署 agent:配置并部署一个托管 deep agent。

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