feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: eda_llm_insights
|
||||
kind: function
|
||||
lang: py
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def eda_llm_insights(profile: dict, model: str = \"claude-haiku-4-5-20251001\") -> dict"
|
||||
description: "Capa LLM interpretativa del grupo eda. Toma un TableProfile YA CALCULADO (el dict de profile_table) y, con UNA sola llamada al LLM, genera el bloque 'llm': resumen de la tabla, significado de una fila, diccionario de datos, deteccion de PII (RGPD), sugerencias de limpieza y analisis sugeridos. Clave de coste/privacidad: NO envia filas crudas al LLM, solo el perfil AGREGADO (nombres, tipos, % nulos, distinct, top valores agregados de categoricas, stats de numericas, pares de correlacion fuertes). Reusa ask_llm del grupo claude-direct (API directa con token OAuth de Claude). Impura, dict-no-throw."
|
||||
tags: [eda, llm, claude-direct, datascience, profiling, pii, data-dictionary]
|
||||
params:
|
||||
- name: profile
|
||||
desc: "TableProfile ya calculado (el dict que devuelve profile_table()['profile']). Se espera {table, n_rows, columns:[{name, inferred_type, semantic_type, null_pct, distinct_count, numeric:{min,max,mean,p50,...}, categorical:{top:[{value,count,pct}], mode,...}}], correlations:{strong:[{a,b,method,value}]} | None}. Solo se le envia al LLM un resumen agregado; nunca filas crudas."
|
||||
- name: model
|
||||
desc: "id del modelo Anthropic a usar. Default 'claude-haiku-4-5-20251001' (haiku, coste bajo). Para mayor calidad interpretativa, pasar p.ej. 'claude-opus-4-8'."
|
||||
output: "dict dict-no-throw. En exito: {status:'ok', llm:{summary:str, row_meaning:str, dictionary:[{column,description,business_meaning,unit}], pii:[{column,kind,severity}], cleaning:[str], analyses:[str]}}. Las claves que el LLM omita se rellenan con defaults vacios. En error (sin lanzar): {status:'error', error:str}."
|
||||
uses_functions: [ask_llm_py_core]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: true
|
||||
tests: ["test_build_prompt_includes_table_and_columns", "test_build_prompt_includes_numeric_stats_and_top_values", "test_build_prompt_handles_empty_profile", "test_parse_llm_json_plain", "test_parse_llm_json_with_fences", "test_parse_llm_json_with_surrounding_text", "test_parse_llm_json_nested_braces_in_strings", "test_parse_llm_json_raises_without_object", "test_eda_llm_insights_ok_with_monkeypatched_llm", "test_eda_llm_insights_fills_missing_keys", "test_eda_llm_insights_error_on_empty_profile", "test_eda_llm_insights_error_on_empty_llm_response", "test_eda_llm_insights_error_on_unparseable_llm_response"]
|
||||
test_file_path: "python/functions/datascience/eda_llm_insights_test.py"
|
||||
file_path: "python/functions/datascience/eda_llm_insights.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import sys, os
|
||||
sys.path.insert(0, os.path.join("python", "functions"))
|
||||
|
||||
from pipelines.profile_table import profile_table
|
||||
from datascience import eda_llm_insights
|
||||
|
||||
# 1) Perfila la tabla (calculo agregado, sin LLM).
|
||||
r = profile_table("data/ventas.duckdb", "ventas", write_report=False)
|
||||
profile = r["profile"]
|
||||
|
||||
# 2) Interpreta el perfil con UNA llamada al LLM (solo el perfil agregado viaja).
|
||||
out = eda_llm_insights(profile) # haiku por defecto
|
||||
# out = eda_llm_insights(profile, model="claude-opus-4-8") # mas calidad
|
||||
|
||||
if out["status"] == "ok":
|
||||
llm = out["llm"]
|
||||
print(llm["summary"]) # que es la tabla, 2-3 frases
|
||||
print(llm["row_meaning"]) # que representa una fila
|
||||
for d in llm["dictionary"]: # diccionario de datos por columna
|
||||
print(d["column"], "->", d["description"], f"({d['unit']})")
|
||||
for p in llm["pii"]: # datos personales/sensibles RGPD
|
||||
print("PII:", p["column"], p["kind"], p["severity"])
|
||||
print(llm["cleaning"]) # sugerencias de limpieza
|
||||
print(llm["analyses"]) # analisis sugeridos + hipotesis
|
||||
else:
|
||||
print("error:", out["error"])
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando necesites entender SEMANTICAMENTE una tabla ya perfilada: generar un
|
||||
diccionario de datos legible, detectar PII/datos sensibles RGPD, recibir
|
||||
sugerencias de limpieza y una lista de analisis/hipotesis a explorar. Es el
|
||||
paso interpretativo que sigue a `profile_table`: este calcula las metricas, y
|
||||
`eda_llm_insights` las traduce a lenguaje de negocio. El resultado encaja en la
|
||||
clave `llm` del TableProfile (la que `render_eda_markdown` renderiza en la
|
||||
seccion "Analisis LLM").
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Impura: hace 1 llamada de red al LLM.** No es determinista ni gratis.
|
||||
- **Requiere token OAuth de Claude** en `~/.claude/.credentials.json` (via
|
||||
`ask_llm` / grupo `claude-direct`). Sin token, devuelve `{status:'error'}`.
|
||||
- **NO envia filas crudas al LLM**, solo el perfil AGREGADO (nombres, tipos,
|
||||
% nulos, distinct, top valores ya agregados, stats numericas, correlaciones
|
||||
fuertes). Privacidad y coste minimos por diseno — pero requiere que el
|
||||
`profile` venga ya calculado por `profile_table`.
|
||||
- **Modelo `haiku` por defecto** para coste bajo; sube a `claude-opus-4-8` si
|
||||
necesitas interpretacion mas fina (mas caro y lento).
|
||||
- El LLM puede omitir claves: las que falten se rellenan con defaults vacios
|
||||
(`""` o `[]`), nunca lanza por shape incompleto.
|
||||
- El parseo tolera `\`\`\`json` fences y texto alrededor del objeto, pero si el
|
||||
modelo no devuelve ningun objeto JSON, retorna `{status:'error'}`.
|
||||
Reference in New Issue
Block a user