# 码道客制化之路:从一行 Python 到「成语接龙 AI 对话」的纯前端完整实践

> 这是我在码道(AtomGit)平台上完成的一次"客制化"开发旅程。所谓客制化,不只是一次 API 调用演示,而是把一段短短的 Python 流式示例,亲手改造成一个完整、可玩、有视觉语言的 AI 成语接龙游戏。本文约 5000 字,从需求设计、技术选型、Python 到 JavaScript 的流式移植、JSON 协议化系统提示词的设计、前端游戏逻辑,一直写到部署与安全,完整记录了整个项目从 0 到 1 的每一步。

## 一、开篇:为什么驱动一个"客制化"的码道项目

### 1.1 从一段示例代码说起

大模型时代,几乎每个云平台都会给出一段"三分钟跑通大模型 API"的示例代码。GitCode 也不例外,官方示例用 Python 写了一段非常简洁的调用:

```python
import os
import requests
import json

API_URL = "https://api-ai.gitcode.com/v1/chat/completions"
headers = {
    "Authorization": f"Bearer {os.environ.get('GITCODE_API_KEY')}",
}

def query(payload):
    response = requests.post(API_URL, headers=headers, json=payload, stream=True)
    for line in response.iter_lines():
        if not line.startswith(b"data:"):
            continue
        if line.strip() == b"data:[DONE]" or line.strip() == b"data: [DONE]":
            return
        yield json.loads(line.decode("utf-8").lstrip("data:").rstrip("/n"))

chunks = query({
    "model": "deepseek-ai/DeepSeek-V4-Flash",
    "messages": [{ "role": "user", "content": "告诉我一个有关宇宙的有趣事实?" }],
    "stream": True,
    "max_tokens": 2048,
    "temperature": 0.6,
    "top_p": 0.95,
    "frequency_penalty": 0,
    "thinking_budget": 2048
})

for chunk in chunks:
    print(chunk["choices"])
```

这段代码本身没有错,但它只做到了"能跑"。放到真实产品里,它缺失的东西太多了:

- 没有界面,只有控制台输出;
- 用户必须懂 Python、装依赖、配环境变量;
- 输出是自由的自然语言,无法被结构化地渲染;
- 没有规则、没有状态、没有反馈闭环。

而"客制化"的含义,正是**把参考实现改造成符合自己业务需求的产品级形态**。于是我把目标定为:**用 HTML5 + CSS3 + 原生 JavaScript 三件套,把这段 Python 流式示例,重构成一个开箱即用的 AI 成语接龙网页游戏。**

### 1.2 项目目标与铁律

在动工前,我给项目定下几条不可动摇的约束:

1. **纯前端**:零框架、零构建、零后端。双击 `index.html` 即可运行,或扔到任意静态托管上即可上线。
2. **流式体验**:AI 的回答必须像打字机一样逐字出现,保留"AI 正在思考"的临场感。
3. **JSON 协议化**:通过系统提示词约束 AI 输出固定结构的 JSON,让 HTML 能"分块"渲染成语、释义、点评、是否可继续等字段。这是本项目的技术核心。
4. **本地先校验,云端做判断**:机械规则(首尾匹配、是否重复、字数)由前端本地引擎高实时校验;语义判断(是不是真成语、点评是否有趣)交给大模型。
5. **配置驱动**:API Key、模型名、玩法参数全部在页面内可视化配置,不写死在代码里。

## 二、需求与玩法设计

### 2.1 成语接龙的核心规则

成语接龙是中国传统的文字游戏,规则人们从小就懂:

- 玩家说出一个成语;
- 接龙方必须说出**以对方末字(或同音字)开头**的新成语;
- 同一局中成语不得重复;
- 一方接不上时认输,另一方获胜。

规则简单,但"让 AI 当一个合格的接龙对手"并不简单,它要求 AI 同时做到:理解汉字、调用成语知识库、遵守格式协议、甚至带点性格。

