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

管理连接

存储 API 密钥和 OAuth 授权,以便 Managed Deep Agents 在运行时对外部服务身份验证。

一个连接把一个受管深 agent 关联到一个外部服务,例如 GitHub、Notion 或 Tavily。凭据存在你的 LangSmith 工作区里。在一个自写工具或 MCP 服务器定义里用 connections.get(...) 在运行时解析它。

连接让你能:

  • 把凭据挡在你项目之外:mda deploy 在每次部署时把 .env 值收集为部署机密。而一个连接的值留在工作区里。
  • 给每个调用者他们自己的身份:一个用户所有的连接解析提出请求那人的凭据,所以 agent 读那个人的文档并以他们的名义行动。一个环境变量对所有人只有一个值。
  • 跳过 OAuth 管线:Managed Deep Agents 运行授权往返,所以项目不需要回调路由、令牌存储或同意屏。这些流程由你的 Managed Deep Agent 自动处理。
  • 跨 agent 复用一个 slug:连接属于工作区,所以多个部署能解析同一个 slug。每个部署在该 slug 下持有它自己的凭据:从每个项目根运行 mda connections create <slug> 一次。轮换一个部署的值在其下次运行时生效,无需重新部署。

把一个凭据存在 LangSmith 中、跨你的 Managed Deep Agents 使用,只需一条命令:

bash
uv run mda connections create organization-tavily --secret-from-env TAVILY_API_KEY

而读取它只需一行:

python
api_key = await connections.get("organization-tavily", {"type": "agent"})

本页其余部分讲述围绕这些步骤的两个选择:谁拥有凭据,以及服务如何身份验证。

NOTE

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

选择一个凭据所有者

每个 connections.get(...) 调用都点名它所解析凭据的拥有者:

所有者解析为何时使用
agent一个属于该部署的凭据。每个调用者都用它。每个调用者都需要同一种能力:用 Tavily 做网页搜索、一个共享知识库,或向一个团队频道发帖。
user调用者本人的凭据。每人授权他们自己的账户。agent 以提问者的身份行动:搜索只有他们能看见的 Notion 页面、以他们的名义提一个 issue,或以他们的身头发邮件。
python
tavily_key = await connections.get("organization-tavily", {"type": "agent"})
notion_token = await connections.get("engineering-notion", {"type": "user"})

connections.get(...) 把一个凭据值解析为字符串。

一个连接是一个容器,且它属于工作区而非某个所有者。它内部的凭据有所有者。没有 --owner 标志:你用的创建模式设定所有者。

connections.get(...) 选择一个所有者。它不创建一个。为一个没有自己凭据的所有者请求,对 agent 会使该运行失败,对 user 抛出一个 credential_authorization_required 中断。在同一个 slug 下为另一个所有者存储的凭据不满足这次查找。

agent 所有的凭据属于项目的部署,所以至少一次成功的 mda deploy 之后再创建它们。一个部署只读它自己拥有的凭据。要从第二个部署解析同一个 slug,从那个项目根再运行一次 mda connections create <slug>:在一个已存在的 slug 上,该命令附加一个新的 agent 所有凭据而非失败。

识别调用者

一个用户所有的连接针对 Managed Deep Agents 附加到运行的调用者身份来解析。那个身份来自哪里,取决于 agent 如何被调用:

界面调用者身份
Slack 频道发消息的 Slack 用户。
LangSmith Studio已登录的 LangSmith 用户。
SDK 客户端或自定义前端项目的身份声明所身份验证的那个身份。

默认的身份声明验证一个 LangSmith API 密钥。那个密钥身份验证的是调用客户端,而非某个个人,所以每个出示它的调用者都解析到同一个身份。要给每个登录的人他们自己的凭据,声明 Supabase 身份。

代码从不向 connections.get(...) 传一个用户 ID。运行时解析调用者并返回那人的凭据。

NOTE

Slack 和 Studio 会为调用者完成授权往返。对自定义频道的一流支持尚在开发中。今天要以调用者身份构建一个自定义前端,见处理授权中断并联系 LangChain 团队。

选择一个创建模式

