# EDA Service API Key 配置映射关系

## 📊 配置架构总览

```
Pulumi Config YAML (加密存储)
    ↓
index.ts (TypeScript 读取)
    ↓
K8s Deployment Env Variables
    ↓
config.py (Python 读取)
    ↓
应用代码使用
```

---

## 🔑 三种 AI 模型的完整配置对应关系

### 1. OpenAI GPT-4/GPT-4o

| 层级 | 配置名称 | 文件位置 | 行号 |
|------|---------|---------|------|
| **Pulumi Config** | `edaservice:openaiKey` | `Pulumi.edaservice-staging.yaml` | 26-27 |
| **TypeScript 读取** | `const openaiKey = config.require("openaiKey")` | `index.ts` | ~33 |
| **K8s Env 变量** | `OPENAI_API_KEY` | `index.ts` (env注入) | 111-113 |
| **Python 读取** | `self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")` | `config.py` | 15 |

**Pulumi 配置内容**:
```yaml
edaservice:openaiKey:
  secure: v1:EkbjT7H797vozN2r:SB7xlFzr8oL2LpgdcGrU3yLS+ex9zicnPZJJg2B9sym/...
```

---

### 2. Claude 3 (AWS Bedrock)

| 层级 | 配置名称 | 文件位置 | 行号 |
|------|---------|---------|------|
| **Pulumi Config** | `edaservice:awsAccessKeyId`<br>`edaservice:awsSecretAccessKey` | `Pulumi.edaservice-staging.yaml` | 7-10 |
| **TypeScript 读取** | `const awsAccessKeyId = config.require("awsAccessKeyId")`<br>`const awsSecretAccessKey = config.require("awsSecretAccessKey")` | `index.ts` | ~34-35 |
| **K8s Env 变量** | `BEDROCK_AWS_ACCESS_KEY_ID`<br>`BEDROCK_AWS_SECRET_ACCESS_KEY` | `index.ts` (env注入) | 115-121 |
| **Python 读取** | `self.BEDROCK_AWS_ACCESS_KEY_ID = os.getenv("BEDROCK_AWS_ACCESS_KEY_ID", "")`<br>`self.BEDROCK_AWS_SECRET_ACCESS_KEY = os.getenv("BEDROCK_AWS_SECRET_ACCESS_KEY", "")` | `config.py` | 55-56 |

**Pulumi 配置内容**:
```yaml
edaservice:awsAccessKeyId:
  secure: v1:DieWxAGC1G2jcfHg:Z/hKbv20eq9Zc45L9dfzWnyH7Dbm8KrCu/sXjg/RZPYUYd5b

edaservice:awsSecretAccessKey:
  secure: v1:N8rzWPDIHkZGotTQ:qVpeYKQcQdiLCL1XMYJmooGEKuVXdxwzH0ahMYh6+1YAxr1L...
```

**重要说明**:
- Claude 使用的是 **AWS Bedrock** 服务，不是直接的 Claude API
- 认证方式是 **AWS IAM 凭证**（Access Key ID + Secret Access Key）
- 这就是为什么配置名称是 `awsAccessKeyId` 而不是 `claudeAPIKey`

---

### 3. Qwen (阿里云 DashScope) - 新增

| 层级 | 配置名称 | 文件位置 | 行号 | 状态 |
|------|---------|---------|------|------|
| **Pulumi Config** | `edaservice:qwenAPIKey`<br>`edaservice:dashscopeBaseURL` | `Pulumi.edaservice-*.yaml` | 待添加 | ⏭️ 待配置 |
| **TypeScript 读取** | `const qwenAPIKey = config.require("qwenAPIKey")`<br>`const dashscopeBaseURL = config.get("dashscopeBaseURL")` | `index.ts` | 待添加 | ⏭️ 待修改 |
| **K8s Env 变量** | `QWEN_API_KEY`<br>`DASHSCOPE_BASE_URL` | `index.ts` (env注入) | 待添加 | ⏭️ 待修改 |
| **Python 读取** | `self.QWEN_API_KEY = os.getenv("QWEN_API_KEY", "")` | `config.py` | 59 | ✅ 已完成 |