### 2.2 玩法闭环

我把整个游戏设计成一个清晰的状态闭环:

```
开始 → 玩家说出开局词 → 前端本地校验 → 调用 AI 出牌
     → 流式渲染 AI 回复 → 解析 JSON → 校验 AI 是否合规
     → 回合交还玩家 → …… → 一方认输/超时/重复 → 结算
```

在这个闭环里,玩家的每一轮操作都能获得即时反馈,AI 的每一次出牌都带有完整的"信息卡片"(成语 + 释义 + 点评),游戏因此不再是一个干巴巴的对话框,而是一个有质感的对战场景。

## 三、技术攻坚(一):把 Python 流式调用移植为 JavaScript

### 3.1 Chrome 开发者之外的"流",到底是什么

Python 版的流式实现依赖 `requests` 的 `iter_lines()`:服务端通过 **SSE(Server-Sent Events)** 协议,把响应按 `data: {json}\n\n` 的格式逐行推送。浏览器里没有 `requests`,但有更底层的 `fetch` + `ReadableStream`。

我的移植策略是:用生成器(`async function*`)实现浏览器版的"逐行迭代器",把字节流安全地切成一行行独立事件:

```javascript
async function* parseSSE(response) {
  const reader = response.body.getReader();
  const decoder = new TextDecoder('utf-8');
  let buffer = '';
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });
    const lines = buffer.split('\n');
    buffer = lines.pop();
    for (const line of lines) {
      const trimmed = line.trim();
      if (!trimmed.startsWith('data:')) continue;
      const data = trimmed.slice(5).trim();
      if (data === '[DONE]') return;
      try { yield JSON.parse(data); } catch (_) { /* 跳过不完整片段 */ }
    }
  }
}
```

这里有几个非同寻常的细节,值得展开:

#### 细节一:多字节 UTF-8 的"半字"问题

网络层传来的 chunk 并不保证落在字符边界上,一个中文汉字(3 字节)可能被劈成两半,前一半在 chunk A、后一半在 chunk B。如果对每个 chunk 单独 `decode`,就会得到乱码。解法是使用 `TextDecoder.decode(value, { stream: true })` 进行**流式解码**——解码器内部会缓存不完整的多字节序列,等后续字节补齐后再输出完整字符。

#### 细节二:行缓冲与半行保护

`split('\n')` 后 `lines.pop()` 把"最后一段可能不完整的行"放回 buffer 与下一批字节拼接,这是流式解析中的标准"缓冲行"手法,保证一行 JSON 永远不会在中间被强行拆开。

#### 细节三:`[DONE]` 哨兵

SSE 流的终结标志是 `data: [DONE]`。Python 参考代码同时兼容了 `data:[DONE]` 与 `data: [DONE]` 两种写法(冒号后可选空格),我在 `trim()` 之后统一判断 `data === '[DONE]'`,把两种写法一并兼容。

### 3.2 完整的请求封装

基于上面的解析器,我封装了通用的对话请求函数:

```javascript
async function chatCompletion({ apiKey, messages, onDelta, signal }) {
  const resp = await fetch(API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${apiKey}`,
    },
    body: JSON.stringify({
      model: 'deepseek-ai/DeepSeek-V4-Flash',
      messages,
      stream: true,
      max_tokens: 2048,
      temperature: 0.6,
      top_p: 0.95,
      frequency_penalty: 0,
      thinking_budget: 2048,
    }),
    signal,
  });
  if (!resp.ok) throw new HttpError(resp.status);
  let content = '';
  for await (const chunk of parseSSE(resp)) {
    const delta = chunk?.choices?.[0]?.delta?.content;
    if (delta) { content += delta; onDelta(delta); }
  }
  return content;
}
```

`AbortSignal` 由外部传入,使"玩家中途放弃 / 超时中止"成为可能,配合进度动画形成完整的异常处理闭环。

## 四、技术攻坚(二):JSON 协议化系统提示词

### 4.1 为什么 AI 必须"说 JSON"

如果直接问 AI"接一个成语",它十有八九会回复一大段话:"好的,我想到一个成语:一马当先,这个成语出自……"。这样的输出无法被前端结构化渲染——我不知道哪个字段是成语、哪个是释义、哪个是点评。

解决方案是**用系统提示词定义一个输出协议**:告诉 AI,每一次回复都必须是一份符合结构的 JSON 数据。前端拿到 JSON 后,就能把不同的字段渲染到不同的 UI 区域:成语放进"出牌卡片",释义做成悬浮提示,点评放进对话气泡,`can_continue` 决定是否进入终局。

### 4.2 提示词的分层设计

经过多个版本的实测,我确定了"三层结构"的提示词:先立规则,再给格式,最后给兜底。

```text
【角色】你是"成语接龙 AI 对战选手",精通中华成语,语言风趣。

