@qubiit/lmagent 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/.editorconfig +18 -0
  2. package/AGENTS.md +169 -0
  3. package/CLAUDE.md +122 -0
  4. package/CONTRIBUTING.md +90 -0
  5. package/LICENSE +21 -0
  6. package/README.md +195 -0
  7. package/config/commands.yaml +194 -0
  8. package/config/levels.yaml +135 -0
  9. package/config/models.yaml +192 -0
  10. package/config/settings.yaml +405 -0
  11. package/config/tools-extended.yaml +534 -0
  12. package/config/tools.yaml +437 -0
  13. package/docs/assets/logo.png +0 -0
  14. package/docs/commands.md +132 -0
  15. package/docs/customization-guide.md +445 -0
  16. package/docs/getting-started.md +154 -0
  17. package/docs/how-to-start.md +242 -0
  18. package/docs/navigation-index.md +227 -0
  19. package/docs/usage-guide.md +113 -0
  20. package/install.js +1044 -0
  21. package/package.json +35 -0
  22. package/pyproject.toml +182 -0
  23. package/rules/_bootstrap.md +138 -0
  24. package/rules/agents-ia.md +607 -0
  25. package/rules/api-design.md +337 -0
  26. package/rules/automations-n8n.md +646 -0
  27. package/rules/code-style.md +570 -0
  28. package/rules/documentation.md +98 -0
  29. package/rules/security.md +316 -0
  30. package/rules/stack.md +395 -0
  31. package/rules/testing.md +326 -0
  32. package/rules/workflow.md +353 -0
  33. package/scripts/create_skill.js +300 -0
  34. package/scripts/validate_skills.js +283 -0
  35. package/skills/ai-agent-engineer/SKILL.md +394 -0
  36. package/skills/ai-agent-engineer/references/agent-patterns.md +149 -0
  37. package/skills/api-designer/SKILL.md +429 -0
  38. package/skills/api-designer/references/api-standards.md +13 -0
  39. package/skills/architect/SKILL.md +285 -0
  40. package/skills/architect/references/c4-model.md +133 -0
  41. package/skills/automation-engineer/SKILL.md +352 -0
  42. package/skills/automation-engineer/references/n8n-patterns.md +127 -0
  43. package/skills/backend-engineer/SKILL.md +261 -0
  44. package/skills/backend-engineer/assets/fastapi-project-structure.yaml +74 -0
  45. package/skills/backend-engineer/references/debugging-guide.md +174 -0
  46. package/skills/backend-engineer/references/design-patterns.md +208 -0
  47. package/skills/backend-engineer/scripts/scaffold_backend.py +313 -0
  48. package/skills/bmad-methodology/SKILL.md +202 -0
  49. package/skills/bmad-methodology/references/scale-adaptive-levels.md +141 -0
  50. package/skills/browser-agent/SKILL.md +502 -0
  51. package/skills/browser-agent/scripts/playwright_setup.ts +16 -0
  52. package/skills/code-reviewer/SKILL.md +306 -0
  53. package/skills/code-reviewer/references/code-review-checklist.md +16 -0
  54. package/skills/data-engineer/SKILL.md +474 -0
  55. package/skills/data-engineer/assets/pg-monitoring-queries.sql +154 -0
  56. package/skills/data-engineer/references/index-strategy.md +128 -0
  57. package/skills/data-engineer/scripts/backup_postgres.py +221 -0
  58. package/skills/devops-engineer/SKILL.md +547 -0
  59. package/skills/devops-engineer/references/ci-cd-patterns.md +265 -0
  60. package/skills/devops-engineer/scripts/docker_healthcheck.py +125 -0
  61. package/skills/document-generator/SKILL.md +746 -0
  62. package/skills/document-generator/references/pdf-generation.md +22 -0
  63. package/skills/frontend-engineer/SKILL.md +532 -0
  64. package/skills/frontend-engineer/references/accessibility-guide.md +146 -0
  65. package/skills/frontend-engineer/scripts/audit_bundle.py +144 -0
  66. package/skills/git-workflow/SKILL.md +374 -0
  67. package/skills/git-workflow/references/git-flow.md +25 -0
  68. package/skills/mcp-builder/SKILL.md +471 -0
  69. package/skills/mcp-builder/references/mcp-server-guide.md +23 -0
  70. package/skills/mobile-engineer/SKILL.md +502 -0
  71. package/skills/mobile-engineer/references/platform-guidelines.md +160 -0
  72. package/skills/orchestrator/SKILL.md +246 -0
  73. package/skills/orchestrator/references/methodology-routing.md +117 -0
  74. package/skills/orchestrator/references/persona-mapping.md +85 -0
  75. package/skills/orchestrator/references/routing-logic.md +110 -0
  76. package/skills/performance-engineer/SKILL.md +549 -0
  77. package/skills/performance-engineer/references/caching-patterns.md +181 -0
  78. package/skills/performance-engineer/scripts/profile_endpoint.py +170 -0
  79. package/skills/product-manager/SKILL.md +488 -0
  80. package/skills/product-manager/references/prioritization-frameworks.md +126 -0
  81. package/skills/prompt-engineer/SKILL.md +433 -0
  82. package/skills/prompt-engineer/references/prompt-patterns.md +158 -0
  83. package/skills/qa-engineer/SKILL.md +441 -0
  84. package/skills/qa-engineer/references/testing-strategy.md +166 -0
  85. package/skills/qa-engineer/scripts/run_coverage.py +147 -0
  86. package/skills/scrum-master/SKILL.md +225 -0
  87. package/skills/scrum-master/references/sprint-ceremonies.md +159 -0
  88. package/skills/security-analyst/SKILL.md +390 -0
  89. package/skills/security-analyst/references/owasp-top10.md +188 -0
  90. package/skills/security-analyst/scripts/audit_security.py +242 -0
  91. package/skills/seo-auditor/SKILL.md +523 -0
  92. package/skills/seo-auditor/references/seo-checklist.md +17 -0
  93. package/skills/spec-driven-dev/SKILL.md +342 -0
  94. package/skills/spec-driven-dev/references/phase-gates.md +107 -0
  95. package/skills/supabase-expert/SKILL.md +602 -0
  96. package/skills/supabase-expert/references/supabase-patterns.md +19 -0
  97. package/skills/swe-agent/SKILL.md +311 -0
  98. package/skills/swe-agent/references/trajectory-format.md +134 -0
  99. package/skills/systematic-debugger/SKILL.md +512 -0
  100. package/skills/systematic-debugger/references/debugging-guide.md +12 -0
  101. package/skills/tech-lead/SKILL.md +409 -0
  102. package/skills/tech-lead/references/code-review-checklist.md +111 -0
  103. package/skills/technical-writer/SKILL.md +631 -0
  104. package/skills/technical-writer/references/doc-templates.md +218 -0
  105. package/skills/testing-strategist/SKILL.md +476 -0
  106. package/skills/testing-strategist/references/testing-pyramid.md +16 -0
  107. package/skills/ux-ui-designer/SKILL.md +419 -0
  108. package/skills/ux-ui-designer/references/design-system-foundation.md +168 -0
  109. package/skills_overview.txt +94 -0
  110. package/templates/PROJECT_KICKOFF.md +284 -0
  111. package/templates/SKILL_TEMPLATE.md +131 -0
  112. package/templates/USAGE.md +95 -0
  113. package/templates/agent-python/README.md +71 -0
  114. package/templates/agent-python/agent.py +272 -0
  115. package/templates/agent-python/config.yaml +76 -0
  116. package/templates/agent-python/prompts/system.md +109 -0
  117. package/templates/agent-python/requirements.txt +7 -0
  118. package/templates/automation-n8n/README.md +14 -0
  119. package/templates/automation-n8n/webhook-handler.json +57 -0
  120. package/templates/backend-node/Dockerfile +12 -0
  121. package/templates/backend-node/README.md +15 -0
  122. package/templates/backend-node/package.json +30 -0
  123. package/templates/backend-node/src/index.ts +19 -0
  124. package/templates/backend-node/src/routes.ts +7 -0
  125. package/templates/backend-node/tsconfig.json +22 -0
  126. package/templates/backend-python/Dockerfile +11 -0
  127. package/templates/backend-python/README.md +78 -0
  128. package/templates/backend-python/app/core/config.py +12 -0
  129. package/templates/backend-python/app/core/database.py +12 -0
  130. package/templates/backend-python/app/main.py +17 -0
  131. package/templates/backend-python/app/routers/__init__.py +1 -0
  132. package/templates/backend-python/app/routers/health.py +7 -0
  133. package/templates/backend-python/requirements-dev.txt +6 -0
  134. package/templates/backend-python/requirements.txt +4 -0
  135. package/templates/backend-python/tests/test_health.py +9 -0
  136. package/templates/checkpoint.yaml +117 -0
  137. package/templates/database/README.md +474 -0
  138. package/templates/frontend-react/README.md +446 -0
  139. package/templates/plan.yaml +320 -0
  140. package/templates/session.yaml +125 -0
  141. package/templates/spec.yaml +229 -0
  142. package/templates/tasks.yaml +330 -0
  143. package/workflows/bugfix-backend.md +380 -0
  144. package/workflows/documentation.md +232 -0
  145. package/workflows/generate-prd.md +320 -0
  146. package/workflows/ideation.md +396 -0
  147. package/workflows/new-agent-ia.md +497 -0
  148. package/workflows/new-automation.md +374 -0
  149. package/workflows/new-feature.md +290 -0
  150. package/workflows/optimize-performance.md +373 -0
  151. package/workflows/resolve-github-issue.md +524 -0
  152. package/workflows/security-review.md +291 -0
  153. package/workflows/spec-driven.md +476 -0
  154. package/workflows/testing-strategy.md +296 -0
  155. package/workflows/third-party-integration.md +277 -0
