
Laboratorio de investigación de seguridad: reproducción de CVE-2025-52467 (ejecución de código en el flujo de trabajo pull_request_target de pgai / exfiltración de GITHUB_TOKEN) — instantánea de timescale/pgai
Artefacto de investigación automatizado — no es el proyecto original.
Este repositorio es un laboratorio desechable construido por un sistema automatizado para una tesis de maestría en la Université Laval sobre la reproducción de vulnerabilidades publicadas en flujos de trabajo de GitHub Actions. Es una instantánea verbatim de
timescale/pgaien el commit2a209b058c60823b57e8b7775b10244ff340eb8b(2025-05-14), redistribuida bajo la licencia de ese proyecto, cuyo archivo se incluye sin cambios en esta instantánea.El proyecto original no está involucrado, nunca es el objetivo, y la vulnerabilidad estudiada aquí ya es pública. Cada secreto y variable en este repositorio es un valor ficticio generado aleatoriamente — no hay ninguna credencial real presente. Las referencias a acciones e imágenes de ejecutores están fijadas a lo que resolvieron el 2025-05-14; consulta
pinning.mden la salida del sistema para cada cambio realizado en la instantánea.Preguntas u objeciones: [email protected]
Una biblioteca de Python que transforma PostgreSQL en un motor de recuperación robusto y listo para producción para aplicaciones RAG y Agentic.
🔄 Crea y sincroniza automáticamente embeddings vectoriales a partir de datos de PostgreSQL y documentos de S3. Los embeddings se actualizan automáticamente a medida que cambian los datos.
🔍 Potente búsqueda vectorial y semántica con pgvector y pgvectorscale.
🛡️ Listo para producción desde el primer momento: admite procesamiento por lotes para una generación eficiente de embeddings, con manejo integrado de fallos de modelos, límites de tasa y picos de latencia.
🐘 Funciona con cualquier base de datos PostgreSQL, incluidos Timescale Cloud, Amazon RDS, Supabase y más.
Arquitectura básica: El sistema consta de una aplicación que tú escribes, una base de datos PostgreSQL y workers vectorizadores sin estado. La aplicación define una configuración de vectorizador para incrustar datos de fuentes como PostgreSQL o S3. Los workers leen esta configuración, procesan la cola de datos en embeddings y texto fragmentado, y escriben los resultados de vuelta. La aplicación luego consulta estos datos para impulsar RAG y la búsqueda semántica.
La fortaleza clave de esta arquitectura reside en su resiliencia: las modificaciones de datos realizadas por la aplicación están desacopladas del proceso de embedding, lo que garantiza que los fallos en el servicio de embedding no afecten las operaciones de datos principales.
Primero, instala el paquete pgai.``` pip install pgai
Luego, instala los componentes de la base de datos pgai. Puedes hacerlo desde la terminal usando la CLI o en el código de tu aplicación Python usando el paquete de Python pgai.```
# from the cli
pgai install -d <database-url>
# or from the python package, often done as part of your application setup
import pgai
pgai.install(DB_URL)
Esta guía de inicio rápido demuestra cómo pgai Vectorizer permite la búsqueda semántica y RAG sobre datos de PostgreSQL al crear y sincronizar automáticamente los embeddings a medida que los datos cambian.
El "ingrediente secreto" clave de pgai Vectorizer es su enfoque declarativo para la generación de embeddings. Simplemente define tu pipeline y deja que Vectorizer maneje la complejidad operativa de mantener los embeddings sincronizados, incluso cuando los endpoints de embeddings no son confiables. Puedes definir una versión simple del pipeline de la siguiente manera:```sql CREATE TABLE IF NOT EXISTS wiki ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, url TEXT NOT NULL, title TEXT NOT NULL, text TEXT NOT NULL )
SELECT ai.create_vectorizer( 'wiki'::regclass, loading => ai.loading_column(column_name=>'text'), destination => ai.destination_table(target_table=>'wiki_embedding_storage'), embedding => ai.embedding_openai(model=>'text-embedding-ada-002', dimensions=>'1536') )
El vectorizador creará automáticamente embeddings para todas las filas de la
tabla `wiki` y, lo que es más importante, mantendrá los embeddings sincronizados con los
datos subyacentes a medida que estos cambien. **Piénsalo casi como declarar un índice** en
la tabla `wiki`, pero en lugar de que la base de datos gestione la estructura de datos del índice
por ti, el Vectorizador gestiona los embeddings.
## Ejecutar el inicio rápido
**Requisitos previos:**
- Una base de datos PostgreSQL ([instrucciones de Docker](https://docs.timescale.com/self-hosted/latest/install/installation-docker/)).
- Una clave de API de OpenAI (usamos OpenAI para los embeddings en el inicio rápido, pero puedes usar [múltiples proveedores](#modelos-de-embedding-compatibles)).
Crea un archivo `.env` con lo siguiente:```
OPENAI_API_KEY=<your-openai-api-key>
DB_URL=<your-database-url>
Puedes descargar el código Python completo y el requirements.txt del ejemplo de inicio rápido y ejecutarlo en el mismo directorio que el archivo .env.
Pgai requiere que se instalen algunas tablas de catálogo y funciones en la base de datos. Esto se realiza mediante la función pgai.install, que instalará los componentes necesarios en el esquema ai de la base de datos.```python
pgai.install(DB_URL)
### Crear el vectorizador
Esto define el vectorizador, que le indica al sistema cómo crear los embeddings a partir de la columna `text` de la tabla `wiki`. El vectorizador crea una vista `wiki_embedding` que podemos consultar para obtener los embeddings (como veremos a continuación).```python
async def create_vectorizer(conn: psycopg.AsyncConnection):
async with conn.cursor() as cur:
await cur.execute("""
SELECT ai.create_vectorizer(
'wiki'::regclass,
if_not_exists => true,
loading => ai.loading_column(column_name=>'text'),
embedding => ai.embedding_openai(model=>'text-embedding-ada-002', dimensions=>'1536'),
destination => ai.destination_table(view_name=>'wiki_embedding')
)
""")
await conn.commit()
En este ejemplo, ejecutamos el worker del vectorizador una vez para crear los embeddings de los datos existentes.```python worker = Worker(DB_URL, once=True) worker.run()
En una aplicación real, no llamaríamos al worker manualmente de esta manera cada vez que queramos crear los embeddings. En su lugar, ejecutaríamos el worker en segundo plano y este se ejecutaría de forma continua, consultando trabajo del vectorizer.
Puedes ejecutar el worker en segundo plano desde la aplicación, la CLI o Docker. Consulta la documentación del [vectorizer worker](https://github.com/pvharmo2/gha-lab-e8902eccd3/blob/main/docs/vectorizer/worker.md) para más detalles.
### Buscar los artículos del wiki mediante búsqueda semántica
Esta es una búsqueda semántica estándar de pgvector en PostgreSQL. La búsqueda se realiza contra la vista `wiki_embedding`, que es creada por el vectorizer e incluye todas las columnas de la tabla `wiki` más la columna `embedding` y el texto de los fragmentos. Esta función devuelve tanto la columna `text` completa de la tabla `wiki` como fragmentos más pequeños del texto que son más relevantes para la consulta.```python
@dataclass
class WikiSearchResult:
id: int
url: str
title: str
text: str
chunk: str
distance: float
async def _find_relevant_chunks(client: AsyncOpenAI, query: str, limit: int = 1) -> List[WikiSearchResult]:
# Generate embedding for the query using OpenAI's API
response = await client.embeddings.create(
model="text-embedding-ada-002",
input=query,
encoding_format="float",
)
embedding = np.array(response.data[0].embedding)
# Query the database for the most similar chunks using pgvector's cosine distance operator (<=>)
async with pool.connection() as conn:
async with conn.cursor(row_factory=class_row(WikiSearchResult)) as cur:
await cur.execute("""
SELECT w.id, w.url, w.title, w.text, w.chunk, w.embedding <=> %s as distance
FROM wiki_embedding w
ORDER BY distance
LIMIT %s
""", (embedding, limit))
return await cur.fetchall()
Este código destaca por lo que no está haciendo. Se trata de una inserción simple de un nuevo artículo en la tabla wiki. No necesitamos hacer nada diferente para crear los embeddings, ya que el worker vectorizador se encargará de actualizar los embeddings a medida que los datos cambien.```python
def insert_article_about_pgai(conn: psycopg.AsyncConnection):
async with conn.cursor(row_factory=class_row(WikiSearchResult)) as cur:
await cur.execute("""
INSERT INTO wiki (url, title, text) VALUES
('https://en.wikipedia.org/wiki/pgai', 'pgai', 'pgai is a Python library that turns PostgreSQL into the retrieval engine behind robust, production-ready RAG and Agentic applications. It does this by automatically creating vector embeddings for your data based on the vectorizer you define.')
""")
await conn.commit()
### Realizar RAG con el LLM
Este código realiza RAG con el LLM. Utiliza la función `_find_relevant_chunks` definida anteriormente para encontrar los fragmentos de texto más relevantes de la tabla `wiki` y luego utiliza el LLM para generar una respuesta.```python
query = "What is the main thing pgai does right now?"
relevant_chunks = await _find_relevant_chunks(client, query)
context = "\n\n".join(
f"{chunk.title}:\n{chunk.text}"
for chunk in relevant_chunks
)
prompt = f"""Question: {query}
Please use the following context to provide an accurate response:
{context}
Answer:"""
response = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: prompt }],
})
print("RAG response:")
print(response.choices[0].message.content)
Busca otras guías de inicio rápido:
Explora más sobre el vectorizer:
Nuestra biblioteca de Python pgai te permite trabajar con embeddings generados a partir de tus datos:
Puedes usar los embeddings vectoriales para:
También ofrecemos una extensión de PostgreSQL que puede realizar llamadas a modelos LLM directamente desde SQL. Esto suele ser útil para casos de uso como clasificación, resumen y enriquecimiento de datos en tus datos existentes.
El vectorizer está diseñado para ser flexible y personalizable. Cada vectorizer define un pipeline para crear embeddings a partir de tus datos. El pipeline se define mediante una serie de componentes que se aplican en secuencia a los datos:
Los siguientes modelos son compatibles para la generación de embeddings:
Simplemente crear embeddings vectoriales es fácil y sencillo. El desafío es que los LLM son algo poco fiables y los endpoints presentan fallos intermitentes y/o rendimiento degradado. Una parte crítica para manejar correctamente los fallos es que tus operaciones primarias de modificación de datos (INSERT, UPDATE, DELETE) no deben depender de la operación de embedding. De lo contrario, tu aplicación estará caída cada vez que el endpoint sea lento o falle y la experiencia de tus usuarios se verá afectada.
Normalmente, necesitarías implementar un pipeline de MLops personalizado para manejar correctamente los fallos de los endpoints. Esto comúnmente implica un sistema de colas como Kafka, workers especializados y otra infraestructura para manejar la cola y reintentar las solicitudes fallidas. Esto es mucho trabajo y es fácil equivocarse.
Con pgai, puedes omitir todo eso y centrarte en construir tu aplicación porque el vectorizer gestiona los embeddings por ti. Hemos incorporado lógica de colas y reintentos para manejar los distintos modos de fallo que puedes encontrar. Como hacemos este trabajo en segundo plano, las operaciones primarias de modificación de datos no dependen de la operación de embedding. Por eso pgai está listo para producción desde el primer momento.
Muchas bases de datos vectoriales especializadas crean embeddings por ti. Sin embargo, normalmente fallan cuando los endpoints de embedding están caídos o degradados, colocando la carga del manejo de errores y los reintentos de nuevo sobre ti.
¡Damos la bienvenida a contribuciones a pgai! Consulta la página de Contribuciones para más información.
pgai aún está en una etapa temprana. Ahora es un gran momento para ayudar a dar forma a la dirección de este proyecto; actualmente estamos decidiendo prioridades. Echa un vistazo a la lista de características en las que estamos pensando trabajar. Siéntete libre de comentar, ampliar la lista o unirte al foro de Discusiones.
Para comenzar, echa un vistazo a cómo contribuir y cómo configurar un entorno de desarrollo/pruebas.
Timescale es una empresa de bases de datos PostgreSQL. Para aprender más, visita timescale.com.
Timescale Cloud es una plataforma en la nube de alto rendimiento, centrada en el desarrollador, que proporciona servicios de PostgreSQL para las cargas de trabajo más exigentes de IA, series temporales, análisis y eventos. Timescale Cloud es ideal para aplicaciones de producción y proporciona alta disponibilidad, copias de seguridad en streaming, actualizaciones a lo largo del tiempo, roles y permisos, y una gran seguridad.