自然语言配表 3.0:从“能用“到“能发“再到“能扛“

自然语言配表 3.0:从“能用“到“能发“再到“能扛“svg xmlns http www w3 org 2000 svg style display none svg

大家好,我是讯享网,很高兴认识大家。这里提供最前沿的Ai技术和互联网信息。



 
  
    
     
      
     
  
    
    

AI 辅助游戏开发 · 工具部署优化 · 集卡配置 · 工程化治理

导语 上回分享了 2.0 版本——用专用模式解决高复杂度场景,四表联动一次成功率大幅提升。但实际推给团队其他策划时发现,本地 embedding 模型太重(476MB 模型 + 2GB torch),分发成本比功能开发还痛苦。拿分发问题来说:策划的开发机没有 Python 环境,同步 2.5GB 依赖链也不现实。所以 3.0 第一刀砍向部署:把 embedding 迁移到内网 API,策划端体积从 2.5GB 降到 80MB。部署问题解决后,又接了集卡玩法的配表需求——6 张表、严格的 ID 层级编码、字段别名漂移风险——逼出了第二个专用流程 CardCollectionConfigGenerator。最后对 4855 行的 data_generator.py 做了模块拆分,拆成 8 个独立文件,补齐了增量构建、统一错误码、回归测试等工程化护栏。代码量从 4,500 → 9,400 行(拆分后),新增 4 个独立模块和 2 个回归脚本。下面是完整的技术方案和踩坑记录。


问题链

  1. 想让其他策划用这个工具
  2. 工具依赖本地 sentence-transformers 模型(476MB)
  3. sentence-transformers 依赖 torch(~2GB)
  4. 策划的开发机不一定有 Python 环境,更别说装 torch
  5. 即使通过 SVN 分发模型文件,pip install 依赖链也很痛苦

解决思路

公司内网有 AI 网关,提供 OpenAI 兼容的 API。实测发现它支持 /v1/embeddings 端点(文档里没写,但实际可用),可以完全替代本地模型。

迁移后的依赖变化:

依赖 迁移前 迁移后 sentence-transformers 需要(476MB 模型) ❌ 移除 torch 需要(~2GB) ❌ 移除 model_files/ 目录 需要 ❌ 移除 网络访问 仅 LLM 调用 LLM + Embedding API chromadb 需要 需要(不变)

2.1 适配器模式

核心设计:写一个 MomiEmbeddingFunction 类实现 ChromaDB 的 EmbeddingFunction 接口,内部通过 HTTP 调用内网 API。

GPT plus 代充 只需 145class MomiEmbeddingFunction(EmbeddingFunction): def __call__(self, input: Documents) -> Embeddings: # 分批调用 /v1/embeddings # 每批 128 条,批次间隔 7 秒 # 429 限流时线性退避重试 

这样对上层代码完全透明——vector_store_builder.pyrag_query.pydata_generator.py 只需要把 import 换一下。

2.2 涉及的文件改动(6 个文件)

文件 改动 momi_embedding.py 🆕 新建,API Embedding 适配器 vector_store_builder.py 替换 import + 预计算架构 rag_query.py 替换 import + 构造函数签名 data_generator.py 构建 embed_cfg dict web_ui.py 两处索引构建代码的配置注入 main.py 配置注入 config.json 新增 embedding 配置块 requirements.txt 移除 sentence-transformers

坑 1:API 文档没提 Embedding 端点

现象:AI 接入手册只写了文本生成 API,没提 embedding。

解决:直接试 → 成功了。测试了 3 个模型都可用(ada-002/3-small/3-large)。

教训:API 文档不全时,直接试比猜靠谱。

坑 2:429 频率限制密集轰炸(最大的坑)

经历了 4 轮参数调优。

根因:从 API 响应头发现精确限流规则:

X-RateLimit-Limit: 10 # 10 次请求/分钟 x-ratelimit-limit-tokens:  # 35 万 tokens/分钟 

10 RPM 意味着每分钟最多 10 个请求。

调优过程

