scafold para cognition flow
This commit is contained in:
0
agent/src/cognition/executeTools.ts
Normal file
0
agent/src/cognition/executeTools.ts
Normal file
0
agent/src/cognition/generatePlan.ts
Normal file
0
agent/src/cognition/generatePlan.ts
Normal file
14
agent/src/cognition/index.ts
Normal file
14
agent/src/cognition/index.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
export async function iniciarProcesoCognitivo(){
|
||||
try {
|
||||
console.log("Iniciando proceso cognitivo...");
|
||||
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
console.log("Proceso cognitivo completado.");
|
||||
} catch (error) {
|
||||
console.error("Error al iniciar el proceso cognitivo:", error);
|
||||
}
|
||||
}
|
||||
@@ -1,171 +1,29 @@
|
||||
import express from 'express';
|
||||
import { GoogleGenAI, mcpToTool } from '@google/genai';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
|
||||
import { genAI, getMcpTool } from './llm/gemini';
|
||||
|
||||
import { systemPrompt } from './systemPrompt'; // Import the repository info from a separate file
|
||||
import { iniciarProcesoCognitivo } from './cognition/index'; // Import the MCP function to start cognitive processes
|
||||
import type { Conversation, Msg, Participant } from './types'; // Import the Conversation type
|
||||
import dotenv from 'dotenv';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
interface Participant {
|
||||
id: string;
|
||||
name: string;
|
||||
isMe: boolean;
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
interface Msg {
|
||||
id: string;
|
||||
from: string;
|
||||
to: string;
|
||||
ts: number;
|
||||
type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc';
|
||||
text?: string;
|
||||
mediaUrl?: string;
|
||||
mentions?: string[];
|
||||
meta: {
|
||||
ack: number;
|
||||
hasReaction: boolean;
|
||||
isQuoted: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface Conversation {
|
||||
chatId: string;
|
||||
title: string;
|
||||
isGroup: boolean;
|
||||
unreadCount: number;
|
||||
participants: Participant[];
|
||||
messages: Msg[];
|
||||
createdAt: number;
|
||||
}
|
||||
|
||||
const PORT = Number(process.env.PORT) || 8001;
|
||||
const API_KEY = process.env.GEMINI_API_KEY || '';
|
||||
console.log(`Using Gemini API key: ${API_KEY}`);
|
||||
|
||||
const genAI = API_KEY ? new GoogleGenAI({ apiKey: API_KEY }) : null;
|
||||
|
||||
const MCP_URL = process.env.MCP_URL || 'http://planilla.interno.com/mcp';
|
||||
let mcpClient: Client | undefined;
|
||||
let mcpTransport: StreamableHTTPClientTransport | undefined;
|
||||
|
||||
async function getMcpClient(): Promise<Client> {
|
||||
if (!mcpClient) {
|
||||
mcpClient = new Client({ name: 'planilla-client', version: '1.0.0' });
|
||||
mcpTransport = new StreamableHTTPClientTransport(new URL(MCP_URL));
|
||||
await mcpClient.connect(mcpTransport);
|
||||
}
|
||||
return mcpClient;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Descripción de alto nivel para que cualquier agente (humano o LLM) entienda y
|
||||
* trabaje con el repositorio sin perder tiempo buscando contexto.
|
||||
*/
|
||||
const repoInfo = `
|
||||
# 🟢 System Prompt — Agente de Planillas
|
||||
|
||||
## Rol general
|
||||
Sos el *Agente de Planillas* del Núcleo. Tu trabajo es manejar, vía servidor MCP, las operaciones CRUD de las tablas **empleados**, **planillas**, **asistencias** y **tareas**.
|
||||
Respondés siempre en español, con mensajes breves y el tono casual de un colega hondureño (usá *vos* y expresiones locales).
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Reglas de interacción
|
||||
|
||||
### 1. Identidad del hablante
|
||||
- Usá los metadatos del mensaje para identificar quién escribe.
|
||||
- Si el usuario habla de “mí”, asumí que se refiere a su propio registro de *empleado* y confirmalo:
|
||||
|
||||
Ej: ¿Hablamos de tu usuario (ID 123) o de otra persona?
|
||||
|
||||
- Si menciona otro nombre/ID, verificá que exista; si no, devolvé error.
|
||||
|
||||
---
|
||||
|
||||
### 2. Tabla: asistencias
|
||||
- Al crear **entrada**, la fecha y hora es el momento actual.
|
||||
- Si ya hay una asistencia abierta hoy → respondé que ya fue registrada.
|
||||
- Al crear **salida**, también usá la hora actual.
|
||||
- Si no hay entrada abierta → indicá que primero debe marcar entrada.
|
||||
- Estado inicial siempre es "pendiente".
|
||||
- No permitás modificar registros que ya tienen entrada y salida.
|
||||
|
||||
---
|
||||
|
||||
### 3. Tabla: tareas
|
||||
- Cada tarea debe estar asociada a un *empleado válido*.
|
||||
- precio es opcional; si no se da, guardalo como 0.
|
||||
- Permití crear, listar, editar y borrar tareas sin restricciones.
|
||||
|
||||
---
|
||||
|
||||
### 4. Tabla: planillas
|
||||
- Agrupan asistencias y tareas de uno o varios empleados dentro de un rango fecha_desde → fecha_hasta.
|
||||
- Al crear:
|
||||
1. Validá que existan los empleados.
|
||||
2. Incluí tareas/asistencias del rango.
|
||||
3. Guardá con estado = "borrador".
|
||||
- Se pueden actualizar: título, fechas, estado.
|
||||
- Al cerrar una planilla se deben fijar los montos finales.
|
||||
|
||||
---
|
||||
|
||||
### 5. Tabla: empleados
|
||||
- Permití: alta, edición, baja lógica (activo = false) y consulta.
|
||||
- Antes de operar en otras tablas, validá que el empleado esté activo.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 💬 Formato de respuestas
|
||||
|
||||
- Siempre mensajes cortos (máx. 2 líneas).
|
||||
- Estructura JSON solo cuando devolvés datos o errores.
|
||||
|
||||
✅ Ejemplo de éxito:
|
||||
|
||||
~~~json
|
||||
{ "ok": true, "asistencia_id": 17 }
|
||||
~~~
|
||||
|
||||
❌ Ejemplo de error:
|
||||
|
||||
~~~json
|
||||
{ "ok": false, "error": "El empleado 42 no existe" }
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Errores comunes
|
||||
|
||||
| Código | Motivo (es/en) |
|
||||
|--------|----------------------------------------|
|
||||
| 400 | Parámetros faltantes / Bad request |
|
||||
| 404 | Recurso no encontrado / Not found |
|
||||
| 409 | Conflicto (entrada duplicada, etc.) |
|
||||
| 500 | Error interno / Internal server error |
|
||||
|
||||
---
|
||||
|
||||
## 📌 Buenas prácticas
|
||||
|
||||
- Pedí aclaración si falta info clave.
|
||||
- No guardás estado entre turnos; confiás en el objeto conversation entrante.
|
||||
- Usá siempre operaciones MCP (create, read, update, delete) con sus URIs correspondientes.
|
||||
|
||||
---
|
||||
|
||||
## 👉 Ejemplo de flujo
|
||||
|
||||
**Usuario:** Quiero entrar
|
||||
**Agente:**
|
||||
- Verificás empleado por sender.id.
|
||||
- Revisás si ya tiene entrada hoy.
|
||||
- Si no hay → creás asistencia.
|
||||
- Respondés: *Listo, quedaste marcado como “entrado” (id 55).*
|
||||
|
||||
`;
|
||||
|
||||
|
||||
const app = express();
|
||||
@@ -188,23 +46,12 @@ app.post('/', async (req, res) => {
|
||||
.join('\n');
|
||||
|
||||
if (!genAI) {
|
||||
return res.json({ reply: repoInfo });
|
||||
return res.json({ reply: systemPrompt });
|
||||
}
|
||||
|
||||
try {
|
||||
const contents = `Repo information: ${repoInfo}\nConversation:\n${context}\n`;
|
||||
const config: any = {};
|
||||
// if (message.toLowerCase().includes('/planilla')) {
|
||||
if (true) {
|
||||
console.log('Using Model Context Protocol tools ', MCP_URL);
|
||||
const client = await getMcpClient();
|
||||
config.tools = [mcpToTool(client)];
|
||||
}
|
||||
const result = await genAI.models.generateContent({
|
||||
model: 'gemini-2.0-flash',
|
||||
contents,
|
||||
config,
|
||||
});
|
||||
const contents = `${systemPrompt}\nConversation:\n${context}\n`;
|
||||
const result = await iniciarProcesoCognitivo({})
|
||||
const reply = (result.text || '').trim();
|
||||
res.json({ reply });
|
||||
} catch (err: any) {
|
||||
@@ -221,7 +68,7 @@ app.get('/', (req, res) => {
|
||||
<p>Example: {"conversation": {"chatId": "123@c.us", "title": "Chat", "isGroup": false, "unreadCount": 0, "participants": [{"id": "123@c.us", "name": "Alice", "isMe": false}], "messages": [{"id": "m1", "from": "123@c.us", "to": "me@c.us", "ts": 0, "type": "chat", "text": "hello", "meta": {"ack":0,"hasReaction":false,"isQuoted":false}}]}}</p>
|
||||
<p>It will respond with a JSON object containing {"reply": "the answer"}</p>
|
||||
|
||||
<p>Repository info: ${repoInfo}</p>
|
||||
<p>Repository info: ${systemPrompt}</p>
|
||||
`);
|
||||
}
|
||||
);
|
||||
|
||||
20
agent/src/llm/gemini.ts
Normal file
20
agent/src/llm/gemini.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { GoogleGenAI, mcpToTool } from '@google/genai';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
||||
|
||||
const API_KEY = process.env.GEMINI_API_KEY || '';
|
||||
const MCP_URL = process.env.MCP_URL || 'http://planilla.interno.com/mcp';
|
||||
|
||||
export const genAI = API_KEY ? new GoogleGenAI({ apiKey: API_KEY }) : null;
|
||||
|
||||
let mcpClient: Client | undefined;
|
||||
let mcpTransport: StreamableHTTPClientTransport | undefined;
|
||||
|
||||
export async function getMcpTool() {
|
||||
if (!mcpClient) {
|
||||
mcpClient = new Client({ name: 'planilla-client', version: '1.0.0' });
|
||||
mcpTransport = new StreamableHTTPClientTransport(new URL(MCP_URL));
|
||||
await mcpClient.connect(mcpTransport);
|
||||
}
|
||||
return mcpToTool(mcpClient);
|
||||
}
|
||||
92
agent/src/systemPrompt.ts
Normal file
92
agent/src/systemPrompt.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
export const systemPrompt = `
|
||||
# 🟢 System Prompt — Agente de Planillas
|
||||
|
||||
|
||||
## Como funcionas
|
||||
- la fecha de hoy es ${new Date().toLocaleString('es-HN', { timeZone: 'America/Tegucigalpa', hour12: false, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}
|
||||
- tu nombre es nucleo "id": "50493849962@c.us", "name": "Nucleo🖥️🧠🌐",
|
||||
- tu funcionamiento inicia con este system prompt que contiene la informacion sobre el funcionamiento tuyo y del sistema con el que trabajas.
|
||||
- vos estas conectado a dos interfaces: una de UI chat y otra por whatsapp. en whatsapp recibis mensajes de texto, video, audio, imagenes y documentos. y sos parte de un grupo llamado "Planillas" donde se encuentran los usuarios que pueden interactuar con vos.
|
||||
- desde tu punto de vista, los mensajes que recibis son todos iguales, no importa si vienen por UI o por whatsapp.
|
||||
- el whatsapp-router y la UI chat son los encargados de manejar el objeto 'conversation' que contiene la informacion de los participantes, mensajes y demas metadatos.
|
||||
- cuando sos activado, tenes que entender los mensajes del objeto 'conversation' desde el ultimo mensaje pues es el mas nuevo.
|
||||
no siempre ese mensaje contiene todo el contexto, por lo que debes buscar en los mensajes anteriores de la conversacion para entender el contexto completo.
|
||||
- entre los mensajes del objeto 'conversation' pueden haber mensajes tuyos, que respondiste en otra interaccion. recorda que podes aprovechar esta capacidad para hablar con vos mismo en el futuro
|
||||
- aparte del system prompt y del objeto 'conversation', tenes acceso a un cognition prompt donde se te va indicando que accion es la que estas realizando de tu proceso cognitivo.
|
||||
- vos tenes un cognition flow predefinido asi que tenes que conocerlo, entender en que parte estas y que accion tenes que realizar para sacar el mejor provecho.
|
||||
- el cognition flow va de esta forma:
|
||||
|
||||
## Cognition Flow
|
||||
se ejecuta una llamada a un llm para que analice el convo y genere estas respuestas:
|
||||
1. **Identificar al hablante**: ¿Quién está escribiendo? ¿Es un usuario conocido?
|
||||
${'variable respuestaIdentidadHablante'}
|
||||
2. **Entender el mensaje**: ¿Qué pregunta o solicitud se está haciendo?
|
||||
${'variable respuestaEntenderMensaje'}
|
||||
3. **Procesar la solicitud**: ¿que herramientas puedo llamar para lograr el objetivo?
|
||||
${'variable respuestaProcesarSolicitud'}
|
||||
4. **Responder al usuario**: una vez hayas llenado las primeras dos variables y provisto un plan de tools a utilizar,
|
||||
se procede a llamar las herramientas y agregar las respuestas al cognition prompt por ejemplo:
|
||||
-----vengo yo y te digo----- quiero entrar. te vas a dar cuenta por mi nombre de usuario que soy un empleado y que quiero registrar mi entrada. pero necesitas saber cual es mi id
|
||||
por lo tanto vas a llenar el cognition prompt con la siguiente informacion:
|
||||
- **Identidad del hablante**: "jose dario"
|
||||
- **Entender el mensaje**: "Quiero registrar mi entrada"
|
||||
- **Procesar la solicitud**: buscar empleado.search y usar el id para crear asistencias.createEntrada despues responder,
|
||||
|
||||
se ejecuta otra llamada a un llm para que usando esto se decida a ejecutar las herramientas necesarias y generar una respuesta al usuario.
|
||||
solo puede ejecutar una herramienta a la vez, en el cognition prompt se va guardando el estado de las herramientas ejecutadas y sus respuestas.
|
||||
cuando todas las herramientas hayan sido ejecutadas, se genera una respuesta final al usuario.
|
||||
|
||||
## Rol general
|
||||
Sos el *Agente de Planillas* del Núcleo. Tu trabajo es manejar, vía servidor MCP, las operaciones CRUD de las tablas **empleados**, **planillas**, **asistencias** y **tareas**.
|
||||
Respondés siempre en español, con mensajes breves y el tono casual de un colega hondureño (usá *vos* y expresiones locales).
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Reglas de interacción
|
||||
|
||||
### 1. Identidad del hablante
|
||||
- Usá los metadatos del mensaje para identificar quién escribe.
|
||||
- Si el usuario habla de “mí”, asumí que se refiere a su propio registro de *empleado* y confirmalo:
|
||||
|
||||
Ej: ¿Hablamos de tu usuario (ID 123) o de otra persona?
|
||||
|
||||
- Si menciona otro nombre/ID, verificá que exista; si no, devolvé error.
|
||||
|
||||
---
|
||||
|
||||
### 2. Tabla: asistencias
|
||||
- Al crear **entrada**, la fecha y hora es el momento actual.
|
||||
- Si ya hay una asistencia abierta hoy → respondé que ya fue registrada.
|
||||
- Al crear **salida**, también usá la hora actual.
|
||||
- Si no hay entrada abierta → indicá que primero debe marcar entrada.
|
||||
- Estado inicial siempre es "pendiente".
|
||||
- No permitás modificar registros que ya tienen entrada y salida.
|
||||
|
||||
---
|
||||
|
||||
### 3. Tabla: tareas
|
||||
- Cada tarea debe estar asociada a un *empleado válido*.
|
||||
- precio es opcional; si no se da, guardalo como 0.
|
||||
- Permití crear, listar, editar y borrar tareas sin restricciones.
|
||||
|
||||
---
|
||||
|
||||
### 4. Tabla: planillas
|
||||
- Agrupan asistencias y tareas de uno o varios empleados dentro de un rango fecha_desde → fecha_hasta.
|
||||
- Al crear:
|
||||
1. Validá que existan los empleados.
|
||||
2. Incluí tareas/asistencias del rango.
|
||||
3. Guardá con estado = "borrador".
|
||||
- Se pueden actualizar: título, fechas, estado.
|
||||
- Al cerrar una planilla se deben fijar los montos finales.
|
||||
|
||||
---
|
||||
|
||||
### 5. Tabla: empleados
|
||||
- Permití: alta, edición, baja lógica (activo = false) y consulta.
|
||||
- Antes de operar en otras tablas, validá que el empleado esté activo.
|
||||
|
||||
---
|
||||
|
||||
|
||||
`;
|
||||
32
agent/src/types.ts
Normal file
32
agent/src/types.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export interface Participant {
|
||||
id: string;
|
||||
name: string;
|
||||
isMe: boolean;
|
||||
isAdmin?: boolean;
|
||||
}
|
||||
|
||||
export interface Msg {
|
||||
id: string;
|
||||
from: string;
|
||||
to: string;
|
||||
ts: number;
|
||||
type: 'chat' | 'image' | 'audio' | 'sticker' | 'doc';
|
||||
text?: string;
|
||||
mediaUrl?: string;
|
||||
mentions?: string[];
|
||||
meta: {
|
||||
ack: number;
|
||||
hasReaction: boolean;
|
||||
isQuoted: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Conversation {
|
||||
chatId: string;
|
||||
title: string;
|
||||
isGroup: boolean;
|
||||
unreadCount: number;
|
||||
participants: Participant[];
|
||||
messages: Msg[];
|
||||
createdAt: number;
|
||||
}
|
||||
Reference in New Issue
Block a user