Contexto
En AlfaStrakhovanie el equipo de data engineering procesa más de 50 merge requests por semana para Spark jobs, modelos dbt y Airflow DAGs. Los engineers senior dedicaban 4-6 horas semanales a comentarios repetitivos: revisar naming, detectar patrones SQL, comprobar que los modelos nuevos usaran los data contracts correctos.
El objetivo era simple: automatizar el 60% rutinario del feedback de review para que las personas pasaran más tiempo en arquitectura y lógica, no en checks repetitivos.
Arquitectura
GitLab Webhook (MR event)
│
▼
FastAPI service (webhook handler)
│
├── fetch MR diff via GitLab API
├── chunk diff by file/hunk
│
▼
Qdrant retrieval (RAG)
│
├── query: Confluence data contracts
├── query: internal coding standards docs
├── query: similar past MR reviews
│
▼
Claude API
│
├── system prompt: role + company context
├── retrieved context (top-k chunks)
├── MR diff
│
▼
Structured JSON output (per-file comments)
│
▼
GitLab API → post inline comments on MR
Trade-offs
RAG vs fine-tuning. Fine-tuning quedó fuera rápido: la base de código y las reglas internas cambian más rápido que cualquier ciclo de retraining. RAG sobre documentación actual de Confluence daba contexto vivo sin retraining.
Claude vs GPT-4o. Probamos ambos. Claude producía reviews más aplicables y cuidadosos en patrones Spark/SQL, y generaba menos ruido con false positives.
Tamaño de chunks del diff. El punto útil estuvo alrededor de 150 líneas. Archivos completos empeoraban recall y diluían el contexto.
Fragmento clave
async def retrieve_context(diff_chunk: str, k: int = 8) -> list[str]:
embedding = await embed(diff_chunk)
results = await qdrant_client.search(
collection_name="internal_docs",
query_vector=embedding,
limit=k,
with_payload=True,
)
return [hit.payload["text"] for hit in results]async def generate_review(diff: str, context: list[str]) -> ReviewOutput:
response = await anthropic.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=2048,
system=REVIEW_SYSTEM_PROMPT,
messages=[
{
"role": "user",
"content": f"<context>\n{chr(10).join(context)}\n</context>\n\n<diff>\n{diff}\n</diff>",
}
],
)
return parse_review(response.content[0].text)Resultados
- Tiempo hasta el primer review: de 18h promedio a 11h
- False positive rate: alrededor de 8% en validación manual sobre 200 reviews
- Adoption: 3 equipos en producción, 2 más en onboarding
- Coste: aproximadamente $0.04 por MR review
Qué mejoraría después
- Chunking por límites semánticos, no por número de líneas.
- Confidence score explícito para cada comentario.
- Human-in-the-loop para pipelines críticos, donde un approve del agente no debería verse como decisión final.