@@ -0,0 +1,607 @@
1
+ # Reglas para Agentes de IA - LMAgent
2
+
3
+ > **Tipo**: `rule` | **Versión**: 2.1 | **Actualización**: 2026-01
4
+
5
+ ## 📌 Quick Reference
6
+
7
+ | Principio | Regla |
8
+ |-----------|-------|
9
+ | **Tool-first** | El LLM decide, las tools ejecutan. NUNCA ejecutar código directo del LLM. |
10
+ | **Stateless** | Agentes sin estado en memoria. Usar Redis para persistencia. |
11
+ | **Observable** | Logging de TODAS las interacciones LLM + Cost Tracking obligatorio. |
12
+ | **MCP Standard** | Usar Model Context Protocol (MCP) para definir herramientas. |
13
+ | **Guardrails** | Timeout, rate limit y validación de outputs OBLIGATORIOS. |
14
+
15
+ ### 👥 Roles que usan esta regla
16
+ `ai-agent-engineer`, `prompt-engineer`, `backend-engineer`, `architect`
17
+
18
+ ---
19
+
20
+ Este documento define las reglas y mejores prácticas para el desarrollo de agentes de IA.
21
+
22
+ ## Principios Fundamentales
23
+
24
+ ### 1. Separación de Responsabilidades
25
+ ```
26
+ ┌─────────────────────────────────────────────────────────────┐
27
+ │ AGENTE = Orquestador de Herramientas │
28
+ │ │
29
+ │ ┌─────────────┐ │
30
+ │ │ LLM │ ← Razonamiento y decisiones │
31
+ │ └──────┬──────┘ │
32
+ │ │ │
33
+ │ ▼ │
34
+ │ ┌─────────────┐ │
35
+ │ │ Tools │ ← Acciones concretas │
36
+ │ └──────┬──────┘ │
37
+ │ │ │
38
+ │ ▼ │
39
+ │ ┌─────────────┐ │
40
+ │ │ State │ ← Memoria y contexto (Redis) │
41
+ │ └─────────────┘ │
42
+ └─────────────────────────────────────────────────────────────┘
43
+ ```
44
+
45
+ ### 2. Stateless Agents
46
+ - Los agentes NO deben almacenar estado en memoria
47
+ - Usar Redis para estado entre invocaciones
48
+ - Cada llamada debe ser autocontenida
49
+
50
+ ### 3. Observable Everything
51
+ - Logging de todas las interacciones con LLM
52
+ - Tracking de costos por sesión
53
+ - Métricas de latencia y errores
54
+
55
+ ---
56
+
57
+ ## Estructura de Agentes
58
+
59
+ ### Directorio
60
+ ```
61
+ agents/
62
+ ├── config/
63
+ │ ├── agents.yaml # Definición de agentes
64
+ │ ├── tools.yaml # Registry de herramientas
65
+ │ └── models.yaml # Configuración de LLMs
66
+ ├── python/
67
+ │ ├── __init__.py
68
+ │ ├── base_agent.py # Clase base
69
+ │ ├── agent_runner.py # Ejecutor
70
+ │ ├── tools/ # Herramientas
71
+ │ │ ├── __init__.py
72
+ │ │ ├── registry.py # Registry de tools
73
+ │ │ ├── base_tool.py # Interface base
74
+ │ │ ├── http_tool.py
75
+ │ │ ├── database_tool.py
76
+ │ │ └── ...
77
+ │ ├── prompts/ # Templates de prompts
78
+ │ │ ├── system/
79
+ │ │ └── personas/
80
+ │ └── llm/ # Providers
81
+ │ ├── base_provider.py
82
+ │ ├── openai_provider.py
83
+ │ └── ...
84
+ └── README.md
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Diseño de Herramientas
90
+
91
+ ### Interface Base
92
+ ```python
93
+ from abc import ABC, abstractmethod
94
+ from pydantic import BaseModel, Field
95
+ from typing import Any, Dict
96
+
97
+ class ToolResult(BaseModel):
98
+ """Resultado estándar de una herramienta."""
99
+ success: bool
100
+ data: Any = None
101
+ error: str | None = None
102
+ metadata: Dict[str, Any] = {}
103
+
104
+ class BaseTool(BaseModel, ABC):
105
+ """Interface base para todas las herramientas."""
106
+
107
+ name: str
108
+ description: str
109
+
110
+ @abstractmethod
111
+ async def execute(self, **kwargs) -> ToolResult:
112
+ """Ejecuta la herramienta."""
113
+ pass
114
+
115
+ def to_function_schema(self) -> Dict:
116
+ """Genera schema para function calling de OpenAI/Anthropic."""
117
+ pass
118
+ ```
119
+
120
+ ### Reglas de Diseño de Tools
121
+
122
+ ✅ **Hacer:**
123
+ - Una herramienta = una responsabilidad
124
+ - Parámetros opcionales con defaults sensatos
125
+ - Retornar siempre `ToolResult` consistente
126
+ - Incluir descripción clara para el LLM
127
+ - Logging de todas las ejecuciones
128
+ - Timeouts explícitos
129
+
130
+ ❌ **No hacer:**
131
+ - Tools que hacen múltiples cosas
132
+ - Parámetros sin tipos o descripciones
133
+ - Excepciones no manejadas
134
+ - Llamadas externas sin timeout
135
+ - Logs con datos sensibles
136
+
137
+ ### Ejemplo de Tool Bien Diseñada
138
+ ```python
139
+ from lmagent.tools.base import BaseTool, ToolResult
140
+ from pydantic import Field
141
+ from typing import Optional
142
+ import httpx
143
+ import structlog
144
+
145
+ logger = structlog.get_logger()
146
+
147
+ class HttpRequestTool(BaseTool):
148
+ """
149
+ Realiza requests HTTP a APIs externas.
150
+
151
+ Usa esta herramienta cuando necesites:
152
+ - Obtener datos de una API externa
153
+ - Enviar datos a un webhook
154
+ - Verificar disponibilidad de un servicio
155
+
156
+ NO uses para:
157
+ - Acceso a base de datos (usa database_query)
158
+ - Operaciones en Redis (usa redis_cache)
159
+ """
160
+
161
+ name: str = "http_request"
162
+ description: str = "Make HTTP requests to external APIs"
163
+
164
+ async def execute(
165
+ self,
166
+ url: str = Field(..., description="Full URL to request"),
167
+ method: str = Field("GET", description="HTTP method"),
168
+ headers: Optional[dict] = Field(None, description="Request headers"),
169
+ body: Optional[dict] = Field(None, description="Request body for POST/PUT"),
170
+ timeout: int = Field(30, description="Timeout in seconds")
171
+ ) -> ToolResult:
172
+ """Ejecuta request HTTP."""
173
+ logger.info(
174
+ "http_request_start",
175
+ url=url,
176
+ method=method
177
+ )
178
+
179
+ try:
180
+ async with httpx.AsyncClient(timeout=timeout) as client:
181
+ response = await client.request(
182
+ method=method,
183
+ url=url,
184
+ headers=headers,
185
+ json=body
186
+ )
187
+
188
+ logger.info(
189
+ "http_request_complete",
190
+ url=url,
191
+ status_code=response.status_code
192
+ )
193
+
194
+ return ToolResult(
195
+ success=response.is_success,
196
+ data={
197
+ "status_code": response.status_code,
198
+ "body": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text,
199
+ "headers": dict(response.headers)
200
+ },
201
+ metadata={
202
+ "elapsed_ms": response.elapsed.total_seconds() * 1000
203
+ }
204
+ )
205
+
206
+ except httpx.TimeoutException:
207
+ logger.warning("http_request_timeout", url=url, timeout=timeout)
208
+ return ToolResult(
209
+ success=False,
210
+ error=f"Request timed out after {timeout}s",
211
+ metadata={"suggestion": "Try increasing timeout or check if service is available"}
212
+ )
213
+ except Exception as e:
214
+ logger.error("http_request_error", url=url, error=str(e))
215
+ return ToolResult(
216
+ success=False,
217
+ error=str(e)
218
+ )
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Diseño de Prompts
224
+
225
+ ### System Prompt Template
226
+ ```markdown
227
+ You are {agent_name}, an AI assistant specialized in {domain}.
228
+
229
+ ## Your Role
230
+ {role_description}
231
+
232
+ ## Available Tools
233
+ You have access to the following tools:
234
+ {tools_list}
235
+
236
+ When using tools, always:
237
+ 1. Think about which tool is most appropriate
238
+ 2. Provide all required parameters
239
+ 3. Handle errors gracefully
240
+
241
+ ## Response Guidelines
242
+ - Be concise and direct
243
+ - Use structured formats when appropriate
244
+ - Cite sources when using external data
245
+ - Admit when you don't know something
246
+
247
+ ## Constraints
248
+ - Maximum {max_tokens} tokens per response
249
+ - Do not make up information
250
+ - Do not access tools outside your allowed set
251
+ ```
252
+
253
+ ### Mejores Prácticas de Prompts
254
+
255
+ 1. **Sea específico**: Evitar instrucciones vagas
256
+ ```
257
+ ❌ "Ayuda al usuario"
258
+ ✅ "Ayuda al usuario a encontrar información sobre productos.
259
+ Si no encuentras el producto, sugiere alternativas similares."
260
+ ```
261
+
262
+ 2. **Dé ejemplos (Few-shot)**:
263
+ ```
264
+ ## Examples
265
+
266
+ User: "Find product with SKU ABC123"
267
+ Assistant: I'll search for that product.
268
+ [Uses: search_product(sku="ABC123")]
269
+ Result: Found "Widget Pro" priced at $99.99
270
+
271
+ User: "What's the status of order 789?"
272
+ Assistant: Let me check that order.
273
+ [Uses: get_order(order_id="789")]
274
+ Result: Order is "shipped", tracking: XYZ...
275
+ ```
276
+
277
+ 3. **Estructure el output**:
278
+ ```
279
+ ## Output Format
280
+
281
+ Always respond in this JSON format:
282
+ {
283
+ "thinking": "Your reasoning process",
284
+ "action": "Tool name or 'respond'",
285
+ "action_input": {...} or null,
286
+ "response": "Final response to user" or null
287
+ }
288
+ ```
289
+
290
+ 4. **Defina límites claros**:
291
+ ```
292
+ ## You CANNOT:
293
+ - Access production databases directly
294
+ - Send emails without user confirmation
295
+ - Make purchases or financial transactions
296
+ - Access files outside the project directory
297
+ ```
298
+
299
+ ---
300
+
301
+ ## Manejo de Errores
302
+
303
+ ### Estrategia de Reintentos
304
+ ```python
305
+ from tenacity import retry, stop_after_attempt, wait_exponential
306
+
307
+ class AgentRunner:
308
+ @retry(
309
+ stop=stop_after_attempt(3),
310
+ wait=wait_exponential(multiplier=1, min=1, max=10)
311
+ )
312
+ async def call_llm(self, messages: list) -> LLMResponse:
313
+ """Llama al LLM con reintentos automáticos."""
314
+ return await self.llm_provider.complete(messages)
315
+ ```
316
+
317
+ ### Fallback de Modelos
318
+ ```python
319
+ async def get_completion(self, messages: list) -> str:
320
+ """Intenta con modelo principal, fallback a secundario."""
321
+ try:
322
+ return await self.primary_model.complete(messages)
323
+ except RateLimitError:
324
+ logger.warning("primary_model_rate_limited, using fallback")
325
+ return await self.fallback_model.complete(messages)
326
+ except Exception as e:
327
+ logger.error("llm_error", error=str(e))
328
+ raise
329
+ ```
330
+
331
+ ### Errores del Agente
332
+ ```python
333
+ class AgentError(Exception):
334
+ """Error base de agentes."""
335
+ pass
336
+
337
+ class ToolExecutionError(AgentError):
338
+ """Error ejecutando herramienta."""
339
+ def __init__(self, tool_name: str, error: str):
340
+ self.tool_name = tool_name
341
+ super().__init__(f"Tool '{tool_name}' failed: {error}")
342
+
343
+ class CostLimitExceeded(AgentError):
344
+ """Se excedió el límite de costo."""
345
+ def __init__(self, current_cost: float, limit: float):
346
+ self.current_cost = current_cost
347
+ self.limit = limit
348
+ super().__init__(f"Cost ${current_cost:.2f} exceeded limit ${limit:.2f}")
349
+
350
+ class MaxIterationsExceeded(AgentError):
351
+ """Se excedió el número máximo de iteraciones."""
352
+ pass
353
+ ```
354
+
355
+ ---
356
+
357
+ ## Observabilidad
358
+
359
+ ### Logging Estructurado
360
+ ```python
361
+ import structlog
362
+
363
+ logger = structlog.get_logger()
364
+
365
+ class Agent:
366
+ async def run(self, user_input: str) -> str:
367
+ run_id = generate_run_id()
368
+
369
+ logger.info(
370
+ "agent_run_start",
371
+ run_id=run_id,
372
+ agent_name=self.name,
373
+ input_length=len(user_input)
374
+ )
375
+
376
+ try:
377
+ result = await self._execute(user_input)
378
+
379
+ logger.info(
380
+ "agent_run_complete",
381
+ run_id=run_id,
382
+ iterations=self.iteration_count,
383
+ total_cost=self.cost_tracker.total,
384
+ output_length=len(result)
385
+ )
386
+
387
+ return result
388
+
389
+ except Exception as e:
390
+ logger.error(
391
+ "agent_run_error",
392
+ run_id=run_id,
393
+ error=str(e),
394
+ error_type=type(e).__name__
395
+ )
396
+ raise
397
+ ```
398
+
399
+ ### Trajectory Logging
400
+ ```python
401
+ class TrajectoryLogger:
402
+ """Registra el historial completo de una ejecución."""
403
+
404
+ def __init__(self, run_id: str):
405
+ self.run_id = run_id
406
+ self.steps: list[TrajectoryStep] = []
407
+
408
+ def log_step(
409
+ self,
410
+ step_num: int,
411
+ thought: str,
412
+ action: str,
413
+ action_input: dict,
414
+ observation: str
415
+ ):
416
+ step = TrajectoryStep(
417
+ step=step_num,
418
+ timestamp=datetime.utcnow(),
419
+ thought=thought,
420
+ action=action,
421
+ action_input=action_input,
422
+ observation=observation
423
+ )
424
+ self.steps.append(step)
425
+
426
+ # Log visual para debugging
427
+ print(f"🤠 INFO STEP {step_num}")
428
+ print(f"💭 THOUGHT: {thought}")
429
+ print(f"🎬 ACTION: {action}")
430
+ print(f"📤 OBSERVATION: {observation[:200]}...")
431
+
432
+ def save(self, path: str):
433
+ """Guarda trajectory para análisis posterior."""
434
+ with open(path, 'w') as f:
435
+ json.dump([s.model_dump() for s in self.steps], f, indent=2)
436
+ ```
437
+
438
+ ### Cost Tracking
439
+ ```python
440
+ class CostTracker:
441
+ """Monitorea costos de API por sesión."""
442
+
443
+ def __init__(self, max_cost: float = 2.00):
444
+ self.max_cost = max_cost
445
+ self.total = 0.0
446
+ self.calls: list[CostEntry] = []
447
+
448
+ def track(
449
+ self,
450
+ model: str,
451
+ input_tokens: int,
452
+ output_tokens: int
453
+ ) -> float:
454
+ """Registra y valida costo de llamada."""
455
+ cost = self._calculate_cost(model, input_tokens, output_tokens)
456
+
457
+ self.calls.append(CostEntry(
458
+ model=model,
459
+ input_tokens=input_tokens,
460
+ output_tokens=output_tokens,
461
+ cost=cost,
462
+ timestamp=datetime.utcnow()
463
+ ))
464
+
465
+ self.total += cost
466
+
467
+ if self.total >= self.max_cost:
468
+ raise CostLimitExceeded(self.total, self.max_cost)
469
+
470
+ return cost
471
+
472
+ def _calculate_cost(
473
+ self,
474
+ model: str,
475
+ input_tokens: int,
476
+ output_tokens: int
477
+ ) -> float:
478
+ """Calcula costo según modelo."""
479
+ # Ver config/models.yaml para precios
480
+ prices = PRICING.get(model, {"input": 0.01, "output": 0.03})
481
+ return (
482
+ (input_tokens / 1000) * prices["input"] +
483
+ (output_tokens / 1000) * prices["output"]
484
+ )
485
+ ```
486
+
487
+ ---
488
+
489
+ ## Seguridad
490
+
491
+ ### Guardrails Obligatorios
492
+
493
+ 1. **Validar outputs de LLM** antes de ejecutar
494
+ 2. **Limitar herramientas** a las necesarias
495
+ 3. **Rate limiting** por usuario/sesión
496
+ 4. **Sanitizar inputs** del usuario
497
+ 5. **No exponer** API keys en logs
498
+
499
+ ### Sandbox para Ejecución de Código
500
+ ```python
501
+ class SandboxTool(BaseTool):
502
+ """Ejecuta código en ambiente aislado."""
503
+
504
+ name: str = "sandbox_execute"
505
+ description: str = "Execute code in isolated Docker container"
506
+
507
+ # Límites de seguridad
508
+ MAX_MEMORY = "256m"
509
+ MAX_CPU = "0.5"
510
+ MAX_TIMEOUT = 300
511
+ NETWORK = "none" # Sin acceso a red
512
+
513
+ async def execute(
514
+ self,
515
+ code: str,
516
+ language: str = "python",
517
+ timeout: int = 30
518
+ ) -> ToolResult:
519
+ # Sanitizar código
520
+ if self._is_dangerous(code):
521
+ return ToolResult(
522
+ success=False,
523
+ error="Code contains potentially dangerous operations"
524
+ )
525
+
526
+ # Ejecutar en container
527
+ result = await self._run_in_docker(
528
+ code=code,
529
+ language=language,
530
+ timeout=min(timeout, self.MAX_TIMEOUT)
531
+ )
532
+
533
+ return result
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Integración con n8n
539
+
540
+ Los agentes deben exponerse como endpoints HTTP para que n8n pueda invocarlos:
541
+
542
+ ```python
543
+ from fastapi import APIRouter
544
+ from pydantic import BaseModel
545
+
546
+ router = APIRouter(prefix="/agents", tags=["agents"])
547
+
548
+ class AgentRequest(BaseModel):
549
+ agent_name: str
550
+ input: str
551
+ context: dict = {}
552
+ max_cost: float = 2.00
553
+
554
+ class AgentResponse(BaseModel):
555
+ success: bool
556
+ output: str
557
+ cost: float
558
+ iterations: int
559
+ trajectory_id: str | None = None
560
+
561
+ @router.post("/run", response_model=AgentResponse)
562
+ async def run_agent(request: AgentRequest) -> AgentResponse:
563
+ """
564
+ Ejecuta un agente con el input dado.
565
+
566
+ Diseñado para ser llamado desde n8n via HTTP Request node.
567
+ """
568
+ agent = await load_agent(request.agent_name)
569
+ agent.cost_tracker.max_cost = request.max_cost
570
+
571
+ result = await agent.run(request.input, context=request.context)
572
+
573
+ return AgentResponse(
574
+ success=True,
575
+ output=result,
576
+ cost=agent.cost_tracker.total,
577
+ iterations=agent.iteration_count,
578
+ trajectory_id=agent.trajectory_logger.run_id
579
+ )
580
+ ```
581
+
582
+ ---
583
+
584
+ ## ✅ Checklist de Validación (Antes de Deploy)
585
+
586
+ ### Diseño del Agente
587
+ - [ ] Arquitectura elegida y documentada (ReAct, Tool-only, etc.)
588
+ - [ ] Tools definidas con schemas MCP/Pydantic estrictos
589
+ - [ ] System Prompt aprobado por /prompt engineer
590
+
591
+ ### Seguridad (Guardrails)
592
+ - [ ] Timeout configurado en TODAS las tools
593
+ - [ ] Rate limit por usuario/sesión
594
+ - [ ] Cost limit configurado (default: $2.00)
595
+ - [ ] Outputs del LLM validados antes de ejecutar
596
+ - [ ] Sin API keys en logs
597
+
598
+ ### Observabilidad
599
+ - [ ] Logging estructurado con structlog
600
+ - [ ] Trajectory logging habilitado
601
+ - [ ] Cost tracking implementado
602
+ - [ ] Métricas de latencia expuestas
603
+
604
+ ### Integración
605
+ - [ ] Endpoint HTTP para n8n creado
606
+ - [ ] Documentación de API generada
607
+ - [ ] Tests de evals pasando (Faithfulness > 0.7)