“这个赛季谁进球最多?”这个问题只需要一次排序:按进球数降序,取第一名,遇到并列再补一个规则。可换成“谁对球队进攻最重要?”,同样是一句中文短问句,难度却突然翻了几个量级。原因不在于语法更复杂,而在于“最重要”这个词里根本没有可以直接排序的字段。搜索引擎可以把“进球最多”当成关键词处理,却无法把“最重要”当成关键词处理,因为它背后是一个必须先被定义、才能被计算的概念。半岛体育AI在设计体育搜索时,把这一类问题单独拿出来处理,核心原则只有一句话:系统可以帮用户把主观词变成指标,但不能偷偷做,而且要让用户看见它选了什么。

排序问题、定义问题、无解问题

体育搜索里的“最”字问句,大致可以分成三种。分清它们,是后面所有设计的起点。

  1. 排序问题:指标已经存在于数据库里,例如进球、助攻、场均得分。系统只要选对字段、时间范围和过滤条件,排序就是确定的。“谁进球最多”“谁场均助攻最高”都属于这一类。
  2. 定义问题:词语是一个综合概念,例如重要、稳定、有威胁、进攻贡献大。它不对应任何单一字段,需要先决定用哪些字段、怎样组合,才能排序。“谁对球队进攻最重要”属于这一类。
  3. 无解问题:所需信息数据库里根本没有,例如“谁最有领袖气质”“谁最让对手害怕”。这类问题可以引用文字资料给出讨论,但不应该假装能用数字回答。

可靠的搜索系统首先要做的是分类,而不是急着回答。把定义问题误判成排序问题,得到的是一个看似确定、实则任意的名单;把它误判成无解问题,则白白放弃了本来可以给用户提供的有价值的分析。在半岛体育AI搜索的设计里,这一步分类发生在实体识别与时间解析之后、生成查询之前,因为只有知道问题属于哪一类,后面才知道该去调用哪种查询。

“重要”这个词,至少可以指七件事

回到“谁对球队进攻最重要”。把它落到一支足球示例球队上,“重要”至少可以有下面七种读法,每一种对应不同的数据字段。

  • 直接产出:进球、预期进球(xG)的累计值,对应 goals、xg。
  • 创造机会:助攻、关键传球、预期助攻,对应 assists、key_passes、xa。
  • 推进球权:带球推进、向前传球把球送入进攻三区的次数,对应 progressive_carries、progressive_passes。
  • 出场时间:一名球员是否长期在场,往往决定了他产出的机会,对应 minutes。
  • 参与率:球队进攻过程中,球员参与了多大比例的射门链,对应 shot_chain_involvement。
  • 战术角色:他是终结者、组织者还是牵扯防守的诱饵,这类信息往往来自位置数据或标注。
  • 缺阵影响:他不在场时,球队的进攻数据会下降多少。这需要做对比,而不是读一个字段。

这七种读法互相并不一致。一名主要任务是拉开空间的边锋,进球数可能不高,但参与率与推进数据出色;一名站在禁区里的中锋,进球与预期进球领先,但组织数据平平。任何一个固定公式都会偏向某一种球员,而“重要”偏偏是一个允许多种答案的词。所以问题的关键不在于找到“唯一正确的公式”,而在于让用户知道当前答案是按哪几个维度得出的。

三种“偷偷定义”,为什么都不可取

面对这类问题,一个只想尽快给出答案的系统,最容易走进下面三条捷径。

捷径一:让大模型直接“凭感觉”排名

语言模型可以立刻给出一份“最重要的三名球员”,并附上颇具说服力的理由。但这些理由来自训练语料里的叙述习惯,而不是当前数据库里的数值。用户无法知道排名是否基于本赛季,也无法检验说法是否站得住。这是最不可取的一种,因为它同时丢失了数据依据和口径说明。

捷径二:悄悄套一个固定权重公式

有的系统会在后台放一个公式,比如“重要性等于进球乘0.4加助攻乘0.3加关键传球乘0.3”。公式本身并非不能用,问题在于用户看不到它。同一份数据换一组权重就可能换一个第一名,用户却以为自己看到的是“客观结果”。权重是一种价值判断,隐藏价值判断比价值判断本身更危险。

捷径三:把定义问题降级成最容易的排序问题

最省事的办法是把“最重要”悄悄替换成“进球最多”,然后按进球排序。这样一来,系统总能给出一个明确答案,代价是问题被改写了:一名出场时间很长、创造了大量机会却很少射门的球员,会在这个榜单里消失。