**需要添加的 Pulumi 配置**:
```yaml
# 使用 pulumi config set 命令添加
edaservice:qwenAPIKey:
  secure: <自动加密的值>

edaservice:dashscopeBaseURL: "https://dashscope-intl.aliyuncs.com/api/v1"
```

---

## 🔍 为什么 Claude 不是 claudeAPIKey？

Claude 3 通过 **AWS Bedrock** 服务调用，而不是 Anthropic 的直接 API，因此：

**AWS Bedrock 方式** (当前使用):
- 认证: AWS Access Key ID + Secret Access Key
- 优点: 企业级 SLA，与 AWS 服务集成
- 调用方式: boto3 SDK → AWS Bedrock → Claude 3

**Anthropic API 方式** (未使用):
- 认证: Claude API Key
- 调用方式: anthropic SDK → Claude API

---

## 📝 配置对比表

| 模型 | Pulumi Config Key | K8s Env Variable | Python Config Attribute | 认证方式 |
|------|------------------|------------------|------------------------|---------|
| **OpenAI** | `openaiKey` | `OPENAI_API_KEY` | `OPENAI_API_KEY` | OpenAI API Key |
| **Claude** | `awsAccessKeyId`<br>`awsSecretAccessKey` | `BEDROCK_AWS_ACCESS_KEY_ID`<br>`BEDROCK_AWS_SECRET_ACCESS_KEY` | `BEDROCK_AWS_ACCESS_KEY_ID`<br>`BEDROCK_AWS_SECRET_ACCESS_KEY` | AWS IAM 凭证 |
| **Qwen** | `qwenAPIKey`<br>`dashscopeBaseURL` | `QWEN_API_KEY`<br>`DASHSCOPE_BASE_URL` | `QWEN_API_KEY` | DashScope API Key |

---

## 🚀 Qwen 部署配置命令

### 配置命令（3个环境）

```bash
cd deployments/pulumi

# === Nightly 环境 ===
pulumi stack select edaservice-nightly
pulumi config set --secret edaservice:qwenAPIKey sk-426efxxxxxxxxxxxxxxxxxxxxxxxxxaba
pulumi config set edaservice:dashscopeBaseURL https://dashscope-intl.aliyuncs.com/api/v1

# === Staging 环境 ===
pulumi stack select edaservice-staging
pulumi config set --secret edaservice:qwenAPIKey sk-426efxxxxxxxxxxxxxxxxxxxxxxxxxaba
pulumi config set edaservice:dashscopeBaseURL https://dashscope-intl.aliyuncs.com/api/v1

# === Production 环境 ===
pulumi stack select edaservice-prod
pulumi config set --secret edaservice:qwenAPIKey <production-api-key>
pulumi config set edaservice:dashscopeBaseURL https://dashscope-intl.aliyuncs.com/api/v1
```

**执行后效果**:
- `Pulumi.edaservice-*.yaml` 文件会自动更新
- API Key 会被加密存储（类似 OpenAI 的方式）

---

## 📖 配置验证

### 查看当前配置

```bash
# 查看所有配置
pulumi config

# 查看特定配置（加密的会显示 [secret]）
pulumi config get edaservice:openaiKey      # [secret]
pulumi config get edaservice:awsAccessKeyId  # [secret]
pulumi config get edaservice:qwenAPIKey      # [secret]

# 查看明文配置
pulumi config get edaservice:dashscopeBaseURL
```

### 对比三种模型配置

```bash
cd deployments/pulumi
pulumi stack select edaservice-staging

# OpenAI 配置
pulumi config get edaservice:openaiKey
# 输出: [secret]

# Claude (AWS Bedrock) 配置
pulumi config get edaservice:awsAccessKeyId
pulumi config get edaservice:awsSecretAccessKey
# 输出: [secret]

# Qwen 配置
pulumi config get edaservice:qwenAPIKey
pulumi config get edaservice:dashscopeBaseURL
# 输出: [secret] 和明文 URL
```

---

## 🔄 完整数据流图

### OpenAI
```
Pulumi.*.yaml
├── edaservice:openaiKey: [encrypted]
    ↓
index.ts
├── const openaiKey = config.require("openaiKey")
├── env: [{ name: "OPENAI_API_KEY", value: openaiKey }]
    ↓
K8s Pod
├── export OPENAI_API_KEY="sk-..."
    ↓
config.py
├── self.OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
    ↓
svc/openai.py
└── global_client = OpenAI(api_key=config.OPENAI_API_KEY)
```