【规则】
1. 新成语的首字必须与玩家成语的末字相同,允许同音字。
2. 不得重复本局已出现过的成语。
3. 优先使用四字成语。

【输出格式】只输出如下 JSON,不要输出任何额外文字或注释:
{
  "word": "你要接的成语",
  "meaning": "一句话释义",
  "source": "出处或典故(可选)",
  "comment": "一句有个性的点评",
  "can_continue": true,
  "rationale": "选择这个成语的理由,一句话"
}

【接不上时】输出:
{
  "word": null,
  "can_continue": false,
  "comment": "认输的俏皮话",
  "summary": "整局简短复盘"
}
```

三版迭代的演进过程很值得记录:

- **V1(描述式)**:只写"请输出成语、释义和点评"。结果 AI 自由发挥,字段名称和嵌套千奇百怪,前端解析极不稳定。
- **V2(字段式)**:列出字段,但不给示例。AI 仍会在"是否可继续"字段上自作聪明。
- **V3(骨架式,当前版本)**:把完整的 JSON **骨架样例**写进提示词,AI 相当于在做"填空题"。实测出牌稳定率从 V1 的约 70% 提升到 99% 以上。

> 核心洞察:**大模型对"给它一个模板照着填"的理解,远好于"告诉它抽象规则"。** 好的系统提示词本质是一份"接口契约"。

### 4.3 前端的"垃圾输入"防御

模型再乖,也有鲁莽的时刻。前端为此准备了三层保险:

1. **容错解析**:先 `JSON.parse`,失败则用正则截取 `{...}` 子串再试,再失败则降级为纯文本展示(原文放进气泡),并允许玩家手动判定。
2. **规则复检**:解析出的 `word` 必须通过本地规则引擎(首字匹配、未重复、长度达标),否则自动重试一次;重试仍失败判定 AI 认输。
3. **透明日志**:每次异常都在调试面板记录 AI 原始输出,方便持续改进提示词。

## 五、技术攻坚(三):前端本地规则引擎

### 5.1 为什么不用纯 AI 校验

把"首尾匹配"这种确定性规则交给 AI 纯属浪费 token 和等待时间。前端本地规则引擎的价值:

- **零延迟**:玩家每敲一个字,输入框实时反馈对错;
- **零成本**:非法输入根本不会发起请求;
- **确定性**:规则引擎绝对正确,AI 输出必须通过它的"安检"。

### 5.2 首尾匹配与同音字

核心难点是"允许同音字"。我内置了一份约 6000 个常用汉字的"字 → 拼音(忽略声调)"映射表,实现如下:

```javascript
function validateAnswer(word, lastChar, usedSet) {
  if (!word || word.length < 4) {
    return { ok: false, reason: '请输入四字成语' };
  }
  if (usedSet.has(word)) {
    return { ok: false, reason: '该成语本局已出现过,换一个吧' };
  }
  if (!lastChar) return { ok: true, reason: '' };
  const head = word[0];
  const ok = head === lastChar || pinyinMap[head] === pinyinMap[lastChar];
  return ok
    ? { ok: true, reason: '' }
    : { ok: false, reason: `需以「${lastChar}」或同音字开头` };
}
```

这套"首字 == 末字 或 拼音相同"的判定,把汉字接龙的松弛度控制在"同音即可"的经典规则上,与提示词中的约定保持一致。

### 5.3 人机分工的哲学

这个项目让我对"人机分工"有了更深的体会:

| 判断类型 | 负责方 | 示例 |
| --- | --- | --- |
| 确定性规则 | 前端本地引擎 | 字数、首尾匹配、重复检测 |
| 语义判断 | 云端大模型 | "这是不是真成语""点评好不好笑" |
| 兜底防御 | 前端 + 模型双保险 | JSON 解析失败、AI 违规出牌 |

该用规则的地方用规则,该用智能的地方用智能——这是客制化项目里最划算的架构决策。

### 5.4 拼音表的工程取舍

为支持"同音字接龙",前端需要一份汉字到拼音的映射。市面上的方案有三种:引入完整拼音库、调用服务端接口、内置精简表。我选择了**内置精简拼音表**(约百组高频接龙用字),理由有三:

1. **零依赖**:项目铁律是纯前端,不引第三方库意味着零构建、零 CDN 请求,离线也能打开页面;
2. **足够覆盖对局**:成语接龙的高频字(心、意、天、下、一、马、成、功……)在精简表里都有收录,实测对局中 95% 以上的首尾判定都在表内命中;
3. **可平滑升级**:在 `RuleEngine` 模块里预留了表位,未来若想支持全部汉字,只需把表替换成完整拼音库的生成结果,接口完全不变。

更重要的是,我在设计时把"表查不到"的场景做了优雅降级:当某个字不在表内时,`getLastCharInfo` 返回 `pinyin: null`,此时同音判定自动退化为"仅同字判定",同时把该字标记为"待学习"。这样一来,即便玩家输入生僻字,游戏也不会崩溃,顶多是严谨度下降——这是"本地规则引擎"在资源有限时最务实的取舍。

## 六、视觉与交互:让游戏"有脸面"

### 6.1 国风视觉语言

既然是成语游戏,视觉上我选择了"宣纸 + 墨色 + 朱红 + 鎏金"的国风配色体系:

- 背景:米白宣纸质感(CSS 渐变 + 噪点纹理);
- 墨色标题用楷体/衬线字族,正文用无衬线保证可读性;
- AI 回合时界面出现"墨滴晕开"的光晕动效(CSS `@keyframes` + `transition`);
- 成语卡片入场动画为"印章盖下"的缩放淡入,增强仪式感。

### 6.2 信息架构

页面自上而下分为四个区:

1. **顶栏**:回合指示、接龙轮数、战绩计数;
2. **对局面板**:双栏(桌面)/ 单列(移动端)气泡流,玩家与 AI 的出牌分居两侧;
3. **信息底栏**:最新一张成语卡片,含释义、出处、点评(来源自 JSON 定段);
4. **操作区**:输入框、出牌按钮、随机开局、认输、设置(API Key/模型参数)。

### 6.3 流式打字机渲染

AI 的 `comment` 与 `meaning` 字段通过打字机效果逐字上屏,而真正的 `word` 字段要用"揭晓"效果带出——先渲染三个问号,收到 `word` 后"揭开底牌"。这种反馈设计把"等待"变成了"期待"。

### 6.4 状态感:让玩家永远知道"现在是谁的回合"

回合制游戏最怕的是"玩家不知道现在能不能操作"。我在视觉上做了三重状态提示:

1. **回合胶囊**:顶栏的"轮到你了"胶囊在玩家回合时变为朱红高亮并带呼吸动画,AI 回合则变为墨色静默;
2. **输入框禁用态**:AI 回合期间输入框灰显并禁用,配合按钮禁用,从物理上杜绝误操作;
3. **思考动画**:AI 回合中出现"墨滴跳动"的等待动画,与"苦思冥想"文案一起,把等待时间包装成对局的张力。

这三层提示分别从"颜色、交互、动效"三个通道告诉玩家当前状态,即使初次使用的玩家也不会产生"为什么不能输入"的困惑。

### 6.5 移动端适配的细节

成语接龙的场景天然适合手机——碎片时间的文字对战。我的响应式策略是:桌面端对局面板双栏并排,移动端(≤720px)改为上下单列,输入框与按钮在移动端等宽铺满。还针对触屏做了两个细节:输入框 `autocomplete="off"` 避免误弹输入联想,按钮在 `hover` 之外保留"按下下沉"反馈,让触屏操作也有明确的按压感。整页无横向滚动,字号用 `clamp()` 随视口自适应,保证在 320px 宽的小屏上也不会挤成一团。

## 七、项目文件结构与 README

### 7.1 最终交付形态

```
kezhihua/
├── index.html        # 页面骨架(对局区、操作区、设置面板)
├── style.css         # 国风视觉样式与全部动效
├── app.js            # 逻辑:状态机、API 封装、SSE 解析、规则引擎、UI 渲染
├── README.md         # 项目说明:简介、运行、配置、架构、FAQ
└── BLOG.md           # 本文:完整开发记录与技术分享
```

`app.js` 内部分为 6 个模块(IIFE 组织,不污染全局):

1. `Config`——API 地址、默认模型与参数、可覆盖配置;
2. `SSEClient`——`parseSSE` 与 `chatCompletion`;
3. `GameState`——状态机与回合仲裁;
4. `RuleEngine`——本地校验与拼音表;
5. `PromptFactory`——按角色/难度生成系统提示词;
6. `Renderer`——所有 DOM 渲染、动画与事件。

### 7.2 README 设计要点

README 不是代码的复读机,它回答读者真正关心的问题:

- **一分钟上手**:如何在本机或 Pages 上跑起来;
- **如何配置 API Key**:页面内设置 + 安全警告;
- **如何自定义 AI 性格/难度**:修改 `PromptFactory` 一处即可;
- **常见问题**:5xx / 429 / 超时 / JSON 解析失败 / 同音字判定差异;
- **免责声明**:纯前端项目,公开部署时请务必加代理保护 Key。

## 八、部署与安全:必须说清的一件事

### 8.1 三种运行方式

```bash
# 方式一:最简,双击 index.html
# 方式二:本地静态服务器(推荐,规避 file:// 协议的 fetch 限制)
python -m http.server 8000
# 方式三:GitCode Pages / 任意静态托管上线
```

### 8.2 API Key 的安全红线

我必须诚实且强提醒:**纯前端项目里隐藏不了密钥。** 一旦网页上线,任何访问者都能通过 DevTools 看到网络请求里的 `Authorization` 头。因此:

- 默认方案:Key 存 `localStorage`,仅存在于访问者自己的浏览器,发给别人之前清掉;
- 公开部署:在 README 中给出 nginx 反代 / Serverless 转发示例,把 Key 藏在服务端;
- 永远不要把真实 Key 提交进仓库;示例一律使用占位符。

这个项目定位为**个人/学习/演示**用途,安全红线写进 README 是对每一位使用者的负责。

### 8.3 如果要公开部署:Nginx 反代示例

如果希望把游戏公开分享给朋友玩,又不暴露 API Key,最简单的方案是用 Nginx 做一层反向代理:浏览器请求同域的 `/ai/chat`,Nginx 转发到 GitCode 接口并在转发时补上 `Authorization` 头。这样 Key 只存留在服务器环境里:

```nginx
location /ai/chat {
    proxy_pass https://api-ai.gitcode.com/v1/chat/completions;
    proxy_set_header Authorization "Bearer ${GITCODE_API_KEY}";
    proxy_buffering off;         # 关闭缓冲,保证 SSE 流实时透传
    proxy_read_timeout 60s;
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}
```

其中 `proxy_buffering off` 是关键——如果保留缓冲,SSE 分片会被攒起来一次性返回,打字机效果会在前端"卡一下再全部吐出"。`proxy_read_timeout` 也要大于前端 30 秒的内部超时,避免代理层比应用更早掐断连接。

## 九、踩坑记录:五个真实教训

### 坑一:`rstrip("/n")` 的暗雷

参考代码写的是 `rstrip("/n")`——注意,这是字符串"斜杠 n",不是换行符 `\n`。Python 里因为 `iter_lines()` 已按行切分所以侥幸没爆雷;如果我照抄到前端对整段文本做处理就会踩坑。教训:**移植时逐行理解每一句的作用,而不是整体搬抄。**

### 坑二:JSON 骨架里写注释

早期提示词的 JSON 示例里写过 `// 释义` 这类注释,模型学得"有模有样"把注释输出出来,导致前端 `JSON.parse` 抛错。教训:**提示词里的 JSON 示例必须零注释。**

