Ctrl K
ComunidadGuías…

fia-service/README.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Fraud Investigation Agent (FIA)

Python service that consumes blocked transactions from Kafka and produces
human-readable investigation reports via a fine-tuned Phi-3-mini-4k-instruct
model. Reports are written to PostgreSQL `investigationReports` and surfaced
to analysts. **FIA never touches the real-time authorization path.**

## Pipeline

```
RDA (decision = DECLINE)
   │ fire-and-forget Kafka publish
   ▼
transactions.blocked   ─►   FIA consumer
                              │
                              ▼  Phi-3 prompt
                            LLM ────► JSON report
                              │
                              ▼
                     investigationReports (Postgres)
```

## Setup

### Full setup (real LLM)

```bash
cd fia-service
python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt        # ~3 GB: torch + transformers + accelerate
cp .env.example .env
PYTHONPATH=. python -m src.main
```

The **first run downloads ≈7.6 GB of Phi-3-mini-4k-instruct weights** to
`~/.cache/huggingface` (or `$HF_HOME` if set). Expect 5–15 minutes on a
typical home connection. Subsequent boots load from cache in seconds.

### Device selection

`LLM_DEVICE=auto` resolves in this order:

1. `cuda` if `torch.cuda.is_available()`.
2. `mps` on Apple Silicon (Metal Performance Shaders).
3. `cpu` otherwise — works but ≥30 s per report; not recommended for live use.

On non-CUDA backends FIA forces `attn_implementation="eager"` because Phi-3's
default flash-attention kernels only run on CUDA. dtype defaults to `float16`
on CUDA/MPS and `float32` on CPU.

### Lightweight setup (rule-based fallback only)

For wiring tests, CI, or laptops without spare disk, skip the heavy stack:

```bash
pip install kafka-python==2.0.2 sqlalchemy==2.0.25 psycopg2-binary==2.9.9 \
            pydantic==2.6.4 python-dotenv==1.0.1
FIA_DISABLE_LLM=true PYTHONPATH=. python -m src.main
```

Two related knobs control how FIA degrades when the LLM is unavailable:

- **`FIA_DISABLE_LLM=true`** — skips the torch / transformers import
  **entirely**. Use this when you haven't installed the LLM dependencies, or
  for CI runs that have no business loading 7.6 GB of weights. Combine with
  the trimmed `pip install` above and FIA stays under a few hundred MB.
- **`FIA_FALLBACK_ON_LLM_FAILURE=true`** (default) — keeps the import in
  place but degrades to a deterministic rule-based report when the model
  fails to load, OOMs at generation time, or returns JSON that fails schema
  validation. Use this in environments where you'd normally expect the LLM
  to work but want the pipeline to stay parseable on the bad day.

Either path produces real `investigationReports` rows; the difference is
whether the LLM import is attempted at all.

## Configuration

| Variable | Default | Purpose |
| --- | --- | --- |
| `KAFKA_BROKERS` | `localhost:9092` | Kafka cluster |
| `KAFKA_BLOCKED_TOPIC` | `transactions.blocked` | Topic RDA publishes DECLINEs to |
| `KAFKA_CONSUMER_GROUP` | `fraud-investigation` | Distinct from `pattern-analysis` |
| `POSTGRES_*` | see `.env.example` | Same `fraud_db` shared with RDA/PAA/MLA |
| `LLM_MODEL_NAME` | `microsoft/Phi-3-mini-4k-instruct` | HF model id |
| `LLM_MODEL_PATH` | _(unset)_ | Local fine-tuned checkpoint dir; overrides `LLM_MODEL_NAME` |
| `LLM_DEVICE` | `auto` | `auto`, `cpu`, `cuda`, `cuda:0`, `mps` |
| `LLM_DTYPE` | `auto` | `float16`, `bfloat16`, `float32` |
| `LLM_MAX_NEW_TOKENS` | `384` | Cap on report length |
| `LLM_TEMPERATURE` | `0.2` | Lower = more deterministic |
| `FIA_DISABLE_LLM` | `false` | Skip the `torch`/`transformers` import + 7.6 GB Phi-3 load entirely. Forces every report through the rule-based fallback. Use for CI, lightweight dev setups, and laptops without disk for the weights. |
| `FIA_FALLBACK_ON_LLM_FAILURE` | `true` | Degrade to rule-based on LLM error (load failure, OOM, schema-invalid output). Honoured only when `FIA_DISABLE_LLM=false`. |
| `METRICS_PORT` | `9094` | `/livez`, `/readyz`, `/stats` |

## Idempotency

`investigationReports.transactionId` is `UNIQUE`. The writer uses
`INSERT ... ON CONFLICT DO NOTHING`, so re-delivered Kafka messages do not
crash the consumer and do not produce duplicate reports.

## Health endpoints

- `GET /livez` → `{"status": "UP"}`
- `GET /readyz` → 200 if Kafka is connected, 503 otherwise
- `GET /stats` → counters + active LLM model id

## Schema (LLM JSON contract)

```json
{
  "verdict": "FRAUD_CONFIRMED | LIKELY_LEGITIMATE | UNCERTAIN",
  "agent_confidence": 0.0,
  "recommended_action": "BLOCK | CONTACT_CUSTOMER | MANUAL_REVIEW | RELEASE",
  "key_indicators": ["short string", "..."],
  "narrative": "3-5 sentence analyst-readable summary"
}
```

Pydantic enforces this contract; bad output triggers fallback or retry.