尝试 batch_size delay 结果 第 1 轮 64 0s 密集 429,崩溃 第 2 轮 32 0.5s 仍然频繁 429 第 3 轮 16 2.0s 偶尔 429,退避策略有问题 第 4 轮 128 7.0s ✅ 零 429,14.9 分钟完成

关键洞察:限流按请求次数计,不按 batch_size 计。所以应该大 batch + 长间隔

坑 3:ChromaDB 自动调用 vs 预计算架构

现象:让 ChromaDB 在 upsert 时自动调用 embedding 函数,无法精确控制 API 节奏。

解决:改为预计算 —— 先统一计算所有 9100 条的向量,再 upsert(..., embeddings=vectors) 直接写入。

效果:embedding 阶段完全可控(871.7s),写入阶段极快(18.2s)。

坑 4:退避策略选择

指数退避 3^attempt(3, 9, 27, 81, 243s)对已知的固定速率限制太激进。改为线性退避 10 * attempt(10, 20, 30s),增长平缓且够用。


重建索引数据

指标 值 总文档数 9,100(849 schema + 8,251 sample) 向量维度 1536(text-embedding-3-small) 总耗时 14.9 分钟 429 错误次数 0

分发体积变化

组件 迁移前 迁移后 模型 + torch ~2.5 GB ❌ 移除 momi_embedding.py — 4 KB chroma_db/ ~50 MB ~80 MB 策划端需下载 ~2.5 GB ~80 MB

1. API 限流规则要从响应头实测,不能猜。

文档没写,但 X-RateLimit-Limit 响应头明确告诉你 10 RPM。所有调优从这个数字出发。

2. 大批量 API 调用要用预计算架构。

不要让框架自动调用 API,先统一预计算,再批量写入。API 节奏完全可控。

3. 已知 RPM 限制下,大 batch + 长间隔 > 小 batch + 短间隔。

限流按请求次数算。token 额度充裕时,batch_size 越大越好。

4. 指数退避不适合固定速率限制。

已知窗口大小时,线性退避更合理。


方向 说明 状态 集卡配置专用流程 卡册+卡集+卡片+物品+奖励五表联动 ✅ 全项目 Review 字段一致性、链路稳定性、架构去重 ✅ data_generator 拆分 4855 行单文件 → 8 个职责模块 ✅ 构建机流水线 定期重建向量索引 → 分发 🟡 待做

背景

集卡玩法需要同时生成 6 张表(卡册、卡集、卡片、物品、奖励映射、奖励包),且表间有严密的 ID 层级依赖,完全靠通用模式无法稳定产出。

ID 层级设计

集卡 ID 是有语义的编码:

GPT plus 代充 只需 145卡册ID: 1001 (生活集卡前缀10 × 100 + 序号) 卡集ID:  (卡册ID × 100 + 卡集序号) 卡片ID:  (卡集ID × 100 + 卡片序号) 金钻奖励: 91001 (90000 + 卡册ID) 

ID 编码包含了父子关系,根据 ID 可以反推所属卡册/卡集,不需要额外字段存储层级关系。

核心实现

