├── diagram.png
├── diagram2.jpg
├── static
├── img
│ └── bg3.jpg
├── index.html
├── js
│ └── app.js
└── css
│ └── app.css
├── requirements.txt
├── facturas.csv
├── testchromadb.py
├── main.py
├── main_rag.py
├── main_agentia.py
└── README.md
/diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acoronadoc/chatbot-sample/HEAD/diagram.png
--------------------------------------------------------------------------------
/diagram2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acoronadoc/chatbot-sample/HEAD/diagram2.jpg
--------------------------------------------------------------------------------
/static/img/bg3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/acoronadoc/chatbot-sample/HEAD/static/img/bg3.jpg
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi>=0.85.1
2 | uvicorn>=0.18.0
3 | websockets==11.0.3
4 | openai===1.55.3
5 | chromadb===0.5.23
6 | duckdb===1.1.3
7 |
--------------------------------------------------------------------------------
/facturas.csv:
--------------------------------------------------------------------------------
1 | fecha,cliente,pais,importe
2 | 2024-01-01,01,ES,15
3 | 2024-01-12,02,ES,15
4 | 2024-01-23,03,ES,15
5 | 2024-02-01,01,UK,15
6 | 2024-02-01,02,ES,15
7 | 2024-02-11,01,ES,15
8 | 2024-03-01,01,UK,15
9 | 2024-03-01,02,ES,15
10 | 2024-03-01,03,ES,15
11 |
--------------------------------------------------------------------------------
/testchromadb.py:
--------------------------------------------------------------------------------
1 | import chromadb
2 |
3 | client = chromadb.Client()
4 |
5 | collection = client.create_collection("all-my-documents")
6 |
7 | collection.add(
8 | documents=[
9 | "La empresa Lostsys se dedica a ofrecer servícios y productos a empresas sobre informática corporativa como software de gestión, CRMs, ERPs, portales corporativos, eCommerce, formación, DevOps, etc.",
10 | "En Lostsys podemos ayudarte ha mejorar tus procesos de CI/CD con nuestros productos y servícios de DevOps.",
11 | "En Lostsys podemos ayudarte a digitalizarte con nuestros servícios de desarrollo de aplicaciones corporativas.",
12 | "En Lostsys te podemos entrenar y formar a múltiples áreas de la informática corporativa como desarrollo, Data, IA o DevOps.",
13 | "En Lostsys te podemos desarrollar una tienda online para vender por todo el mundo y mas allà.",
14 | "En Lostsys te podemos desarrollar un eCommerce para vender por todo el mundo y mas allà",
15 | ],
16 | ids=["id1", "id2","id3", "id4","id5", "id6"]
17 | )
18 |
19 | results = collection.query(
20 | query_texts=["necesito formación"],
21 | n_results=2
22 | )
23 |
24 | print(results)
25 |
--------------------------------------------------------------------------------
/static/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
14 |
15 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/static/js/app.js:
--------------------------------------------------------------------------------
1 | var chatinput = document.getElementById("chatinput");
2 | var lines = document.getElementById("lines");
3 | var loadingbar = document.getElementById("loadingbar");
4 | var linesData = [ ];
5 |
6 | socket = opensocket( "/init" );
7 |
8 | function submitText() {
9 | var txt = chatinput.innerText;
10 | chatinput.innerText = "";
11 |
12 | lines.innerHTML += "" + txt + "
";
13 |
14 | linesData.push( { "role": "user", "content": txt } );
15 | socket.send( JSON.stringify( linesData ) );
16 | }
17 |
18 | function opensocket( url ) {
19 | socket = new WebSocket( "ws://" + location.host + url );
20 |
21 | socket.addEventListener("open", (event) => { });
22 |
23 | socket.addEventListener("close", (event) => { socket = opensocket( "/init" ); });
24 |
25 | socket.addEventListener("message", (event) => processMessage(event) );
26 |
27 | return socket;
28 | }
29 |
30 | function processMessage(event) {
31 | rdata = JSON.parse( event.data );
32 |
33 | if ( rdata.action == "init_system_response" ) {
34 | loadingbar.style.display = "block";
35 | lines.innerHTML += "";
36 | linesData.push( { "role": "assistant", "content": "" } );
37 | } else if ( rdata.action == "append_system_response" ) {
38 | slines = lines.querySelectorAll(".server");
39 | slines[ slines.length -1 ].innerHTML += rdata.content.replaceAll( "\n", "
" );
40 | linesData[ linesData.length -1 ].content += rdata.content;
41 | } else if ( rdata.action == "finish_system_response" ) {
42 | loadingbar.style.display = "none";
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
2 | from fastapi.responses import HTMLResponse, RedirectResponse
3 | from fastapi.staticfiles import StaticFiles
4 | from openai import AsyncOpenAI
5 | from websockets.exceptions import ConnectionClosed
6 |
7 | import uvicorn
8 |
9 | ENDPOINT = "http://127.0.0.1:39281/v1"
10 | #MODEL = "phi-3.5:3b-gguf-q4-km"
11 | #MODEL = "deepseek-r1-distill-qwen-14b:14b-gguf-q4-km"
12 | MODEL = "llama3.2:3b-gguf-q4-km"
13 |
14 | client = AsyncOpenAI(
15 | base_url=ENDPOINT,
16 | api_key="not-needed"
17 | )
18 |
19 | app = FastAPI()
20 |
21 | app.mount("/static", StaticFiles(directory="static"), name="static")
22 |
23 | @app.get("/", response_class=HTMLResponse)
24 | async def root( request: Request ):
25 | return RedirectResponse("/static/index.html")
26 |
27 | @app.websocket("/init")
28 | async def init( websocket: WebSocket ):
29 | await websocket.accept()
30 |
31 | try:
32 | while True:
33 | data = await websocket.receive_json()
34 |
35 | await websocket.send_json( { "action": "init_system_response" } )
36 | response = await process_messages( data, websocket )
37 | await websocket.send_json( { "action": "finish_system_response" } )
38 | except (WebSocketDisconnect, ConnectionClosed):
39 | print( "Conexión cerrada" )
40 |
41 | async def process_messages( messages, websocket ):
42 | completion_payload = {
43 | "messages": messages
44 | }
45 |
46 | response = await client.chat.completions.create(
47 | top_p=0.9,
48 | temperature=0.6,
49 | model=MODEL,
50 | messages=completion_payload["messages"],
51 | stream=True
52 | )
53 |
54 | respStr = ""
55 | async for chunk in response:
56 | if (not chunk.choices[0] or
57 | not chunk.choices[0].delta or
58 | not chunk.choices[0].delta.content):
59 | continue
60 |
61 | await websocket.send_json( { "action": "append_system_response", "content": chunk.choices[0].delta.content } )
62 |
63 | return respStr
64 |
65 |
66 | uvicorn.run(app, host="0.0.0.0", port=8000)
67 |
68 |
--------------------------------------------------------------------------------
/main_rag.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
2 | from fastapi.responses import HTMLResponse, RedirectResponse
3 | from fastapi.staticfiles import StaticFiles
4 | from openai import AsyncOpenAI
5 | from websockets.exceptions import ConnectionClosed
6 |
7 | import chromadb
8 | import json
9 | import uvicorn
10 |
11 | ENDPOINT = "http://127.0.0.1:39281/v1"
12 | #MODEL = "phi-3.5:3b-gguf-q4-km"
13 | #MODEL = "deepseek-r1-distill-qwen-14b:14b-gguf-q4-km"
14 | MODEL = "llama3.2:3b-gguf-q4-km"
15 |
16 | client = chromadb.Client()
17 |
18 | collection = client.create_collection("all-my-documents")
19 |
20 | collection.add(
21 | documents=[
22 | "La empresa Lostsys se dedica a ofrecer servícios y productos a empresas sobre informática corporativa como software de gestión, CRMs, ERPs, portales corporativos, eCommerce, formación, DevOps, etc.",
23 | "En Lostsys podemos ayudarte ha mejorar tus procesos de CI/CD con nuestros productos y servícios de DevOps.",
24 | "En Lostsys podemos ayudarte a digitalizarte con nuestros servícios de desarrollo de aplicaciones corporativas.",
25 | "En Lostsys te podemos entrenar y formar a múltiples áreas de la informática corporativa como desarrollo, Data, IA o DevOps.",
26 | "En Lostsys te podemos desarrollar una tienda online para vender por todo el mundo y mas allà.",
27 | "En Lostsys te podemos desarrollar un eCommerce para vender por todo el mundo y mas allà",
28 | ],
29 | ids=["id1", "id2","id3", "id4","id5", "id6"]
30 | )
31 |
32 | system_prompt = """
33 | Eres un asistente de la empresa Lostsys que ayuda a sus clientes a encontrar el servicio o producto que les interesa. Sigue estas instrucciones:
34 | - Ofrece respuestas cortas y concisas de no mas de 25 palabras.
35 | - No ofrezcas consejos, productos o servícios de terceros.
36 | - Explica al cliente cosas relacionadas con en la siguiente lista JSON: """
37 |
38 |
39 | client = AsyncOpenAI(
40 | base_url=ENDPOINT,
41 | api_key="not-needed"
42 | )
43 |
44 | app = FastAPI()
45 |
46 | app.mount("/static", StaticFiles(directory="static"), name="static")
47 |
48 | @app.get("/", response_class=HTMLResponse)
49 | async def root( request: Request ):
50 | return RedirectResponse("/static/index.html")
51 |
52 | @app.websocket("/init")
53 | async def init( websocket: WebSocket ):
54 | await websocket.accept()
55 |
56 | try:
57 | while True:
58 | data = await websocket.receive_json()
59 |
60 | await websocket.send_json( { "action": "init_system_response" } )
61 | response = await process_messages( data, websocket )
62 | await websocket.send_json( { "action": "finish_system_response" } )
63 | except (WebSocketDisconnect, ConnectionClosed):
64 | print( "Conexión cerrada" )
65 |
66 | async def process_messages( messages, websocket ):
67 |
68 | results = collection.query(
69 | query_texts=[ messages[ -1 ]["content"] ],
70 | n_results=2
71 | )
72 |
73 | pmsg = [ { "role": "system", "content": system_prompt + str( results["documents"][0] ) } ]
74 | print( json.dumps( pmsg + messages, indent=4) )
75 | completion_payload = {
76 | "messages": pmsg + messages
77 | }
78 |
79 | response = await client.chat.completions.create(
80 | top_p=0.9,
81 | temperature=0.6,
82 | model=MODEL,
83 | messages=completion_payload["messages"],
84 | stream=True
85 | )
86 |
87 | respStr = ""
88 | async for chunk in response:
89 | if (not chunk.choices[0] or
90 | not chunk.choices[0].delta or
91 | not chunk.choices[0].delta.content):
92 | continue
93 |
94 | await websocket.send_json( { "action": "append_system_response", "content": chunk.choices[0].delta.content } )
95 |
96 | return respStr
97 |
98 |
99 | uvicorn.run(app, host="0.0.0.0", port=8000)
100 |
101 |
--------------------------------------------------------------------------------
/static/css/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: rgba( 247, 247, 248 );
3 | font-family: sans-serif;
4 | margin: 0px;
5 | background-image: url( /static/img/bg3.jpg );
6 | }
7 |
8 | #main #chatinputcontainer {
9 | margin: 15px;
10 | padding: 0.75rem;
11 | padding-left: 1rem;
12 | border: rgba( 0, 0, 0, 0.1 ) 1px solid;
13 | border-radius: 2px;
14 | box-shadow: 0 0 4px 1px rgba(143,143,143,.8);
15 | background-color: #fff;
16 | }
17 |
18 | #main #chatinputcontainer form {
19 | margin: 0px;
20 | }
21 |
22 | #main #chatinputcontainer .textarea {
23 | max-height: 200px;
24 | min-height: 36px;
25 | overflow-y: hidden;
26 | border-width: 0px;
27 | width: calc( 100% - 75px );
28 | display: block;
29 | float: left;
30 | background-color: rgba( 0, 0, 0, 0 );
31 | padding: 5px;
32 | color: rgba( 0, 0, 0, 0.8 );
33 | }
34 |
35 | #main #chatinputcontainer br {
36 | clear: both;
37 | height: 1px;
38 | overflow: hidden;
39 | }
40 |
41 | #main #chatinputcontainer button {
42 | vertical-align: top;
43 | padding: 0px;
44 | padding-top: 3px;
45 | width: 48px;
46 | border: 0px;
47 | color: rgba(0,0,0,0.5);
48 | float: right;
49 | background-color: rgba(0,0,0,0);
50 | }
51 |
52 | #main #chatinputcontainer svg {
53 | width: 24px;
54 | height: 24px;
55 | }
56 |
57 | #main #linescontainer {
58 | overflow-y: scroll;
59 | height: calc( 100% - 110px );
60 | }
61 |
62 | #main #lines {
63 | margin: 0px 15px;
64 | padding-top: 10px;
65 | }
66 | #main #lines .line {
67 | max-width: 90%;
68 | clear: both;
69 | float: left;
70 | margin: 5px 0px;
71 | padding: 10px 15px;
72 | border-radius: 5px;
73 | box-shadow: 0 0 4px 1px rgba(143,143,143,.8);
74 | color: rba( 55, 65 , 81 );
75 | font-size: 14px;
76 | border: solid 1px rgb(117, 153, 111);
77 | background-color: rgb(217, 253, 211);
78 | }
79 |
80 | #main #lines .line.server {
81 | float: right;
82 | background-color: #fff;
83 | border: rgba( 0, 0, 0, 0.1 ) 1px solid;
84 | }
85 |
86 | /* loading-bar */
87 | #loading {
88 | position: fixed;
89 | top: 0px;
90 | left: 0px;
91 | width: 100%;
92 | height: 100%;
93 | opacity: 0.25;
94 | background-color: #fff;
95 | z-index: 998;
96 | }
97 |
98 | .loading-bar {
99 | display: block;
100 | -webkit-animation: shift-rightwards 1s ease-in-out infinite;
101 | -moz-animation: shift-rightwards 1s ease-in-out infinite;
102 | -ms-animation: shift-rightwards 1s ease-in-out infinite;
103 | -o-animation: shift-rightwards 1s ease-in-out infinite;
104 | animation: shift-rightwards 1s ease-in-out infinite;
105 | -webkit-animation-delay: .0s;
106 | -moz-animation-delay: .0s;
107 | -o-animation-delay: .0s;
108 | animation-delay: .0s;
109 |
110 | position: fixed;
111 | top: 0;
112 | left: 0;
113 | right: 0;
114 | height: 4px;
115 | z-index: 999;
116 | background: rgb( 32, 33, 35 );
117 | -webkit-transform: translateX(100%);
118 | -moz-transform: translateX(100%);
119 | -o-transform: translateX(100%);
120 | transform: translateX(100%);
121 | }
122 |
123 | @-webkit-keyframes shift-rightwards {
124 | 0% {
125 | -webkit-transform:translateX(-100%);
126 | -moz-transform:translateX(-100%);
127 | -o-transform:translateX(-100%);
128 | transform:translateX(-100%);
129 | }
130 |
131 | 40% {
132 | -webkit-transform:translateX(0%);
133 | -moz-transform:translateX(0%);
134 | -o-transform:translateX(0%);
135 | transform:translateX(0%);
136 | }
137 |
138 | 60% {
139 | -webkit-transform:translateX(0%);
140 | -moz-transform:translateX(0%);
141 | -o-transform:translateX(0%);
142 | transform:translateX(0%);
143 | }
144 |
145 | 100% {
146 | -webkit-transform:translateX(100%);
147 | -moz-transform:translateX(100%);
148 | -o-transform:translateX(100%);
149 | transform:translateX(100%);
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/main_agentia.py:
--------------------------------------------------------------------------------
1 | from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
2 | from fastapi.responses import HTMLResponse, RedirectResponse
3 | from fastapi.staticfiles import StaticFiles
4 | from openai import AsyncOpenAI, OpenAI
5 | from websockets.exceptions import ConnectionClosed
6 |
7 | import duckdb
8 | import uvicorn
9 |
10 | ENDPOINT = "http://127.0.0.1:39281/v1"
11 | #MODEL = "phi-3.5:3b-gguf-q4-km"
12 | #MODEL = "llama3.2:3b-gguf-q4-km"
13 | MODEL = "deepseek-r1-distill-qwen-14b:14b-gguf-q4-km"
14 |
15 | client = AsyncOpenAI(
16 | base_url=ENDPOINT,
17 | api_key="not-needed"
18 | )
19 |
20 | client2 = OpenAI(
21 | base_url=ENDPOINT,
22 | api_key="not-needed"
23 | )
24 |
25 | app = FastAPI()
26 |
27 | app.mount("/static", StaticFiles(directory="static"), name="static")
28 |
29 | @app.get("/", response_class=HTMLResponse)
30 | async def root( request: Request ):
31 | return RedirectResponse("/static/index.html")
32 |
33 | @app.websocket("/init")
34 | async def init( websocket: WebSocket ):
35 | await websocket.accept()
36 |
37 | try:
38 | while True:
39 | data = await websocket.receive_json()
40 |
41 | await websocket.send_json( { "action": "init_system_response" } )
42 | response = await plan_messages( data, websocket )
43 | await websocket.send_json( { "action": "finish_system_response" } )
44 | except (WebSocketDisconnect, ConnectionClosed):
45 | print( "Conexión cerrada" )
46 |
47 | async def plan_messages( messages, websocket ):
48 | pmsg = [
49 | { "role": "system", "content": """
50 | Responde solo 'No' en caso de no ser posible responder con los datos de la tabla.
51 | Responde solo 'No' en caso de pedir información de empresas que no sean Lostsys.
52 | Responde solo la sentencia SQL para la base de datos DuckDB a ejecutar en caso de poder obtener los datos de la tabla.
53 | Responde siempre Sin explicación, Sin notas, Sin delimitaciones.
54 | Disponemos de una tabla de base de datos llamada 'facturas' que contiene estos campos: 'fecha' del tipo VARCHAR usando el formato de fecha 'YYYY-MM-DD', 'cliente' tipo INTEGER que contiene el id de cliente, 'pais' tipo VARCHAR que contiene 'ES' como España y 'UK' como reino unido, 'importe' con el total de la factura.
55 | No puedes suponer nada ni usar otras tablas o datos que no sean los de la tabla 'facturas'.
56 | Para filtrar por el campo fecha usa siempre 'LIKE', nunca utilices funciones de fecha como YEAR, MONTH, EXTRACT.
57 | """ },
58 | { "role": "user", "content": messages[ -1 ]["content"] }
59 | ]
60 |
61 | response = client2.chat.completions.create(
62 | top_p=0.9,
63 | temperature=0.9,
64 | model=MODEL,
65 | messages=pmsg,
66 | )
67 |
68 | r = response.choices[0].message.content
69 | print( r )
70 | r = clean_sql( r )
71 |
72 | if not r.startswith("No"):
73 | await websocket.send_json( { "action": "append_system_response", "content": r } )
74 | await websocket.send_json( { "action": "append_system_response", "content": "\n\nResultado: " + execute__query( r ) } )
75 |
76 | return
77 |
78 | return await process_messages( messages, websocket )
79 |
80 | def execute__query( sql ):
81 | return str( duckdb.sql( sql ).fetchall() )
82 |
83 | def clean_sql( sql ):
84 | if sql.find("<|end_of_text|>") != -1: sql = sql[sql.find("<|end_of_text|>")+15:]
85 | if sql.find("") != -1: sql = sql[sql.find("")+8:]
86 |
87 | sql = sql.strip()
88 |
89 | if sql.startswith("```sql"): sql = sql[6:]
90 | if sql.startswith("```"): sql = sql[3:]
91 | if sql.endswith("```"): sql = sql[:len(sql)-3]
92 | if sql.find("```") != -1: sql = sql[sql.find("```"):]
93 | sql = sql.replace( "FROM facturas", "FROM './facturas.csv'" )
94 | sql = sql.replace( "fecha", "CAST(fecha AS VARCHAR)" )
95 |
96 | return sql
97 |
98 |
99 | async def process_messages( messages, websocket ):
100 | completion_payload = {
101 | "messages": messages
102 | }
103 |
104 | response = await client.chat.completions.create(
105 | top_p=0.9,
106 | temperature=0.6,
107 | model=MODEL,
108 | messages=completion_payload["messages"],
109 | stream=True
110 | )
111 |
112 | respStr = ""
113 | async for chunk in response:
114 | if (not chunk.choices[0] or
115 | not chunk.choices[0].delta or
116 | not chunk.choices[0].delta.content):
117 | continue
118 |
119 | await websocket.send_json( { "action": "append_system_response", "content": chunk.choices[0].delta.content } )
120 |
121 | return respStr
122 |
123 |
124 | uvicorn.run(app, host="0.0.0.0", port=8000)
125 |
126 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Tabla de contenidos
3 |
4 | - [Descripción del proyecto](#descripción-del-proyecto)
5 | - [Pasos para la puesta en marcha](#pasos-para-la-puesta-en-marcha)
6 | - [Arrancar LLM(Large Language Model)](#arrancar-llm(large-language-model))
7 | - [Instalación de dependencias Python](#instalación-de-dependencias-python)
8 | - [Puesta en marcha del chatbot básico](#puesta-en-marcha-del-chatbot-básico)
9 | - [Puesta en marcha del chatbot con RAG](#puesta-en-marcha-del-chatbot-con-rag)
10 | - [Probar el script para trastear con ChromaDB](#probar-el-script-para-trastear-con-chromadb)
11 | - [Puesta en marcha del agente de IA](#puesta-en-marcha-del-agente-de-ia)
12 | - [Técnicas con LLMs](#técnicas-con-llms)
13 | - [LLMs confrontados](#llms-confrontados)
14 | - [Arquitectura de "Verificación por consenso"](#arquitectura-de-"verificación-por-consenso")
15 | - [Arquitectura de "Detección de anomalías"](#arquitectura-de-"detección-de-anomalías")
16 | - [Arquitectura de "Generación y edición iterativa"](#arquitectura-de-"generación-y-edición-iterativa")
17 | - [Referencias](#referencias)
18 |
19 |
20 | # Descripción del proyecto
21 |
22 | Este proyecto busca convertirse en un punto de referencia accesible para cualquier persona interesada en el fascinante mundo de los chatbots y los modelos de lenguaje extenso (LLMs de sus siglas en inglés Large Language Model). Nuestro objetivo es proporcionar una base sólida de conocimientos y herramientas prácticas que permitan a usuarios de todos los niveles, desde principiantes hasta desarrolladores experimentados, adentrarse en la creación de sus propios asistentes y aplicaciones con LLMs. Queremos democratizar el acceso a esta tecnología y fomentar la innovación en el campo de la interacción humano-máquina.
23 |
24 | De momento tenemos implementados y explicados tres técnicas básicas para crear chatbots y aplicaciones con LLMs: Chatbot básico(main.py), RAG(main_rag.py) y un agente de IA(main_agentia.py):
25 |
26 | 
27 |
28 | # Pasos para la puesta en marcha
29 |
30 | ## Arrancar LLM(Large Language Model)
31 |
32 | Para empezar, procedemos a inicializar el modelo de lenguaje LLM(Large Language Model) que servirá como base para nuestro chatbot. Para facilitar su implementación y gestión, decidimos ejecutar los LLMs a través de un contenedor Docker utilizando la plataforma Cortex. Esta elección nos permite aislar el entorno de ejecución del LLM, garantizando una mayor estabilidad y portabilidad del proyecto y ejecutarlo usando la GPU o CPU.
33 |
34 | ```
35 | # Arrancamos el contenendor usando la CPU
36 | docker run -it -d --name cortex -p 39281:39281 menloltd/cortex
37 |
38 | # O alternativamente, usando la GPU. Atención! Requiere los drivers 'nvidia-docker' y evidentemente el hardware adecuado
39 | docker run --gpus all -it -d --name cortex -p 39281:39281 menloltd/cortex
40 | ```
41 |
42 | En este caso, hemos seleccionado Llama 3.2 3B, un modelo de última generación conocido por su capacidad para generar texto de alta calidad aunque podemos usar otros modelos:
43 |
44 | ```
45 | # Descargamos el modelo y lo arrancamos
46 | docker exec -it cortex cortex run llama3.2:3b-gguf-q4-km
47 |
48 | # O alternativamente, podemos usar otros modelos, como Phi de Microsoft
49 | docker exec -it cortex cortex run phi-3.5:3b-gguf-q4-km
50 |
51 | # O alternativamente, podemos usar otros modelos, como DeepSeek
52 | docker exec -it cortex cortex run deepseek-r1-distill-qwen-14b:14b-gguf-q4-km
53 | ```
54 |
55 | Atención! Es importante tener configurado el modelo que tenemos descargado y arrancado en los scripts "main_*.py"
56 |
57 | ## Instalación de dependencias Python
58 |
59 | Para arrancar el chatbot el primer paso obligatório es instalar las dependencias del python(En nuestro caso vamos a crear también un entorno virtual de Python con el objetivo de tener un único espacio con las dependencias de nuestro proyecto):
60 |
61 | ```
62 | # Creación y activación del virtual env
63 | virtualenv env
64 | source env/bin/activate
65 |
66 | # Instalaciión de dependencias
67 | pip install -r requirements.txt
68 | ```
69 |
70 | ## Puesta en marcha del chatbot básico
71 |
72 | Para arrancar el chatbot simplemente arrancar el script 'main.py':
73 |
74 | ```
75 | # Arranque del script
76 | python main.py
77 | ```
78 |
79 | ## Puesta en marcha del chatbot con RAG
80 |
81 | Un RAG (Retrieval Augmented Generation, o Generación Aumentada por Recuperación) es una técnica de inteligencia artificial que combina la capacidad de los LLMs y la habilidad de buscar información específica en una base de datos(Normalmente una base de datos vectorial).
82 |
83 | Para arrancar el chatbot con el RAG simplemente arrancar el script 'main_rag.py':
84 |
85 | ```
86 | # Arranque del script
87 | python main_rag.py
88 | ```
89 |
90 | ### Probar el script para trastear con ChromaDB
91 |
92 | Para arrancar el script y trastear con ChromaDB(Base de datos orientada a vectores) ejecutamos el script 'testchromadb.py':
93 |
94 | ```
95 | # Arranque del script
96 | python testchromadb.py
97 | ```
98 |
99 | ## Puesta en marcha del agente de IA
100 |
101 | Un agente de IA es un sistema que percibe su entorno, toma decisiones y realiza acciones con el objetivo de alcanzar metas específicas. Para ello se conecta a distintos tipos de herramientas como bases de datos, APIs, dispositivos, etc.
102 |
103 | Para arrancar el chatbot con el Agente de IA simplemente arrancar el script 'main_agentia.py':
104 |
105 | ```
106 | # Arranque del script
107 | python main_agentia.py
108 | ```
109 | # Técnicas con LLMs
110 |
111 | ## LLMs confrontados
112 |
113 | La idea es confrontar la salida de un LLM con otro LLM y tiene el potencial de mejorar significativamente la calidad y confiabilidad de la información generada. Aquí presentamos algunas arquitecturas posibles y sus implicaciones:
114 |
115 | 
116 |
117 | ### Arquitectura de "Verificación por consenso"
118 |
119 | **Funcionamiento:** Se utilizan múltiples LLMs para generar respuestas a una misma pregunta o tarea(En paralelo). Luego, se comparan las salidas y se selecciona la respuesta que tenga mayor consenso.
120 |
121 | **Ventajas:** Reduce la probabilidad de respuestas incorrectas o sesgadas, ya que se basa en la validación cruzada entre diferentes modelos. El ejecutarse el paralelo no penaliza la UX del usuario.
122 |
123 | **Desventajas:** Aumenta la complejidad y el costo computacional, ya que requiere la ejecución de múltiples LLMs.
124 |
125 | ### Arquitectura de "Detección de anomalías"
126 |
127 | **Funcionamiento:** Un LLM genera una respuesta, y luego otro LLM actúa como un "detector de anomalías" que evalúa la respuesta en busca de inconsistencias, errores o información falsa.
128 |
129 | **Ventajas:** Permite identificar y corregir errores en las respuestas generadas por el primer LLM, mejorando la precisión y confiabilidad de la información.
130 |
131 | **Desventajas:** Requiere un diseño cuidadoso del "detector de anomalías" para evitar falsos positivos o negativos.
132 |
133 | ### Arquitectura de "Generación y edición iterativa"
134 |
135 | **Funcionamiento:** Un LLM genera una primera versión de una respuesta, y luego otro LLM la revisa y edita para mejorar su calidad, claridad o precisión. Este proceso puede repetirse varias veces de forma iterativa.
136 |
137 | **Ventajas:** Permite obtener respuestas más elaboradas y pulidas, ya que se basa en la colaboración entre dos o mas LLMs.
138 |
139 | **Desventajas:** Puede ser un proceso lento y costoso, ya que requiere múltiples iteraciones en série.
140 |
141 | # Referencias
142 |
143 | - Vídeo de Youtube ["Aprende a desarrollar chatbots desde 0"](https://www.youtube.com/watch?v=Q_NLkUsJJ2c)
144 | - Vídeo de Youtube ["Aprende a desarrollar chatbots con RAG(Retrieval-Augmented Generation) usando ChromaDB"](https://www.youtube.com/watch?v=Etx2WSKQUS0)
145 | - Vídeo de Youtube ["Tutorial de desarrollo de IA Agents"](https://www.youtube.com/watch?v=40rc2-QBQ5g)
146 | - Vídeo de Youtube ["Aprende a crear aplicaciones con DeepSeek y Técnicas con LLMs confrontadas"](https://www.youtube.com/watch?v=ACGXJ13cvEQ)
147 | - Artículo ["Cortex: Desplegando LLMs en local"](https://www.albertcoronado.com/2024/12/03/cortex-plataforma-de-ia-para-desplegar-llms-en-local)
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------