# 七牛云 Kling 图像生成：实际使用指南

当 `image_gen` 工具不在当前会话的工具列表中，但七牛云 Kling provider 已配置时，通过 Python 直接调用 API 生成图像。

## 配置位置

```yaml
# ~/.hermes/config.yaml
image_gen:
  provider: qiniu
  model: kling-v2

providers:
  qiniu:
    base_url: https://api.qnaigc.com/v1
    api_key: sk-af1...   # 从 config 读取
```

## API 端点

| 操作 | 端点 | 方法 |
|------|------|------|
| 提交任务 | `POST /v1/images/generations` | `{"model":"kling-v2","prompt":"...","aspect_ratio":"1:1","n":1}` |
| 查询状态 | `GET /v1/images/tasks/{task_id}` | 轮询至 `status=succeed` |
| 测试连通性 | `GET /v1/models` | 验证 API Key 是否有效 |

## 生成流程（异步轮询）

```
提交任务 → 获取 task_id → 每5秒轮询 → status=succeed → 下载图片URL
                              ↓ (约60s for kling-v2)
                          status=failed → 报错
```

### 完整代码骨架

```python
import json, urllib.request, yaml, time

with open(os.path.expanduser("~/.hermes/config.yaml")) as f:
    cfg = yaml.safe_load(f)
api_key = cfg["providers"]["qiniu"]["api_key"]

# Step 1: 提交
payload = json.dumps({"model": "kling-v2", "prompt": prompt, "aspect_ratio": "1:1", "n": 1}).encode()
req = urllib.request.Request("https://api.qnaigc.com/v1/images/generations",
    data=payload,
    headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"})
resp = urllib.request.urlopen(req, timeout=30)
task_id = json.loads(resp.read())["task_id"]

# Step 2: 轮询
deadline = time.time() + 300
while time.time() < deadline:
    time.sleep(5)
    req2 = urllib.request.Request(f"https://api.qnaigc.com/v1/images/tasks/{task_id}",
        headers={"Authorization": f"Bearer {api_key}"})
    status = json.loads(urllib.request.urlopen(req2, timeout=10).read())
    if status["status"] == "succeed":
        image_url = status["data"][0]["url"]
        break
    elif status["status"] == "failed":
        raise Exception(f"Kling failed: {status.get('status_message')}")

# Step 3: 下载到本地
urllib.request.urlretrieve(image_url, save_path)
```

## 模型选择

| 模型 | 速度 | 适用场景 |
|------|------|----------|
| `kling-v2` (推荐) | ~60s | 画质最高，通用文生图 |
| `kling-v1-5` | ~45s | 支持角色特征参考/人脸参考 |
| `kling-v1` | ~30s | 基础文生图，速度快 |

## 画面比例限制

Kling 只支持三种原生比例：

| Hermes 抽象值 | Kling 原生值 | 比例 |
|---------------|-------------|------|
| `square` | `1:1` | 1024×1024 |
| `landscape` | `16:9` | 横版 |
| `portrait` | `9:16` | 竖版 |

**不支持 4:3、3:2 等自定义比例。** 若需求为 4:3，最优方案是用 `1:1`（正方形）生成后 Pillow 居中裁剪。

## 提示词技巧（中文场景）

Kling 对中文提示词理解优秀。中文场景的提示词应包含：
1. **人物外观**：年龄、发型、眼镜、衣着风格
2. **动作姿态**：手势、表情、手持物品
3. **场景环境**：室内/室外、光线、背景元素
4. **整体风格**：写实摄影、清新自然、温暖向上等

示例（高中语文老师场景）：
> 一位年轻的语文女老师，戴着精致的金丝边眼镜，左手拿着一本翻开的语文课本，右手在空中做着手势，正在生动地讲解作文审题技巧。她面带温柔的微笑，眼神专注而有感染力。背景是明亮整洁的高中教室，有墨绿色黑板、整齐的课桌椅，窗外透进柔和的自然光。画面风格清新自然，写实摄影质感。

## 图片 URL 有效期

生成的图片返回签名的临时 URL，**有效期约7天**。务必在生成后立即下载到本地缓存（`~/.hermes/cache/images/`），不要长期依赖远程 URL。

## 生成后 QA：视觉分析回退

当 `vision_analyze` 工具因模型不支持图片输入而失败时（如 DeepSeek），使用阿里云百炼 CLI 作为回退：

```bash
# 先缩图避免 base64 过大（>1MB 可能失败）
python3.12 -c "
from PIL import Image
img = Image.open('input.png')
ratio = 1024 / max(img.size)
img2 = img.resize((int(img.size[0]*ratio), int(img.size[1]*ratio)), Image.LANCZOS)
img2.convert('RGB').save('output.jpg', 'JPEG', quality=85)
"

# Qwen-VL 视觉分析
bl vision analyze --image output.jpg --prompt "请描述..."
```

`bl` 使用 DashScope API Key（已配置在环境中，无需额外设置）。