三条捷径有一个共同点:它们都把“如何定义”这一步藏了起来。半岛体育的设计取向正相反,是把这一步摊开,放到答案的最前面,而不是放到某个用户永远不会点开的说明里。

口径卡片:把定义写在答案旁边

为了让这一步显式化,可以设计一种“口径卡片”,在回答带有主观词的问题时,先把系统采用的定义摊开,再展示结果。卡片里应当包含下面这些信息。

卡片字段 示例内容 作用
问题类型 定义问题:进攻重要性 告诉用户这不是单字段排序
采用的维度 进球、助攻、关键传球、推进传球、出场时间 让用户知道“重要”由什么构成
组合方式 各维度按每90分钟折算后分别排名,取名次平均 避免把不同量纲直接相加
时间范围与样本 本赛季,出场不少于900分钟 过滤掉样本太小的球员
比较对象 同一球队的前场球员避免和不同位置比较
可调整项可去掉出场时间、可只看助攻类 把选择权交还给用户

这张卡片有两层含义。第一,它是给用户看的:用户可以判断这个定义是否符合自己心里的“重要”,不符合就点开某个维度,换成自己想要的。第二,它也是给系统自己看的:口径被写成结构化数据之后,同一份定义可以被重复使用、被记录、被复查,不必每次让语言模型重新“想一遍”。这一点和给答案加证据链是同一条思路。

候选维度从哪来:指标注册表,而不是模型的想象

口径卡片里的“采用的维度”不能由语言模型随口报出,否则它会列出数据库里根本不存在的字段。可行的做法是维护一张指标注册表,把每一个可用的指标登记下来,写清楚它的名字、别名、所在数据表、计算方式、适用位置和已知限制。示例结构如下。

metric_id中文名与别名 来源字段 适用位置已知限制
goals_p90每90分钟进球,“射手”“得分能力” goals, minutes前锋、边锋、前腰 小样本波动大
key_passes_p90 每90分钟关键传球,“创造力” key_passes, minutes 中场、边锋依赖数据源对关键传球的定义
prog_passes_p90 每90分钟推进传球,“推进” progressive_passes, minutes 中场、后卫 不同来源阈值不一致
on_off_diff 在场与不在场的进攻差,“缺阵影响” 球队比赛级数据加出场时间片全部 混杂因素多,只能提示相关

有了这张表,处理“谁对球队进攻最重要”的过程就变成了:语言模型识别出这是“进攻重要性”的定义问题,然后在注册表里检索与“进攻”相关的候选指标,按适用位置过滤,交给用户确认或采用默认组合。模型的作用是在已有指标里做选择,而不是发明指标。如果用户提到的概念在注册表里查不到,比如“压迫强度”,系统应当直接告诉用户当前没有对应指标,而不是拿一个相近的字段冒充。这样,哪些问题能答、哪些暂时不能答,边界是清楚的。

对于第三类“无解问题”,情况类似。“谁最有领袖气质”在指标注册表里没有对应项,系统的正确反应是切换到文本检索:引用已有的报道或人物介绍,并明确标注这些是文字描述,而不是统计结论。这也是为什么体育搜索需要同时保留检索文章和查询数据两种能力,两者对应的是不同类型的问题。

追问的写法:一次只问最影响结果的那件事

面对定义问题,系统可以直接采用默认口径给出答案,也可以先追问。追问不是越多越好。一次抛出五个选项,用户会感到被审问;一次只问一个最能改变结果的选择,则更自然。示例对话如下。

用户:“示例队丙谁对进攻最重要?”

系统:“我按‘进球、创造机会、推进球权’三个维度,用每90分钟数据排了名,结果如下。如果你更看重直接得分,可以只看第一个维度;如果想把出场时间也算进去,我可以重新计算。”

这段回应的特点是先给出一个带口径的答案,再把可调整的选项放在后面,而不是让用户先做选择才能看到结果。对大多数用户来说,先看到一个可以理解的答案,再决定要不要调整,比先回答一串问题更友好。至于哪些情况下必须先追问、哪些情况可以先答后问,则由歧义的严重程度决定:如果不同定义会得出完全相反的结论,才有必要先问。

一个示例:换一个定义,名单就会换人

下面用一支虚构的示例队丙演示。三名球员都是虚构人物,数字全部是演示用的模拟数据,不对应任何真实球员。