所有权决定 agent 如何处置一个凭据。创建模式决定那个凭据如何到达工作区,并取决于外部服务如何身份验证:

模式何时使用用什么创建凭据所有者
不透明机密服务用一个固定 API 密钥或其他静态机密。--secret-from-env、--secret-from-file、stdin,或一个交互式提示agent
通用 OAuth你注册你自己的 OAuth 应用(BYOT),例如 GitHub 或 Google。从目录用 --oauth <service>,或对自定义提供商用 --authorize-url 和 --token-url每个调用者,或 agent 配 --authorize 或客户端凭据
MCP OAuth一个 MCP 服务器公布 OAuth 并自动注册一个客户端。--mcp <url>,或当项目已声明那个用户所有的 MCP 服务器时只用 slug每个调用者,或 agent 配 --authorize

例如:

bash
# Opaque secret: store a fixed API key for the agent
uv run mda connections create organization-tavily --secret-from-env TAVILY_API_KEY

# General OAuth: register your own app, and let each caller authorize
uv run mda connections create frontend-github --oauth github \
  --client-id "********" --secret-from-env GITHUB_CLIENT_SECRET

# MCP OAuth: let the MCP server register a client for you
uv run mda connections create engineering-notion --mcp https://mcp.notion.com/mcp

不要在一个命令里混用模式。例如,--mcp 不能与 --oauth、自定义端点、--client-id 或一个机密值组合。

为连接命名

mda connections create 的第一个参数是一个 slug:你对这个连接起的名字,也是你的代码传给 connections.get(...) 的名字。它不是提供商名,提供商名归 --oauth:

bash
mda connections create frontend-github --oauth github
#                      ^ your slug      ^ catalog provider

slug 在一个工作区内唯一。用小写字母、数字和单个连字符,并选一个能识别连接指向哪个账户的名字,例如 organization-tavily 或 engineering-notion。

创建一个不透明机密

为 agent 存储一个固定机密。从环境变量、文件、stdin 或提示读取值。这个模式适合自定义工具的 API 密钥,例如一个 Tavily 搜索密钥。用 CLI 创建的不透明机密是 agent 所有的。

把源值放进 shell 环境或项目 .env 文件,然后从那个变量创建连接。从项目根运行该命令。CLI 需要一个 LangSmith API 密钥和工作区 ID。

bash
uv run mda connections create organization-tavily --secret-from-env TAVILY_API_KEY

这会把 TAVILY_API_KEY 的值存为一个 agent 所有的机密。运行时代码用 connections.get(...) 解析它。

提供该值的其他方式:

  • --secret-from-file PATH:从一个只含该机密的文件读一个值。
  • stdin:用管道或重定向传入值,例如 printf '%s' "$ACME_API_KEY" | mda connections create acme-api。
  • 交互式提示:在 TTY 上省略值标志。CLI 会隐藏输入,所以机密不出现在屏幕或 shell 历史里。

用 CLI 创建的不透明机密总是 agent 所有的。要按调用者的凭据,用一个 OAuth 连接。

签发一个属于 agent 而非复用个人密钥的密钥。一个专属密钥能被限定作用域、轮换和撤销,而不影响共享它的其他任何东西。

在自定义工具中用一个不透明机密

下面的工具为 agent 解析 organization-tavily 连接,然后把它发送给 Tavily API:

python
import httpx
from langchain.tools import tool
from managed_deepagents import connections

@tool(parse_docstring=True)
async def search_web(query: str) -> str:
    """
    Search the web.

    Args:
        query: Search query.
    """
    api_key = await connections.get("organization-tavily", {"type": "agent"})
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.post(
            "https://api.tavily.com/search",
            json={"api_key": api_key, "query": query, "max_results": 5},
        )
        response.raise_for_status()
        return response.text

把该工具加到 agent,见自定义工具。

创建一个通用 OAuth 连接

注册一个自带应用(BYOT)的 OAuth 客户端,以便调用者能授予 agent 访问一个提供商 API 的权限。在你拥有 OAuth 应用注册的 REST 或 GraphQL API 上用这个模式。对于自动发现和注册客户端的 MCP 服务器,改用 MCP OAuth。