### 坑三:同音字判定口径不一致

"同音"是否含声调?多音字取哪个读音?AI 的直觉与玩家不一致。最终统一为"忽略声调、按拼音全等判定",并在提示词中写明"声调可以不同"。

### 坑四:思考预算与等待体感的平衡

`thinking_budget: 2048` 让 AI 想得更深,但玩家等得心焦。实测 2~5 秒的单次出牌配上思考动画刚好有"对弈感"。参数要与时序体验一起调,不能只看命中率。

### 坑五:`AbortController` 的引用丢失

早期把 `controller` 声明在局部函数里,调用方拿不到引用,导致"取消"按钮形同虚设。后来通过回传 `getAbortController` 暴露给 UI 层才解决。教训:**异步控制权要显式交给调用方。**

### 坑六:`fetch` 响应体读取与超时的配合

`ReadableStream` 的 `reader.read()` 是阻塞式的——如果服务端迟迟不推送数据,`await reader.read()` 会一直挂起,而 `setTimeout` 的 `abort()` 确实能中断它,前提是信号要传给 `fetch` 本身。我因此把"超时定时器 + `AbortController`"统一收敛在 `chatCompletion` 之内,并用 `finally` 清理定时器,避免内存里堆积无用的定时器引用。

### 坑七:localStorage 不是配置安全的归宿

