# 静态页改造指南 · 让任何旧页用上 AI

> 网站之前 100 多个 HTML 都是纯静态的。现在我们部署了 Node 服务，
> 任何一页都能在**两步之内**调出真实 AI 回复。这份是改造模板。

---

## 改造前后对比

### Before（纯静态，没有 AI）

```html
<!-- kindling-middle-level/projects/code-02-add-claude.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <title>给网页接上 AI</title>
  <link rel="stylesheet" href="../../shared.css">
</head>
<body>
  <h1>项目 02：给网页接上 AI</h1>
  <p>下面是一个示例页面，但实际上 AI 回复是写死的——</p>
  <button onclick="alert('（这里假装 AI 回了一句）')">问 AI</button>
</body>
</html>
```

### After（接入 SDK，真的能问 AI）

```html
<!-- kindling-middle-level/projects/code-02-add-claude.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <title>给网页接上 AI</title>
  <link rel="stylesheet" href="../../shared.css">
</head>
<body>
  <h1>项目 02：给网页接上 AI</h1>

  <!-- 1. 实际的 UI -->
  <textarea id="q" rows="3" cols="50" placeholder="问 AI 一个问题..."></textarea>
  <br>
  <button id="ask">问 AI</button>
  <div id="answer" style="white-space: pre-wrap; margin-top: 1rem;"></div>

  <!-- 2. 引入 SDK（一行） -->
  <script src="../../kindling-iteration-2/kindling-ai-sdk.js"></script>

  <!-- 3. 写最少的逻辑 -->
  <script>
    document.getElementById('ask').onclick = async () => {
      const q = document.getElementById('q').value.trim();
      const out = document.getElementById('answer');
      if (!q) return;

      out.textContent = '想一想...';
      out.textContent = '';                       // 清空，准备流式接收

      await kindlingAI.chat({
        system: '你是温和的中学编程小老师，用最简单的中文回答，不超过 3 句话。',
        user: q,
        stream: true,                              // 流式：边想边吐字
        onToken: (t) => { out.textContent += t; }, // 每个字都追加上
        stage: 'mid'                               // mid 控制 max_tokens
      });
    };
  </script>
</body>
</html>
```

**就这样**。两个 `<script>` 块，加 1 个 SDK 引用，就完成了从"假装 AI"到"真 AI"的升级。

---

## 改造一个项目页的具体步骤

### Step 1 · 找到页面里"假装在和 AI 对话"的地方

通常是这种代码：

```js
button.onclick = () => {
  alert('（AI 回复占位）');
  // 或者
  div.textContent = '一段写死的示例回复...';
};
```

### Step 2 · 加一行 SDK 引用

在 `</body>` 之前加：

```html
<script src="/kindling-iteration-2/kindling-ai-sdk.js"></script>
```

> 注意路径：根目录绝对路径 `/kindling-iteration-2/...` 最稳。
> 如果想用相对路径，看页面位置：
> - `kindling-low-level/xxx.html` → `<script src="../kindling-iteration-2/kindling-ai-sdk.js">`
> - `kindling-middle-level/projects/xxx.html` → `<script src="../../kindling-iteration-2/kindling-ai-sdk.js">`

### Step 3 · 把"假回复"换成 `kindlingAI.chat(...)`

模板：

```js
button.onclick = async () => {
  const out = document.getElementById('answer');
  out.textContent = '';
  await kindlingAI.chat({
    system: '你是 ___，要 ___。',  // 角色和风格
    user: document.getElementById('q').value, // 学生输入
    stream: true,
    onToken: (t) => { out.textContent += t; }
  });
};
```

完成。

---

## 五个常见场景模板（直接复制改）

### 场景 1 · 简单问答

```js
const r = await kindlingAI.chat({
  system: '你是恐龙小专家，回答任何问题都引用一个真实恐龙。',
  user: '霸王龙能跑多快？'
});
console.log(r.content);
```

### 场景 2 · 流式输出（推荐，体验好）

```js
await kindlingAI.chat({
  system: '...',
  user: '...',
  stream: true,
  onToken: (t) => { document.getElementById('out').textContent += t; }
});
```

### 场景 3 · 带历史的多轮对话

```js
const history = [];
async function send(userMsg) {
  history.push({ role: 'user', content: userMsg });
  const r = await kindlingAI.chat({
    system: '你是友好的助教。',
    user: userMsg,
    history: history.slice(0, -1)  // 不要把当前这条再 push 一遍
  });
  history.push({ role: 'assistant', content: r.content });
  return r.content;
}
```

### 场景 4 · 让 AI 给作业打分

```js
const r = await kindlingAI.judge({
  rubric: `5 条评分标准：