使用 OAuth 目录

每个 OAuth 提供商都需要同样的五项设置:一个授权 URL、一个令牌 URL、一个令牌端点身份验证方法、授权参数和默认作用域。一个目录条目提供全部五项,所以 --oauth <service> 加你自己的客户端 ID 和机密就是整个配置。

这个目录不是对你能用哪些提供商的一道关卡。对它未覆盖的服务,自己传端点。

用以下命令列出目录:

bash
uv run mda connections catalog

该命令打印每个服务的默认作用域和你在其中注册一个应用的页面。它不调用 LangSmith,所以不需要工作区 ID 或 API 密钥。把第一列中的一个值传给 --oauth。

--oauth 值注册一个应用默认作用域
atlassianAtlassianread:me offline_access
bitbucketBitbucketaccount
boxBox无
click-upClickUp无
discordDiscordidentify email
dropboxDropboxaccount_info.read
facebookFacebookemail public_profile
figmaFigmacurrent_user:read
githubGitHubread:user
gitlabGitLabread_user
googleGoogleopenid https://www.googleapis.com/auth/userinfo.email
hubspotHubSpotoauth
huggingfaceHugging Faceopenid profile email
linearLinearread
linkedinLinkedInopenid profile email
notion-apiNotion API无
patreonPatreonidentity identity[email]
redditRedditidentity
salesforceSalesforceapi refresh_token
slackSlackchat:write
spotifySpotifyuser-read-email
stripe-linkStripe Linkpayment_methods.agentic userinfo:read
twitchTwitchuser:read:email
xXtweet.read users.read offline.access

一个没有默认作用域的服务需要 --scope。

某些服务为它们的 API 和它们的 MCP 服务器运行各自独立的 OAuth 应用

Notion 就是其一。--oauth notion-api 针对 Notion 的 REST API 注册一个应用,而那个应用不授权 Notion 的 MCP 服务器。要用 MCP 服务器,请改创建一个 MCP OAuth 连接。当一个服务两者都提供时查阅提供商的文档。

GitHub 需要一个 OAuth 客户端 ID 和客户端机密。设 GITHUB_CLIENT_SECRET,然后创建连接。把 ******** 替换为客户端 ID:

bash
uv run mda connections create frontend-github \
  --oauth github \
  --client-id "********" \
  --secret-from-env GITHUB_CLIENT_SECRET

创建时,CLI 打印要向提供商注册的重定向 URI。在调用者授权之前,在提供商的应用设置页注册那个 URI。该 URI 跟随你的 LangSmith 主机,例如生产上的 https://api.smith.langchain.com/v1/agent-auth/oauth/callback。

可选标志:

  • --scope SCOPE:替换目录默认值。对每个作用域重复。
  • --allowed-scope SCOPE:任何后续授权可请求的上限。默认为 --scope 值且必须覆盖每个 --scope。
  • --authorization-param KEY=VALUE:额外的授权查询参数。对每个参数重复。
  • --auth-method METHOD:客户端如何向令牌端点身份验证:client_secret_basic、client_secret_post,或公共客户端用 none。默认为目录服务的方法。

用一个自定义端点注册一个提供商

对目录未覆盖的服务,传两个端点外加一个客户端 ID 和作用域:

bash
uv run mda connections create acme \
  --authorize-url https://auth.acme.com/authorize \
  --token-url https://auth.acme.com/token \
  --client-id "********" \
  --secret-from-env ACME_CLIENT_SECRET \
  --scope read

多数提供商会拒绝一个不带 scope 参数的授权请求。对手动注册传 --scope,或在目录覆盖该提供商时用 --oauth <service>。

对像 Stripe 这样在令牌端点需要自定义标头的提供商,往 mda connections create 添加 --token-request-header KEY=VALUE。对每个标头重复该标志。这个选项支持授权码和客户端凭据两种授权。标头值是只写的。

创建一个客户端凭据连接

OAuth 客户端凭据授权把 agent 作为一个应用而非一个人身份验证。传 --grant-type client_credentials,CLI 会在创建连接时请求令牌。没人通过浏览器登录,也没有调用者看到授权提示。