CardCollectionConfigGenerator.generate() 的 6 步流程:

  1. 解析参数(类型/数量,来自配置面板或自然语言)
  2. 按层级规则批量分配全部 ID
  3. LLM 生成卡册+卡集+卡片(创意字段:名称、描述、图标引用)
  4. 代码构造卡片物品(字段严格对齐 item_base_data.schema.json
  5. 代码构造奖励数据(双表联动,不调 LLM)
  6. Schema 校验 + 引用闭环校验 + 写入

步骤 4、5 完全不调 LLM——物品和奖励的业务规则是确定的。

关键设计决策:只引用不新增

card_album_sorting_data(卡册分类表)是存量数据,新卡册只能引用其中 3 个有效 ID,不允许新增行。这个约束被固化在流程规则文件和 Skill 文档中。

踩坑:字段别名漂移

现象:物品构造函数输出旧字段风格(Name/Icon/Rarity),实际表已迁移到新字段(name/icon_reference/quality)。写入后大量字段对不上。

根因:函数参照旧代码写,没有对照实际 schema 文件。

修复:所有字段改为从实际 schema 文件取名,并在 card_schema_guard.py 中加断言防止再次漂移。

原则:字段名以 output/*.schema.json 为唯一权威来源。

踩坑:双表奖励链断裂

现象card_reward_data.Reward 引用了 reward_data.ID,但 reward_data 从未被生成。

修复:新增 _generate_reward_base_data(),根据奖励映射表中出现的全部 Reward 值,反向构造 reward_data 行,确保引用链闭合。流程中加入引用闭环校验。

踩坑:auto_write 未生效

集卡流程末尾直接 return result,写入分支从未执行。补齐 should_write 判断 + 多表顺序写入逻辑。

测试与护栏

  • card_schema_guard.py:静态检查关键字段集合与别名禁用规则
  • run_card_regression_tests.py:2 个端到端集卡回归用例,回归结果 2/2 PASS

对整个项目做系统性 review,扫描字段一致性、生成链路稳定性、质量/测试覆盖、性能/安全四个维度。

主要修复项

P0(影响正确性): item_base_data 字段漂移、auto_write 未生效、card_reward_data → reward_data 断链 —— 见上节集卡部分。

P1(影响可维护性):

main.pyweb_ui.py 各有一份约 400 行的构建逻辑副本。提炼为 index_build_service.py 公共服务:

def build_index(config, config_dir, force_rebuild=False, emit=None) -> dict: ... 

emit 回调让同一份逻辑在 CLI(print)和 Web(SSE 队列)两种场景下复用,完全解耦。

向量增量构建: 旧方案每次全量重建(14 分钟起)。新方案在向量库目录写 .schema_build_state.json 记录每个 schema 文件的 MD5,下次只处理变更文件。未变更时整个构建从 14 分钟降到秒级。

Web API 错误码统一: 旧方案 {"error": "str(e)"} 前端无法精确处理。新方案 {"success": false, "error": {"code": "BUILD_FAILED", "message": "..."}} 前端用 error.code 做精确分支。

P2(提质量): 新建 run_card_regression_tests.py(集卡 E2E 回归)和 card_schema_guard.py(Schema 快照守卫)。


问题

data_generator.py 积累到 4855 行,包含 8 个差异极大的类,每次修改都要在巨型文件里定位,维护成本高。

拆分结果

新文件 职责 行数 dg_logger.py 日志配置 + Prompt 配置读取 ~90 dg_validator.py 数据校验器 ~160 dg_llm_client.py LLM 客户端 + Prompt 构造工具 ~185 dg_core.py 核心单表生成器 ~340 dg_multi.py 跨表联动生成器 ~574 dg_flow.py 流程规则加载器 + 流程分类器 ~280 dg_mission.py 任务配置流程生成器 ~1553 dg_card.py 集卡配置流程生成器 ~1064 data_generator.py 聚合 re-export 入口(向后兼容) ~110

data_generator.py 改为纯 re-export,外部所有 from data_generator import … 的代码一行都不用改

踩坑:TYPE_CHECKING 陷阱

为避免循环导入,子模块用了 TYPE_CHECKING 块做类型注解:

GPT plus 代充 只需 145if TYPE_CHECKING:

from dg_core import DataGenerator 

但代码里有一处直接调用了 DataGenerator._parse_user_ids()(静态方法),运行时 DataGenerator 不存在,报 NameError

修复:在调用点局部导入:

GPT plus 代充 只需 145from dg_core import DataGenerator as _DG user_ids = _DG._parse_user_ids(user_input) 

教训TYPE_CHECKING 只适合纯注解。运行时真正调用类的地方,必须用真实 import 或局部导入。

验证

全部 8 个子模块 py_compile 通过,导入链完整,集卡回归 22 PASS

小讯
上一篇 2026-03-27 11:04
下一篇 2026-03-27 11:02

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/249281.html