球员(虚构) 进球助攻 关键传球 推进传球出场分钟
中锋陈某 142 1821 2350
边锋周某 7 9 52 88 2610
前腰吴某 511 61 104 2480

如果用户问“进球最多”,答案毫无悬念是中锋陈某。如果把“重要”定义为进球加助攻,陈某仍是第一,周某和吴某接近。如果把定义换成“创造机会加推进球权”,第一名换成了前腰吴某,陈某则排在最后。如果再加入出场时间,结论又会有小幅变化。三种合理的定义给出了三张不同的排名,这并不说明系统不可靠,恰恰说明“重要”是一个需要选择的概念。

系统做这件事时,需要的是一个从明细表出发的派生指标计算。下面是一段简化的SQL,先把原始统计折算成每90分钟,再分别排名,展示定义如何落地为查询。

WITH per90 AS (
    SELECT player_id,
           SUM(goals)              * 90.0 / SUM(minutes) AS goals_p90,
           SUM(key_passes)         * 90.0 / SUM(minutes) AS key_passes_p90,
           SUM(progressive_passes) * 90.0 / SUM(minutes) AS prog_passes_p90
    FROM player_game_stats
    WHERE team_id = 'T_003'
      AND season = '2025-26'
    GROUP BY player_id
    HAVING SUM(minutes) >= 900
)
SELECT player_id,
       RANK() OVER (ORDER BY goals_p90 DESC)       AS r_goals,
       RANK() OVER (ORDER BY key_passes_p90 DESC)  AS r_create,
       RANK() OVER (ORDER BY prog_passes_p90 DESC) AS r_progress
FROM per90;

注意这里做了两件容易被忽略的事:一是用总和相除而不是每场比率取平均,避免出场时间不同造成的误差;二是用 HAVING 排除了出场时间过少的球员,防止某人凭几分钟的出场拿到一个漂亮的每90分钟数字。这些都是派生指标里的常见坑,也是为什么“重要”不能让语言模型直接心算的原因。关于统计计算为什么必须交给引擎,可以看大模型会算错平均数怎么办。

派生指标自己的风险

即便把口径摊开,派生指标本身也有几项要小心。

  • 小样本放大波动。出场很少的球员,每90分钟数据可以极其夸张。设置最低出场时间是必要的,但阈值本身也是一个口径,需要标注。
  • 位置差异。不同位置球员的数据分布完全不同。把后卫的进球和前锋的进球放在同一榜单里,得到的排名并没有意义。因此定义问题往往还需要一个合理的比较组,这一点在同一个球员该和谁比中有更详细的讨论。
  • 指标之间存在重叠。关键传球与助攻、推进传球与参与率往往高度相关,把它们全部加进公式,相当于重复计算同一种贡献。组合方式选得不好,会让某种类型的球员被系统性高估。
  • “因果”与“相关”混淆。参与率高,未必是因为这名球员让球队进攻变好,也可能是球队的战术把球集中给他。缺阵影响之类的比较,也只能提示相关,不能直接下因果结论。

这些风险并没有被完全消除的办法,能做的是让用户知道它们存在。半岛体育的答案里,除了口径卡片,还会附上样本范围与限制说明,例如“只统计出场不少于900分钟的球员”“不同位置未做归一化”。这样用户既能看到结果,也能看到结果的边界。

系统真正应该做的,是帮用户把问题问清楚

从“谁进球最多”到“谁对进攻最重要”,搜索技术需要跨过的不是一道计算难关,而是一道语义难关。前者靠关键词匹配加排序即可完成,后者需要系统识别出这是一个带有主观判断的问题,找出可用的数据维度,写出可调整的口径,用引擎完成计算,最后把定义和结果一起交出去。这一串动作里,大模型负责识别问题的性质、提出候选维度、把结果写成人话,而计算与排序必须交给数据库。

更进一步说,好的体育搜索不应该假装消除了“重要”这个词的主观性,而应该帮助用户把它说清楚。用户第一次问“谁最重要”,得到的是一份带有口径的答案;第二次可以说“我更看重创造机会”,系统便调整维度重算。这是一个对话式的、逐步收窄的过程,而不是一次猜中标准答案。当一个问题的复杂度来自语义而不是语法,正确的做法就是把语义上的选择交回给用户。类似的思路也出现在复杂体育搜索的拆解里:先把长问句拆成明确的条件,缺什么就问什么,再去查。让系统老实地说出自己用了哪些维度,用户反而更愿意信任它给出的答案。