Files

7.1 KiB

04 · 多 Agent 预测架构

Profeto 的核心预测路径是 5 个领域专家 Agent 并行分析 + 1 个终裁 Agent 汇总决策。 每个专家只拿到自己维度的数据切片,输出结构化 JSON;终裁综合 5 份报告给出最终预测。

Agent 一览

Agent 职责 数据切片 输出核心
form 近期状态 分析比分与关键事件,判断近期走势 两队近 N 场赛果(含 xG) home_edge + 走势判断
stats 攻防数据 评估进球、射门与控球,量化攻防强度 近 N 场进球/射门/控球/xG 统计 home_edge + 攻防强度
home_away 主客因素 对比主场与客场表现,评估地理优势影响 主队主场战绩 + 客队客场战绩 home_edge + 地理优势
injuries 阵容完整性 汇总伤停与停赛名单,评估战力缺失程度 伤停数据(当前无源 → no_data 门控) home_edgeno_data
h2h 历史交锋 分析过去数年以及近期的交手数据,提取交手规律 近 N 次交锋(含主客方向 + 总计统计) home_edge + 交手规律
aggregator 终裁 权衡 5 份报告 → 最终结论 5 份结构化报告 + 比赛头信息 最终预测 + 各报告采信度

数据流

POST /predict {match_id, mode: "multi"}
   │
   ├─ load_match_header ──► MatchHeader(比赛基础信息,所有 agent 共享)
   │
   ├─ asyncio.gather(并行执行 5 专家, before=match_date 防未来信息泄漏)
   │   ├─ form agent        ─┐
   │   ├─ stats agent         │ 每个 agent 拿到专属数据切片
   │   ├─ home_away agent     │ → no_data 门控 → 调 LLM → 输出 JSON 报告
   │   ├─ injuries agent      │ (无数据 → 跳过 LLM,返回 stub)
   │   └─ h2h agent         ─┘
   │
   ├─ aggregator agent(5 份报告 + 比赛头 → 最终 JSON)
   │
   └─ 存 predictions(mode="multi", agent_outputs JSONB)

执行语义

1. 并行执行

5 个专家通过 asyncio.gather 并发,总延迟 ≈ max(专家延迟) + 终裁延迟 ≈ 2 次串行 LLM 调用。

2. no_data 门控(省 token、防幻觉)

数据切片为空时(如伤停数据源未接入),跳过 LLM 调用,直接返回:

{"agent": "injuries", "status": "no_data", "data_sufficiency": "none",
 "analysis": "该维度无数据,跳过分析。"}

终裁 Agent 会看到这个 no_data 状态,不会编造伤停分析。

3. fail-open(单专家失败不阻断)

单个专家 LLM 调用失败 → 其报告标记 status: error,其余 4 份 + 终裁照常执行。 只有终裁 Agent 失败才会整体返回 502。

4. 防未来信息泄漏

所有切片查询都带 before=match_date,确保只用比赛之前的数据。 这对历史回测(对已完赛比赛跑预测)尤其重要。

输出契约

专家 Agent 统一输出(JSON)

{
  "agent": "h2h",
  "status": "ok",
  "data_sufficiency": "high",
  "analysis": "近 5 次交锋主队 3 胜 1 平 1 负,主场交锋 3 连胜……",
  "home_edge": 0.4,
  "subjective_confidence": 0.7,
  "key_evidence": ["近5次交锋主队3胜", "主场交锋3连胜"]
}
字段 说明
status ok / no_data / error / parse_error
data_sufficiency high / medium / low / none
home_edge -1.0 ~ 1.0,正数=利主队,负数=利客队
subjective_confidence 0.0 ~ 1.0,该专家对自己分析的主观信心(非概率)
key_evidence 关键证据列表(最多 5 条)

终裁 Agent 输出

在现有最终预测 schema 基础上新增 agent_weights:

{
  "pred_home_goals": 2.1,
  "pred_away_goals": 1.0,
  "1x2": "1",
  "subjective_confidence": 0.68,
  "reasoning": "综合 stats 报告的攻防强度与 form 报告的三连胜势头……",
  "agent_weights": {"form": 0.9, "stats": 0.8, "home_away": 0.7, "injuries": 0.0, "h2h": 0.8}
}

agent_weights 体现终裁对各专家报告的采信度(0–1),可用于后续分析"哪个维度对预测贡献大"。注意:这是 LLM 主观权重,非统计权重。

模型分档配置

专家用便宜快模型,终裁用强模型,各自回落 LLM_MODEL:

环境变量 说明 回落
LLM_SPECIALIST_MODEL 5 个专家共用模型 LLM_MODEL
LLM_AGGREGATOR_MODEL 终裁模型 LLM_MODEL

示例(专家用 gpt-4o-mini,终裁用 gpt-4o):

LLM_MODEL=gpt-4o
LLM_SPECIALIST_MODEL=gpt-4o-mini
LLM_AGGREGATOR_MODEL=gpt-4o

Prompt 版本化

每个 Agent 有独立 prompt 文件,位于 src/llm/prompts/agents/:

agents/
├── form_v1.md         # 近期状态专家
├── stats_v1.md        # 攻防数据专家
├── home_away_v1.md    # 主客因素专家
├── injuries_v1.md     # 阵容完整性专家
├── h2h_v1.md          # 历史交锋专家
└── aggregator_v1.md   # 终裁

调用时传 prompt_version: "v1" 即加载所有 *_v1.md。 迭代 prompt 时:复制 h2h_v1.mdh2h_v2.md,改内容,传 prompt_version: "v2"predictions.prompt_version 存的是 multi_v2,与 single 模式的 v1/v2 天然分组,可在 eval summary 中 A/B 对比。

版本纪律(强制)

改 prompt 内容必须 bump 版本号(v2→v3),禁止默默修改 *_v1.md 内容却不改版本。

原因:

  1. 可复现性:predictions.prompt_version 决定哪份 prompt 产生了历史预测;篡改 v1 会让历史预测的 prompt 来源失真,eval 对比失效。
  2. A/B 可信度:get_eval_summary(provider, model, prompt_version) 分组。若 v1 内容在不同时间指向不同 prompt,则 v1 桶内数据不可比。
  3. 缓存一致性:模板内容 hash 写入缓存键(_prompt_template_hash),版本不变则 hash 不变,命中旧缓存。bump 版本自动让旧缓存失效。

流程:改 prompt → 新建 *_v{N+1}.md → 新请求传 prompt_version=v{N+1} → 旧版本文件保持不变(供历史复现)。

代码保证:写入 DB 的 prompt_version_load_prompt_template(version) / load_agent_prompt(name, version) 加载的文件严格一致,不会漂移。

如何新增一个专家 Agent

三步:

1. 写切片函数(src/llm/context_builder.py):

async def weather_slice(header: MatchHeader, *, before=None) -> str:
    """天气切片示例。"""
    return "── 天气 ──\n  比赛日: 小雨 15°C"

2. 注册 AgentSpec(src/llm/agents/orchestrator.pySPECIALIST_SPECS):

AgentSpec(name="weather", system_prompt="你是足球天气影响分析专家。只输出 JSON。",
          slice_fn=weather_slice)

3. 写 prompt 文件(src/llm/prompts/agents/weather_v1.md):

你是足球天气影响分析专家。分析以下天气数据对比赛的影响。
{{context}}
严格按此 JSON 输出……

重启服务即生效,终裁会自动收到第 6 份报告。

与单 Agent 模式的关系

mode: "single" 走原有单次调用路径(一个大 context + 一个 prompt),用于:

  • 与 multi 模式做 A/B 基线对比
  • 快速验证(省 token)
  • 调试单个 prompt

eval summary 按 prompt_version 分组:v1/v2 是 single,multi_v1/multi_v2 是 multi,可直接对比准确率。