En mi serie RAG-ING Ahead, utilizo una pila de recuperación nativa de la nube: procesamiento de voz y documentos, fragmentación, incrustaciones, Azure AI Search y una capa asistente encima. Esa serie respondió a la pregunta que tenía en ese momento, que era esencialmente "¿cómo consigo que un modelo de lenguaje responda preguntas sobre documentos en los que nunca fue entrenado?"
La pila todavía funciona. La generación aumentada de recuperación sigue siendo la forma más práctica de basar un modelo en información privada, específica de un dominio o modificada recientemente sin volver a entrenar nada.[1]Si tiene un corpus y necesita respuestas de él, RAG sigue siendo el punto de partida.
Pero he estado ejecutando ese patrón por un tiempo, en proyectos que duraron más que una demostración, y una pregunta diferente comenzó a molestarme:
El sistema recupera el mismo párrafo, lo razona, produce una buena respuesta y luego descarta todo ese razonamiento. Mañana alguien hace una pregunta relacionada y vuelve a hacer el mismo trabajo, desde cero, al mismo costo, sin garantía de llegar a la misma conclusión.
Mi primer instinto fue ajustar la maquinaria en lugar de cuestionarla. Experimenté con diferentes mecanismos de almacenamiento en caché basados en incrustaciones: reconocer que una pregunta entrante era semánticamente cercana a una que el sistema ya había respondido y entregar la respuesta anterior en lugar de pagar por la recuperación completa y el pase de generación nuevamente. El almacenamiento en caché semántico realmente ayuda con el costo y la latencia, y aún así lo recomendaría. Pero me tomó un tiempo admitir lo que realmente es. Almacena en caché las respuestas, sin comprenderlas. La respuesta almacenada en caché es exactamente tan desechable como la original. No ha mejorado nada en el modelo del dominio del sistema, y en el momento en que una pregunta cae fuera del umbral de similitud, el trabajo comienza desde cero nuevamente. Independientemente de lo que modifiqué, el concepto arquitectónico principal de RAG que se encontraba debajo siguió siendo el mismo.
Ese no es un problema de recuperación. La recuperación está haciendo exactamente aquello para lo que fue diseñada. Es un problema de arquitectura. No existe ningún lugar en un sistema RAG estándar donde se pueda acumular comprensión. Ninguna estrategia de almacenamiento en caché, reclasificación o fragmentación soluciona eso, debido a que todas optimizan la búsqueda, ninguna le da memoria al sistema.
Este artículo trata sobre la construcción de ese lugar que falta. Es el resultado de mi último trabajo y experimentación en torno a RAG, GraphRAG y el razonamiento agente sobre un corpus de documentos, el punto donde los ajustes incrementales dejaron de ser suficientes y el diseño en sí tuvo que cambiar. Lo presentaré en tres partes.
La Parte I es neutral en cuanto al proveedor. Describe la arquitectura como un patrón de diseño: las capas, el modelo de objetos, los modos de falla que existe para sobrevivir y la gobernanza que exige. Nada de esto depende de Azure ni de ninguna base de datos o proveedor de modelos en particular. Si está en AWS, GCP o ejecuta Postgres con pgvector y un modelo local, el diseño aún se mantiene y me gustaría que le fuera útil.
La Parte II es la implementación de Azure. Servicio por servicio, con el razonamiento para cada elección, infraestructura real como código y una aplicación FastAPI en ejecución que puede clonar e implementar.
La parte III es la demostración. Una aseguradora de propiedad sintética llamada Ostermere Mutual, veintiún documentos interconectados y tres tutoriales que muestran el patrón haciendo algo que un sistema de recuperación realmente no puede.
Todo lo que hay en el conjunto de datos es sintético. Ostermere Mutual no existe. Tampoco el regulador, la póliza, los siniestros, la gente, las zonas de viento o las cifras. Nada aquí es asesoramiento legal, de seguros, de suscripción o de reclamaciones, y ninguna página de la demostración representa una interpretación real de una póliza real.
Nota de nomenclatura: la documentación actual de Microsoft utiliza Microsoft Foundry para la plataforma unificada anteriormente llamada Azure AI Foundry. Utilizo Foundry en todo momento, manteniendo los nombres familiares de los servicios de Azure, que hacen que la arquitectura sea más fácil de seguir.
Contenido
Parte I – El diseño:
1. Dónde funciona el RAG clásico y dónde termina
2. La recuperación no es comprensión acumulada
3. Las tres capas
4. Lo que realmente vive en la capa de conocimiento
5. Los seis modos de falla
6. El conocimiento escrito es una clase de riesgo diferente
7. Enrutamiento de consultas
8. Cuándo no deberías construir esto
Parte II: Implementación en Azure:
9. Mapeo de las capas a servicios.
10. Almacenamiento de blobs
11. Inteligencia documental
12. Búsqueda de IA en Azure
13. Cosmos DB
14. Fundición de Microsoft
15. FastAPI en aplicaciones de contenedor
16. Identidad
17. El ciclo de vida de la ingestión
Parte III – La manifestación:
18. Mutualidad Ostermere
19. La regla del alcance
20. La contradicción
21. La fecha de la pérdida.
22. La bóveda de obsidiana
23. El argumento del costo
24. Gobernanza
25. Lo que construiría a continuación
26. Para resumir todo
El proyecto completo (aplicación, infraestructura, conjunto de datos y una bóveda de Obsidian lista para abrir) está disponible en el repositorio de GitHub adjunto en github.com/mcekikj/persistent-knowledge-layer, bajo la licencia MIT.
Parte I – El diseño
1. Dónde funciona el RAG clásico y dónde termina
Un flujo RAG convencional es lo suficientemente simple como para dibujarlo en una línea.
Los documentos se fragmentan, incrustan y almacenan en un índice con capacidad vectorial. Llega una pregunta, el sistema encuentra fragmentos semántica o léxicamente similares y se los entrega al modelo como contexto. El modelo, que no sabe nada sobre su negocio, se hace temporalmente para que parezca que sí.
Esto resuelve un problema real e importante y no quiero subestimarlo. El conocimiento privado de la organización no necesita vivir dentro de los parámetros del modelo. Se recupera cuando es necesario. Es una idea realmente buena y es por eso que el patrón se extendió tan rápido.
Pero mire para qué está optimizada la arquitectura. Está optimizado para la búsqueda en el momento de la consulta. Cada pregunta se trata como la primera pregunta que alguien haya hecho.
Consideremos ahora a un usuario real que trabaja en un problema real durante varias semanas:
¿Qué es el valor real en efectivo? ¿En qué se diferencia del valor del costo de reposición? ¿Cuándo se puede pagar la depreciación recuperable? ¿Qué decisión anterior definió cómo tratamos la depreciación? ¿Qué documento introdujo la excepción y por qué? Un colega me dijo que el umbral es de 15 años. ¿Lo es?
Una sencilla aplicación RAG responde a cada una de estas preguntas de forma independiente. Recupera fragmentos nuevamente, reconstruye el contexto nuevamente y le pide al modelo que razone nuevamente. Cada respuesta bien puede ser buena. Pero la síntesis es desechable, lo que significa que cuando se da la respuesta, la comprensión se evapora. Nada de la sexta pregunta es más fácil porque el sistema ya respondió las cinco primeras.
Y en cuanto a la última pregunta: ¿el umbral es de 15 años? – un sistema de recuperación hará algo peor que fallar. Encontrará el fragmento que dice 15 años y le dirá con confianza que sí.
2. La recuperación no es comprensión acumulada
La analogía a la que vuelvo una y otra vez es la de un investigador con un archivador.
Haces una pregunta. El investigador va al gabinete, saca cuatro documentos, lee los pasajes relevantes y le da una respuesta reflexiva. Esto es realmente útil. Luego guardan los documentos, tiran las notas y olvidan todo el ejercicio. Mañana pides un seguimiento y empiezan de nuevo en el gabinete.
Un mejor investigador hace algo más mientras lee. Ellos:
mantener resúmenes de las fuentes importantes; mantener una página para cada concepto recurrente y conectar los conceptos que resulten estar relacionados; escribir decisiones y el razonamiento detrás de ellas; actualizar una comparación cuando nueva evidencia la cambie; registrar las contradicciones en lugar de resolverlas silenciosamente; mantener una lista actualizada de lo que todavía no pueden responder; y mantenga un enlace de cada reclamo al documento del que proviene.
El primer investigador es un sistema RAG en tiempo de consulta. El segundo es lo que quiero construir.
Esto se acerca al patrón LLM Wiki que esbozó Andrej Karpathy: las fuentes sin procesar permanecen donde están, mientras que un agente mantiene un conjunto de páginas Markdown (entidades, conceptos, comparaciones, referencias cruzadas) que un humano puede leer y navegar. La obsidiana resulta ser una ventana conveniente al resultado.[2]
Es fácil distraerse con el Markdown aquí, así que permítanme ser preciso sobre cuál es realmente la idea importante.
La idea importante no es la obsidiana. Ni siquiera es Markdown. Es que el conocimiento se compila una vez en un artefacto duradero, en lugar de reconstruirse a partir de fragmentos sin procesar en cada solicitud.
Ésa es una afirmación arquitectónica y tiene consecuencias arquitectónicas.
3. Las tres capas
No quiero reemplazar a RAG. Quiero darle un lugar para poner lo que aprende.
La fuente sin procesar sigue siendo lo más fuerte que tiene cuando necesita una redacción exacta, una cita, una referencia de cláusula, un documento recién cargado o la verificación de algo en disputa. Una página generada, por muy cuidadosamente mantenida que sea, es una interpretación derivada. No es evidencia, y en el momento en que dejas que pretenda ser evidencia, has construido algo peligroso.
Entonces, el diseño tiene tres capas y responden a tres preguntas diferentes.
La distinción es práctica, no filosófica. La capa de evidencia es un índice de recuperación. La capa de conocimiento es un modelo del dominio mantenido, estructurado y legible por humanos. El orquestador es lo que sabe que una pregunta sobre la redacción exacta de la política debería ir al primero, y una pregunta sobre por qué decidimos que esto debería ir al segundo.
Una regla mantiene todo unido y vale la pena exponerla en su propia línea:
Una página de conocimiento nunca es una fuente. Siempre es rastreable hasta uno.
Rompe esa regla y ya no tendrás una base de conocimientos. En cambio, tienes una colección de afirmaciones seguras que nadie puede verificar.
4. Lo que realmente vive en la capa de conocimiento
La palabra wiki invita a las personas a imaginarse una carpeta de archivos Markdown. Para una base de conocimientos personales eso está realmente bien. Para una aplicación, quiero una tienda estructurada debajo, con Markdown como vista generada a partir de ella.
El modelo de objetos que he elegido:
La mayoría de ellos no son sorprendentes. Tres de ellos son el punto central y quiero detenerme en ellos.
Decisión – porque el por qué muere primero
Un objeto de decisión contiene la regla, su alcance, su fecha de vigencia, su propietario responsable y su fundamento, con un indicador de dónde proviene el fundamento.
Ese último campo importa más de lo que parece. En mi corpus sintético, una actualización de la suscripción dice que una inspección del techo se activa después de 15 años en una zona de viento específica. La actualización no dice por qué 15. El razonamiento existe exactamente en un lugar: un hilo de correo electrónico entre un analista y un jefe de suscripción, que explica que entre 15 y 20 años, el desplazamiento del techo en la banda de vientos fuertes es aproximadamente 3,4 veces mayor que la banda estándar, y que advierte explícitamente que la gente leerá el número sin el calificador y lo aplicará a todo el libro.
Un correo electrónico no es un documento de política. Ningún sistema de recuperación lo clasifica en un lugar destacado. Y en dieciocho meses, cuando alguien pregunta “¿por qué son 15?”, ese razonamiento desaparece, a menos que algo lo preserve deliberadamente.
Contradicción: un objeto de primera clase, no un error
Esta es la idea que más te animo a que aprendas, independientemente de cualquier otra cosa que utilices en este artículo.
Los corpus reales se contradicen. Dos equipos redactan dos documentos, ambos actuales, ninguno reemplaza al otro, y no están de acuerdo. En cualquier conjunto de documentos mantenido por más de un equipo durante más de un año, ésta es la situación normal, no un caso límite.
Un sistema de recuperación maneja esto catastróficamente mal. Recupera un fragmento, el otro o ambos, y luego solicita a un modelo de lenguaje que los reconcilie en un solo paso hacia adelante, bajo un mensaje del sistema que le indica que sea útil. El modelo producirá una respuesta fluida y segura, pero habrá elegido silenciosamente un bando.
Peor aún, la heurística que más naturalmente recurre es la que gana el documento más reciente. Eso suena sensato y está mal. Lo reciente no es aplicabilidad. Un documento más nuevo puede tener un alcance más limitado, puede abordar un producto diferente o puede haber sido escrito por un equipo sin autoridad sobre la cuestión.
Entonces, la contradicción obtiene su propio objeto, con un estado, ambas declaraciones palabra por palabra, sus fechas de vigencia, un propietario responsable y un campo explícito: por qué_no_resuelto. El trabajo del sistema es detectar el conflicto y negarse a resolverlo.
Pregunta abierta: saber lo que no sabes
El compañero natural. Algunas preguntas aún no pueden responderse, a menudo porque están bloqueadas por una contradicción. Una pregunta abierta que es visible es segura. La misma pregunta, con una silenciosa respuesta errónea, es la que acaba en un expediente de denuncia.
5. Los seis modos de falla que esta arquitectura existe para sobrevivir.
Aquí está la prueba honesta de cualquier arquitectura: ¿qué hace que la cosa más simple no pueda hacer?
Construí el corpus de demostración específicamente para responder a eso. Contiene seis trampas distintas. Un sistema de recuperación puro se aplica a cada uno de ellos, y se aplica con fluidez, produciendo una respuesta que se lee perfectamente bien.
5.1 Sustitución con alcance
Una regla general dice inspeccionar los techos por encima de los 20 años. Una actualización posterior dice inspeccionar durante más de 15 años, pero solo en una zona eólica y solo para nuevos negocios.
La recuperación devuelve el fragmento de 15 años. El modelo dice que el umbral es de 15 años. Ahora exige que se inspeccionen decenas de miles de tejados comunes y las quejas de los intermediarios están totalmente justificadas.
La capa de conocimiento almacena una decisión con alcance: "Solo nuevos negocios. Solo zona H3". El número nunca viaja sin su calificativo.
5.2 Contradicción genuina
El Manual de Tramitación de Reclamaciones dice que los costes de rastreo y acceso están cubiertos de forma estándar hasta 5.000 euros y los gestores pueden autorizar sin necesidad de derivación. El Catálogo de respaldo dice que el rastreo y el acceso es un respaldo pagado opcional, no pagadero a menos que esté en el cronograma.
Ambos documentos están vigentes. Ninguno reemplaza al otro. Los escribieron diferentes equipos.
Un sistema RAG elige uno. La capa de conocimiento plantea una contradicción, nombra un propietario y afirma que no hay respuesta disponible.
5.3 Deriva terminológica
En todo el corpus, el mismo concepto aparece como valor real en efectivo, ACV, base de liquidación en efectivo y valor depreciado. El correo electrónico de un corredor en el conjunto de datos enumera literalmente seis de esos términos y pregunta si son seis cosas o una.
Sin resolución de entidad, su wiki crece en cuatro páginas separadas que no están de acuerdo entre sí por omisión. Con él, una página, cuatro alias y una consulta para cualquiera de ellos llegan al lugar correcto.
5.4 Alcance de la fecha de vigencia
Una reclamación tiene una fecha de pérdida del 20 de febrero de 2026. Una norma entró en vigor el 1 de marzo de 2026. La norma no se puede aplicar a esa reclamación.
Éste es mi favorito, porque un sistema de recuperación no tiene ninguna defensa contra él. La similitud semántica no codifica el tiempo. La parte sobre el umbral de 15 años es sumamente relevante para una pregunta sobre la edad del techo en esa afirmación, y completamente inaplicable. El sistema no sólo está equivocado, sino que lo está del modo más convincente posible.
La solución requiere que el orquestador sepa que la pregunta es sobre una fecha y seleccione la documentación vigente en esa fecha, incluido el mantenimiento de un documento reemplazado que estaba activo en ese momento.
5.5 Justificación de la pérdida
Cubierto arriba. El razonamiento vive en un correo electrónico, la regla vive en una directriz, mientras que la conexión entre ellos no vive en ninguna parte.
5.6 Salto múltiple
“¿Por qué este reclamo fue clasificado en el Nivel 1?” requiere las notas de reclamo, luego las pautas de clasificación, luego el concepto de daños por agua y luego la cláusula de la póliza. Cuatro saltos. La búsqueda de similitudes Top-k no atraviesa, clasifica. Las relaciones escritas se cruzan.
En conjunto, estos seis son el argumento: no que el wiki sea mejor, sino que hay una clase de recuperación de preguntas y respuestas con confianza y de manera incorrecta, y la capa de conocimiento las capta.
6. Escribir conocimientos es una clase de riesgo diferente a responder
Esto es lo que me llevó más tiempo internalizar y cambió mi forma de pensar sobre todo el diseño.
Una respuesta incorrecta en el chat afecta una conversación. Una página de concepto canónico incorrecta afecta todas las respuestas que luego se construyen sobre ella, mientras siga siendo incorrecta y nadie se dé cuenta, porque parece conocimiento.
En el momento en que su sistema comienza a escribir conocimiento persistente, ha pasado de “aplicación de recuperación” a “sistema de registro”, y necesita los controles que vienen con eso.
todo es un parche
La modelo nunca escribe a la tienda. Propone un parche. La aplicación lo valida y, cuando el cambio es importante, un humano lo aprueba.
Observe dónde se ubica “resolver una contradicción”: nunca es automático. Si el sistema pudiera resolver las contradicciones por su propia autoridad, el objeto de la contradicción sería inútil.
La procedencia es una cadena, no un campo.
Un objeto cuyas fuentes no puedan identificarse debe eliminarse, no corregirse. No puedes arreglar algo si no sabes de dónde viene.
La estancamiento es una propiedad que debes rastrear
Una página puede ser correcta el lunes y equivocada el viernes porque debajo se reemplazó una fuente. Entonces, cada objeto derivado lleva last_validated_at, y la fuente de la que se derivó lleva reemplazad_by. Cuando se reemplaza una fuente, todo lo que se deriva de ella se marca como obsoleto y no debe presentarse como actual hasta que se vuelva a derivar.
7. Enrutamiento: ¿qué capa responde a esto?
No todas las preguntas necesitan ambas capas, y enviar todo a ambas es la forma de crear algo costoso y lento.
Dos cosas acerca de este diagrama son deliberadas.
En primer lugar, la verificación temporal es anterior a la recuperación, no posterior. Si primero clasifica por similitud y luego descarta los resultados no aplicables, los documentos no aplicables ya habrán consumido su top-k. En producción, coloque las fechas efectivas y reemplazadas en el índice y filtre en la consulta misma, de modo que el conjunto de candidatos quede restringido antes de la clasificación. La demostración toma un atajo aquí que debería reconocer: aplica el filtro de fecha inmediatamente después de la recuperación, que se comporta de manera idéntica en este tamaño de corpus pero silenciosamente mataría de hambre a top-k en uno grande. El principio es que la demostración lo cambia por un esquema de índice más simple.
En segundo lugar, la verificación de contradicciones es una puerta de salida. No importa qué camino tomó la pregunta. Si el tema es impugnado, el sistema se detiene. En código, eso es más o menos:
def consulta(self, pregunta, modo_solicitado, top_k, as_of=None): modo = self.choose_mode(pregunta, modo_solicitado) wiki_items = self._search_wiki(pregunta, top_k) si el modo está en {"wiki", "híbrido"} else[]evidencia = self.evidence.search(question, top_k) if modo en {"evidence", "hybrid"} else[]if as_of: # La fecha SOBRE la que se trata la pregunta, no la fecha en la que se hace. evidencia = self._filter_by_date(evidencia, como_de) # Extrae cada contradicción que toca un concepto recuperado, incluso si el # objeto de contradicción en sí no está clasificado. Alguien que pregunta sobre el rastreo y el # acceso obtiene el conflicto, ya sea que hayan usado o no la palabra "contradicción". contradicciones = self._contradictions_for(wiki_items) advertencias = self._warnings(evidencia, contradicciones, como_de) contexto = self._build_context(wiki_items, evidencia, contradicciones, como_de) respuesta = self.model.answer(pregunta, contexto) …
Y la instrucción que va al modelo es inequívoca:
CONTRADICCIONES NO RESUELTAS. DEBE presentar ambas posiciones con sus fuentes y declarar que la posición no está resuelta. NO DEBE elegir entre ellos y NO DEBE preferir el documento más reciente; lo reciente no es aplicable.
8. Cuándo no deberías construir esto
Preferiría omitir esta arquitectura que aplicarla incorrectamente, así que permítanme ser directo sobre los casos en los que RAG simple es la mejor opción de ingeniería.
Quédese con RAG simple cuando:
el corpus es pequeño y rara vez se consulta, por lo que no hay nada que amortizar; los usuarios abrumadoramente quieren una búsqueda exacta de la fuente, no una síntesis; los documentos se agitan tan rápido que cualquier síntesis derivada queda obsoleta antes de usarse; no hay ningún conocimiento entre sesiones que valga la pena preservar; es un prototipo de corta vida; la latencia de ingestión tiene que ser mínima; su organización aún no puede gobernar el conocimiento persistente generado por IA. Ésta no es una limitación técnica y es la que la gente ignora.
Construya la capa de conocimiento cuando:
el mismo dominio es consultado repetidamente por personas cuyo trabajo continúa durante las sesiones; la síntesis entre fuentes es normal, no excepcional; las decisiones y sus fundamentos deben sobrevivir a la rotación de personal; las excepciones, los alcances y las contradicciones realmente importan; los expertos en el dominio necesitan ver y corregir lo que cree el sistema; un seguimiento de auditoría desde la respuesta hasta la fuente es un requisito, no algo agradable de tener.
Esta es una decisión sobre la carga de trabajo más que una cuestión de nuevos buenos y viejos malos, y la respuesta honesta para muchas aplicaciones es que no es necesario.
Parte II: Implementación en Azure
Todo lo anterior es deliberadamente portátil. Ahora déjame construirlo correctamente en Azure, la plataforma con la que trabajo a diario.
9. Mapeo de las capas a servicios.
10. Blob Storage: lo único que no se puede regenerar
Todo lo demás en esta arquitectura es derivado. Los fragmentos se pueden volver a fragmentar. Las incrustaciones se pueden volver a incrustar. En principio, toda la wiki se puede recompilar desde cero. Los documentos originales no se pueden recuperar de nada.
Por eso reciben el trato correspondiente:
raw-sources/ {workspace-id}/ {document-id}/ original-file.pdf ← nunca reescrito contenido-extraído.json ← Salida de Document Intelligence ingestion-metadata.json ← qué se ejecutó, cuándo, qué versión del modelo wiki-export/ {workspace-id}/ Home.md Conceptos/ · Decisiones/ · Contradicciones/ · Fuentes/ En Bicep, la parte que importa son tres propiedades: recurso blobService 'Microsoft.Storage/storageAccounts/blobServices@2023-05-01' = { padre: nombre de almacenamiento: propiedades 'default': { // Los originales deben sobrevivir a un error de ingestión. El control de versiones y la eliminación temporal son // el seguro más barato disponible para el único artefacto que el sistema no puede regenerar. isVersioningEnabled: true deleteRetentionPolicy: { enable: true, días: 30 } containerDeleteRetentionPolicy: { enable: true, días: 30 } } } Y en la cuenta de almacenamiento en sí, una línea que yo defendería en cualquier implementación de producción: enableSharedKeyAccess: false // sin cadenas de conexión, nunca
El proceso de escritura del wiki nunca debe poder tocar los originales. Se trata de un requisito de flujo de trabajo de procedencia, auditoría y eliminación, y es mucho más fácil de aplicar con contenedores separados y asignaciones de funciones limitadas que con buenas intenciones.
11. Inteligencia documental: utilizada de forma selectiva
Mi corpus de demostración es .txt y .md, por lo que la aplicación lo lee directamente. Los verdaderos documentos de seguros son escaneos, formularios, tablas y firmas, y el diseño en sí suele ser pesado. Una tabla de límites de respaldo reducida a un párrafo es peor que inútil.
Azure AI Document Intelligence le ofrece modelos personalizados y prediseñados que devuelven texto, tablas, marcas de selección y estructura.[3]Mi regla general:
texto limpio y Markdown → un analizador simple, sin cargo; archivos PDF legibles por máquina → un analizador de PDF, si es realmente suficiente; escaneos, formularios, diseños complejos, tablas → Document Intelligence; conserve siempre el JSON extraído junto al original y mantenga los desplazamientos de página y extensión para que una cita pueda apuntar a una ubicación, no solo a un documento.
Ese último punto se amortiza por sí solo la primera vez que un revisor de cumplimiento pregunta "¿dónde dice eso exactamente?"
12. Azure AI Search: la capa de evidencia
Cada registro indexado contiene tanto texto buscable como su vector:
{ "id": "INS-SYN-004-0", "workspace_id": "ostermere-insurance-demo", "source_id": "INS-SYN-004", "title": "Actualización de suscripción de zonas de viento fuerte", "content": "Para nuevos negocios de Hearthmere en la zona H3…", "chunk_number": 0, "content_vector": [0.012, -0.008, 0.031, "…"] }
Azure AI Search se adapta bien a este diseño por una razón específica: los campos de texto y vectoriales coexisten en un único índice, y una consulta híbrida ejecuta las consultas de texto completo y vectoriales en paralelo, fusionando las clasificaciones con Reciprocal Rank Fusion. La clasificación semántica puede luego reordenar los mejores resultados.[4]
Eso importa más de lo que parece. En seguros, la mitad de las consultas son conceptuales (“qué se considera daño repentino por agua”) y la otra mitad son léxicas (“qué cubre HS-TA-01”). La búsqueda de vectores es buena al principio y poco confiable en el segundo: se sabe que las incrustaciones son débiles con identificadores exactos, números de cláusulas y códigos de producto. BM25 los maneja bien, pero no puede soportar la paráfrasis. Quieres ambos y los quieres fusionados en lugar de elegir entre ellos.
de azure.search.documents.models import VectorizedQuery vector_query = VectorizedQuery( vector=self.model.embed(query), k_nearest_neighbors=max(top_k, 10), campos="content_vector", ) resultados = self.client.search( search_text=query, # BM25 leg vector_queries=[vector_query], # vector leg filter=f"workspace_id eq '{self.workspace_id}'", # ajuste de seguridad select=["id", "source_id", "title", "content", "chunk_number"], top=top_k, )
Tenga en cuenta el filtro, porque realiza un trabajo de seguridad, no un ajuste de relevancia.
⚠️ La similitud de vectores no es autorización. Nada sobre la distancia del coseno respeta su modelo de permiso. El recorte de seguridad debe ser un filtro estricto en un campo indexado, aplicado en el momento de la consulta, en cada consulta. Debe aplicarse de manera idéntica a la capa de conocimiento y al Markdown exportado. Una página wiki que sintetiza tres documentos que el usuario no puede leer sigue siendo una filtración de datos, sólo que está bien formateada.
Para sistemas más grandes, la vectorización integrada puede mover la fragmentación y la incrustación en el proceso del indexador.[5]Mantengo las llamadas de incrustación en la aplicación aquí puramente, para que el flujo sea visible y explicable en un artículo.
Una nota sobre el tamaño de la implementación real de esto: la demostración se ejecuta en el nivel de búsqueda gratuito, y la búsqueda vectorial más híbrida funciona bien allí para un corpus de 21 documentos. Lo que el nivel gratuito renuncia es el ranking semántico y el soporte de identidad administrada en el servicio en sí, ambos son condicionales en Bicep, así que trate lo básico como el piso para la producción y lo gratuito como una forma perfectamente buena de validar el diseño de forma gratuita.
13. Cosmos DB: la capa de conocimiento
Cosmos DB para NoSQL contiene la wiki estructurada. Todo el diseño encaja en tres decisiones.
La clave de partición es /workspace_id. Cada objeto wiki lleva un discriminador de tipo, por lo que conceptos, decisiones, contradicciones y preguntas abiertas viven en un solo contenedor. Eso significa que una consulta de una sola partición puede extraer el conocimiento de un espacio de trabajo completo sin distribución entre particiones, que es exactamente el patrón de acceso que tiene este sistema.
clave de partición: {rutas: ['/workspace_id'] tipo: 'Hash'}
No hay base de datos de gráficos, todavía. La gente recurre a Gremlin o Neo4j en el momento en que escuchan "relaciones". Yo lo haría retroceder. Los registros de relaciones explícitas en un almacén de documentos manejan todo lo que este sistema realmente hace: encontrar vecinos, seguir un borde escrito, representar una página conceptual con sus enlaces. Eso es uno o dos saltos.
Un almacén de gráficos gana su lugar cuando el recorrido profundo es en sí mismo la carga de trabajo: análisis de impacto de múltiples saltos, centralidad, búsqueda de rutas a través de una gran red. Si no lo hace, está pagando por una segunda base de datos y un segundo lenguaje de consulta para evitar escribir WHERE c.source_id = @id.
Sin servidor, por ahora. La facturación sigue las unidades de solicitud consumidas, lo que se adapta a una demostración y a una carga de trabajo inicial elevada. Pase al rendimiento aprovisionado una vez que haya medido el consumo real de RU, no antes y no porque la palabra sin servidor esté de moda.[6]
El acceso al plano de datos utiliza el propio sistema RBAC (asignaciones de roles SQL) de Cosmos, que es independiente de Azure RBAC y sorprende a las personas:
recurso cosmosDataRole 'Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments@2024-11-15' = { padre: nombre del cosmos: guid(cosmos.id, identidad.id, 'data-contributor') propiedades: { principalId: identidad.properties.principalId // 00000000-…-000000000002 es el roleDefinitionId del colaborador de datos de Cosmos DB integrado: '${cosmos.id}/sqlRoleDefinitions/00000000-0000-0000-0000-000000000002' alcance: cosmos.id } }
Combinado con enableLocalAuth: verdadero, no hay ninguna clave que filtrar.
14. Microsoft Foundry: modelos ahora, agentes después
Foundry proporciona el chat y las implementaciones de integración. La aplicación se comunica con ella a través de la interfaz Azure OpenAI v1 utilizando el SDK estándar de OpenAI Python, lo que significa que no hay ningún parámetro de versión de API que buscar cada pocos meses:[7]
desde openai importar OpenAI desde azure.identity importar DefaultAzureCredential, get_bearer_token_provider credential = get_bearer_token_provider( DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default" ) client = OpenAI(base_url="https://YOUR-RESOURCE.openai.azure.com/openai/v1/", api_key=credencial) respuesta = client.responses.create(model="TU-DESPLIEGUE-CHAT", entrada=prompt)
Para esta demostración, la aplicación FastAPI organiza explícitamente: incrustar, recuperar, buscar en la wiki, crear contexto, llamar al modelo, validar, escribir. Lo hice a propósito ya que cada paso es visible y puedes poner un punto de interrupción en cualquiera de ellos.
La evolución natural es Foundry Agent Service, donde la misma orquestación se convierte en un agente con un conjunto de herramientas restringido:[8]
search_evidence() search_wiki() get_concept() check_contradictions() ← la puerta, como herramienta propone_wiki_patch() ← propone únicamente; no se puede aplicar apply_approved_patch() ← requiere un token de aprobación export_obsidian_vault()
No le daría a un agente acceso ilimitado a la base de datos el primer día. Cada herramienta aplica su propia validación, autorización, registro y esquema de entrada restringido. Tenga en cuenta que propone_wiki_patch y apply_approved_patch son herramientas independientes: el agente puede llegar a la primera y no puede llegar a la segunda sin un humano en el medio. Esa separación es todo el modelo de gobernanza, expresado como una superficie API.
Para la extracción, utilice salidas estructuradas, que restringen la respuesta a un esquema JSON en lugar de simplemente solicitar un JSON válido.[9]La diferencia se vuelve obvia la primera vez que una extracción de producción devuelve un JSON casi válido.
Puedo informarlo por experiencia ahora, porque la implementación de esta pila produjo dos notas de campo que vale la pena transmitir:
JSON truncado, en el primer documento. gpt-5-mini es un modelo de razonamiento y sus tokens de razonamiento se gastan con el mismo presupuesto de max_output_tokens que la respuesta. Con un límite de 5000 tokens, el JSON de extracción llegó cortado a la mitad de la cadena. La solución en el repositorio: un presupuesto de 16 000 tokens, razonamiento: {“esfuerzo”: “bajo”} para el trabajo de extracción, modo JSON (text.format: json_object) para restringir el decodificador y un reintento. Un detalle que me costó dos ejecuciones de inicialización fallidas: la primera inicialización completa tuvo éxito sin el modo JSON, luego dos ejecuciones posteriores fallaron en documentos diferentes. JSON de solo aviso no falla de manera confiable; falla de manera intermitente, lo cual es peor. Los resultados estructurados restringidos por esquemas siguen siendo la verdadera solución; este es el pragmático. Una peculiaridad de nomenclatura de implementación. Una implementación de incrustación con un nombre idéntico a su modelo (text-embedding-3-small) resultó correcta en cada verificación de estado y luego devolvió modelo_desconocido en cada llamada, tanto en la ruta v1 como en la clásica. Una implementación idéntica denominada embed-3-small funcionó en la primera solicitud. Las implementaciones de chat no muestran el problema. Bicep ahora mantiene el nombre de la implementación y el nombre del modelo como parámetros separados, con esta historia en la descripción.
15. FastAPI en aplicaciones de contenedor
La superficie API:
GET /health GET /wiki enumera objetos, filtrables por tipo GET /wiki/contradictions el registro de contradicciones GET /wiki/{item_id} POST /query { question, mode, top_k, as_of } POST /ingest/text POST /ingest/file POST /cost-estimate POST /export/obsidian POST /export/obsidian.zip
Un detalle de implementación que me costó algo de tiempo de depuración y que vale la pena transmitir: /wiki/contradicciones debe declararse antes de /wiki/{item_id}, o FastAPI coincide primero con el parámetro de ruta y busca alegremente un objeto wiki con la identificación "contradicciones". El orden de las rutas es significativo.
Container Apps es el tiempo de ejecución adecuado aquí porque está basado en contenedores sin pedirme que ejecute Kubernetes.[10]La elección de escala vale una palabra:
escala: { // Para producción, 1 réplica en caliente evita arranques en frío; para una demostración, 0 no cuesta nada. minReplicas: minReplicas maxReplicas: 3 }
minReplicas es un parámetro (predeterminado 1) porque la respuesta correcta depende de lo que esté ejecutando. En producción, escalar a cero parece dinero gratis y luego le cobra un arranque en frío más un apretón de manos del modelo en la primera solicitud después de cada escalado. Para una demostración que se realiza desde una máquina de desarrollador, cero es exactamente lo correcto: la implementación validada en este artículo se ejecutó en cero y no costó nada mientras estaba inactiva.
16. Identidad: sin claves, en ningún lugar
Toda la implementación se ejecuta en una identidad administrada asignada por el usuario con roles de alcance limitado y cada servicio tiene la autenticación local deshabilitada. La aplicación contenedora obtiene AZURE_CLIENT_ID en su entorno, DefaultAzureCredential lo recoge y nunca se emite ningún secreto a la aplicación.
{ nombre: 'AZURE_CLIENT_ID', valor: identidad.properties.clientId }
Esto es más importante en esta arquitectura que en una aplicación RAG simple, y vale la pena explicar por qué. Un sistema de recuperación lee. Este sistema escribe conocimiento persistente sobre el que se construirán otras respuestas. El radio de explosión de una credencial comprometida no es "alguien leyó sus documentos", sino "alguien editó lo que cree su organización". Trate la ruta de escritura en consecuencia.
El infra/main.bicep completo en el repositorio aprovisiona todo: almacenamiento con control de versiones, AI Search con enableLocalAuth, Cosmos con asignaciones de roles SQL y sin servidor, Foundry con implementaciones de ambos modelos, Key Vault, Log Analytics, Application Insights, el entorno Container Apps y la aplicación en sí, además de las seis asignaciones de roles que los conectan y, cuando pasa su propio implementadorPrincipalId, un conjunto reflejado para su cuenta de usuario, que es lo que permite que el script de inicialización y las pruebas se ejecutan desde una máquina desarrolladora sin una sola clave.
creación de grupo az -n rg-wikirag-demo -l swedencentral creación de grupo de implementación az -g rg-wikirag-demo -f infra/main.bicep -p namePrefix=wikirag
17. El ciclo de vida de la ingestión
Ese detalle del medio es el que la gente pasa por alto. Cuando el modelo extrae conceptos de un documento nuevo, debe ver lo que la wiki ya sabe. De lo contrario, inventa la “base de liquidación en efectivo” como un concepto completamente nuevo, sin saber que el valor en efectivo real ya existe con ese alias exacto, y su base de conocimientos se bifurca silenciosamente.
Resolución de entidades en la demostración:
def _resolve_concept(self, nombre, alias): """Identificación exacta → título → alias. Sin esto, 'base de liquidación en efectivo', 'valor depreciado' y 'ACV' se convierten cada uno en su propia página y el wiki se fragmenta en sinónimos. La demostración se detiene en la coincidencia de alias. Un sistema de producción agrega similitud incrustada y un paso de adjudicación de LLM para el medio ambiguo, que es donde viven las fallas interesantes. """ candidatos = {nombre.lower(), *(a.lower() para a in alias)} para concepto en self.repository.list_items("concept"): if concept["id"] == slugify(name): devuelve concepto conocido = {concepto["title"].lower(), *(a.lower() para a in concept.get("aliases",[]))} si los candidatos son conocidos: concepto de devolución devolución Ninguno
La demostración se envía con una prueba de aprobación que incorpora un documento que utiliza la frase "valor depreciado" y afirma que no se crea ninguna página de concepto nuevo, pero se resuelve en el valor en efectivo real existente.
Y esto es lo que sucede sin medidas de resolución más contundentes, medidas en lugar de argumentadas. Cuando ejecuté los veintiún documentos a través de la extracción en vivo gpt-5-mini en la pila implementada, el modelo propuso conceptos que la coincidencia de alias solo podía resolver parcialmente. El resultado fueron 149 objetos conceptuales, de los cuales el gráfico seleccionado tiene 19, un factor de fragmentación de aproximadamente 7 veces. Los conceptos extraídos por máquina no son erróneos, simplemente están nombrados de maneras que no se anticipan en una lista de alias (“Requisito de inspección del techo”, “Límite de autorización de 5.000 EUR”). Ese número es el argumento concreto para incorporar la similitud y la adjudicación de un LLM en la cadena de resolución.
Parte III – La manifestación
18. Mutualidad Ostermere
El corpus sintético son veintiún documentos para una aseguradora de propiedad imaginaria. Es lo suficientemente pequeño como para leerlo en una tarde y está diseñado deliberadamente para que los seis modos de falla de la etapa 5 estén presentes en él.
Esas fuentes informales no son decoración. En el acta de la reunión es donde se registra formalmente la contradicción como no resuelta. En el hilo del correo electrónico es donde existe la única explicación del umbral de 15 años. En mi experiencia, así es exactamente como funcionan las organizaciones reales; las reglas están en los documentos y los motivos están en la bandeja de entrada de alguien.
Una nota sobre la procedencia de los datos: todos los documentos del corpus son sintéticos y los escribí yo (pulidos con IA) para este artículo, por lo que no existen restricciones de licencia para su uso. El conjunto de datos se envía con el repositorio bajo la misma licencia MIT que el código.
Compilado, ese corpus produce:
21 fuentes · 19 conceptos · 28 relaciones tipográficas · 4 comparaciones · 5 decisiones · 2 contradicciones · 4 preguntas abiertas · 2 procesos
19. Tutorial uno: la regla de alcance
curl -X POST http://localhost:8000/query -H 'Content-Type: application/json' -d '{ "question": "¿Cuál es el umbral de inspección del techo?", "mode": "hybrid" }' La recuperación simple encuentra la actualización H3, que dice 15 años y dice "15 años". La capa de conocimiento devuelve el objeto de decisión: { "id": "h3-roof-inspection-threshold", "type": "decision", "summary": "Para nuevos negocios de Hearthmere en la zona H3, solicite una inspección del techo cuando la cubierta principal del techo tenga más de 15 años. Fuera de H3, el umbral general de más de 20 años sigue vigente…", "scope": "Solo nuevos negocios. Solo zona H3. No se aplica a las políticas vigentes escritas antes de que existiera el modelo de zona.", "rationale": "Entre 15 y 20 años, la frecuencia del desplazamiento total o casi total de la cubierta en la banda de vientos severos es aproximadamente 3,4 veces la banda estándar para la misma edad del techo…", "rationale_source": "INS-SYN-018", "accountable_owner": "D. Lindqvist (Jefe de suscripción de propiedades)" }
El número nunca aparece sin su alcance. Y la razón, recuperada de un correo electrónico, está ahí, lo que significa que dentro de dieciocho meses, cuando alguien pregunte por qué 15, la respuesta existe.
El autor del correo electrónico, dicho sea de paso, predijo exactamente este fracaso por escrito:
"Por favor, no permitan que esto se convierta en una regla general de 15 años. Si alguien lee la actualización sin el calificativo de zona, la aplicará a todo el libro, exigiremos inspecciones en decenas de miles de tejados perfectamente normales y las quejas de los intermediarios estarán totalmente justificadas".
Ésta es una cita sintética que escribí para dejar claro un punto, pero no creo que sea irreal.
20. Tutorial dos: la contradicción
Éste es el que pondría delante de un arquitecto escéptico.
curl -X POST http://localhost:8000/query -H 'Tipo de contenido: aplicación/json' -d '{ "question": "¿El rastreo y el acceso están cubiertos por Hearthmere?", "mode": "wiki" }'
Un sistema de recuperación responde a esto. Recupera una parte (del manual de reclamaciones o del catálogo de avales) y le dice "sí, hasta 5.000 € como estándar" o "sólo si compró HS-TA-01". Ambas respuestas están respaldadas por un documento real. Ambos están equivocados, porque la empresa no tiene posición.
El sistema híbrido responde:
{ "answer": "La posición NO ESTÁ RESUELTA. La base de conocimientos mantiene una contradicción no resuelta que cubre esta pregunta, por lo que no se da respuesta…", "warnings": [ "CONTRADICCIÓN NO RESUELTA (con-001): Rastreo y acceso: ¿cobertura estándar o respaldo pagado? El sistema no elegirá entre las fuentes en conflicto. Propietario: Y. Tanaka (Producto)." ], "contradictions": [{ "id": "con-001", "status": "unresolved", "accountable_owner": "Y. Tanaka (Producto)", "statements": [ { "source_id": "INS-SYN-012", "locator": "Chapter 7.3", " Effective_date": "2025-11-01", "statement": "Los costos de rastreo y acceso están cubiertos como estándar bajo Hearthmere, hasta EUR 5.000…" }, { "source_id": "INS-SYN-008", "locator": "HS-TA-01", " Effective_date": "2026-01-01", "statement": "El rastreo y el acceso es un respaldo opcional (HS-TA-01)… Cuando no aparece en el cronograma de la póliza, los costos de rastreo y acceso no se pagan". } ], "why_not_resolved": "Ambos documentos están actualizados. Ninguno reemplaza al otro. Fueron escritos por diferentes equipos. El Catálogo de endosos es el documento más reciente, pero lo reciente no significa aplicabilidad…" }] }
Ambas declaraciones. Ambas fuentes. Ambas fechas. Un ser humano responsable. Y no hay respuesta, porque no hay nadie.
Ese JSON es el andamio determinista del modo de demostración. Esto es lo que hace la pila implementada con la misma pregunta: gpt-5-mini, en vivo, a través de Cosmos DB y AI Search:
Hay una CONTRADICCIÓN NO RESUELTA relevante para esta pregunta. No trate el asunto como resuelto.
INS-SYN-012 (Capítulo 7.3, en vigor el 1 de noviembre de 2025): "Los costos de rastreo y acceso están cubiertos como estándar en Hearthmere, hasta 5000 EUR. Los manejadores deben autorizar gastos razonables hasta este límite sin remisión". INS-SYN-008 (HS-TA-01, en vigor el 1 de enero de 2026): “El rastreo y el acceso es un endoso opcional (HS-TA-01), límite de 5.000 EUR, prima indicativa de 24 EUR. Cuando no aparece en el cronograma de la póliza, los costos de rastreo y acceso no se pagan”.
Esta contradicción NO ESTÁ RESUELTA. Propietario responsable: Y. Tanaka (Producto) [con-001].
No puedo resolver ni indicar si el rastreo y el acceso están cubiertos por Hearthmere debido al conflicto no resuelto anterior.
A la modelo no se le pidió que fuera cautelosa en general. Se le entregó el objeto de contradicción y una regla, y siguió la regla.
El efecto de segundo orden es la parte que encuentro realmente interesante. Una vez que una contradicción es un objeto almacenado, otras cosas pueden depender de ella. El corpus contiene una pregunta del asegurado sobre el reclamo CLM-1155 (¿se paga el costo del estudio de detección de fugas?) que no puede responderse hasta que se resuelva este conflicto. Entonces, se almacena como una pregunta abierta con block_by: “con-001”.
El sistema sabe por qué no puede responder y sabe quién tiene que decidir antes de que pueda hacerlo.
21. Tutorial tres: la fecha de la pérdida
Reclamación CLM-1108: daños por tormenta, cubierta del techo desplazada, fecha de pérdida 20 de febrero de 2026. El techo tiene 18 años. La propiedad, según la clasificación actual, estaría en la zona H3.
Pregunte a un sistema de recuperación si se requería una inspección y encontrará la regla H3 (15 años y 18 > 15) y le indicará que el techo superó el umbral y que no había ninguna inspección registrada. Suena como un hallazgo. Es el comienzo de una declinación errónea.
Porque la norma H3 entró en vigor el 1 de marzo de 2026. Nueve días después de la pérdida. Y la póliza se redactó en septiembre de 2025, antes de que existiera el modelo de zona, por lo que la propiedad no tenía clasificación de zona el día en que se desprendió el techo.
curl -X POST http://localhost:8000/query -H 'Tipo de contenido: aplicación/json' -d '{ "question": "¿Qué umbral de inspección de techo se aplicó a esta propiedad?", "mode": "evidence", "as_of": "2026-02-20" }'
El parámetro as_of es la fecha sobre la que se trata la pregunta, no la fecha en la que se formula. Una vez configurado, INS-SYN-004 queda completamente excluido del conjunto de candidatos. No estaba vigente. El umbral aplicable era la regla general de los 20 años, el techo tenía 18 años, estaba por debajo del umbral y no se incumplió ningún requisito.
El mismo mecanismo funciona en la otra dirección. Pregunte sobre base de liquidación con as_of: “2025-08-01” y el sistema recupera la póliza reemplazada v1.1, porque esa es la versión que regía una pérdida en esa fecha, y la marca:
{ "source_id": "INS-SYN-009", "in_force_at_as_of": true, "note": "reemplazado hoy, pero vigente el 2025-08-01; esta es la versión que rige una pérdida en esa fecha" }
Según la versión 1.1, la depreciación no era recuperable. Con la versión 1.2 actual, lo es. Mismo término, misma política, respuesta opuesta, y lo único que los distingue es una fecha que la búsqueda de similitudes no puede encontrar.
Ambos comportamientos están cubiertos por pruebas en el repositorio y ambos se reproducen en la implementación en vivo. En as_of: 2026-02-20 el modelo implementado respondió:
Respuesta corta: Se aplicó el umbral de inspección general de >20 años. [INS-SYN-003]
Por qué: El 20 de febrero de 2026, la directriz de Hearthmere requirió una inspección cuando la cubierta principal del techo tiene más de 20 años [INS-SYN-003]. El umbral de 15 años del H3… no entró en vigor hasta el 1 de marzo de 2026; esta póliza se redactó el 14 de septiembre de 2025 (por lo que la regla H3 no se aplicaba) y el techo tenía ~18 años y estaba perdido. [INS-SYN-018] [INS-SYN-013]
Y al_del: 2025-08-01:
Base de liquidación: Valor real en efectivo (ACV), definido como costo de reposición menos depreciación, para reclamaciones de construcción elegibles según Ostermere Hearthmere v1.1. [INS-SYN-009] ¿Es recuperable la depreciación? No. Según la versión 1.1, la depreciación no se recupera después de la reparación. [INS-SYN-009]
Son las dos pruebas que conservaría si tuviera que eliminar una por una.
Uno más, en vivo y sin guión: la pregunta de los múltiples saltos
La sección 5.6 afirmó que las relaciones tipificadas permiten que el sistema atraviese lugares donde la búsqueda por similitud solo clasifica. No había escrito una demostración para ello, así que le pregunté directamente a la pila implementada: "Explique por qué la pérdida de agua CLM-1042 se colocó en el Nivel 1 de clasificación bajo el proceso de clasificación de reclamos de propiedad". La respuesta recorrió toda la cadena:
Criterios que rigen el nivel 1: Nivel 1: el estándar se define como “Daño visible menor, sin problemas de seguridad, sin entrada continua de agua y exposición estimada por debajo de EUR 5000” [INS-SYN-002]. Cómo CLM-1042 coincide con esos criterios: la propiedad permaneció habitable, no se informó ninguna falla eléctrica [INS-SYN-005]; el flujo de agua se detuvo en aproximadamente 40 minutos [INS-SYN-005]; daños confinados al camino del agua [INS-SYN-005; INS-SYN-014]; estimación de reparación inicial 3.800 EUR, menos de 5.000 EUR [INS-SYN-005; INS-SYN-002]. Orientación operativa de apoyo: el Manual de Manejo de Reclamaciones enumera indicadores de un escape repentino de agua… que se alinean con los hechos de CLM-1042 [INS-SYN-012]. CLM-1155 contrasta explícitamente a CLM-1042 como una falla repentina y datable [INS-SYN-014]. Nota sobre el estado provisional: la cobertura y las exclusiones siguen sujetas a una determinación de cobertura formal por parte de un profesional de reclamos autorizado [INS-SYN-005].
Cuatro fuentes, cuatro saltos y el límite de decisión humana preservado al final sin que nadie lo pida.
Una observación honesta de la misma sesión: mi primera formulación de esa pregunta (“…¿y cómo se aplica la cobertura de daños por agua?”) quedó bloqueada por la contradicción: la evidencia de CLM-1042 toca el concepto impugnado de rastreo y acceso, por lo que el sistema presentó con-001 y se negó a tomar una determinación definitiva en lugar de responder a la parte de clasificación. La puerta se equivoca al detenerse. Para un flujo de trabajo de reclamos, considero que es el valor predeterminado correcto, pero es una verdadera compensación: una puerta agresiva a veces retendrá una respuesta que el usuario legítimamente necesitaba y ajustar ese límite es parte del funcionamiento del sistema.
22. Viéndolo: la bóveda de obsidiana
La aplicación exporta todo el estado de Cosmos DB a un almacén. Cincuenta y cinco páginas, todas generadas, nada escrito a mano:
obsidian_vault/ Home.md Preguntas abiertas.md Conceptos/ 19 páginas Fuentes/ 21 páginas Decisiones/ 5 páginas Comparaciones/ 4 páginas Contradicciones/ 2 páginas ← con-001, con-002 Procesos/ 2 páginas
Abra la carpeta en Obsidian y podrá navegar por enlaces, inspeccionar vínculos de retroceso, seguir el gráfico, detectar páginas huérfanas y, lo más importante, ver lo que cree el sistema y decirle que está mal.
Creo que esa última capacidad es el argumento más fuerte a favor de toda esta arquitectura. Un índice vectorial es excelente desde el punto de vista operativo y completamente opaco para un experto en el dominio. No puede entregarle a un asegurador una incorporación de 1.536 dimensiones y preguntarle: "¿Esto le parece adecuado?" Puede entregarles una página de Markdown que diga que el umbral es de 15 años, pero solo en H3, y solo para nuevos negocios, y aquí está el por qué, y aquí están los cuatro documentos de donde proviene.
Le dirán en treinta segundos si es correcto. Markdown hace que la memoria sea auditable por las personas que realmente conocen el dominio. Ninguna otra parte de la pila hace eso.
23. El argumento del costo, honestamente
La capa de conocimiento cuesta más en el momento de la ingestión. No voy a fingir lo contrario.
Una canalización RAG extrae e incrusta cada documento una vez. La canalización híbrida además resume, extrae conceptos y afirmaciones, resuelve entidades en comparación con la wiki existente, genera parches de relación y comparación, valida y regenera Markdown. La amplificación de escritura es real y no es pequeña.
El argumento es que este costo anticipado reduce el razonamiento repetido en el momento de la consulta. Entonces, la pregunta no es si la construcción del wiki cuesta más (claramente lo es), sino si la compilación se amortiza a través de un uso futuro suficiente.
El modelo en cost_model/cost_model.py:
Compilación = D × Td × M RAG simple = Q × Tr Híbrido = Q × (Tw + V × Tv)
Con los valores predeterminados ilustrativos: 50 documentos, 6000 tokens cada uno, 1000 preguntas, 6000 tokens de contexto recuperados por pregunta RAG versus 1350 tokens de contexto wiki, verificación sin procesar en el 25% de las preguntas:
Ahora las advertencias, porque un gráfico como este puede inducir a error fácilmente:
Este es el volumen simbólico, no el precio. Ignora los tokens de salida, los costos de integración, la capacidad de búsqueda de IA, las RU de Cosmos, el cómputo de aplicaciones de contenedores y las páginas de inteligencia de documentos. Trata a todos los tokens como iguales. En la práctica, la ingesta y la respuesta pueden utilizar diferentes clases de modelos, y ahí es donde se salvan muchas vidas, porque un modelo pequeño puede responder desde un contexto wiki conciso, mientras que uno más fuerte está reservado para la conciliación y las actualizaciones. No garantiza nada. Un agente mal gobernado que reescribe toda la wiki en cada ingesta borrará cualquier ahorro que usted haya modelado. La economía depende enteramente de políticas disciplinadas de actualización.
Un punto de datos medido, a partir de la implementación de esta pila exacta: la ingesta de los veintiún documentos a través de la extracción e incrustaciones en vivo de gpt-5-mini, la ejecución de todos los tutoriales de este artículo y la regeneración de la bóveda desde Cosmos DB cuestan aproximadamente entre 0,20 y 0,30 dólares en total. La pila inactiva (búsqueda de nivel gratuito, aplicación de contenedor de escala a cero, Cosmos sin servidor) quema alrededor de 0,05 dólares al día. Con este tamaño de corpus, el costo de compilación es dinero del café. La economía sólo resulta interesante a escala, que es para lo que sirve el modelo anterior.
La suposición del tamaño del contexto también sobrevivió al contacto con el sistema implementado. Medido a través de un conjunto de preguntas conceptuales en la pila en vivo, el contexto wiki promedió aproximadamente 500 tokens contra aproximadamente 1.750 para el contexto de evidencia equivalente: una proporción de 3,5 veces, en el mismo rango que las 4,4 veces que asume el modelo. Los números absolutos son menores que los del modelo, porque los documentos sintéticos son cortos. El ratio es la parte que se traslada a un corpus real. En todo caso, el ratio medido es ligeramente más conservador que el supuesto, lo que hace que el punto de equilibrio se mueva más tarde, no antes; vale la pena saberlo antes de citar el modelo en una reunión presupuestaria.
Ejecute /cost-estimate con sus propias suposiciones. Luego, deséchelos y use la telemetría de su corpus real y combinación de consultas, con el precio de la calculadora de Azure actual.[11]
Y, sinceramente, el argumento simbólico es el argumento más débil a favor de esta arquitectura. Los rendimientos reales son:
terminología consistente entre sesiones y entre personas; decisiones y fundamentos que sobreviven a la persona que las hizo irse; contradicciones que son visibles en lugar de resolverse silenciosamente; un rastro de auditoría desde cualquier respuesta hasta su fuente; un artefacto de conocimiento que un experto en el dominio puede revisar; continuidad entre sesiones de agentes y entre actualizaciones de modelos.
Me haría cargo de unos pocos millones de tokens de entrada.
24. Gobernanza, reformulada
Debido a que el sistema escribe, necesita controlarlo, un sistema de solo lectura no lo hace.
Preservar la procedencia. Cada declaración se rastrea hasta un fragmento, un lapso, un documento, en la versión de la que se deriva.
Parche, nunca escribas. El modelo propone; validación determinista y, para cualquier cosa importante, un ser humano dispone.
Detectar estancamiento. last_validated_at, reemplazado_por, fuente_fecha_efectiva. Cuando se reemplaza una fuente, todo lo que se derive de ella quedará obsoleto hasta que se vuelva a derivar.
Recorte la seguridad en cada capa. Rutas de blobs, registros de búsqueda, objetos Cosmos, exportaciones de Markdown, herramientas de agentes, cachés, telemetría. Consecuentemente. Una página sintetizada nunca debe mostrar información a la que el lector no pueda acceder en la fuente.
Mantenga humana la decisión humana. En la demostración, la IA puede resumir una admisión, recuperar evidencia de políticas, identificar información faltante, proponer un nivel de clasificación provisional y señalar reglas conflictivas. No puede determinar la cobertura, rechazar un reclamo, evaluar el fraude, calificar un riesgo, resolver una contradicción o informar a un cliente que se ha tomado una decisión.
Las actas sintéticas del grupo de trabajo contienen la mejor articulación de esto que logré escribir, y lo dejaré como el principio de gobernanza para toda la arquitectura:
Actualmente, los cuidadores adoptan el nivel de clasificación propuesto por el asistente en aproximadamente el 90% de los casos, lo cual está bien, pero sólo porque ellos mismos leen la ingesta. Quitar el controlador del bucle elimina lo que hace que el 90% sea confiable.
Esa es la trampa en una frase. Un sistema automatizado gana credibilidad bajo la revisión humana, y luego esa credibilidad se utiliza como argumento para eliminar la revisión.
25. Lo que construiría a continuación
El repositorio es una base, no un producto. Las lagunas de las que soy más consciente:
Event Grid y la ingesta asincrónica basada en colas (la demostración se ingiere de forma sincrónica porque es más fácil de ejecutar localmente). Document Intelligence con preservación de páginas y espacios, de modo que las citas apunten a ubicaciones. Salidas estructuradas con esquema JSON estricto en cada extracción y parche. Resolución de entidades con similitud incorporada y adjudicación de LLM, no solo coincidencia de alias. Una interfaz de usuario de aprobación humana para parches de alto riesgo: en este momento, el ciclo de vida existe en el diseño y la ruta de bajo riesgo existe en el código. Herramientas de Foundry Agent Service, que proponen y aplican como superficies con permisos separados. Conjuntos de evaluación para recuperación, síntesis y el difícil: precisión de actualización. ¿Cómo se prueba que una base de conocimientos cambió correctamente? Cuadros de mando de frescura y contradicción. Un registro de contradicciones que nadie mira es sólo un archivo de registro. Recorte de seguridad por inquilino, de extremo a extremo. Modele el enrutamiento por complejidad y riesgo de la tarea.
El número 7 es el problema de investigación genuinamente abierto y todavía no tengo una buena respuesta.
26. Para resumir todo
RAG es el motor de evidencia de esta arquitectura, y nada aquí es su obituario. Le da al modelo acceso a material fuente original, relevante y actual, y no hay sustituto para eso.
Lo que no hace es recordar.
La capa de conocimiento agrega lo que faltaba: una representación mantenida, estructurada e inspeccionable de lo que el sistema ya ha resuelto, con los alcances intactos, la justificación preservada, las contradicciones visibles y una línea de regreso a la evidencia para cada afirmación.
RAG pregunta: ¿Qué debo recuperar para esta pregunta?
La capa de conocimiento: ¿Qué debería ser cierto de forma duradera después de procesar todo hasta ahora?
El orquestador: ¿Cuál de ellas necesito para responder a esto de forma segura, ahora mismo?
En Azure, esa separación se asigna claramente:
Blob Storage conserva los originales, lo único que no se puede regenerar. Document Intelligence extrae el contenido difícil y mantiene los intervalos. Azure AI Search almacena evidencia recuperable y recortada por seguridad. Cosmos DB almacena los conceptos, relaciones, decisiones y contradicciones en evolución. Microsoft Foundry proporciona los modelos y la ruta a los agentes. FastAPI en Container Apps ejecuta la orquestación, el alcance temporal y la puerta de contradicción. La obsidiana hace que todo sea visible para las personas que saben si está bien.
Es más trabajo que un simple RAG y cuesta más ingerirlo. A cambio, obtienes memoria organizacional, síntesis consistente, relaciones explícitas, un historial de decisiones visible y, la parte a la que sigo volviendo, un sistema que te dirá “dos de nuestros documentos no están de acuerdo y nadie ha decidido todavía” en lugar de inventar algo con confianza.
Esa última capacidad no es una característica. Es la razón para construirlo.
La aplicación ya no busca en un montón de documentos. Lentamente está construyendo un modelo revisable de un dominio, manteniendo al mismo tiempo la evidencia original lo suficientemente cercana como para comparar cada conclusión importante.
Gracias por tomarse el tiempo de explorar esta arquitectura conmigo. Se convirtió en una pieza más larga de lo que pretendía, porque el diseño seguía teniendo una parte más que vale la pena explicar. El proyecto FastAPI, las plantillas de Bicep, los veintiún documentos sintéticos y la bóveda de Obsidian están todos en el repositorio, y creo firmemente que le brindan un punto de partida práctico para construir su propia capa de conocimiento persistente. Clónelo, ejecútelo en modo de demostración sin ninguna credencial de Azure e intente descifrarlo; realmente me gustaría saber dónde falla.
Divulgación: soy un MVP de Microsoft. Este artículo refleja mi propio trabajo y opiniones independientes; Microsoft no participó ni revisó su contenido. Todo el uso de Azure descrito se basa en documentación pública y en mi propia implementación.
Referencias
[1]P. Lewis et al., Generación aumentada de recuperación para tareas de PNL intensivas en conocimiento (2020), NeurIPS 2020
[2]A. Karpathy, LLM Wiki (2025), GitHub Gist
[3]Microsoft, modelo de diseño de inteligencia documental (2026), Microsoft Learn
[4]Microsoft, descripción general de la búsqueda híbrida: Azure AI Search (2026), Microsoft Learn
[5]Microsoft, Vectorización integrada en Azure AI Search (2026), Microsoft Learn
[6]Microsoft, Azure Cosmos DB sin servidor (2026), Microsoft Learn
[7]Microsoft, ciclo de vida de la API Azure OpenAI v1 (2026), Microsoft Learn
[8]Microsoft, descripción general del servicio Foundry Agent (2026), Microsoft Learn
[9]Microsoft, Salidas estructuradas con Azure OpenAI (2026), Microsoft Learn
[10]Microsoft, implementación de una aplicación web Flask o FastAPI en aplicaciones de contenedor de Azure (2026), Microsoft Learn
[11]Microsoft, Planificar y administrar los costos de Azure AI Search (2026), Microsoft Learn