Módulo 3: Detectar Hallucinations en Código
Hallucinations en Imports y APIs
Hallucinations en Imports y APIs
Descripción de la cápsula
En la cápsula anterior clasificaste las hallucinations en 4 tipos. Ahora vas a profundizar en los dos primeros: imports falsos y APIs con signatures inventadas. Son los tipos más comunes — representan aproximadamente el 60% de las hallucinations en código generado por AI — y los más fáciles de detectar si sabes dónde mirar.
Esta cápsula te da un proceso sistemático para verificar imports y API calls. No se trata de memorizar cada función de cada librería — eso es imposible. Se trata de desarrollar el instinto de cuándo verificar y las técnicas de cómo hacerlo rápido.
Imports Falsos: El Caso Más Común
Por qué los imports son el target #1 de hallucinations
Los LLMs ven miles de imports durante el entrenamiento. Para librerías populares como FastAPI, pandas, sklearn, o requests, el modelo ha visto decenas de miles de combinaciones de imports. El problema: no todas esas combinaciones corresponden a la misma versión de la librería, y muchas combinaciones que el LLM genera son extrapolaciones de lo que ha visto, no copias de imports reales.
Anatomía de un import falso
from fastapi.security import OAuth2TokenValidator
# ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
# módulo real clase inventada
# (fastapi.security (OAuth2PasswordBearer sí existe,
# sí existe) OAuth2TokenValidator no)
El patrón más común: el módulo es real pero la clase/función es inventada. El LLM sabe que fastapi.security existe porque lo ha visto cientos de veces. Pero inventa un nombre de clase que encaja con el patrón del módulo.
Los 5 patrones más comunes de imports falsos
Patrón 1: Combinación de conceptos existentes
El LLM combina dos conceptos reales en un nombre que no existe:
# Existen por separado:
from collections import OrderedDict
from collections import defaultdict
# ❌ El LLM combina ambos:
from collections import OrderedDefaultDict
# No existe — nunca se implementó en Python estándar
# Existen por separado:
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import MultiLabelBinarizer
# ❌ El LLM combina conceptos:
from sklearn.metrics import roc_auc_multiclass
# No existe — para multiclass se usa roc_auc_score con multi_class="ovr"
Patrón 2: Naming convention extrapolado
El LLM ve un patrón de naming y lo extiende:
# Existen en FastAPI:
from fastapi.security import OAuth2PasswordBearer
from fastapi.security import OAuth2AuthorizationCodeBearer
# ❌ El LLM extiende el patrón:
from fastapi.security import OAuth2TokenValidator
from fastapi.security import OAuth2RefreshTokenBearer
# El patrón OAuth2[X]Bearer / OAuth2[X]Validator parece lógico,
# pero estas clases no existen
Patrón 3: Submódulo que debería existir pero no existe
# json es un módulo estándar
import json
# ❌ El LLM asume submódulos que siguen convenciones de otras librerías:
from json.exceptions import JSONDecodeError
# "json.exceptions" no existe como submódulo
# ✅ Lo correcto: from json import JSONDecodeError
# logging tiene handlers
import logging
# ❌ El LLM inventa handlers específicos:
from logging.handlers import JSONHandler
# No existe — hay RotatingFileHandler, TimedRotatingFileHandler, etc.
# Para JSON logging se usa python-json-logger (paquete separado)
Patrón 4: Alias popular que no es el nombre real
# ❌ "Router" parece más intuitivo que "APIRouter"
from fastapi import Router
# ✅ Lo correcto:
from fastapi import APIRouter
# ❌ "JsonResponse" con esa capitalización
from fastapi.responses import JsonResponse
# ✅ Lo correcto:
from fastapi.responses import JSONResponse
# La diferencia: "Json" vs "JSON"
Patrón 5: Import de versión anterior
# ❌ Pydantic v1 style (deprecated en v2)
from pydantic import validator
# ✅ Pydantic v2 style:
from pydantic import field_validator
# ❌ httpx versión anterior:
from httpx import TestClient
# ✅ Para testing de FastAPI con httpx:
from httpx import ASGITransport, AsyncClient
# TestClient está en starlette: from starlette.testclient import TestClient
Proceso de verificación para imports
Cuando veas un import que no reconoces, sigue este proceso:
PASO 1: ¿Reconoces el import?
├── Sí → Probablemente correcto (pero verifica si hace mucho que no usas la librería)
└── No → Continúa a paso 2
PASO 2: ¿El módulo base es real?
├── python -c "import fastapi.security"
├── Si falla → Todo el import es falso
└── Si funciona → El módulo es real, verifica la clase/función
PASO 3: ¿La clase/función existe en el módulo?
├── python -c "from fastapi.security import OAuth2TokenValidator"
├── Si funciona → Import válido
└── Si da ImportError → Hallucination confirmada
PASO 4: ¿Qué SÍ existe en ese módulo?
├── python -c "import fastapi.security; print(dir(fastapi.security))"
└── Busca la alternativa correcta en el output
Verificación en terminal
# Verificar un import específico
python -c "from fastapi.security import OAuth2TokenValidator"
# Output: ImportError: cannot import name 'OAuth2TokenValidator'
# Ver qué existe en un módulo
python -c "import fastapi.security; print([x for x in dir(fastapi.security) if not x.startswith('_')])"
# Output: ['HTTPAuthorizationCredentials', 'HTTPBasic', 'HTTPBasicCredentials',
# 'HTTPBearer', 'OAuth2', 'OAuth2AuthorizationCodeBearer',
# 'OAuth2PasswordBearer', 'OAuth2PasswordRequestForm',
# 'OAuth2PasswordRequestFormStrict', 'OpenIdConnect', 'SecurityScopes']
# Verificar si un paquete está instalado
pip show pyjwt
# Output: Name: PyJWT, Version: 2.8.0, etc.
# Buscar una función en la documentación del paquete
python -c "import jwt; help(jwt.decode)"
APIs con Signatures Inventadas: El Caso Más Sutil
Qué los hace diferentes de imports falsos
Con imports falsos, la función/clase no existe. Python te da un ImportError inmediato. Con APIs inventadas, la función sí existe pero se usa con argumentos que no existen o con valores que no son válidos.
Esto los hace más peligrosos porque:
- El import no falla
- En algunos casos, el argumento extra es ignorado silenciosamente (con
**kwargs) - El error puede aparecer solo en runtime, cuando se llama la función con ciertos inputs
Anatomía de una API inventada
# La función existe
decoded = jwt.decode(token, secret, algorithms=["HS256"]) # ✅ Correcto
# La función existe pero con un parámetro inventado
decoded = jwt.decode(token, secret, algorithms=["HS256"], verify=True) # ❌
# ^^^^^^^^^^^
# parámetro inventado — "verify" no existe
# lo real: options={"verify_signature": True}
Los 5 patrones más comunes de APIs inventadas
Patrón 1: Valor de argumento inventado
La función y el parámetro existen, pero el valor no es válido:
import pandas as pd
df = pd.DataFrame({"a": [1, 2, 3]})
# ❌ orient="dict" no es un valor válido
result = df.to_json(orient="dict")
# ✅ Valores válidos:
# "split", "records", "index", "columns", "values", "table"
result = df.to_json(orient="records")
# Nota: df.to_dict() existe como método separado
# El LLM mezcló to_json(orient=...) con to_dict()
Patrón 2: Método encadenado que no existe
El LLM genera un método que encaja con la API de la librería pero no existe:
from sqlalchemy import select
from sqlalchemy.orm import Session
# ❌ .eager_load() no existe como método de Select
stmt = select(User).where(User.active == True).eager_load(User.posts)
# ✅ Lo correcto:
from sqlalchemy.orm import joinedload
stmt = select(User).where(User.active == True).options(joinedload(User.posts))
# ❌ .filter_by_date() no existe en QuerySet
users = db.query(User).filter_by_date(created_at__gte=start_date)
# ✅ Lo correcto (SQLAlchemy):
users = db.query(User).filter(User.created_at >= start_date)
Patrón 3: Constructor con parámetros de otro dominio
from pydantic import BaseModel, Field
class Product(BaseModel):
# ❌ unique=True y index=True son conceptos de base de datos, no de Pydantic
sku: str = Field(..., unique=True, index=True)
name: str = Field(..., min_length=1, max_length=200)
price: float = Field(..., gt=0)
# ✅ Pydantic Field() acepta: default, alias, title, description,
# gt, ge, lt, le, min_length, max_length, pattern, etc.
# Para constraints de DB: usar SQLAlchemy Column(unique=True, index=True)
Patrón 4: Función con signature de versión diferente
# ❌ En Pydantic v2, la API cambió
from pydantic import BaseModel
class User(BaseModel):
name: str
class Config: # ❌ v1 style
orm_mode = True
# ✅ En Pydantic v2:
class User(BaseModel):
name: str
model_config = {"from_attributes": True} # v2 style
# ❌ pytest.raises con match como argumento posicional
import pytest
with pytest.raises(ValueError, "invalid input"):
process_data(bad_input)
# ✅ Lo correcto:
with pytest.raises(ValueError, match="invalid input"):
process_data(bad_input)
Patrón 5: Función real con tipo de retorno asumido incorrectamente
import os
# ❌ El LLM asume que os.getenv retorna siempre str
port: int = int(os.getenv("PORT"))
# Si PORT no está definido, os.getenv retorna None
# int(None) → TypeError
# ✅ Lo correcto:
port: int = int(os.getenv("PORT", "8000"))
# O con validación:
port_str = os.getenv("PORT")
if port_str is None:
raise ValueError("PORT environment variable is required")
port = int(port_str)
Caso de estudio: La hallucination que costó 4 horas
Imagina este escenario real:
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allowed_origins=["http://localhost:3000"], # ❌ HALLUCINATION
allowed_methods=["GET", "POST", "PUT", "DELETE"],
allowed_headers=["*"],
allow_credentials=True,
)
El developer copia este código. Lo ejecuta. La app arranca sin errores. Pero CORS no funciona — el frontend en localhost:3000 sigue recibiendo errores de CORS.
¿Por qué? Porque el parámetro real es allow_origins (sin "d"), no allowed_origins. El parámetro extra es ignorado silenciosamente por **kwargs. CORS se configura con los defaults (que no incluyen localhost:3000).
El developer pasa 4 horas:
- Revisando headers del browser (1 hora)
- Buscando el error en el frontend (1 hora)
- Probando diferentes configuraciones de CORS (1 hora)
- Finalmente, leyendo la documentación oficial y comparando parámetro por parámetro (30 min)
- Encuentra
allow_originsvsallowed_origins(30 min)
4 horas por una "d" extra. Ese es el costo de una hallucination de Tipo 2/3 que pasa silenciosamente.
Proceso de verificación para APIs
PASO 1: ¿La función existe?
├── python -c "import jwt; print(type(jwt.decode))"
├── Si da AttributeError → La función no existe (Tipo 1)
└── Si funciona → La función es real, verifica los argumentos
PASO 2: ¿Los argumentos son correctos?
├── python -c "import inspect; import jwt; print(inspect.signature(jwt.decode))"
├── Compara los parámetros que ves con los que el código usa
└── Cualquier parámetro que no aparece en la signature → sospechoso
PASO 3: ¿Los valores son válidos?
├── Busca en la documentación los valores aceptados
├── Para enums/opciones: la doc lista los valores válidos
└── Para tipos: verifica que el tipo del valor coincide
PASO 4: Verificar con un quick test
├── Ejecuta la función con los argumentos del código
├── Si da TypeError → Argumento no aceptado
├── Si funciona pero el resultado es inesperado → Valor incorrecto
└── Si funciona y el resultado es correcto → API es válida
Verificación en terminal
# Ver la signature de una función
python -c "import inspect; import jwt; print(inspect.signature(jwt.decode))"
# Output: (jwt, key='', algorithms=None, options=None, ...)
# Ver la documentación completa
python -c "import jwt; help(jwt.decode)"
# Quick test de un valor de parámetro
python -c "
import pandas as pd
df = pd.DataFrame({'a': [1]})
try:
print(df.to_json(orient='dict'))
except ValueError as e:
print(f'Error: {e}')
"
# Output: Error: Invalid value 'dict' for option 'orient'
# Verificar documentación oficial en terminal
python -c "import pandas; help(pandas.DataFrame.to_json)"
Estrategia: Cuándo Verificar y Cuándo Confiar
La regla del 80/20 para imports y APIs
No puedes verificar cada import y cada API call en cada archivo que AI genera — la productividad caería a cero. Necesitas una estrategia de cuándo verificar:
SIEMPRE verificar:
├── Imports que no has usado personalmente antes
├── Submodules específicos (from X.Y.Z import ...)
├── API calls con parámetros que no reconoces
├── Funciones de seguridad (auth, encryption, hashing)
└── Funciones de librerías que actualizaron recientemente
VERIFICAR SI HAY DUDA:
├── Imports de librerías que conoces pero de módulos poco comunes
├── API calls con valores de parámetros específicos
├── Métodos encadenados (.method1().method2().method3())
└── Funciones de testing con argumentos no-estándar
GENERALMENTE CONFIAR:
├── Import de módulos estándar de Python (os, json, datetime)
├── Import de la clase principal de un framework (FastAPI, BaseModel)
├── API calls que has usado cientos de veces
└── Funciones con signatures simples (1-2 argumentos conocidos)
Regla práctica: "Si no lo he escrito antes, verifico"
La regla más simple y efectiva: si es un import o API call que no has escrito tú personalmente al menos una vez, verifica. No importa si suena correcto. No importa si Claude Code lo generó con confianza. Si es la primera vez que lo ves, verifica.
Esta regla es conservadora al principio (verificas mucho) pero se relaja naturalmente con la experiencia: cada import verificado se convierte en uno que conoces para la próxima vez.
Hallucinations de Import por Librería
Top 5 librerías con más hallucinations de imports
Basado en patrones comunes de hallucinations en código Python generado por AI:
1. FastAPI / Starlette
# Hallucinations comunes:
from fastapi.security import OAuth2TokenValidator # ❌
from fastapi import Router # ❌ (es APIRouter)
from fastapi.responses import JsonResponse # ❌ (es JSONResponse)
from fastapi import QueryParam # ❌ (es Query)
from starlette.middleware import SessionMiddleware # ❌ (es from starlette.middleware.sessions)
2. SQLAlchemy
# Hallucinations comunes:
from sqlalchemy.orm import relationship, backref, lazy_load # ❌ lazy_load no existe
from sqlalchemy import Column, Integer, UniqueConstraint # ⚠️ UniqueConstraint es real pero se importa diferente
from sqlalchemy.ext.asyncio import AsyncSession, async_session # ❌ async_session como función no existe así
3. Pydantic
# Hallucinations comunes:
from pydantic import validator # ⚠️ v1 — deprecated en v2, es field_validator
from pydantic import Schema # ❌ no existe
from pydantic.types import EmailStr # ❌ es from pydantic import EmailStr
from pydantic import ConfigDict # ⚠️ Depende de la versión
4. sklearn / scikit-learn
# Hallucinations comunes:
from sklearn.metrics import roc_auc_multiclass # ❌
from sklearn.preprocessing import TextVectorizer # ❌ (es CountVectorizer o TfidfVectorizer)
from sklearn.model_selection import StratifiedKFoldCV # ❌ (es StratifiedKFold)
from sklearn.ensemble import XGBoostClassifier # ❌ (XGBoost es un paquete separado)
5. pytest
# Hallucinations comunes:
from pytest import mock # ❌ (es from unittest.mock o from pytest_mock)
from pytest import parametrize # ❌ (es @pytest.mark.parametrize como decorator)
from pytest.fixtures import fixture # ❌ (es @pytest.fixture como decorator)
Conexión con Proyecto
Cómo se manifiesta en el proyecto integrador
En el codebase del proyecto integrador (módulo 8), hay 1-2 imports falsos y 1 API inventada plantados. Ejemplos del tipo de hallucination que podrías encontrar:
# En el archivo de autenticación del proyecto:
from fastapi.security import OAuth2PasswordBearer # ✅ Correcto
from fastapi.security import SecurityScopes # ✅ Correcto
# Pero en otro archivo:
from fastapi.security import TokenValidator # ❌ No existe
La técnica: cuando revisas un codebase, verifica todos los imports que no reconoces antes de pasar a la lógica. Es la verificación más rápida (1-2 segundos por import con python -c) y elimina las hallucinations más obvias primero.
Proceso para el proyecto
- Listar todos los imports únicos del codebase
- Marcar los que reconoces vs los que no
- Verificar los que no reconoces con
python -c - Anotar hallucinations encontradas con tipo y ubicación
- Pasar a verificación de APIs (siguiente nivel)
Troubleshooting
Problema 1: "Verifico un import pero me da error porque no tengo el paquete instalado"
Causa: No tienes la librería instalada localmente.
Solución: Primero verifica que el paquete existe buscando en https://pypi.org/project/[nombre]/ o ejecutando pip index versions [nombre]. Si el paquete existe, instálalo: pip install [nombre]. Luego verifica el import. Si el paquete no existe en PyPI, el paquete completo es una hallucination.
Problema 2: "El import funciona pero no estoy seguro de que sea la forma correcta"
Causa: Puede haber múltiples formas de importar la misma cosa.
Solución: Verifica la documentación oficial. Muchas librerías tienen imports deprecados que aún funcionan pero no son recomendados. Ejemplo: from pydantic import validator funciona en Pydantic v2 pero está deprecado. Que funcione no significa que sea correcto para tu versión.
Problema 3: "¿Cómo verifico APIs si la función acepta **kwargs?"
Causa: Funciones con **kwargs aceptan cualquier argumento sin error, haciendo la verificación más difícil.
Solución: Para funciones con **kwargs:
- Lee la documentación para saber qué parámetros son realmente procesados
- Busca en el código fuente de la función qué kwargs se extraen
- Haz un quick test: pasa el parámetro y verifica si tiene efecto
- Ejemplo:
requests.get(url, verify_ssl=False)no da error peroverify_sslno tiene efecto — verifica que SSL sigue activo
Problema 4: "Hay demasiados imports para verificar en un archivo grande"
Causa: Archivo con 30+ imports es overwhelming para verificar uno por uno.
Solución: Prioriza:
- Filtra imports de módulos estándar de Python (generalmente correctos)
- Filtra imports de la clase principal de cada librería (generalmente correctos)
- Enfócate en imports de submodules y funciones específicas
- Usa un linter como
flake8oruffque detecta imports que no existen o no se usan
Problema 5: "El código usa una versión de la librería diferente a la que tengo"
Causa: La hallucination puede ser código válido de otra versión.
Solución: Verifica qué versión de la librería usa el proyecto (pip show [librería] o revisa requirements.txt). Luego verifica contra la documentación de esa versión específica. Lo que es correcto en v1 puede ser incorrecto en v2.
Ejercicios
Ejercicio 1: Verificar imports (Fácil)
Para cada import, determina si es correcto o una hallucination. Verifica con python -c si tienes duda:
# 1
from fastapi import FastAPI, HTTPException, Depends
# 2
from pydantic import BaseModel, Field, EmailStr
# 3
from sqlalchemy.orm import joinedload, selectinload
# 4
from fastapi.security import OAuth2PasswordBearer, OAuth2TokenValidator
# 5
from collections import OrderedDict, namedtuple, ChainMap
Ver solución
-
✅ Correcto.
FastAPI,HTTPException, yDependsson imports reales de FastAPI. -
✅ Correcto.
BaseModel,Field, yEmailStrson imports reales de Pydantic. Nota:EmailStrrequierepip install pydantic[email]opip install email-validator. -
✅ Correcto.
joinedloadyselectinloadson eager loading strategies reales de SQLAlchemy ORM. -
⚠️ Parcialmente correcto.
OAuth2PasswordBearerexiste.OAuth2TokenValidatorno existe — es una hallucination. Las clases reales enfastapi.securityincluyenOAuth2PasswordBearer,OAuth2AuthorizationCodeBearer,HTTPBearer,HTTPBasic,SecurityScopes, etc. -
✅ Correcto.
OrderedDict,namedtuple, yChainMapson todos parte decollectionsen Python estándar.
Ejercicio 2: Detectar la API inventada (Medio)
En cada snippet, hay una API call incorrecta. Encuéntrala:
Snippet A:
import pandas as pd
df = pd.read_csv("data.csv")
summary = df.describe()
json_output = df.to_json(orient="records")
filtered = df.query("age > 30")
sorted_df = df.sort_values(by="name", ascending=True)
unique_names = df["name"].unique_values()
Snippet B:
from pathlib import Path
config_dir = Path.home() / ".config" / "myapp"
config_dir.mkdir(parents=True, exist_ok=True)
config_file = config_dir / "settings.json"
content = config_file.read_text(encoding="utf-8")
config_file.write_text('{"key": "value"}', encoding="utf-8")
files = list(config_dir.iterdir())
size = config_file.file_size()
Ver solución
Snippet A: df["name"].unique_values() es incorrecto. El método real es df["name"].unique(). No existe unique_values() en pandas Series. El LLM generó un nombre más descriptivo pero incorrecto.
Snippet B: config_file.file_size() es incorrecto. El método real es config_file.stat().st_size. No existe file_size() en pathlib.Path. Si quieres el tamaño del archivo, necesitas llamar .stat() primero y luego acceder a .st_size.
Ejercicio 3: Verificar parámetros de API (Medio)
Estos API calls usan funciones reales. ¿Los parámetros son correctos?
# Call 1
import jwt
token = jwt.encode({"user": "alice"}, "secret", algorithm="HS256")
# Call 2
import requests
response = requests.get("https://api.example.com", verify_ssl=False)
# Call 3
from sqlalchemy import create_engine
engine = create_engine("sqlite:///db.sqlite", echo=True, pool_size=5)
# Call 4
import logging
logging.basicConfig(level=logging.DEBUG, format="%(message)s")
Ver solución
Call 1: ✅ Correcto. jwt.encode() acepta payload, key, y algorithm como parámetros. Nota que es algorithm (singular) en encode, no algorithms (plural, que se usa en decode).
Call 2: ❌ Hallucination. El parámetro verify_ssl no existe en requests.get(). El parámetro correcto es verify. requests.get("https://api.example.com", verify=False) es lo correcto. Lo peor: verify_ssl=False no causa error (se pasa como **kwargs) pero no tiene efecto — SSL verification sigue activa.
Call 3: ⚠️ Depende. echo=True es correcto. pool_size=5 es correcto para bases de datos que soportan pooling (PostgreSQL, MySQL). Para SQLite, pool_size no aplica porque SQLite no usa connection pooling de la misma manera. No dará error, pero no tiene el efecto esperado con SQLite.
Call 4: ✅ Correcto. level y format son parámetros reales de logging.basicConfig().
Ejercicio 4: Investigación de hallucination (Difícil)
Claude Code generó este código de autenticación. Sin ejecutarlo, identifica todas las hallucinations:
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi.authentication import AuthenticationMiddleware
from jose import jwt, JWTError
from passlib.context import CryptContext
from pydantic import BaseModel, EmailStr
from datetime import datetime, timedelta
from typing import Optional
app = FastAPI()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/token")
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE = 30
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
return token_data
Ver solución
Hallucination encontrada:
from fastapi.authentication import AuthenticationMiddleware # ❌ HALLUCINATION
fastapi.authentication no existe como módulo. Esta es una hallucination Tipo 1 (import falso).
Si necesitas un middleware de autenticación en FastAPI:
- Para OAuth2/JWT: usa
fastapi.security(que ya está importado correctamente) - Para middleware de Starlette:
from starlette.middleware.authentication import AuthenticationMiddleware - FastAPI no tiene su propio módulo
authentication
El resto del código es correcto:
from jose import jwt, JWTError→ Correcto (python-jose)from passlib.context import CryptContext→ CorrectoCryptContext(schemes=["bcrypt"], deprecated="auto")→ CorrectoOAuth2PasswordBearer(tokenUrl="auth/token")→ Correctojwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)→ Correctojwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])→ Correcto
Nota: AuthenticationMiddleware no se usa en ningún lugar del código después del import, lo cual es otra señal — un import no usado a menudo indica que fue generado por el LLM como parte del "patrón" de auth pero no es necesario.
Ejercicio 5: Construir checklist personal (Difícil)
Basándote en lo aprendido, construye un checklist de verificación de imports y APIs para una librería que usas en tu trabajo. El checklist debe incluir:
- Top 5 imports que verificarías siempre
- Top 3 API calls con parámetros fácilmente confundibles
- Proceso de verificación en 3 pasos
Ver solución (ejemplo con FastAPI + SQLAlchemy)
Top 5 imports para verificar:
- Cualquier import de
fastapi.security(subclasses de OAuth2 son targets frecuentes) - Imports de
sqlalchemy.ext.asyncio(la API async tiene diferencias sutiles) - Imports de
pydanticen general (v1 vs v2 es fuente constante de hallucinations) - Imports de
starletteque no pasen por FastAPI (muchos se re-exportan, pero no todos) - Imports de librerías de testing (
httpxvsstarlette.testclientvspytest-asyncio)
Top 3 API calls confundibles:
CORSMiddleware:allow_origins(noallowed_origins)Field()de Pydantic: no aceptaunique,index,nullable(esos son de SQLAlchemy)create_engine():pool_size(nomax_pool_size)
Proceso en 3 pasos:
python -c "from X import Y"→ ¿El import existe?python -c "import inspect; print(inspect.signature(Y))"→ ¿La signature es correcta?- Quick test con valores del código → ¿El comportamiento es el esperado?
Resumen
En esta cápsula aprendiste:
- Los imports falsos siguen 5 patrones: combinación de conceptos, naming extrapolado, submódulos asumidos, alias populares, imports de otra versión
- Las APIs inventadas siguen 5 patrones: valores de argumento inventados, métodos encadenados falsos, parámetros de otro dominio, signatures de otra versión, tipos de retorno asumidos
- El proceso de verificación para imports: verificar módulo → verificar clase → ver qué existe en el módulo
- El proceso de verificación para APIs: verificar función → inspeccionar signature → verificar valores → quick test
- La regla del 80/20: verificar siempre imports desconocidos y submodules; confiar en imports estándar y clases principales
- La regla simple: "si no lo he escrito antes personalmente, verifico"
- Las hallucinations de API con
**kwargsson las más peligrosas porque fallan silenciosamente
Próxima cápsula: Hallucinations en Lógica — código que se ve correcto pero implementa algo diferente a lo que dice.
Recursos Adicionales
- Python inspect module - Cómo inspeccionar signatures de funciones programáticamente
- FastAPI Security Documentation - Referencia oficial para clases de seguridad
- Pydantic v1 → v2 Migration Guide - Guía de cambios entre versiones
- SQLAlchemy Documentation - Referencia para verificar ORM APIs
- PyPI Search - Verificar existencia de paquetes Python
- ruff — Python Linter - Linter rápido que detecta imports no existentes
Debugging & Code Review with Claude Code — Módulo 3, Cápsula 03 Claude Code Agentic Development Path — Guía #6 de 11