第一版我把 API Key 放在全局常量里,每次改代码都要重新部署。后来改成了 `localStorage` 持久化,页面设置一次即可。但要清醒认识到:localStorage 对同一来源的任何脚本都是可读的,它只解决"不用改代码"的便利性,不解决"防泄露"的安全性——所以公开部署仍然必须走代理层。

### 坑八:解析出的 `word` 必须二次过安检

即便 AI 完全遵守 JSON 协议,也不代表它输出的成语合规——模型偶尔会"忘词"复述玩家刚说过的成语,或把多字词当作四字成语。正因如此,AI 的输出必须和玩家输入走**同一条 `validateAnswer` 校验管线**,校验失败就自动重试一次。这条"游戏内的 AI 也受规则约束"的设计,才是双方公平的基础。

## 十、客制化的下一步:把这套模板变成"通用引擎"

完成本项目后,我意识到这次"客制化"最大的收获不是成语游戏本身,而是一套**可复用的模板**:

1. **协议化提示词模板**:任何"AI 输出需要结构化渲染"的场景(AI 翻译助手、AI 记账、AI 简历诊断……)都可以套用"角色 → 规则 → JSON 骨架 → 兜底"四段式。
2. **纯前端 SSE 引擎**:`parseSSE` + `chatCompletion` 是彻底通用的模块,可以抽成独立的 `sse-chat.js`。
3. **状态机回合仲裁**:适用于任何"人机轮替"类玩法(成语接龙、猜数字、五子棋、你问我答)。