### Claude (AWS Bedrock)
```
Pulumi.*.yaml
├── edaservice:awsAccessKeyId: [encrypted]
├── edaservice:awsSecretAccessKey: [encrypted]
    ↓
index.ts
├── const awsAccessKeyId = config.require("awsAccessKeyId")
├── const awsSecretAccessKey = config.require("awsSecretAccessKey")
├── env: [
│     { name: "BEDROCK_AWS_ACCESS_KEY_ID", value: awsAccessKeyId },
│     { name: "BEDROCK_AWS_SECRET_ACCESS_KEY", value: awsSecretAccessKey }
│   ]
    ↓
K8s Pod
├── export BEDROCK_AWS_ACCESS_KEY_ID="AKIA..."
├── export BEDROCK_AWS_SECRET_ACCESS_KEY="..."
    ↓
config.py
├── self.BEDROCK_AWS_ACCESS_KEY_ID = os.getenv("BEDROCK_AWS_ACCESS_KEY_ID", "")
├── self.BEDROCK_AWS_SECRET_ACCESS_KEY = os.getenv("BEDROCK_AWS_SECRET_ACCESS_KEY", "")
    ↓
svc/claude.py
└── boto3.client(
      aws_access_key_id=config.BEDROCK_AWS_ACCESS_KEY_ID,
      aws_secret_access_key=config.BEDROCK_AWS_SECRET_ACCESS_KEY
    )
```

### Qwen (新增)
```
Pulumi.*.yaml (待添加)
├── edaservice:qwenAPIKey: [encrypted]
├── edaservice:dashscopeBaseURL: "https://..."
    ↓
index.ts (待修改)
├── const qwenAPIKey = config.require("qwenAPIKey")
├── const dashscopeBaseURL = config.get("dashscopeBaseURL")
├── env: [
│     { name: "QWEN_API_KEY", value: qwenAPIKey },
│     { name: "DASHSCOPE_BASE_URL", value: dashscopeBaseURL }
│   ]
    ↓
K8s Pod
├── export QWEN_API_KEY="sk-..."
├── export DASHSCOPE_BASE_URL="https://..."
    ↓
config.py (✅ 已完成)
├── self.QWEN_API_KEY = os.getenv("QWEN_API_KEY", "")
    ↓
svc/qwen.py (✅ 已完成)
└── dashscope.api_key = api_key
    dashscope.base_http_api_url = base_url
```

---

## 📌 关键发现

**Claude 的配置名称解释**:

| Pulumi Config 名称 | 实际含义 | 为什么这样命名 |
|-------------------|---------|---------------|
| `awsAccessKeyId` | AWS Bedrock Access Key ID | Claude 通过 AWS Bedrock 调用 |
| `awsSecretAccessKey` | AWS Bedrock Secret Access Key | Claude 通过 AWS Bedrock 调用 |

**不是**:
- ❌ `claudeAPIKey` (因为不是直接用 Anthropic API)
- ❌ `bedrockAPIKey` (因为 Bedrock 用 AWS IAM 认证)

**而是**:
- ✅ `awsAccessKeyId` + `awsSecretAccessKey` (AWS IAM 标准凭证)

---

## 🆚 三种模型认证方式对比

| 模型 | 服务提供商 | 认证方式 | 凭证类型 | SDK |
|------|-----------|---------|---------|-----|
| **OpenAI** | OpenAI | API Key | 单个字符串 | `openai` |
| **Claude** | AWS Bedrock | AWS IAM | Access Key + Secret | `boto3` |
| **Qwen** | 阿里云 DashScope | API Key | 单个字符串 | `dashscope` |

---

## ✅ Qwen 配置参考现有模型

Qwen 的配置方式与 **OpenAI 相同**（单个 API Key），而不是像 Claude 那样需要两个凭证。

### 对比

| 项目 | OpenAI | Qwen |
|------|--------|------|
| Pulumi Config Key | `openaiKey` | `qwenAPIKey` |
| K8s Env Variable | `OPENAI_API_KEY` | `QWEN_API_KEY` |
| Python Config | `OPENAI_API_KEY` | `QWEN_API_KEY` |
| 凭证数量 | 1 个 | 1 个 |
| 加密存储 | ✅ 是 | ✅ 是 |