当提供商签发机器对机器的 OAuth 凭据时用这个模式。当提供商的 API 只以一个人身份验证时,改授权一个 agent 所有的账户。

一个客户端凭据连接需要一个令牌 URL、一个客户端 ID 和一个客户端机密。设 ACME_CLIENT_SECRET,然后创建连接。把 ******** 替换为客户端 ID:

bash
uv run mda connections create acme-api \
  --grant-type client_credentials \
  --token-url https://auth.acme.com/oauth/token \
  --client-id "********" \
  --secret-from-env ACME_CLIENT_SECRET \
  --scope read

CLI 打印 slug、授权类型和它所存储的作用域。从项目目录运行它,因为令牌属于那个项目的部署。当令牌请求失败时,CLI 会删除连接而非留下一个不可用的。

--oauth <service> 也有效,针对一个带令牌 URL 的目录条目。目录的默认作用域和授权参数不适用于这个授权,所以对一个需要作用域的提供商传 --scope。

可选标志:

  • --scope SCOPE:请求一个作用域。对每个作用域重复。
  • --auth-method METHOD:把客户端机密作为 client_secret_basic 或 client_secret_post 发送。默认为 client_secret_basic。公共客户端(none)不能用这个授权。
  • --token-param KEY=VALUE:向令牌请求添加一个参数,例如 --token-param audience=https://api.acme.com。对每个参数重复。这个授权自己设 grant_type、client_id、client_secret 和 scope,所以那四个键会被拒绝。

这个授权拒绝授权码标志 --authorize-url、--authorization-param 和 --allowed-scope。

一个客户端凭据连接总是 agent 所有的,所以每个调用者都作为该应用行动。运行时代码像解析其他任何 agent 所有凭据那样解析它:

python
access_token = await connections.get("acme-api", {"type": "agent"})

授权一个 agent 所有的 OAuth 账户

当提供商签发它自己的应用凭据(例如一个 Slack bot 令牌、一个 GitHub App 安装令牌,或一个 Notion 内部集成令牌)时,优先把它作为一个 agent 所有的机密存储。一个应用凭据被限定到应用、可单独撤销,且不依赖任何人的账户。当提供商通过 OAuth 签发那个应用身份时,创建一个客户端凭据连接。

当提供商不提供应用身份、且其 API 只以一个人身份验证时,用 --authorize。

默认情况下,一个 OAuth 连接在运行时从每个调用者收集一个授权。传 --authorize 让你自己登录一次并为该部署存储授权。此后每个调用者都作为那一个账户行动,且没有调用者看到授权提示:

bash
uv run mda connections create support-linear \
  --oauth linear \
  --client-id "********" \
  --secret-from-env LINEAR_CLIENT_SECRET \
  --scope read --scope write \
  --authorize

CLI 为你登录所用的账户启动一个授权流程,并把结果授权存为该部署所有。运行时代码把它作为一个 agent 所有的凭据解析:

python
access_token = await connections.get("support-linear", {"type": "agent"})

--authorize 只适用于 OAuth 连接。把它与 --oauth、自定义端点或 --mcp 组合。从项目目录运行它,因为授权属于那个项目的部署。

当每个调用者都应作为同一个共享账户而非作为他们自己行动时,用一个 agent 所有的 OAuth 账户。一个授予问答 agent 读访问的公司 Notion 账户就是一个例子。

TIP

授权一个你团队拥有的专属账户,而非你自己的。你登录所用的账户会成为 agent 为每个调用者采取的每个行动背后的身份。用一个个人账户会让你损失三样东西:

  • agent 获得你在该提供商的完全访问。
  • 提供商的审计日志会为 agent 所做的显示你的名字。
  • 当你自己的访问改变时,agent 停止工作。

以这种方式做出的授权始终绑定到授权它的那个账户,即使是专属账户。一次密码重置、一个被撤销的会话,或一个被停用的账户都会终结该授权,所以把那个账户当作生产基础设施对待。

给那个专属账户 agent 所需的最窄 --scope 值。设 --allowed-scope 以封顶任何后续授权能请求的范围。

在自定义工具中访问一个 OAuth 令牌

