> ## Documentation Index
> Fetch the complete documentation index at: https://docs.profy.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# 环境变量

> SDK 与集成侧的配置项：名称、默认值、解析顺序与常见坑

# 环境变量

这一页只列**对外部开发者有意义**的配置项——即你在自己的服务里能设、且会改变行为的那些。平台内部的服务配置（数据库连接、对象存储凭证、模型密钥等）不在此列，它们由 Profy 运维，对你不可见也不需要关心。

## SDK 配置

### 认证与地址

| 变量               | 用途                         | 默认值                    |
| ---------------- | -------------------------- | ---------------------- |
| `PROFY_API_KEY`  | 平台 API Key，形如 `sk-pro-...` | 无（必填）                  |
| `PROFY_BASE_URL` | API 基地址                    | `https://api.profy.cn` |

解析顺序是**构造参数优先于环境变量**：

```python theme={null}
Profy(api_key="sk-pro-xxx")        # 显式参数最优先
Profy()                            # 回落 PROFY_API_KEY
```

`PROFY_API_KEY` 缺失时构造直接抛错，不会静默降级成匿名：

```
Profy: api_key is required. Pass it directly or set the PROFY_API_KEY
environment variable.
```

<Note>
  `PROFY_BASE_URL` 尾部斜杠会被自动去掉，所以 `https://api.profy.cn/` 与 `https://api.profy.cn` 等价。私有化部署或本地联调时改这一项即可，SDK 其余行为不变。
</Note>

### OAuth 应用

做第三方应用（代表多个 Profy 用户操作）时用应用凭证而不是 API Key：

| 变量                 | 用途                       | 默认值                    |
| ------------------ | ------------------------ | ---------------------- |
| `PROFY_APP_ID`     | OAuth 应用的 client\_id     | 无（必填）                  |
| `PROFY_APP_SECRET` | OAuth 应用的 client\_secret | 无（必填）                  |
| `PROFY_BASE_URL`   | 同上，共用                    | `https://api.profy.cn` |

两者缺一即抛错：

```
ProfyApp: client_id and client_secret are required. Pass them directly or
set PROFY_APP_ID / PROFY_APP_SECRET environment variables.
```

授权请求的默认 scope 是 `events:write`。

<Warning>
  `PROFY_APP_SECRET` 是**服务端凭证**。它一旦出现在浏览器包、移动端 App 或任何客户端代码里，就等同于公开——OAuth 的授权码换取 token 必须在你的服务端完成。
</Warning>

## 不通过环境变量的配置

有几项刻意不做成环境变量，值得说明为什么：

| 配置     | 传递方式                                      | 原因                                  |
| ------ | ----------------------------------------- | ----------------------------------- |
| 终端用户身份 | `X-End-User-Id` 请求头，或客户端 `end_user_id` 参数 | 一个进程通常服务很多终端用户，做成进程级环境变量必然串号        |
| 超时     | 构造参数 `timeout`                            | 默认总超时 300 秒、连接超时 10 秒；不同调用的合理超时差异极大 |
| 模型选择   | 请求体参数                                     | 属于单次调用的决策，不是部署期配置                   |

<Note>
  终端用户身份这一项最容易被误解。它看起来"每个部署固定"，但实际上一个 API Key 前置多个用户才是常态——所以它必须能按请求变，做成环境变量就锁死了。单用户脚本可以在构造客户端时设一次 `end_user_id`；服务端集成则每次调用传。
</Note>

## 代理与网络

SDK 内部的 HTTP 客户端**显式设置了 `trust_env=False`**，也就是说：

* `HTTP_PROXY` / `HTTPS_PROXY` / `ALL_PROXY` **不会**被 SDK 读取
* `NO_PROXY` 同样不生效
* 系统 CA 与 `.netrc` 也不参与

<Warning>
  这是最容易踩的一个坑：你在机器上配了代理，`curl` 通、`requests` 通，但 SDK 就是直连。

  这不是缺陷。走代理会让文件直传（预签名 URL）出现两种 Authorization 打架的问题——SDK 自带的 Bearer 头会让对象存储忽略查询串签名并返回 400。为避免这种极难归因的失败，SDK 统一不吃环境代理。

  需要代理时通过构造参数传入自定义 HTTP 客户端，不要指望环境变量。
</Warning>

## MCP 集成

远程 MCP Server 不需要任何环境变量，只需要一个配置文件：

