> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# LangSmith 部署的控制平面 API 参考

控制平面 API 是 [LangSmith 部署](/langsmith/deployment) 的一部分。通过控制平面 API，您可以以编程方式创建、管理和自动化您的 [代理服务器](/langsmith/agent-server) 部署——例如，作为自定义 CI/CD 工作流的一部分。

请在侧边栏的 **控制平面 API** 部分浏览完整的 API 参考，或参考端点组：

* [集成 (v1)](/api-reference/integrations-v1/list-github-integrations)：GitHub 集成和仓库列表
* [部署 (v2)](/api-reference/deployments-v2)：创建、管理和更新代理服务器部署
* [监听器 (v2)](/api-reference/listeners-v2)：用于自托管企业组织的监听器资源
* [认证服务 (v2)](/api-reference/auth-service-v2)：OAuth 提供商配置和认证流程

## 主机

控制平面在云数据区域的主机：

| 美国                               | 欧盟                                  |
| -------------------------------- | ----------------------------------- |
| `https://api.host.langchain.com` | `https://eu.api.host.langchain.com` |

**注意**：LangSmith 的自托管部署将为控制平面提供自定义主机。控制平面 API 可通过路径 `/api-host` 访问。例如，`http(s)://<host>/api-host/v2/deployments`。更多详情请参阅[自托管使用指南](/langsmith/self-host-usage#configuring-the-application-you-want-to-use-with-langsmith)。

## 认证

要向控制平面 API 进行身份验证，请将 `X-Api-Key` 头设置为有效的 LangSmith API 密钥，并将 `X-Tenant-Id` 头设置为要定位的有效工作区 ID。

示例 `curl` 命令：

```shell theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
curl --request GET \
  --url http://localhost:8124/v2/deployments \
  --header 'X-Api-Key: LANGSMITH_API_KEY'
  --header 'X-Tenant-Id': WORKSPACE_ID'
```

## 版本控制

每个端点路径都以版本为前缀（例如 `v1`、`v2`）。

## 快速开始

1. 调用 `POST /v2/deployments` 创建一个新的部署。响应体包含部署 ID (`id`) 和最新（也是第一个）修订版的 ID (`latest_revision_id`)。
2. 调用 `GET /v2/deployments/{deployment_id}` 检索部署。将 URL 中的 `deployment_id` 设置为部署 ID (`id`) 的值。
3. 通过调用 `GET /v2/deployments/{deployment_id}/revisions/{latest_revision_id}` 轮询修订版 `status`，直到 `status` 为 `DEPLOYED`。
4. 调用 `PATCH /v2/deployments/{deployment_id}` 更新部署。

## 示例代码

以下是示例 Python 代码，演示如何编排控制平面 API 来创建部署、更新部署和删除部署。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import os
import time

import requests
from dotenv import load_dotenv


load_dotenv()

# 必需的环境变量
CONTROL_PLANE_HOST = os.getenv("CONTROL_PLANE_HOST")
LANGSMITH_API_KEY = os.getenv("LANGSMITH_API_KEY")
WORKSPACE_ID = os.getenv("WORKSPACE_ID")
INTEGRATION_ID = os.getenv("INTEGRATION_ID")
MAX_WAIT_TIME = 1800  # 30 分钟


def get_headers() -> dict:
    """返回向控制平面 API 发送请求的通用头。"""
    return {
        "X-Api-Key": LANGSMITH_API_KEY,
        "X-Tenant-Id": WORKSPACE_ID,
    }


def create_deployment() -> str:
    """创建部署。返回部署 ID。"""
    headers = get_headers()
    headers["Content-Type"] = "application/json"

    deployment_name = "my_deployment"

    request_body = {
        "name": deployment_name,
        "source": "github",
        "source_config": {
            "integration_id": INTEGRATION_ID,
            "repo_url": "https://github.com/langchain-ai/langgraph-example",
            "deployment_type": "dev",
            "build_on_push": False,
            "custom_url": None,
            "resource_spec": None,
        },
        "source_revision_config": {
            "repo_ref": "main",
            "langgraph_config_path": "langgraph.json",
            "image_uri": None,
        },
        "secrets": [
            {
                "name": "OPENAI_API_KEY",
                "value": "test_openai_api_key",
            },
            {
                "name": "ANTHROPIC_API_KEY",
                "value": "test_anthropic_api_key",
            },
            {
                "name": "TAVILY_API_KEY",
                "value": "test_tavily_api_key",
            },
        ],
    }

    response = requests.post(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments",
        headers=headers,
        json=request_body,
    )

    if response.status_code != 201:
        raise Exception(f"创建部署失败: {response.text}")

    deployment_id = response.json()["id"]
    print(f"已创建部署 {deployment_name} ({deployment_id})")
    return deployment_id


def get_deployment(deployment_id: str) -> dict:
    """获取部署。"""
    response = requests.get(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
        headers=get_headers(),
    )

    if response.status_code != 200:
        raise Exception(f"获取部署 ID {deployment_id} 失败: {response.text}")

    return response.json()


def list_revisions(deployment_id: str) -> list[dict]:
    """列出修订版。

    返回列表按 created_at 降序排序（最新的在前）。
    """
    response = requests.get(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions",
        headers=get_headers(),
    )

    if response.status_code != 200:
        raise Exception(
            f"列出部署 ID {deployment_id} 的修订版失败: {response.text}"
        )

    return response.json()


def get_revision(
    deployment_id: str,
    revision_id: str,
) -> dict:
    """获取修订版。"""
    response = requests.get(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions/{revision_id}",
        headers=get_headers(),
    )

    if response.status_code != 200:
        raise Exception(f"获取修订版 ID {revision_id} 失败: {response.text}")

    return response.json()


def patch_deployment(deployment_id: str) -> None:
    """部分更新部署。"""
    headers = get_headers()
    headers["Content-Type"] = "application/json"

    # 这会创建一个新的修订版，因为包含了 source_revision_config
    response = requests.patch(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
        headers=headers,
        json={
            "source_config": {
                "build_on_push": True,
            },
            "source_revision_config": {
                "repo_ref": "main",
                "langgraph_config_path": "langgraph.json",
            },
        },
    )

    if response.status_code != 200:
        raise Exception(f"部分更新部署失败: {response.text}")

    print(f"已部分更新部署 ID {deployment_id}")


def wait_for_deployment(deployment_id: str, revision_id: str) -> None:
    """等待修订版状态变为 DEPLOYED。"""
    start_time = time.time()
    revision, status = None, None
    while time.time() - start_time < MAX_WAIT_TIME:
        revision = get_revision(deployment_id, revision_id)
        status = revision["status"]
        if status == "DEPLOYED":
            break
        elif "FAILED" in status:
            raise Exception(f"修订版 ID {revision_id} 失败: {revision}")

        print(f"等待修订版 ID {revision_id} 变为 DEPLOYED...")
        time.sleep(60)

    if status != "DEPLOYED":
        raise Exception(
            f"等待修订版 ID {revision_id} 变为 DEPLOYED 超时: {revision}"
        )


def delete_deployment(deployment_id: str) -> None:
    """删除部署。"""
    response = requests.delete(
        url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
        headers=get_headers(),
    )

    if response.status_code != 204:
        raise Exception(
            f"删除部署 ID {deployment_id} 失败: {response.text}"
        )

    print(f"部署 ID {deployment_id} 已删除")


if __name__ == "__main__":
    # 创建部署并获取最新修订版
    deployment_id = create_deployment()
    revisions = list_revisions(deployment_id)
    latest_revision = revisions["resources"][0]
    latest_revision_id = latest_revision["id"]

    # 等待最新修订版变为 DEPLOYED
    wait_for_deployment(deployment_id, latest_revision_id)

    # 部分更新部署并获取最新修订版
    patch_deployment(deployment_id)
    revisions = list_revisions(deployment_id)
    latest_revision = revisions["resources"][0]
    latest_revision_id = latest_revision["id"]

    # 等待最新修订版变为 DEPLOYED
    wait_for_deployment(deployment_id, latest_revision_id)

    # 删除部署
    delete_deployment(deployment_id)
```

***

<div className="source-links">
  <Callout icon="terminal-2">
    [通过 MCP 将这些文档](/use-these-docs) 连接到 Claude、VSCode 等，以获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/langsmith/api-ref-control-plane.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
