El problema que me hizo construir esto
Creé una canalización de tres agentes que funcionó muy bien para tareas cortas. Pero en el momento en que la conversación se prolongó y un agente necesitó recordar una decisión pasada, todo se vino abajo.
Así es exactamente como se rompió: Agent_Planner decidiría que el proyecto debería usar PostgreSQL. Luego pasaban veinte turnos de “suena bien” y “ya lo haré”. Al final, Agent_Reviewer hablaba y preguntaba qué tecnología de almacenamiento estábamos usando. Incluso con toda la transcripción sin editar ahí mismo, en la ventana de contexto, el agente no pudo responder de manera confiable.
Estaba ejecutando este proceso localmente como un proyecto paralelo para EmiTechLogic solo para ver hasta dónde podía impulsar la coordinación de múltiples agentes antes de que chocara contra una pared. Resulta que no pasó mucho tiempo.
Inicialmente, supuse que esto era sólo una limitación del modelo. No lo es. Es un problema de arquitectura de memoria que generalmente desencadena uno de dos grandes dolores de cabeza dependiendo de cómo intente solucionarlo.
La solución alternativa: la búsqueda vectorial y la trampa relacional
Si cambia a la búsqueda vectorial, soluciona el problema de ruido pero inmediatamente crea uno diferente. Un almacén de vectores recupera fragmentos que se parecen a su consulta; no recupera relaciones entre hechos.
Si una decisión clave se encuentra en un fragmento y una nota de dependencia crítica sobre esa decisión se encuentra en otro, una búsqueda de similitudes no tiene forma de combinarlos, sin importar qué tan bueno sea su modelo de incorporación.
Ambos enfoques tocan techos estructurales diferentes. En lugar de adivinar qué compromiso era “suficientemente bueno”, decidí medirlos a ambos.
¿Cuál es realmente este problema?
Para que quede claro lo que no es este artículo: este no es un problema de compresión de tokens ni un problema de obsolescencia. Es un problema de recuperación estructural. Algunas preguntas sólo pueden responderse combinando dos hechos enunciados por separado, y ni una ventana de contexto creciente ni un índice vectorial tienen un mecanismo para hacerlo. Se trata de un modo de fallo completamente diferente a los que he escrito antes y necesitaba un punto de referencia diferente.
La configuración de la prueba
Para probar esto, creé cinco escenarios deterministas que contenían 18 consultas calificadas y ejecuté las tres arquitecturas de memoria exactamente con las mismas conversaciones.
Todos los resultados a continuación provienen de ejecuciones reales de ese punto de referencia utilizando una configuración localizada:
Entorno: Python 3.12, solo CPU (no se necesita GPU) Llamadas API: Coherencia cero: Reproducida de manera idéntica en dos máquinas separadas
Repositorio de código: puede encontrar la implementación completa y ejecutar las pruebas usted mismo aquí: https://github.com/Emmimal/context-graph-benchmark/
Qué significa aquí "gráfico de contexto"
Un almacén de memoria plana (ya sea una transcripción de chat sin formato o un índice vectorial) trata cada turno como una unidad de texto independiente. Para recuperar algo, simplemente busque la unidad que mejor se adapte a su consulta.
Un gráfico de contexto cambia por completo la estructura subyacente. Trata la memoria como entidades distintas con relaciones escritas que las conectan:
AuthModule —–> DEPENDS_ON —–> RateLimiter Agent_Implementer —–> ASSIGNED_TO —–> AuthModule
La recuperación en este modelo significa atravesar estas relaciones en lugar de simplemente hacer coincidir palabras clave o vectores semánticos.
Esa diferencia estructural sólo importa para una clase específica de preguntas: cualquier cosa que requiera combinar dos hechos declarados por separado.
Considere una pregunta como: "¿Qué equipo posee el componente que depende del servicio que eligió X?"
No hay ningún fragmento de respuesta único en ningún lugar del historial de conversación sin procesar. La respuesta no existe como un bloque de texto. Sólo existe como un camino a través de múltiples hechos. Una tienda de pisos no puede construir ese camino sobre la marcha. Un gráfico lo atraviesa.
¿Para quién es esto?
Vale la pena desarrollar este enfoque si ejecuta canalizaciones de múltiples agentes donde la decisión de un agente debe ser recuperada correctamente por un agente diferente muchos turnos después. Está diseñado para sistemas donde las preguntas requieren habitualmente combinar dos o más hechos declarados por separado, o cualquier conversación de larga duración con agentes donde el costo simbólico de reenviar el historial se está convirtiendo en una partida real.
Debe omitirlo para tareas de un solo agente y de un solo turno porque no hay ningún estado entre agentes que perder. Omítalo si sus consultas son siempre búsquedas de un solo hecho sin combinaciones. Vector RAG le brinda la mayor precisión a una fracción del costo de ingeniería. Finalmente, omítelo si tu equipo no tolera una parte móvil adicional. Un gráfico necesita un paso de extracción (que se basa en reglas en este punto de referencia, pero requiere una llamada de LLM en producción) que una tienda plana evita.
Si su sistema multiagente finaliza su trabajo en un único intercambio, el paso de contexto simple funciona bien. Este problema aparece específicamente cuando las conversaciones se prolongan y las decisiones deben sobrevivir más allá del turno en el que se tomaron.
Las tres arquitecturas
Por qué no hay convocatorias de LLM en el punto de referencia
Deliberadamente omití las convocatorias de LLM de cada etapa de este punto de referencia: no hay LLM para extracción, ninguno para responder consultas y ninguno para calificar.
Si un LLM real se encargara de la extracción, el punto de referencia mediría la variación del LLM tanto como las diferencias arquitectónicas reales. El uso de sustitutos deterministas basados en reglas garantiza que cada ejecución produzca exactamente los mismos números.
Realicé esta prueba de forma independiente en dos máquinas diferentes mientras escribía este artículo. La salida coincidió byte por byte, manteniendo la precisión con cuatro decimales y el token cuenta atrás hasta el número entero exacto.
Construyendo un punto de referencia que no favorezca secretamente el gráfico
La forma más fácil de hacer que un gráfico gane un punto de referencia es formularle únicamente preguntas claras y de un solo hecho. Eso no prueba nada. Para que las pruebas sean justas, cada escenario sigue cuatro reglas estrictas:
Los distractores superan a los hechos: cada escenario contiene muchos más giros de “suena bien”, “lo comprobaré” y “no hay obstáculos por mi parte” que decisiones concretas reales. Las consultas abarcan la distancia física: algunas consultas se realizan justo después de que se establece un hecho (directas), algunas se realizan muchos turnos después (distantes) y otras requieren unir dos hechos separados (unir). Un ejemplo de consulta de unión es: "¿De qué componente depende el módulo propiedad de Agent_Implementer?" Algunas consultas son fáciles a propósito: se incluyen búsquedas directas de un solo hecho específicamente para brindar una oportunidad justa a las arquitecturas planas. La calificación es completamente determinista: el punto de referencia utiliza la comparación de subcadenas con una verdad escrita a mano en lugar de depender de un juez de LLM. @dataclass clase Turno: turn_id: int turn_type: TurnType # HECHO, DISTRACTOR o CONSULTA hablante: str texto: str asunto: str | Ninguno = Ninguno # triple estructurado, FACT convierte solo predicado: str | Ninguno = Ninguno objeto: str | Ninguno = Ninguno fact_id: str | Ninguno = Ninguno tipo_consulta: str | Ninguno = Ninguno # "directo", "distante", "unirse" require_fact_ids: tupla = () ground_truth: str | Ninguno = Ninguno
El punto de referencia cubre cinco escenarios distintos en diferentes dominios: planificación de software, un canal de investigación, respuesta a incidentes, escalada de atención al cliente y un canal de datos.
En estas cinco configuraciones, hay un total de 18 consultas divididas en tres categorías específicas:
6 Consultas directas: Consultas realizadas inmediatamente después de declararse el hecho. 7 consultas a distancia: las búsquedas se realizan muchas vueltas después de que se indica el hecho. 5 Consultas conjuntas: preguntas que requieren combinar dos hechos declarados por separado para obtener la respuesta.
Arquitectura 1: volcado de historial sin procesar
Cada turno se agrega a una transcripción plana y la transcripción completa se reenvía en cada consulta. Esto es exactamente lo que obtienes por defecto cuando no diseñas un sistema de memoria a propósito.
Construí esto para que sirviera como una base genuinamente justa. Obtiene la transcripción completa y perfecta sin nada oculto. La extracción de respuestas utiliza la superposición de palabras clave con derivaciones ligeras, buscadas desde el giro más reciente hacia atrás. Esta configuración refleja fielmente cómo un aviso lleno de contexto tiende a ponderar lo reciente de todos modos.
clase RawHistoryDump: def ingest(self, turn: Turn) -> Ninguno: self.transcript.append(f"{turn.speaker}: {turn.text}") def respuesta_query(self, query_turn: Turn) -> tuple[str, int]: Prompt = self._build_prompt(query_turn) # TODOS los tokens de transcripción = count_tokens(prompt) respuesta = self._extract_answer(query_turn) devuelve respuesta, tokens
El modelo de costos coincide exactamente con lo que se ve en producción: cada consulta reenvía todo el historial de conversaciones en crecimiento.
Arquitectura 2: RAG de solo vector
Cada giro, tanto el hecho como el distractor, queda incrustado y almacenado como un fragmento. Una tienda de vectores real no sabe de antemano qué giros importarán más adelante. En una consulta, se recuperan los K fragmentos más similares.
Utilicé TF-IDF en lugar de una API de integración neuronal por la misma razón por la que evité las llamadas a LLM en otros lugares. TfidfVectorizer no tiene un estado aleatorio, lo que lo hace determinista por construcción. Tampoco es un sustituto de juguete. TF-IDF es un método real de recuperación dispersa utilizado en RAG de producción, a menudo combinado con incrustaciones densas en una configuración híbrida.
clase VectorOnlyRAG: def _retrieve(self, query_text: str) -> lista[str]: si no self.chunks: return[]corpus = self.chunks + [query_text] vectorizer = TfidfVectorizer() matriz = vectorizer.fit_transform(corpus) sims = cosine_similarity(matrix[-1], matriz[:-1]).flatten() top_idx = sims.argsort()[::-1][:self.top_k] devuelve [self.chunks[i] para i en top_idx si sims[i] > 0]
(La implementación real envuelve fit_transform en un bloque try/except para manejar el raro caso extremo de una consulta que contiene solo palabras vacías. Lo omití aquí por espacio, pero está en el repositorio).
El techo estructural sigue estando claro: una consulta conjunta requiere combinar dos hechos distintos. Cuando esos hechos se exponen en dos turnos diferentes, ningún fragmento contiene ambas piezas de información. Ningún modelo de integración puede solucionar esa limitación por sí solo.
Arquitectura 3: el gráfico de contexto
Los hechos se escriben como (sujeto, predicado, objeto) triples en un multigrafo dirigido por NetworkX. Los giros de distractor nunca se escriben en absoluto. Este es el único lugar donde esta arquitectura obtiene una ventaja que las otras dos no tienen: filtrar datos antes de que lleguen al almacenamiento.
En producción, ese paso de filtrado es una llamada de LLM que realiza la extracción de entidades. En este punto de referencia, es determinista porque la configuración del escenario ya etiqueta qué giros son hechos. Estoy aislando exactamente lo que hace la arquitectura de almacenamiento y recuperación por sí sola, manteniendo la extracción constante como una suposición declarada. No pretendo haber resuelto la extracción de forma gratuita.
clase ContextGraph: def ingest(self, turn: Turn) -> Ninguno: si turn.subject es Ninguno: return # los distractores no llevan triple estructurado; no almacenado self.graph.add_node(turn.subject) self.graph.add_node(turn.object) self.graph.add_edge(turn.subject, turn.object, predicate=turn.predicate, fact_id=turn.fact_id)
El recorrido de consulta de unión es la parte que realiza el trabajo real. Realiza un recorrido de dos saltos a través de los nodos del gráfico en lugar de buscar un único fragmento de texto que contenga ambos hechos.
def _answer_join(self, query_turn, mencionado): para entidad en mencionado: out_edges, in_edges = self._edges_touching(entidad) intermedios = [v para _, v, _ en out_edges] + [u para u, _, _ en in_edges] para intermedio en intermedios: más_fuera, _ = self._edges_touching(intermedio) para _, objetivo, datos en más_fuera: si objetivo != entidad: # puntuar candidatos por relevancia de predicado…
Aquí está la diferencia en el espacio de búsqueda entre los tres:
Lo que realmente sucedió cuando lo ejecuté por primera vez
La primera ejecución completa, con las tres arquitecturas construidas, calificó el gráfico de contexto con una precisión del 0%.
Incluyo esto porque es la parte que omiten la mayoría de las publicaciones de "Construí X". Podría haber reescrito los escenarios para que fueran más amigables en lugar de depurar el código. Eso me habría dado un resultado falso. Lo rastreé en su lugar.
Error 1: discrepancia en el vocabulario de la entidad
Los nodos del gráfico recibieron nombres como Project_Alpha o AuthModule. Las consultas, escritas de la forma en que las formularía un agente, decían "este proyecto" o "el módulo de autenticación". Una coincidencia literal de subcadena entre el texto de la consulta y el nombre del nodo no encontró absolutamente nada.
Este es exactamente el mismo problema de falta de coincidencia de vocabulario por el que la gente critica la búsqueda de vectores. Simplemente llega al gráfico en el momento de la escritura en lugar del momento de la consulta.
La solución fue una pequeña tabla de alias que reemplazaba un paso real de vinculación de entidades, que normalmente sería manejado por una llamada de LLM en producción. Usar una gráfica no te saca de este problema. Simplemente traslada el problema de la recuperación en el momento de la consulta a la resolución en el momento de la escritura. Se trata de un coste de ingeniería continuo, no de una solución única.
Error 2: Devolver hechos obsoletos con total confianza
Este es exactamente el problema que le señalaría primero a cualquiera que envíe este patrón en un entorno de producción.
Un escenario presenta un ticket de soporte que comienza con un nivel de prioridad "alto" y se reclasifica a "crítico" a mitad de la conversación. Al preguntar "¿cuál es la prioridad actual?", el gráfico arrojó "alto": el valor obsoleto, con exactamente la misma confianza que le habría dado al actual.
La causa fue simple: mi primera implementación de ingest() simplemente agregó cada ventaja nueva y nunca eliminó la anterior. El gráfico contenía dos aristas HAS_PRIORITY que se originaban en el mismo nodo. Cualquier borde que se visitara primero en el orden de iteración ganó la búsqueda, ignorando por completo qué hecho era realmente actual.
# el error Ticket_4471 –HAS_PRIORITY–> "high" # indicado primero Ticket_4471 –HAS_PRIORITY–> "critical" # indicado más adelante, reemplaza al primero # ambos bordes existen a la vez; nada le dice al gráfico cuál es "ahora"
Un volcado de chat plano buscado con sesgo de actualidad tiende a mostrar la mención más reciente simplemente escaneando hacia atrás. Por el contrario, un gráfico sin modelo de tiempo devuelve cualquiera de los hechos con la misma confianza estructural porque los gráficos no saben de forma nativa que una relación ha sido reemplazada a menos que usted se lo indique explícitamente.
Ese modo de falla es peor que una búsqueda difusa que devuelve un fragmento obsoleto. El gráfico parece completamente autorizado incluso cuando es completamente incorrecto.
La solución: cuando un hecho nuevo reafirma un par existente (sujeto, predicado), la ventaja anterior se elimina antes de que se escriba la nueva.
def ingest(self, turn: Turn) -> Ninguno: si turn.subject es Ninguno: devuelve self.graph.add_node(turn.subject) self.graph.add_node(turn.object) stale_edges = [ (u, v, k) para u, v, k, datos en self.graph.edges(keys=True, data=True) if u == turn.subject y data.get("predicate") == turn.predicate ] para u, v, k en stale_edges: self.graph.remove_edge(u, v, key=k) self.graph.add_edge(turn.subject, turn.object, predicate=turn.predicate, fact_id=turn.fact_id)
Si envía algo como esto, manejar la sustitución de hechos no es opcional. Es la línea exacta entre construir una capa de memoria confiable y construir una responsabilidad importante.
Resultados finales de referencia
Cinco escenarios, 18 consultas, totalmente deterministas, reproducidas de manera idéntica en dos máquinas separadas.
El gráfico de contexto gana en precisión y utiliza aproximadamente 18 veces menos tokens por consulta que el volcado sin formato. Esto no es una compensación: es una victoria en ambos ejes.
El costo del token de Vector RAG también es bajo y no es el principal diferenciador del gráfico. Ambas arquitecturas recuperan una cantidad limitada de elementos, por lo que ambas siguen siendo económicas independientemente de la duración de la conversación. Lo que separa el gráfico del vector RAG es la columna de unión: 80% versus 20%. Esa brecha es el argumento estructural a favor de un gráfico: la similitud vectorial no tiene una forma nativa de combinar dos hechos declarados por separado.
La precisión del volcado sin procesar fue mayor de lo que esperaba: 61,1%, y se lo merece. Una transcripción perfecta y sin pérdidas con una concordancia decente de palabras clave funciona bien en búsquedas de un solo hecho. Se desmorona específicamente en las uniones (40%) por la misma razón estructural que el vector RAG, solo que con una factura simbólica mucho mayor.
Se dejó una limitación a propósito: dos consultas en el escenario de canalización de datos fallan porque se refieren a una entidad por descripción en lugar de por nombre: "el conjunto de datos que actualmente tiene una anomalía" en lugar de nombrar Upstream_Orders directamente. Arreglar eso requiere una comprensión semántica real de una cláusula descriptiva, no una simple coincidencia de alias. Ampliar la tabla de alias para cubrir mis propias consultas de prueba significaría sobreajustar el punto de referencia en lugar de representar una limitación real, por lo que permanece roto. Si sus consultas de producción se inclinan hacia referencias descriptivas, presupuesta un paso de resolución basado en LLM en lugar de una tabla de alias estática en constante crecimiento.
Cómo aumenta el costo del token con la duración de la conversación
Mi suposición de trabajo al comenzar fue que el costo del token de volcado sin procesar aumenta O (N ^ 2) a medida que crecen las conversaciones. Lo medí en lugar de asumirlo, porque enviar una afirmación de complejidad imprecisa a una audiencia que la verifica es una forma rápida de perder credibilidad.
La configuración: un hecho declarado una vez, seguido de un número creciente de turnos de relleno (que van desde 10 hasta 800), seguido de una única consulta que solicita ese hecho. Esto aísla el costo del token por consulta como una función pura de la duración de la conversación, con el contenido de la información completamente fijo.
Cuando la duración de la conversación aumentó 80 veces (de 10 a 800 turnos), el recuento de tokens del volcado sin procesar aumentó 64,15 veces. Mientras tanto, el vector RAG y el gráfico de contexto crecieron 1,00 veces, completamente planos.
Los tokens por consulta del volcado sin procesar son O(N), que es lineal en la duración de la conversación y converge en aproximadamente 12,6 tokens por turno de relleno. No es cuadrático. La historia de O(N^2) solo se vuelve precisa si se suma el costo de una conversación completa de múltiples consultas: las consultas Q, cada una ejecutada contra una transcripción que ha crecido linealmente, rondan el costo total de O(NQ). Ese es el número real, sólo que uno más preciso que "cada consulta cuesta O(N^2)".
Vector RAG y el gráfico de contexto se mantienen fijos en O(1) por consulta porque ambas arquitecturas solo extraen un número limitado de elementos, independientemente de la duración de la conversación.
Lo que señalaría antes de llevar esto a producción
Vale la pena aclarar algunas cosas antes de copiar este patrón en una aplicación real.
Sobre la latencia: Vector RAG es en realidad la arquitectura más lenta aquí, no el gráfico. Reajusta TF-IDF en todo el corpus en cada llamada de consulta en lugar de mantener un índice incremental. En promedio en los cinco escenarios, la respuesta a las consultas del gráfico de contexto fue de 0,050 ms frente a los 1,764 ms de Vector RAG.
Esa brecha se cierra en una implementación real en la que se almacenaría en caché el vectorizador en lugar de reajustarlo desde cero: el comportamiento predeterminado medido como punto de referencia, no las versiones diseñadas en el mejor de los casos. El pico ocasional del gráfico a 1,9 ms se debe en su totalidad a consultas de unión que recorren múltiples rutas de candidatos antes de calificar.
Sobre lo que realmente hace la tabla de alias: la tabla de alias de entidad que permite que "el módulo de autenticación" se resuelva en AuthModule es un sustituto codificado para la vinculación de entidades reales. En producción, ese paso es una convocatoria de LLM. El punto de referencia es determinista porque codifiqué los alias que anticipé; eso no significa que el problema de la falta de coincidencia de vocabulario se resuelva para frases de consulta arbitrarias. Es un costo real y continuo que estoy señalando, no ocultando.
En cuanto a la estimación de tokens: utilicé una heurística de ~4 caracteres por token en lugar de tiktoken, porque tiktoken descarga su archivo de clasificación BPE desde una URL remota en el primer uso, una dependencia de red oculta en un punto de referencia creado para no tener ninguna. La heurística se aplica de manera idéntica en las tres arquitecturas, por lo que no puede sesgar la comparación entre ellas, pero los números absolutos de tokens son aproximaciones.
Sobre lo que este punto de referencia no probó: los giros de distracción aquí son charlas genéricas: "no hay bloqueadores de mi parte", "suena bien". El ruido real de la producción se acerca tópicamente a los hechos reales. Esperaría que la precisión de las tres arquitecturas disminuya bajo el ruido del adversario, y no lo he medido, por lo que no afirmaré que la ventaja se mantenga.
Sobre lo que falta para el uso en producción: extracción de entidades reales (la interfaz ingest() ya acepta un triplete estructurado, por lo que el intercambio en un extractor basado en LLM es un cambio contenido), indexación de vectores incrementales, poda de gráficos para conversaciones de larga duración que acumulan entidades indefinidamente y almacenamiento persistente. El repositorio incluye una ruta de exportación de NetworkX a Neo4j para cualquiera que necesite durabilidad y escrituras simultáneas de múltiples agentes, pero ese es un paso opcional, no una mejora del rendimiento. Las razones para dar ese salto son las garantías transaccionales y la concurrencia, no la velocidad de consulta bruta.
Lo que realmente dicen los números
Nada de esto necesitaba un modelo más grande o una ventana de contexto más larga. Cada resultado surgió al cambiar la forma en que se representa la información, no la cantidad de datos que se acumulan en un mensaje.
Si toma solo un número de este artículo, tome la brecha de consulta de unión: 80% frente a 20-40%. Ese es el verdadero argumento a favor de la memoria estructurada, no el ahorro simbólico.
Si bien los ahorros simbólicos son reales y mensurables, son secundarios. En este punto de referencia, las preguntas que requerían dos hechos de partes completamente diferentes de la conversación fueron donde la arquitectura gráfica mostró su mayor ventaja. Esa brecha se mantuvo consistentemente en los cinco escenarios, no solo en los que resultaron fáciles de representar en un gráfico.
El proyecto completo (cinco escenarios, tres arquitecturas, el conjunto de pruebas que bloquea estos números como pruebas de regresión y la ruta de exportación de Neo4j) está disponible en el repositorio a continuación.
Código fuente completo: https://github.com/Emmimal/context-graph-benchmark/
Referencias
[1]Liu, NF, Lin, K., Hewitt, J., Paranjape, A., Bevilacqua, M., Petroni, F. y Liang, P. (2024). Perdido en el medio: cómo los modelos de lenguaje utilizan contextos largos. Transacciones de la Asociación de Lingüística Computacional, 12, 157-173. https://doi.org/10.1162/tacl_a_00638
[2]Zhang, W., Zhou, Y., Qu, H. y Li, H. (2026). Software poco estructurado: contexto de ingeniería, estructura y entropía de evolución en sistemas multiagente recableados en tiempo de ejecución (arXiv:2603.15690). arXiv. https://arxiv.org/abs/2603.15690
[3]A. Kollegger, “Context Graphs & Agentic Decisions”, Blog para desarrolladores de Neo4j, 31 de enero de 2026. [En línea]. Disponible: https://medium.com/neo4j/context-graphs-agentic-decisions-9a125f22f411
[4]W. Lyon, “Cuando sus agentes comparten un cerebro: creación de memoria multiagente con Neo4j”, Blog para desarrolladores de Neo4j, 13 de abril de 2026. [En línea]. Disponible: https://medium.com/neo4j/when-your-agents-share-a-brain-building-multi-agent-memory-with-neo4j-bac609f17b23
[5]Macklin, N., Zaim, Z. y Erdl, A. (2026). Gráficos de contexto y memoria de IA en todo el mundo. Blog para desarrolladores de Neo4j. https://medium.com/neo4j/context-graphs-and-ai-memory-across-the-globe-bb17e293df32
[6]Documentación de NetworkX. https://networkx.org/
[7]Desarrolladores de Scikit-learn, “TfidfVectorizer”, Documentación de Scikit-learn. [En línea]. Disponible: https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html
[8]OpenAI. Contando fichas con tiktoken. https://github.com/openai/tiktoken
[9]Documentación del controlador Neo4j Python. https://neo4j.com/docs/api/python-driver/current/
Divulgación
Todo el código de este artículo fue escrito por mí y es un trabajo original, desarrollado y probado en Python 3.12 (Windows, PyCharm). Los números de referencia provienen de ejecuciones reales del código en el repositorio vinculado y se pueden reproducir clonándolo y ejecutando benchmark.py y medida_scaling.py, excepto cuando el artículo indique explícitamente que un número es una heurística o una estimación en lugar de un resultado medido. No tengo ninguna relación financiera con ninguna herramienta, biblioteca o empresa mencionada en este artículo.