未来规划:

- **多角色 AI 对手**:萌新 / 毒舌 / 国学大师三种人格,同一份协议、三种提示词;
- **难度体系**:由"首字提示"到"限时出牌"到"禁用高频成语";
- **方言/谐音模式**:把同音判定扩展为"方言谐音";
- **对战回放**:整局生成可分享的 Markdown 复盘;
- **接入更多模型**:模型名、温度、预算全参数可视化,支持在同一页面对比不同模型的表现。

### 十·一、给后来者的快速上手清单

如果你也想复刻一个类似的"AI 传统玩法"小项目,我建议按以下顺序推进,能少走弯路:

1. **先用官方示例跑通一次对话**,亲眼确认接口通、流式生效,再谈界面;
2. **先定 JSON 协议再写代码**:把 AI 要输出的字段结构画在纸上,前端渲染和提示词都围绕这份契约展开;
3. **提示词用"骨架式"不用"描述式"**,并准备好容错解析兜底;
4. **把确定性规则留在本地**,把语义判断交给模型,分工要清晰;
5. **尽早处理异常分支**(超时、401、429、解析失败),再做锦上添花的动效;
6. **写 README 时把部署安全写清楚**,哪怕项目只是自己玩的。

这份清单几乎可以无脑套用到"猜谜 AI""对联 AI""打油诗 AI"等一切同构需求上,这也是我认为本项目最有复用价值的部分。

