Labs

AI赛马预测新突破:PostgreSQL与Claude构建自动化预测流水线

AI赛马预测新突破:PostgreSQL与Claude构建自动化预测流水线

过去六个月,一位开发者构建了一个专注于赛马预测的AI系统。这并非简单的“历史结果→预测”模型,而是一个多阶段的流水线:从数据质量管理、特征工程,到Claude推理,最终生成排序推荐。

以下是他从中获得的经验。

架构概览

该AI预测系统的核心架构如下:

  • 数据抓取:netkeiba scrape
  • 数据存储:PostgreSQL (horse_races / horse_entries)
  • 每日批处理:fetch_horse_racing.py
  • AI推理接口:Supabase Edge Function (ai-hub: horse.predict)
  • 大模型推理:Claude haiku
  • 结果整合:horse_race_predictions_ensemble
  • 准确性评估:evaluate_accuracy.ts (每周评估)

数据质量分数 (DQS)

预测准确性很大程度上取决于数据质量。作者为15个字段计算了数据质量分数(DQS,范围0-100),部分计算逻辑示例如下:

(  CASE WHEN weight IS NOT NULL THEN 10 ELSE 0 END +  CASE WHEN weight_diff IS NOT NULL THEN 10 ELSE 0 END +  CASE WHEN last_3f IS NOT NULL THEN 15 ELSE 0 END +  CASE WHEN prev_last_3f IS NOT NULL THEN 10 ELSE 0 END +  CASE WHEN jockey_id IS NOT NULL THEN 10 ELSE 0 END +  CASE WHEN trainer_id IS NOT NULL THEN 10 ELSE 0 END +  CASE WHEN odds IS NOT NULL THEN 15 ELSE 0 END  -- + 8 more fields...) AS data_quality_score

任何DQS低于60的条目都会被跳过。作者指出,这个简单的过滤条件对预测准确性的提升,甚至超过了任何模型本身的改变。

特征工程:排名分数

作者根据经验贡献度,对八个因素进行了加权处理,以生成排名分数。关键因素及其权重如下:

  • 历史名次率:25% (最稳定的信号)
  • 最后3F时间 (last_3f):20% (后期速度是重要预测指标)
  • 赔率倒数:15% (市场智慧的体现)
  • 骑师胜率:15% (骑师效应真实存在)
  • 体重变化:10% (状态信号)
  • 最后3F与前一场比赛的对比:10% (动量趋势)
  • 最佳时间记录:5% (能力上限指标)

Claude推理提示词

作者使用Claude来生成解释,而不仅仅是分数。以下是其提示词的结构:

const prompt = `您是一位赛马预测专家。[RACE INFORMATION]<<>>${raceInfo}<<>>[HORSE DATA]<<>>${horseData}<<>>请综合考虑以下因素,推荐前3匹马:1. 优先考虑DQS >= 70的马匹。2. 强调最佳时间记录和最后3F表现。3. 将体重变化 ±10kg 标记为风险因素。4. 每条推荐解释不超过100个字符。输出格式:JSON`;

提示词中的<<>>块用于保护系统,防止从抓取到的赛事数据中注入提示词攻击(prompt injection)。

解决N+1查询问题

最初的实现存在N+1查询问题:每次评估运行时,针对50场比赛,会产生2次查询×50场=100次查询。

// 优化前:N+1查询for (const race of races) {  // query for race details  // query for horse entries}

为了解决这个问题,作者利用PostgreSQL的WITH RECURSIVE CTE(公用表表达式)和JSONB_AGG函数,将多次查询优化为一次高效的查询:

-- 优化后:单次查询WITH race_data AS (  SELECT    r.id AS race_id,    JSONB_BUILD_OBJECT(      'race_details', r,      'horse_entries', (        SELECT JSONB_AGG(he)        FROM horse_entries he        WHERE he.race_id = r.id      )    ) AS data  FROM    races r  WHERE    r.date = '2023-10-26')SELECT JSONB_AGG(data) FROM race_data;

这种方法通过一次数据库往返,就能获取所有比赛及其参赛马匹的详细信息,显著提升了数据获取效率。

↗ 阅读原文