---

## 🔧 实际配置示例

### 查看当前 Staging 配置

```bash
cd deployments/pulumi
pulumi stack select edaservice-staging

# 查看 OpenAI 配置
pulumi config get edaservice:openaiKey
# 输出: [secret]

# 查看 Claude (AWS) 配置
pulumi config get edaservice:awsAccessKeyId
# 输出: [secret]
pulumi config get edaservice:awsSecretAccessKey
# 输出: [secret]

# 查看所有配置
pulumi config
```

### 添加 Qwen 配置（与 OpenAI 方式相同）

```bash
# 设置 API Key（自动加密）
pulumi config set --secret edaservice:qwenAPIKey sk-426efxxxxxxxxxxxxxxxxxxxxxxxxxaba

# 设置 Endpoint（明文，可选）
pulumi config set edaservice:dashscopeBaseURL https://dashscope-intl.aliyuncs.com/api/v1

# 验证配置
pulumi config get edaservice:qwenAPIKey
# 输出: [secret]
```

执行后，`Pulumi.edaservice-staging.yaml` 会自动更新：
```yaml
edaservice:qwenAPIKey:
  secure: v1:<random-salt>:<encrypted-value>

edaservice:dashscopeBaseURL: "https://dashscope-intl.aliyuncs.com/api/v1"
```

---

## 📚 相关代码位置

### Pulumi TypeScript (index.ts)

**配置读取** (约第 33-36 行):
```typescript
const openaiKey = config.require("openaiKey");                    // OpenAI
const awsAccessKeyId = config.require("awsAccessKeyId");          // Claude (AWS)
const awsSecretAccessKey = config.require("awsSecretAccessKey");  // Claude (AWS)

// ===== 需要添加 =====
const qwenAPIKey = config.require("qwenAPIKey");                  // Qwen
const dashscopeBaseURL = config.get("dashscopeBaseURL") || "https://dashscope-intl.aliyuncs.com/api/v1";
```

**环境变量注入** (约第 111-121 行，在 edaservice-worker):
```typescript
env: [
  // ... 其他环境变量 ...
  {
    name: "OPENAI_API_KEY",      // OpenAI
    value: openaiKey,
  },
  {
    name: "BEDROCK_AWS_ACCESS_KEY_ID",      // Claude (AWS)
    value: awsAccessKeyId,
  },
  {
    name: "BEDROCK_AWS_SECRET_ACCESS_KEY",  // Claude (AWS)
    value: awsSecretAccessKey,
  },
  // ===== 需要添加 =====
  {
    name: "QWEN_API_KEY",        // Qwen
    value: qwenAPIKey,
  },
  {
    name: "DASHSCOPE_BASE_URL",  // Qwen Endpoint
    value: dashscopeBaseURL,
  },
]
```

**注意**: 同样的环境变量也需要添加到 `edaservice-api` Deployment（约第 251-255 行附近）

---

## 🎯 总结

### 配置映射关系

| 维度 | OpenAI | Claude | Qwen |
|------|--------|--------|------|
| **Pulumi 配置名** | `openaiKey` | `awsAccessKeyId`<br>`awsSecretAccessKey` | `qwenAPIKey`<br>`dashscopeBaseURL` |
| **环境变量名** | `OPENAI_API_KEY` | `BEDROCK_AWS_ACCESS_KEY_ID`<br>`BEDROCK_AWS_SECRET_ACCESS_KEY` | `QWEN_API_KEY`<br>`DASHSCOPE_BASE_URL` |
| **凭证数量** | 1 个 | 2 个 | 1-2 个 (Key必需, URL可选) |
| **认证服务** | OpenAI API | AWS Bedrock | 阿里云 DashScope |

### 关键理解

1. **Claude 使用 AWS 凭证** - 因为通过 AWS Bedrock 调用
2. **Qwen 使用单个 API Key** - 类似 OpenAI 的配置方式
3. **所有敏感信息都加密存储** - 使用 Pulumi 的 `--secret` 标志

---

**文档版本**: v1.0
**最后更新**: 2025-02-25
