soccer-agent-toolbelt
oracle-devrel/oracle-ai-developer-hub · Agent Skill
Fornisce accesso ai dati delle partite, alle previsioni ML e alla memoria per analizzare partite di calcio e dati della Coppa del Mondo.
Vai all'installazione ↓Opzioni di installazione
Il prompt con anteprima è selezionato di default. Puoi passare al comando diretto.
Copia il prompt, incollalo in Claude, Codex o un altro assistente: analizzerà la pagina dello skill e lo installerà.
I'd like to install this Claude Code skill: https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/workshops/soccer-analytics-agent/.claude/skills/soccer-agent-toolbelt Please review the SKILL.md file, verify the skill is legitimate, then copy the skill folder into .claude/skills/ of my project.
Il comando diretto salta la revisione: prima ispeziona il sorgente.
npx skills add https://github.com/oracle-devrel/oracle-ai-developer-hub/tree/main/workshops/soccer-analytics-agent/.claude/skills/soccer-agent-toolbelt
Leggi SKILL.md e i file correlati prima di installare. Clicca un file per l'anteprima.
Contenuto di SKILL.md
Testo originale dell'autore · sola letturaSoccer Agent Toolbelt
You have 13 tools available. Invoke each via the dispatcher with JSON args.
Hybrid-first rule for coding agents
When you are building, debugging, or demoing the agent, treat hybrid_retrieve as the default retrieval path for explanatory grounding. It is the workshop showcase: model prediction documents plus football facts stored in Oracle through langchain-oracledb OracleVS, retrieved with native hybrid search when possible or Oracle Text + vector reciprocal-rank fusion on Oracle AI Database Free.
Use vector_search deliberately as the semantic-only baseline over semantic_memory when you want to contrast "plain vector similarity" with hybrid retrieval. Do not replace hybrid_retrieve with vector_search in the agent loop, UI demos, or final Grok chat unless the LangChain OracleVS store is unavailable.
LangGraph OracleDB observability rule
Every live /chat turn should leave an ordered step trace in Oracle through langgraph-oracledb OracleStore, under namespace ("soccer-agent", "agent-steps", session_id). Use this for observability/debugging: it records turn_start, grounding_retrieved, each model_response, each tool_call, each tool_result, and final_response or tool_budget_exhausted.
Inspect it via the API after a chat turn:
curl http://localhost:8000/observability/<session_id> | uv run python -m json.tool
Or from Python:
uv run python - <<'PY'
from soccer_agent.observability.langgraph_steps import list_steps
for step in list_steps("<session_id>"):
print(step.value["step_index"], step.value["event_type"], step.value.get("tool_name"))
PY
If this returns no rows after a real chat turn, run uv run python scripts/init_memory.py and uv run python scripts/verify.py; the verifier must report langgraph-oracledb OracleStore observability setup.
Tools
sql_query
Run a read-only SELECT against the soccer schema.
Allowed tables: MATCH_RESULTS, GOALSCORERS, SHOOTOUTS, WC2026_VENUES, PREDICCIONES_FINAL, SOCCER_LANGCHAIN_DOCS, VW_COMPETITIVE_MATCHES, VW_TEAM_STATISTICS, AGENT_SESSIONS, WORKING_MEMORY, EPISODIC_MEMORY, SEMANTIC_MEMORY.
Args: {"sql": "<SELECT statement>"}
vector_search
Semantic-only similarity over distilled facts in semantic_memory. The embedding is computed in-database via VECTOR_EMBEDDING(ALL_MINILM_L6_V2 USING :t AS DATA) — no external embedding API call is made. Use this to show the baseline that only ranks embedded fact summaries; it does not search cached ML prediction documents.
Args: {"query": "<text>", "limit": 5, "fact_type": "team_decade"}
hybrid_retrieve
Default evidence retrieval path. Hybrid retrieval over the LangChain OracleVS table SOCCER_LANGCHAIN_DOCS, populated by langchain-oracledb after ML inference. It combines ML prediction documents and football facts with native HYBRID VECTOR INDEX when available, or Oracle Text + vector reciprocal-rank fusion on Oracle AI Database Free. Prefer this before vector_search for any explanation that Grok or a coding agent will use.
Args: {"query": "Spain Brazil World Cup prediction", "limit": 5, "search_mode": "hybrid"}
predict_match
On-demand 92-feature XGBoost inference for current or hypothetical matches.
Args: {"home_team": "Spain", "away_team": "Brazil", "neutral": true}
get_elo
FootballElo rating for one team, including tournament-tier ratings.
Args: {"team": "Spain"}
get_team_form
Rolling and weighted recent form plus goal averages.
Args: {"team": "Spain", "n": 10}
get_h2h
Head-to-head record from team_a's perspective.
Args: {"team_a": "Spain", "team_b": "Brazil"}
get_momentum
Streaks, unbeaten run, clean-sheet rate, comeback rate, draw tendency, and blowouts.
Args: {"team": "Spain", "n": 15}
get_poisson_xg
Poisson expected-goals lambdas and outcome probabilities.
Args: {"home_team": "Spain", "away_team": "Brazil", "n": 20}
get_tournament_context
World Cup, continental, qualifying, friendly, and big-game context for one team.
Args: {"team": "Spain"}
lookup_prediction
Precomputed prediction from PREDICCIONES_FINAL.
Args: {"home_team": "Spain", "away_team": "Brazil"}
remember
Write a fact to semantic memory.
Args: {"fact_type": "...", "subject_key": "...", "summary": "...", "source": {}}
recall
Recent N turns of episodic memory for the current session.
Args: {"limit": 8}
How to invoke
From the repo root with the uv env active:
uv run python .claude/skills/soccer-agent-toolbelt/tools/run_tool.py <tool> '<json args>' [--session SID]
Examples:
uv run python .claude/skills/soccer-agent-toolbelt/tools/run_tool.py sql_query \
'{"sql":"SELECT home_team, away_team FROM match_results WHERE tournament = '\''FIFA World Cup'\'' AND ROWNUM <= 5"}'
uv run python .claude/skills/soccer-agent-toolbelt/tools/run_tool.py lookup_prediction \
'{"home_team":"Spain","away_team":"Brazil"}'
uv run python .claude/skills/soccer-agent-toolbelt/tools/run_tool.py hybrid_retrieve \
'{"query":"Spain Brazil World Cup prediction evidence","limit":3}'
Contrast it with the semantic-only baseline when teaching the difference:
uv run python .claude/skills/soccer-agent-toolbelt/tools/run_tool.py vector_search \
'{"query":"Spain Brazil World Cup prediction evidence","limit":3}'
Expected contrast: hybrid_retrieve can return doc_type=prediction rows from PREDICCIONES_FINAL with a retrieval_mode such as native_hybrid or fallback_rrf; vector_search returns only distilled facts from semantic_memory. That difference is the Oracle AI Database vector-store showcase.
The dispatcher prints the result as a single JSON line to stdout. Tool surface mirrors soccer_agent/agent/tools.py exactly — same schemas as the deployed FastAPI agent uses.
Adding your own tool
If you want to add a 14th tool (e.g. one that reads team stats, schedules a future match, or writes to a custom table):
1. Append a new schema to TOOL_SCHEMAS in soccer_agent/agent/tools.py.
2. Add a if name == "your_tool": branch in dispatch(...). Return a JSON-serializable dict; the loop will surface it back to the model.
3. The soccer-agent-toolbelt dispatcher (this skill) and the FastAPI agent both pick it up automatically — they share the same TOOL_SCHEMAS list.
Pitfalls when building on Oracle AI Database
These are the sharp edges we hit shipping the workshop. They aren't theoretical — every one bit a real session.
python-oracledb (3.x)
IS JSONCLOBs auto-decode to Python dict/list.json.loads(value)on a column declaredCLOB CHECK (... IS JSON)raisesTypeError: the JSON object must be str, bytes or bytearray, not dictbecause the driver already parsed it. Guard withisinstance(val, (str, bytes, bytearray))before decoding.- LOB locators die after connection close. If you fetch CLOB rows inside
with get_connection() as conn: ...and then build dataclasses or call.read()AFTER thewithblock exits, you getDPY-1001: not connected to database. Materialize CLOBs INSIDE thewithblock:
```python
with get_connection() as conn:
cur = conn.cursor()
cur.execute(...)
rows = [
{c: (v.read() if hasattr(v, "read") else v)
for c, v in zip(cols, row)}
for row in cur.fetchall()
]
return rows
```
- Pass float32 vectors as
array.array('f', list), not numpy ndarrays.oracledbdoes not bind numpy directly toVECTORcolumns. load_dotenv()with no args needs a stack frame. If you pipe Python to stdin (uv run python - <<EOF),find_dotenv()raisesAssertionError. Pass an explicit path:load_dotenv(Path.cwd() / ".env").
Vector, hybrid search, and observability
- Hybrid-first default. For agent grounding, UI demos, and final Grok answers, use
hybrid_retrievefirst so the answer can cite cached ML prediction documents and football facts from the LangChain OracleVS vector store. Usevector_searchas a semantic-only comparison or fallback, not as the primary path. - Observe the loop, not just the final answer.
soccer_agent.observability.langgraph_stepsstores every turn/tool step withlanggraph-oracledbOracleStore.GET /observability/{session_id}is the fastest proof that the agent persisted its execution path. - Lower distance is better.
ORDER BY VECTOR_DISTANCE(embedding, :q, COSINE) ASC(ascending is the default; just don't putDESC). - Use
FETCH FIRST :n ROWS ONLY, notLIMIT.LIMITis not valid Oracle SQL. - Bind the query vector once. Don't compute
embed_one(query)twice — call it once and reuse thearray.array. - Refresh
SOCCER_LANGCHAIN_DOCSafter model changes. Runuv run python scripts/load_langchain_vectors.py --resetafterscripts/load_predictions.pyso hybrid retrieval reflects the latestPREDICCIONES_FINALprobabilities. - HYBRID VECTOR INDEX is optional on Oracle AI Database Free.
hybrid_retrievefalls back to Oracle Text + vector reciprocal-rank fusion if nativeOracleHybridSearchRetrieversupport is unavailable.
Schema and grants
CREATE MINING MODELis required forDBMS_VECTOR.LOAD_ONNX_MODEL. Workshop setup grants it; if you re-create the user manually, add the grant.DBMS_VECTORis alreadyEXECUTEtoPUBLICon Oracle AI Database Free; do NOT try to grant it explicitly — SYSTEM lacksGRANT ANY OBJECT PRIVILEGEand you'll hitORA-01031.
In-DB ONNX embedding model
- Model name in SQL is uppercase with underscores (e.g.
ALL_MINILM_L6_V2), NOT the HuggingFace path. Set the model name via theORACLE_EMBED_MODELenv var. - Vanilla HuggingFace ONNX exports don't work. Oracle requires the tokenizer baked into the graph; otherwise
VECTOR_EMBEDDING(...)returnsORA-54426: Tensor "input_ids" contains multiple dimensions (2) of variable size. Use theonnx2oraclePyPI package (notoml4py, which is a 4-file stub on PyPI). - Embedding dim must match the
VECTOR(384, FLOAT32)column type. If you switch toall-mpnet-base-v2(768 dims), update the schema too.
OCI GenAI Inference (Grok 4)
- Bearer-token auth (sk-...) works against
/actions/chatand/actions/embedTextonly. The control plane (https://generativeai...) rejects the bearer key — you can't list models with it. Look in the OCI Console for the model catalog. - Compartment OCID is required in the request body, not just for auth. Always include
servingMode.compartmentId(the workshop'sgrok_client.pydoes this). - Native tool calling (the
toolsarray) returns HTTP 400 on every model tested with this auth path. Use the prompt-protocol pattern: append tool schemas to the system message, instruct the model to emit{"tool": "...", "args": {...}}as a single JSON object when calling a tool, parse it out of the response text. Seesoccer_agent/agent/grok_client.py:_inject_tool_protocoland_parse_tool_calls. role: "tool"messages get rejected withouttoolCallId. Since the bearer-auth endpoint never emits atoolCallId, route tool results back asrole: "system"messages. Skip any persistedrole: "tool"turns when rebuilding the message list for the next iteration — they will break the next API call.