这个工具解析已身份验证调用者的 GitHub 连接并调用 GitHub REST API:

python
import httpx
from langchain.tools import tool
from managed_deepagents import connections

@tool
async def get_github_user() -> str:
    """Get the authenticated caller's GitHub login."""
    # Replace `frontend-github` with the name of your connection.
    access_token = await connections.get("frontend-github", {"type": "user"})
    async with httpx.AsyncClient(timeout=30.0) as client:
        response = await client.get(
            "https://api.github.com/user",
            headers={"Authorization": f"Bearer {access_token}"},
        )
        response.raise_for_status()
        return response.json()["login"]

创建一个 MCP OAuth 连接

对于支持 OAuth 客户端注册的远程 MCP 服务器,Managed Deep Agents 会发现服务器的 OAuth 元数据并自动注册一个客户端。你不提供客户端 ID 或客户端机密。

先声明 MCP 服务器

在 tools/mcp.py 中添加该服务器并引用连接 slug。

python
from managed_deepagents import connections, define_mcp

mcp = define_mcp(
    servers={
        "notion": {
            "transport": "http",
            "url": "https://mcp.notion.com/mcp",
            "connection": connections.get("engineering-notion", {"type": "user"}),
        },
    },
)

更多信息,见连接到 MCP 服务器。

从项目声明创建

当 slug 精确匹配项目中一个用户所有的 MCP 连接时,只用 slug 创建。CLI 从 MCP 声明读服务器 URL:

bash
uv run mda connections create engineering-notion

从一个显式 MCP URL 创建

当你想显式命名服务器 URL、或 slug 尚未在项目里声明时,传 --mcp:

bash
uv run mda connections create engineering-notion --mcp https://mcp.notion.com/mcp

一个像 mcp.notion.com/mcp 这样无协议的值会被存为 https://mcp.notion.com/mcp。

如果服务器不能自动注册一个客户端,CLI 会如此说明并指向你改用通用 OAuth(--oauth 或手动端点)。

mda deploy 对缺失的用户所有 MCP 连接应用同样的推断。它保持现有连接不变、通过 OAuth 发现创建每个唯一缺失的 MCP 连接,并在发现或注册不可用时于部署前失败。

处理授权中断

Managed Deep Agents 为你运行 OAuth 往返。项目不需要回调路由、令牌存储、刷新逻辑或同意屏。运行暂停,调用者授予访问,运行带着那人的凭据恢复。

在第一个模型回合之前,Managed Deep Agents 检查该运行需要的每个用户所有连接。如果调用者缺一个或多个授权,凭据关卡抛出单个 LangGraph 中断。Slack 和 LangSmith Studio 为调用者处理那个中断。一个自定义前端从 LangChain 前端 SDK 读同样的载荷并在调用者连接后恢复。

用户所有的连接需要一个已身份验证的调用者。匿名或仅 agent 的运行无法完成凭据关卡。那个身份来自哪里,见识别调用者。

中断载荷

用 useStream(@langchain/react、@langchain/vue、@langchain/svelte)或 injectStream(@langchain/angular)时,待处理中断在 stream.interrupt 上。凭据关卡载荷是 stream.interrupt.value。

对一个缺失的 OAuth 授权,credentials 中每个条目都带有调用者在何处完成同意的 URL:

json
{
  "type": "credential_authorization_required",
  "message": "Connect the following integrations to continue.",
  "credentials": [
    {
      "slug": "engineering-notion",
      "kind": "oauth2",
      "connect_url": "https://api.smith.langchain.com/v1/agent-auth/...",
      "auth_id": "sess_abc123"
    }
  ]
}

一个中断列出每个缺失的授权。在恢复之前处理每个条目。一个带 "kind": "secret" 的条目表示一个用户所有的 API 密钥而非一个 OAuth 授权。Slack 和 Studio 不收集那些。更多信息,见查看当前限制。

字段何时存在含义
type总是判别符。必须是 credential_authorization_required。
message总是展示在待授权之上的人类可读摘要。
credentials总是每个缺失授权一个条目。
credentials[].slug总是要授权的连接 slug。
credentials[].kind总是密码式 API 密钥为 secret,OAuth 为 oauth2。
credentials[].connect_urloauth2调用者完成同意的 HTTPS URL。
credentials[].auth_idoauth2用于轮询状态的授权会话 id。

