球员F在第38分钟传球,球员G接球射门得分。这一幕能不能变成知识图谱里的一条边:球员F → 助攻 → 球员G?能,而且看上去很自然。可是如果整个赛季有几百场比赛、每场若干次助攻,图里就会多出成千上万条同类的边,每条边上又要挂比赛、时间、比分状态、传球方式……到这个程度,它已经不像一条“关系”,更像一次“事件”。要不要把每一次助攻都放进知识图谱,答案取决于一件事:你以后打算用图来问什么问题。
两类东西:长期关系与比赛事件
先把图谱里可能存放的信息分成两类,界线其实相当清楚。
| 长期关系 | 比赛事件 | |
|---|---|---|
| 例子 | 球员属于球队、教练执教球队、球队参加联赛 | 一次助攻、一次射门、一次换人、一张黄牌 |
| 持续时间 | 数周到数年,有起止区间 | 瞬间或几秒钟,只有发生时刻 |
| 数量级 | 与球员、球队数量同级 | 与比赛数量成倍相关,增长很快 |
| 典型问题 | 他曾属于哪些球队,谁与谁执教过同一支队 | 他与谁的配合次数最多,比分落后时谁组织进攻 |
| 变更方式 | 会被更新、结束、修正 | 发生后基本不变,只可能被更正 |
这个区分并不是概念游戏。长期关系是图谱的骨架,用少量的边描述“谁和谁在什么时期有什么联系”;比赛事件则是流水,它天然更适合放进按时间排序的事件表。把两者用同样的方式一股脑塞进图里,图会迅速膨胀,而其中大多数边一辈子也不会被问到。
先问三个问题,再决定要不要节点化
半岛体育在设计中,对“某类事件要不要进入图谱”用三个问题来判断,顺序不能反。
问题一:这类问题必须走多跳吗?
如果问题是“球员G本赛季一共有多少次助攻”,它是纯粹的计数和聚合,一张事件表加一条 SQL 就够,放进图里没有任何收益。但如果问题是“球员G的助攻来源里,有多少人曾经和他在同一支球队效力过”,就同时涉及事件(助攻)和长期关系(曾同队),这时事件和关系需要连起来问,图的优势才显现。
问题二:事件本身有没有需要继续描述的属性?
把助攻画成一条边,边上能挂的属性有限。如果还想记录这次助攻“发生在比分落后时”“来自角球”“接球后又做了一次盘带”,这些信息会让边变得越来越像一个独立的对象。此时就该把它提升为事件节点:一个“助攻事件”节点,分别连向传球者、射门者、比赛,并带着时间点和情境字段。节点化的意义是让事件本身也能作为多个关系的连接点。
问题三:值得承受存储与维护代价吗?
事件节点化的代价很实际:节点与边的数量按比赛场数放大,写入要保证与比赛记录一致,查询规划要避免把大量无关事件扫出来。如果前两个问题的答案都是“否”,就不必节点化,保持在事件表里,需要的时候通过球员编号、比赛编号与图谱关联即可。
一个示例:配合关系该怎么查
考虑一个具体问题(示例):“示例队甲本赛季,球员F给谁的助攻最多?这几个人里,谁是他上赛季在别的球队时就认识的?”
前半句可以用事件表直接算:
SELECT scorer_id, COUNT(*) AS assists_to
FROM goal_events
WHERE assist_player_id = 'P_0710'
AND season = '示例赛季'
AND team_id = 'T_0031'
GROUP BY scorer_id
ORDER BY assists_to DESC
LIMIT 5;
后半句“上赛季在别的球队时就认识”则涉及关系:这几个人与球员F在上赛季是否有过同队归属,且区间重叠。这一步交给图谱,用归属关系的有效区间判断。两个查询的结果拼在一起,才是完整答案。这里事件表负责“数出来”,图谱负责“连起来”,二者分工,而不是把所有事件都变成图里的边。
如果这类“配合网络”的问题成为高频需求,比如需要经常查“两名球员之间的传球—射门链条”,才有理由把助攻事件节点化。此时的判断依据是查询量与查询形态,而不是“图谱里东西越多越先进”的直觉。
知识图谱越大越好吗?关系质量比节点数量重要
说到存储代价,不得不谈一个常见误区:以为图谱规模是衡量水平的指标,节点几百万、边几千万,听起来就很强。但对于回答体育问题而言,图谱真正的价值来自关系的质量,而不是数量。质量至少体现在四个方面:
- 准确性:每条关系是否真实存在。一条错误的“教练执教球队”边,会让所有依赖它的多跳查询一起出错,而且错得很隐蔽。
- 时间完整性:关系是否带有效区间。缺少时间的“曾效力于”只能回答有没有,无法回答什么时候。这一点在时间知识图谱那篇里有专门讨论。
- 可追溯性:每条关系有没有来源标识,出问题时能不能回溯到原始记录。
- 语义一致性:同一种关系是不是始终用同一个定义。比如“助攻”的判定在不同数据来源里并不一致,混进去就会出现相同关系名、不同含义的边。
因此图谱需要一套关系校验,而不是一味地追加。比较实用的校验包括:同一球员同一时期不能同时是两支同联赛球队的正式成员;一场比赛里助攻者与射门者必须是同一支球队的登记球员;事件发生时间必须落在比赛的起止时间之内。这些规则不复杂,却能挡掉相当一部分脏数据。
需要强调,知识图谱和大模型结合并不会自动让答案变可靠,图谱本身的质量决定了这个结合的上限。半岛体育对图谱的态度是:宁可先把少数核心关系做得可查、可校验、有时间,再逐步扩展,也不愿意用大量来源不明的边把图撑大。
事件节点化的三个实用规则
如果决定要把某类事件放进图谱,有几条经验规则能减少后悔的机会。
- 只节点化高价值事件。进球、助攻、红黄牌、换人、伤停,这些事件数量有限、语义清晰;而每一次传球、每一次触球,数量太大,通常放在事件表或特征表里更合适。
- 事件节点要保留回指。每个事件节点都携带比赛编号和数据源事件编号,方便和原始事件表对齐、校验、重建。
- 分层存放,按需连接。长期关系放在图谱主体里,事件放在旁边的事件层,问题需要时才把两者连起来。这样图谱主体保持精简,事件层可以按赛季分区、按需归档。
第二条尤其重要。事件节点如果和原始记录脱节,一旦数据源修订了某次助攻的归属,图里的旧节点就会变成没人能解释的“孤儿”。回指字段让修订可以同步,也让答案能回答“这个数是哪些事件加起来的”。这也呼应了半岛体育在答案溯源里提到的证据链设想。
用一笔粗账看存储代价
不妨用一个纯粹假设的数字算一下,只为说明量级关系,并非任何真实数据。假设示例联赛一个赛季有300场比赛,每场平均记录12次进球助攻事件,那么一个赛季大约3600个助攻事件。如果每个事件节点连三条边(传球者、射门者、比赛),就是3600个节点加约1万条边,这还只是助攻。若再把射门、犯规、换人一并节点化,很快会变成十几万级别的事件节点;若继续下沉到每一次传球,规模会再放大几个数量级。而同一个赛季的球员与球队归属关系,可能只有几百条。
这笔账说明一件事:事件的数量与比赛场次成正比,而长期关系的数量与球员数量成正比,两者的增长速度完全不同。把两类东西放在同一个图里,图的绝大部分体积都会被事件占据,而多数长期关系的查询却要在这片体积里穿行。分层存放的好处正在这里:主体图谱小而稳定,查询规划器可以先在长期关系里缩小范围,再去事件层里精确取数。
与存储相比,更容易被低估的是维护成本。事件一旦节点化,就意味着每个事件都需要通过校验:所属比赛存在吗,两名球员当时在场吗,时间点合理吗。事件越多,校验和修订的工作量越大。每增加一类事件进图谱,都等于给数据质量增加一笔长期开销,这也是前面强调“先问三个问题”的原因。
图不是越细越好,是越准、越可查越好
回到开头:一次助攻值不值得放进图谱?大多数情况下,单次助攻更适合待在事件表里,与球员、比赛、球队用编号相连;当“配合链条”“情境关联”成为反复出现的问题,才有必要把它升格为节点。而图谱主体,应该留给那些寿命长、有区间、被多跳问题反复用到的关系。
知识图谱最容易被做成“把一切都连起来”,但真正好用的图谱往往是克制的:每一类节点和边都能说出它服务于哪一类问题,每一条关系都能说出它的来源和有效期。至于图谱与其他检索方式如何配合回答体育问题,可以参考半岛体育GraphRAG相关的讨论。