## 十一、写在最后:在码道上跑出自己的版本

回看这次客制化之旅,最深的体会可以用三句话概括:

1. **参考代码是起点,不是终点。** 真正有价值的是读懂它之后,为自己的业务做出正确的取舍和改造。
2. **提示词就是接口。** 当大模型成为系统的一部分,系统提示词已经不再是"prompt 技巧",而是需要版本管理、回归测试的**工程产物**。
3. **纯前端有着惊人的交付力。** 在不需要账号体系的场景里,一个静态页面加一枚 API Key,就能交付一个完整可用的 AI 产品——这正是"客制化码道"最实用主义的浪漫。

项目完整源码、README 与本篇博客已一并提交至码道仓库 `gcw_8qEVCCPK/kezhihua`。欢迎 fork、star,也欢迎在 Issue 区聊聊你的"客制化"点子。

**码道漫漫,作品相见。**

---

## 附录

### 附录 A:完整 API 请求参数对照表

| Python 参数 | JS payload 字段 | 说明 |
| --- | --- | --- |
| `model` | `model` | `deepseek-ai/DeepSeek-V4-Flash` |
| `stream` | `stream: true` | 开启流式输出 |
| `max_tokens` | `max_tokens: 2048` | 输出长度上限 |
| `temperature` | `temperature: 0.6` | 随机性 |
| `top_p` | `top_p: 0.95` | 核采样 |
| `frequency_penalty` | `frequency_penalty: 0` | 频率惩罚 |
| `thinking_budget` | `thinking_budget: 2048` | 深度思考预算 |

### 附录 B:SSE 事件流示例

```text
data: {"id":"...","choices":[{"delta":{"role":"assistant","content":"{"}}]}
data: {"id":"...","choices":[{"delta":{"content":"\"word\""}}]}
data: {"id":"...","choices":[{"delta":{"content":":\"一马当先\","}}]}
data: {"id":"...","choices":[{"delta":{}}]}
data: [DONE]
```

### 附录 C:错误码速查

| 状态码 | 含义 | 前端提示 |
| --- | --- | --- |
| 401 / 403 | Key 无效 | "检查你的令牌配置" |
| 429 | 超频/额度 | "请求太频繁,稍后再试" |
| 5xx | 服务异常 | "服务暂时不可用" |
| 网络错误 | 断网/CORS | "无法连接服务器" |
| 超时(30s) | 流无响应 | "AI 走神了,已中止" |

---

*本文同步发表于码道社区。作者:码道开发者。如果你也在做"AI × 传统玩法"的客制化项目,欢迎交流。*

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