在你的 UI 中读取中断

渲染这个中断的前端是一个 web 应用,所以无论 agent 用 Python 还是 TypeScript 写,这些例子都是 TypeScript。

在 stream.interrupt 上检测凭据关卡载荷,渲染一张连接卡片,然后在每个授权都被存储后用 stream.respond 恢复:

tsx
import { useStream } from "@langchain/react";

type CredentialAuthorizationRequired = {
  type: "credential_authorization_required";
  message?: string;
  credentials: Array<{
    slug: string;
    kind: "oauth2" | "secret";
    connect_url?: string;
    auth_id?: string;
  }>;
};

function isCredentialAuthorization(
  value: unknown
): value is CredentialAuthorizationRequired {
  return (
    typeof value === "object" &&
    value !== null &&
    "type" in value &&
    value.type === "credential_authorization_required"
  );
}

export function Chat() {
  const stream = useStream({
    apiUrl: "https://your-deployment.example",
    assistantId: "agent",
  });

  const payload = stream.interrupt?.value;
  const credentialAuth = isCredentialAuthorization(payload) ? payload : null;

  return (
    <div>
      {/* messages … */}
      {credentialAuth && (
        <ConnectCard
          payload={credentialAuth}
          onComplete={async (connectedSlugs) => {
            await stream.respond(
              {
                type: "credential_authorization_completed",
                connected_slugs: connectedSlugs,
              },
              { interruptId: stream.interrupt?.id }
            );
          }}
        />
      )}
    </div>
  );
}
vue
<script setup lang="ts">
import { computed } from "vue";
import { useStream } from "@langchain/vue";

const stream = useStream({
  apiUrl: "https://your-deployment.example",
  assistantId: "agent",
});

const credentialAuth = computed(() => {
  const value = stream.interrupt.value?.value;
  return value?.type === "credential_authorization_required" ? value : null;
});

async function onComplete(connectedSlugs: string[]) {
  await stream.respond(
    {
      type: "credential_authorization_completed",
      connected_slugs: connectedSlugs,
    },
    { interruptId: stream.interrupt.value?.id }
  );
}
</script>

<template>
  <div>
    <!-- messages … -->
    <ConnectCard
      v-if="credentialAuth"
      :payload="credentialAuth"
      @complete="onComplete"
    />
  </div>
</template>
svelte
<script lang="ts">
  import { useStream } from "@langchain/svelte";

  const stream = useStream({
    apiUrl: "https://your-deployment.example",
    assistantId: "agent",
  });

  $: payload = stream.interrupt?.value;
  $: credentialAuth =
    payload?.type === "credential_authorization_required" ? payload : null;

  async function onComplete(connectedSlugs: string[]) {
    await stream.respond(
      {
        type: "credential_authorization_completed",
        connected_slugs: connectedSlugs,
      },
      { interruptId: stream.interrupt?.id }
    );
  }
</script>