```json .mcp.json theme={null}
{
  "mcpServers": {
    "profy": {
      "type": "streamable-http",
      "url": "https://mcp.profy.cn/mcp"
    }
  }
}
```

鉴权走 IDE 已有的登录会话（Bearer token 或 Cookie），不通过环境变量传密钥。工具清单见 [MCP Tools](/zh/developers/reference/mcp-tools)。

## 完整示例

<CodeGroup>
  ```bash .env theme={null}
  PROFY_API_KEY=sk-pro-xxxxxxxxxxxxxxxx
  # 私有化部署或本地联调时才需要改
  # PROFY_BASE_URL=https://api.example.internal
  ```

  ```python Python theme={null}
  import os
  from profy import Profy

  # 三种写法等价度依次降低：
  # 1) 全靠环境变量（最省事，但终端用户身份必须按调用传）
  async with Profy() as client:
      await client.agents.run("my-expert", "你好", end_user_id="user-123")

  # 2) 显式 key + 客户端级终端用户（单用户脚本）
  async with Profy(api_key=os.environ["PROFY_API_KEY"], end_user_id="user-123") as client:
      await client.agents.run("my-expert", "你好")

  # 3) 全显式（多租户服务端，base_url 指向私有部署）
  async with Profy(
      api_key=os.environ["PROFY_API_KEY"],
      base_url=os.environ.get("PROFY_BASE_URL", "https://api.profy.cn"),
  ) as client:
      await client.agents.run("my-expert", "你好", end_user_id=request.user_id)
  ```

  ```python OAuth 应用 theme={null}
  import os
  from profy import ProfyApp

  app = ProfyApp()   # 读 PROFY_APP_ID / PROFY_APP_SECRET

  url = app.authorization_url(
      redirect_uri="https://your.app/callback",
      scope="events:write",
      state=csrf_token,
  )
  ```
</CodeGroup>

## 边界与失败态

| 症状                            | 原因                    | 处理                        |
| ----------------------------- | --------------------- | ------------------------- |
| 构造客户端就抛 `api_key is required` | `PROFY_API_KEY` 未设或拼错 | 检查变量名与进程是否加载了 `.env`      |
| 请求全部 400 且提示缺 header          | 没传终端用户身份              | 客户端级设 `end_user_id` 或按调用传 |
| 配了代理但 SDK 直连                  | SDK `trust_env=False` | 传自定义 HTTP 客户端，不要靠环境变量     |
| 私有部署请求打到公网                    | `PROFY_BASE_URL` 未生效  | 确认在构造客户端**之前**已加载环境变量     |
| `ProfyApp` 抛缺 client\_id      | 只设了 `PROFY_API_KEY`   | 应用凭证与 API Key 是两套，不能互替    |

<Note>
  第四条尤其常见：很多加载 `.env` 的库是在 import 时才注入变量，而模块级创建的客户端已经在那之前构造完了。稳妥做法是把客户端创建放进函数里，别放模块顶层。
</Note>

## 验证配置

```python theme={null}
import os
from profy import Profy

print("key set:", bool(os.environ.get("PROFY_API_KEY")))
print("base url:", os.environ.get("PROFY_BASE_URL", "https://api.profy.cn (default)"))

async with Profy(end_user_id="verify-1") as client:
    resp = await client.agents.run("your-expert", "回一个字")
    print(resp)
```

跑通说明 key、地址、终端用户三项都对。任一项错都会在这里就报出来，而不是等到线上。

## 相关页面

<CardGroup cols={2}>
  <Card title="认证" icon="key" href="/zh/developers/authentication">
    API Key 与 OAuth 的区别
  </Card>

  <Card title="端点速查" icon="list" href="/zh/developers/reference/endpoints">
    全部 `/v1/*` 端点
  </Card>

  <Card title="错误码" icon="triangle-exclamation" href="/zh/developers/error-codes">
    配置错误对应的码
  </Card>

  <Card title="MCP Tools" icon="plug" href="/zh/developers/reference/mcp-tools">
    IDE 集成
  </Card>
</CardGroup>

<Note>
  核对日期 2026-08-11。来源：`sdk/python/profy/client.py`（`DEFAULT_BASE_URL`、`_DEFAULT_TIMEOUT`、`trust_env=False`、`_end_user_headers`）、`sdk/python/profy/app.py`（`PROFY_APP_ID` / `PROFY_APP_SECRET` / `DEFAULT_SCOPE`）、`services/core/src/routes/platform-api/caller.ts`（终端用户头校验）。
</Note>
