TFT 智能助手进化史:当截图识别遇上 RAG 知识库
从零开始写一个 TFT 工具系列已经到第四篇了。我们有了数据采集器(
tft_data_manager.py)、阵容分析引擎(tft_converter.py)、截图识别器(tft_screen_capture.py),但它们都只解决了“看见”和“计算”的问题。玩家真正的痛点——“这阵容后期怎么补强?”“装备先给谁?”——需要的是游戏攻略知识。这一篇,我将用 RAG 技术补上这缺失的一环,让工具真正从“能用”变成“好用”。
一、为什么大模型需要“翻书”?
大语言模型如 ChatGPT 和 Claude 虽然知识广博,却存在两个显著短板:
- 知识时效性局限:模型训练数据存在截止日期,无法获取之后的新信息
- 虚构回答倾向:面对未知问题时,模型倾向于编造看似合理的答案而非承认无知
以校园问答场景为例:当新生询问"明天图书馆开放时间"时,未经辅助的大模型只能凭空猜测。但若系统能先查询学校官网的最新公告,再将准确信息交由模型整理回答,可靠性将显著提升。
这正是 RAG(检索增强生成)技术的核心理念:在生成回答前,先为模型检索并提供最相关的参考信息。
二、RAG 的工作流程,一张图看懂
一个典型的 RAG 流程分为两阶段:
离线阶段(准备知识库)
-
收集所有相关文档资源,包括网页内容、PDF文件和数据库记录等
-
将每份文档分割成500字左右的文本块
-
使用嵌入模型(例如OpenAI的text-embedding-3-small)将每个文本块转换为向量表示
-
将所有生成的向量存储至向量数据库(如FAISS、Chroma或Pinecone)中
在线阶段(回答问题)
-
用户提问:“明天图书馆几点开门?”
-
把问题也用同一个嵌入模型转成向量。
-
在向量数据库中检索最相似的几个文本块(Top-K)。
-
把这些文本块和用户问题一起拼成一个 prompt 发给大模型。
-
大模型根据参考资料生成最终答案。
直观比喻:大模型是个聪明但健忘的教授,RAG 就是他的助教,在回答问题前先去图书馆把相关书籍抱过来放桌上。
三、最小可运行示例:用 FAISS + OpenAI 搭建 RAG
下面我用 Python 写一个极简的 RAG 示例,不用复杂的类封装。我们会用到:
-
openai官方库(调用嵌入模型和对话模型) -
faiss-cpu(轻量级向量数据库) -
numpy(处理向量)
pip install openai faiss-cpu numpy
3.1 准备“知识库”
假设我们有三条校园公告,把它们切成短句作为文档块:
documents = [
"图书馆开放时间:周一至周五 8:00-22:00,周六日 9:00-18:00。",
"食堂一楼供应早餐 7:00-9:00,午餐 11:30-13:00,晚餐 17:30-19:00。",
"校园卡挂失请携带身份证到行政楼 102 办理,补办费用 20 元。",
"期末考试期间图书馆延长开放至 23:00(仅限工作日)。",
"校医院位于东区 5 号楼,急诊电话 6275-1234。"
]
3.2 把文档块转成向量(嵌入)
import openai
import numpy as np
openai.api_key = "your-api-key" # 替换成你的 key
def get_embedding(text):
response = openai.embeddings.create(
model="text-embedding-3-small",
input=text
)
return np.array(response.data[0].embedding, dtype=np.float32)
# 为每个文档块生成向量
doc_vectors = np.array([get_embedding(doc) for doc in documents])
print(f"向量维度:{doc_vectors.shape[1]}") # 输出:向量维度:1536
这里 text-embedding-3-small 会把任意文本映射成一个 1536 维的浮点数向量。语义相近的句子,它们的向量在空间中的距离会很近。
3.3 构建 FAISS 索引
import faiss
dim = doc_vectors.shape[1]
index = faiss.IndexFlatL2(dim) # 使用 L2 距离(欧氏距离)
index.add(doc_vectors)
print(f"索引中存储了 {index.ntotal} 条记录")
IndexFlatL2 是最简单的索引,它会暴力计算查询向量与所有存储向量的欧氏距离,并返回最近的 Top-K。
3.4 检索:找到最相关的文档
def retrieve(query, top_k=2):
query_vec = get_embedding(query).reshape(1, -1)
distances, indices = index.search(query_vec, top_k)
results = []
for i, idx in enumerate(indices[0]):
results.append({
"score": float(distances[0][i]),
"text": documents[idx]
})
return results
# 测试检索
results = retrieve("图书馆周末开到几点?")
for r in results:
print(f"[相似度得分: {r['score']:.2f}] {r['text']}")
你会看到第一条就是关于图书馆开放时间的文档,第二条可能是期末延长开放的信息,相关性很高。
3.5 生成:把检索结果喂给大模型
def ask(question):
# 1. 检索
retrieved_docs = retrieve(question, top_k=2)
context = "\n".join([doc["text"] for doc in retrieved_docs])
# 2. 构造 prompt
prompt = f"""你是一个校园助手,请根据以下参考资料回答用户的问题。
如果参考资料中没有明确答案,请如实说“未找到相关信息”,不要编造。
参考资料:
{context}
用户问题:{question}
回答:"""
# 3. 调用对话模型
response = openai.chat.completions.create(
model="gpt-4o-mini", # 或 gpt-3.5-turbo
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
# 测试
print(ask("图书馆周末开到几点?"))
print(ask("校医院电话是多少?"))
你会看到模型能准确回答,而且不会编造。如果问一个知识库中没有的问题(比如“游泳馆在哪里”),模型会按照 prompt 要求回答“未找到相关信息”,大大抑制了幻觉。
四、RAG 在 Agent 中如何发挥作用?
在现代AI Agent系统中,RAG通常作为众多工具之一被集成使用。以"校园万能助手Agent"为例,它能处理行政咨询、课表查询和图书借阅等多种需求。该Agent背后整合了多个功能模块:
- retrieve_campus_info(query):通过RAG检索校园知识库
- query_course_api(student_id):实时对接教务系统API
- search_library_catalog(book_name):查询图书馆馆藏信息
当用户提出复合型请求时(如"明天有课吗?顺便问下图书馆几点关门"),Agent会智能规划执行流程:首先调用课表API,随后通过RAG查询图书馆开放时间,最终将多源信息整合为自然流畅的回复。这种工作模式被称为ReAct(推理+行动)框架。
在这种架构中,RAG主要承担"静态知识检索"功能,特别适合处理规章制度、公告通知、操作手册等相对固定的文档类信息。
五、RAG 进阶:如果把它装进我们的 TFT 阵容助手
在探讨通用RAG技术的同时,我们始终要记得这个系列文章的最终目标:打造一个TFT智能阵容助手。虽然前文已经实现了英雄识别、羁绊计算和阵容报告生成等基础功能,但玩家真正需要的是更深度的游戏指导:
• 阵容过渡策略 • 四费核心英雄的装备优先级 • 针对特定阵容的克制方案
这些关键信息分散在版本更新公告、职业选手攻略以及各类数据统计中,仅靠大模型原有的知识储备远远不够。通过RAG技术,我们可以将这些外部知识整合到阵容助手中,使其回答从泛泛而谈升级为精准的赛季专属建议。
5.1 为 TFT 构建专属知识库
以 Set 16 为例,我们可以收集以下类型的文档:
documents = [
"Set16 羁绊速查:Bruiser 2/4/6 增加最大生命值;Rapidfire 2/4/6 增加攻速。",
"版本 16.10 改动:Aatrox 技能伤害从 300/450/700 削弱为 270/400/600。",
"热门阵容:6 Bruiser 搭配 Aatrox + Riven 主坦,后期补 Mordekaiser 补充伤害。",
"装备优先级:Aatrox 必带泰坦的坚决+饮血剑,第三件可选夜之锋刃或石像鬼石板甲。",
"克制关系:大量护盾阵容(如 Dragon 羁绊)可用破盾装备克制,或选择真实伤害单位。",
"经济运营:2-1 拉4,2-5 拉5,3-2 拉6,吃利息保血量。"
]
然后像之前那样,把所有文档块向量化存入 FAISS。当用户提问时,助手先去知识库里搜最相关的几条,再结合当前识别到的阵容一起回答。
5.2 混合检索:关键词 + 向量
TFT 知识库的一个显著特点是:许多问题需要精确的关键词匹配。例如当玩家询问"亚托克斯出什么装备"时,如果仅依赖向量检索,可能会遗漏包含"亚托克斯"但语义关联度稍弱的装备推荐,反而错误地将"锐雯"的相关内容作为相似答案(因为二者同属前排战士类型)。
这种情况下可以采用混合检索方案:首先使用 BM25(一种高效的关键词评分算法)快速筛选出包含"亚托克斯"的文档,再通过向量相似度进行语义补充,最终将两组结果合并排序。在 LangChain 或 LlamaIndex 框架中,实现这一流程仅需简单配置即可完成。
5.3 动态知识更新
云顶之弈每两周就会迎来一次版本更新,涉及英雄数值、羁绊效果乃至装备属性的调整。为确保知识库的时效性,我们可以开发一个自动化脚本,定期从CommunityDragon或主流攻略网站抓取最新数据,完成向量化处理后自动更新索引。结合tft_data_manager.py的数据获取功能,整个知识库能够在版本更新后立即完成同步升级。
通过这种机制,我们的TFT助手将实现质的飞跃——不再局限于阵容计算功能,而是进化为真正的"赛季教练":能够根据玩家当前的游戏截图,结合最新版本改动和攻略数据,提供精准的运营建议。
六、从图像识别到 RAG:殊途同归的工程思维
TFT 截图识别时,我整天在和像素、颜色直方图、NCC 分数打交道;切换到 RAG,又开始捣鼓向量数据库和提示词。表面上看,计算机视觉和自然语言处理是两个完全不同的方向,但等两边都碰过之后,我发现底层的思维方式惊人地一致:
-
特征提取:截图识别里,我用颜色直方图把头像抽象成 204 维向量;RAG 里,我用嵌入模型把文档块抽象成 1536 维向量。本质上都是在给数据“打数字指纹”,让计算机用数学方式度量相似度。
-
二阶段筛选:英雄匹配时我先用直方图粗筛 Top-5,再用 NCC 精匹配;RAG 里粗检索 Top-20,再用 Re-rank 精排 Top-3。管你是 CV 还是 NLP,处理大量候选时,这种“粗筛+精排”的架构是相通的。
-
阈值敏感:记得我为
MATCH_THRESHOLD写了整整一个calibrate函数吗?RAG 里同样有相似度阈值的烦恼——检索时到底取几个文档?得分低于多少的文档应该丢弃?调参的哲学在两边都适用。
这些共同点让我悟出一个道理:AI 应用开发的核心不是死磕某个领域的黑魔法,而是把通用的工程模式(缓存、索引、评分、迭代优化)迁移到不同场景里。我现在看问题,不会再说“这是 CV 的问题”或“这是 NLP 的问题”,而是把它拆成“信息提取 → 特征化 → 相似度匹配 → 后处理”这种跨领域的流水线。也算是“看山不是山”了。
未来的 TFT 助手:视觉 + 语言双引擎
当截图识别与 RAG 技术完美结合,一个功能完备的 TFT Agent 已初具雏形:
- 用户上传游戏截图 → 系统自动识别当前阵容
- 结合 RAG 检索的版本攻略 → 智能提示:"当前阵容缺少关键羁绊,建议升8人口补上 Mordekaiser"
- 战绩分析功能 → 通过复盘截图,精准指出装备分配问题
作为一名学生开发者,看着自己开发的工具从单一功能逐步进化为多模态智能体,这种成长过程令人振奋。我始终相信,为自己打造实用工具,正是保持编程热情的最佳动力。这个助手项目我会持续优化完善,也欢迎大家在评论区分享宝贵建议。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)