<div>
  <!-- messages … -->
  {#if credentialAuth}
    <ConnectCard payload={credentialAuth} onComplete={onComplete} />
  {/if}
</div>
ts
import { Component, computed } from "@angular/core";
import { injectStream } from "@langchain/angular";

@Component({
  selector: "app-chat",
  template: `
    <!-- messages … -->
    @if (credentialAuth(); as auth) {
      <app-connect-card
        [payload]="auth"
        (complete)="onComplete($event)"
      />
    }
  `,
})
export class ChatComponent {
  stream = injectStream({
    apiUrl: "https://your-deployment.example",
    assistantId: "agent",
  });

  credentialAuth = computed(() => {
    const value = this.stream.interrupt()?.value;
    return value?.type === "credential_authorization_required" ? value : null;
  });

  async onComplete(connectedSlugs: string[]) {
    await this.stream.respond(
      {
        type: "credential_authorization_completed",
        connected_slugs: connectedSlugs,
      },
      { interruptId: this.stream.interrupt()?.id }
    );
  }
}

关于一般的中断生命周期(stream.interrupt、恢复、检查点),见人在回路。

处理 OAuth 授权(kind: "oauth2")

对每个 OAuth 条目:

  1. 显示 slug 和一个打开 connect_url 的 Connect 控件(仅 HTTPS;拒绝带内嵌凭据的 URL)。
  2. 长轮询 Agent Auth 直到会话完成:
http
GET /v1/agent-auth/oauth-authorization-sessions/{auth_id}?wait_seconds=25

响应 status 为 pending、completed、failed 或 expired。当状态为 pending 时继续轮询。在 completed 时,把那个 slug 标记为已连接。在 failed 或 expired 时,请调用者发起一个新运行以获得一个新鲜的连接链接。

不要把访问令牌放进你的 UI。Agent Auth 为调用者存储授权;agent 在恢复时通过 connections.get(...) 读取它。

恢复运行

在 credentials 中每个条目都连接之后,用以下恢复:

json
{
  "type": "credential_authorization_completed",
  "connected_slugs": ["engineering-notion"]
}

如上所示,把那个对象传给 stream.respond(resume, { interruptId: stream.interrupt?.id })。如果在一个授权仍缺失时恢复,关卡会带着一个新鲜载荷再次中断。

内置的 Slack 处理

当 agent 通过 Slack 频道运行时,Slack 会渲染带一个 HTTPS connect_url 的 OAuth 条目。调用者打开链接、完成同意,Slack 恢复运行,所以没人非得打开 LangSmith 来连接一个服务。

截图占位: 在此添加 Slack OAuth 授权提示的截图。

Slack OAuth authorization prompt

查看与删除连接

用 list 或 get 查看连接元数据。list 显示工作区里的每个连接,不只是本项目使用的那些:

bash
uv run mda connections list
uv run mda connections get organization-tavily
uv run mda connections delete organization-tavily

向 list 或 get 传 --json 以获得机器可读输出。向 delete 传 --yes 以跳过确认提示。

本地开发

对于 mda dev,一个 agent 所有的不透明连接读 MDA_DEV_<SLUG>。CLI 把 slug 转大写并把连字符替换为下划线。例如,organization-tavily 读 MDA_DEV_ORGANIZATION_TAVILY。

用户所有的连接需要一个已身份验证的调用者和 Agent Auth。部署 agent 以端到端地演练授权中断。

查看当前限制

连接是 Managed Deep Agents 公测的一部分。以下缺口适用于当前版本:

  • 用户所有的 API 密钥:一个用户所有的连接持有一个 OAuth 授权。Slack 和 Studio 不收集按调用者的 API 密钥,CLI 也不为其创建一个空槽。改用一个 agent 所有的机密,或如下一节所述在一个自定义前端里收集密钥。
  • 自定义频道:Slack 和 Studio 解析调用者身份并为调用者完成授权。一个自定义前端自己处理授权中断。
  • 工作区 UI:LangSmith 不在 UI 中列出连接。用 mda connections list 和 mda connections get 查看连接元数据。
  • 授权可见性:mda connections list 是工作区作用域的,且不报告哪些部署在某个 slug 下持有凭据。一个以 no agent connection is set for slug '<slug>' 失败的运行意味着该部署不拥有它的任何凭据,即使 list 显示那个 slug。从项目根运行 mda connections create <slug> 以铸造一个。
Store a user-owned API key through Agent Auth

一个带 "kind": "secret" 的中断条目没有 connect_url。要在一个自定义前端里收集一个,用一个隐藏输入提示填值,然后就现有连接 slug 创建凭据:

http
POST /v1/agent-auth/connections
json
{
  "slug": "organization-tavily",
  "display_name": "organization-tavily",
  "credential": {
    "kind": "secret",
    "owner_type": "user",
    "owner_id": "<caller-identity-id>",
    "value": "<api-key>"
  }
}

Agent Auth 为那个 slug 复用现有连接并附加调用者的机密。机密材料是只写的。在一次成功创建之后,把那个 slug 标记为已连接并恢复运行。更多信息,见设置 Agent Auth。

另请参阅


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