1. 具体性（是否有真实例子）
2. 真诚（是否第一人称写）
3. 克制（有没有套话）
4. 记忆点（有没有让人记住的细节）
5. 意图（结尾有没有点出"为什么"）`,
  candidate: document.getElementById('homework').value,
});
// r = { score: 7, reasons: ["...", "..."], suggestion: "..." }
document.getElementById('score').textContent = `得分：${r.score}/10`;
document.getElementById('feedback').innerHTML =
  '<ul>' + r.reasons.map(x => `<li>${x}</li>`).join('') + '</ul>' +
  '<p><strong>改进建议：</strong>' + r.suggestion + '</p>';
```

### 场景 5 · 让 AI 画一张图（通义万相）

```js
const r = await kindlingAI.image({
  prompt: '水墨风格，一只戴眼镜的兔子在书桌前画画',
  size: '1024*1024'
});
document.getElementById('pic').src = r.imageUrl;
```

> 通义万相是异步任务，整张图大约 10–30 秒出来。建议给用户显示一个"正在画..."的提示。

### 场景 6 · 让 AI 看图说话（Qwen-VL-Max）

```js
// 1) 用户选了一张图
fileInput.onchange = async (e) => {
  const file = e.target.files[0];
  const reader = new FileReader();
  reader.onload = async () => {
    // base64 去掉前缀
    const base64 = reader.result.split(',')[1];
    const r = await kindlingAI.vision({
      imageBase64: base64,
      question: '图里有什么？讲给一个 8 岁小朋友听。'
    });
    document.getElementById('desc').textContent = r.description;
  };
  reader.readAsDataURL(file);
};
```

### 场景 7 · 语音输入（浏览器原生，最便宜）

```js
document.getElementById('mic').onclick = async () => {
  const text = await kindlingAI.transcribeNative({
    language: 'zh-CN',
    onPartial: (t) => { input.value = t; }   // 边说边显示
  });
  // text 就是最终识别结果
};
```

---

## 全站批量改造建议

如果想给**全部** code-XX 项目页都加上 AI 能力，最快的方式：

1. 先在 `shared.css` 同级**加一个 `ai-helpers.html` partial**（或干脆放进 `interactive.js`）

2. 在每个项目页 `<head>` 里加：
   ```html
   <script src="/kindling-iteration-2/kindling-ai-sdk.js"></script>
   ```

3. 然后在页面底部加一个**"问问 AI"通用面板**（HTML 模板）：
   ```html
   <section class="ai-panel">
     <h3>试试问问 AI</h3>
     <textarea id="ai-q" rows="2" placeholder="..."></textarea>
     <button id="ai-go">问</button>
     <div id="ai-out"></div>
   </section>
   <script>
     document.getElementById('ai-go').onclick = async () => {
       const out = document.getElementById('ai-out');
       out.textContent = '';
       await kindlingAI.chat({
         system: '你是温和的中学编程小老师，用最简单的中文，不超过 3 段。',
         user: document.getElementById('ai-q').value,
         stream: true,
         onToken: t => out.textContent += t
       });
     };
   </script>
   ```

4. 用 `find . -name '*.html'` + `sed` 批量插入这段。或者写个 Node 脚本扫整个目录加内容。

---

## 测试改造是否成功

改完一个页面后：

1. 浏览器打开 `https://kindling.top/你的页面.html`
2. 按 F12 打开开发者工具 → Network 标签
3. 点页面里的"问 AI"按钮
4. Network 应当出现一条 `POST /api/v1/chat` → 200
5. 页面上出现真的 AI 回复

如果失败：
- **404 /api/v1/chat** → Nginx 反代没生效，重新检查 location 配置
- **502 /api/v1/chat** → Node 服务挂了，`pm2 status` 看一下，`pm2 restart kindling-ai`
- **401 / 403** → DeepSeek key 错或没钱了
- **CORS error** → 修 `.env` 里 `ALLOWED_ORIGIN`，加上你访问的域名

---

## 渐进式上线策略

不用一次改完所有 100 多页。建议的顺序：

1. **第 1 周**：改"中阶代码俱乐部 项目 02"一个页，验证整条链路通了
2. **第 2 周**：把同目录其他 3 个项目页改了
3. **第 3 周**：在每个academy 落地页底部加"试试问问 AI"通用面板
4. **第 4 周**：把审美工作室所有"判官"练习页接通 `kindlingAI.judge`
5. **第 5 周**：智能体实验室项目页接通图像/视觉能力

每改一页都做一次完整的 Network → 200 检查。

---

## 改造一页的"成本估算"

- 一个学生从打开页面到看完 AI 回复：约 200–500 tokens
- DeepSeek 每百万 tokens：¥0.5 输入 + ¥2 输出 ≈ **每次回答不到 1 厘钱**
- 1 万次回答 = 约 ¥10 左右

也就是说：哪怕你每天有 1000 个学生在网站上各问 AI 10 次，月成本也就 ¥30 上下。

— 静态页改造指南 v1
