TFT阵容顾问(AI Agent)第二章之多源格式转换器篇
本文介绍了如何将云顶之弈数据词典转化为实用的阵容分析工具。通过tft_converter.py实现四种输入格式(官方JSON、截图、文本、手动JSON)的统一处理,输出包含英雄详情、羁绊计算、阵容摘要和装备问题的标准化分析报告。核心功能包括ID规范化、数据库懒加载、羁绊计算和装备检测,并设计了命令行调用接口。这个600行代码的工具将原始数据转化为可直接使用的分析助手,展示了"为自己写工具
前言
上一篇我们拿到了云顶之弈的完整数据词典,但这只是第一步。真正有用的工具,是能把你手上任何形式的阵容一键转换成清晰的分析报告。
一、从“有了数据”到“能用起来”
上一篇文章里,我写了一个 tft_data_manager.py,可以从官方接口拉取最新赛季的英雄、羁绊、装备数据,生成本地 JSON 词典。有了这些原材料之后,下一个问题就是:怎么用这些数据解决实际问题?
目前云顶之弈玩家需求背后其实是一个共同的模式:
输入 = 英雄列表,输出 = 羁绊计算 + 阵容摘要 + 装备问题提示。
于是,我动手写了 tft_converter.py,一个能自动识别输入格式、输出标准化分析结果的转换器。这篇博客就来拆解它的设计思路和实现细节,并补充完整的命令行入口,让你可以像使用普通工具一样直接运行它。
二、转换器能做什么?—— 输入输出一览
tft_converter.py 支持四种输入格式:
| 输入类型 | 举例 | 处理方式 |
|---|---|---|
| Riot 官方对局 JSON | 从 Riot API 拿到的 participant.units |
直接解析,提取英雄、星级、装备、站位 |
| 截图识别结果 | tft_screen_capture 模块的输出 |
委托给专门的识别模块,补全 cost |
| 自由文本 | "Aatrox, Riven, Garen" |
正则匹配单词,在本地数据库里查找匹配英雄 |
| 手动 JSON 字符串 | '[{"name":"Aatrox"}]' |
按 Riot JSON 逻辑解析 |
无论输入是什么,最终都会输出一个统一结构的 JSON 对象:
{
"team_size": 8,
"champions": [
{
"id": "TFT16_Aatrox",
"short_id": "Aatrox",
"name_en": "Aatrox",
"star": 2,
"cost": 4,
"items": ["TFT_Item_Bloodthirster"],
"position": {"row": 3, "col": 2}
}
],
"traits": [
{
"id": "TFT16_Bruiser",
"short_id": "Bruiser",
"name_en": "Bruiser",
"count": 4,
"level": 4,
"level_name": "Gold",
"thresholds": [2, 4, 6]
}
],
"summary": {
"front_row_ratio": "3/8",
"main_carry": "Aatrox",
"equipment_ok": false,
"total_items": 5,
"champion_count": 8
},
"equipment_issues": [
"Aatrox(费用≥4) 无装备",
"TFT_Item_GuinsoosRageblade 重复装备 x2"
],
"_source": "riot_json"
}
这个结构非常清晰:英雄详情、激活的羁绊、阵容摘要、潜在问题列表,以及数据来源标记。
三、核心组件逐一拆解
1. ID 规范化 —— 让不同来源的命名都能对上号
来自 Riot API 的英雄 ID 可能是 TFT16_Aatrox,而玩家手打的文本可能是 Aatrox,截图识别出来的也可能是 aatrox。要让它们能在同一个数据库里查询,必须先统一格式。
def normalize_champ_id(raw: str, set_num: int = 16) -> str:
if re.match(rf"^TFT{set_num}_", raw):
return raw
clean = strip_prefix(raw)
return f"TFT{set_num}_{clean}"
strip_prefix函数的作用是移除各类前缀(如TFT16_、TFTSet16_、TFT_Item_等),然后统一添加标准前缀TFT{set_num}_。装备的处理方式类似,只是其标准前缀固定为TFT_Item_。
这种规范化处理的必要性在于:只有统一格式的ID才能准确匹配本地数据库(由tft_data_manager.py生成)中的条目键值。
2. 数据库懒加载 —— 只读一次,全局复用
为了提高性能,转换器在首次加载时将英雄费用、羁绊列表和装备信息缓存为模块级全局变量,采用懒加载模式避免重复读取JSON文件。
_champion_db: Optional[Dict] = None
_trait_db: Optional[Dict] = None
_item_db: Optional[Dict] = None
_trait_dict: Optional[Dict] = None
def _load_db():
global _champion_db, _trait_db, _item_db, _trait_dict
if _champion_db is not None:
return
_champion_db = _read("tft_champion_db.json")
# ...
第一次调用 _load_db() 时,才会去读取四个 JSON 文件并缓存到全局变量中。后续所有查询都直接从内存拿,效率很高。
学生笔记:这种“懒加载”模式在实际项目中非常常见,特别是当数据文件比较大、或者不一定每次都会用到的时候。它兼顾了启动速度和资源占用。
3. 羁绊计算 —— 从英雄列表到激活的羁绊
讲解核心函数函数 calc_traits(champions, set_num)
步骤 1:统计每个羁绊有多少个英雄
trait_counts: Dict[str, int] = {}
for champ in champions:
db_entry = _champion_db.get(champ_id, {})
for t in db_entry.get("traits", []):
trait_counts[t] = trait_counts.get(t, 0) + 1
这里直接用了上一篇文章生成的 tft_champion_db.json 里的 traits 字段(我们已经预先把它处理成了 short_id 列表)。
步骤 2:为每个羁绊查找激活等级
然后给每个已激活的羁绊打上一个等级标签(Bronze / Silver / Gold / Prismatic),方便阅读。
for short_id, count in trait_counts.items():
# 在 _trait_db 或 _trait_dict 中找到对应的羁绊数据
# 获取 levels 数组,例如 [2, 4, 6]
for lvl in sorted(levels):
if count >= lvl:
active_level = lvl
最终按照激活等级降序、人数降序排列,这样最重要的羁绊会排在前面。
4. 摘要与装备问题检测 —— 给阵容一个“体检报告”
build_summary 函数负责生成人类友好的摘要和装备警告。我设计了几个简单但实用的检查项:
前后排比例
根据站位 position.row 来判断(行号 ≥ 3 算前排),输出如 "3/8" 的字符串。这个小细节对于评估阵容坦度很有帮助。
主C识别
遍历所有英雄,找出有装备且费用最高的那一个作为“主C”。虽然逻辑简单,但在大多数情况下是准确的。
装备问题检查
这里有两个规则:
-
高费英雄无装备警告:费用 ≥ 4 却没有装备 → 可能浪费了输出位。
-
重复装备警告:除了少数可叠加装备(如泰坦的坚决),其他装备重复出现就提示,因为它们的唯一被动会浪费。
STACKABLE = {"TFT_Item_TitansResolve", "TFT_Item_BlueBuff", "TFT_Item_Morellonomicon"}
小结
到这一步,我们已经完成了转换器的“智慧内核”:只要喂给它一个格式标准的英雄列表,它就能:
-
统计羁绊数量并判断激活等级;
-
生成阵容摘要(前后排比例、主 C 是谁);
-
列出潜在的装备搭配问题。
就功能而言,它已经是一个完整的阵容分析引擎了。无论输入数据来自哪里,最终都会落到这个引擎里进行计算。
那么问题来了——谁会愿意每次手动把英雄整理成干净的标准化列表再输入呢?这很难长官
在下一篇文章中,我们将给这个引擎装上“眼睛和耳朵”:让它能自动识别来自 Riot API 的对局 JSON、截图识别模块的结果,甚至是你随手在命令行里瞎打的几个英雄英文名。我们还会把它包装成一个像模像样的命令行工具,让你在终端里敲一行命令就能立刻得到完整的阵容分析报告。
更多推荐




所有评论(0)