En este tutorial, implementamos IWE: un sistema de gestión de conocimiento personal de código abierto impulsado por Rust que trata las notas de rebajas como un gráfico de conocimiento navegable. Dado que IWE es una herramienta CLI/LSP diseñada para editores locales. Creamos una base de conocimientos realista para desarrolladores desde cero, conectamos enlaces wiki y enlaces de rebajas en un gráfico dirigido y luego recorrimos cada operación importante de IWE: búsqueda difusa con búsqueda, recuperación contextual con recuperación, visualización de jerarquía con árbol, consolidación de documentos con squash, estadísticas con estadísticas y exportación de gráficos DOT para visualización. Luego vamos más allá de la CLI integrando OpenAI para potenciar las transformaciones de IA estilo IWE: resumen, sugerencia de enlaces y extracción de tareas pendientes, directamente en nuestro gráfico de conocimiento. Finalmente, construimos un canal RAG agente completo donde un agente de IA navega por el gráfico utilizando herramientas de llamada de funciones, realiza razonamiento de múltiples saltos en documentos interconectados, identifica lagunas de conocimiento e incluso genera nuevas notas que se insertan en la estructura existente.
importar subproceso, sys def _install(pkg): subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", pkg]) _install("openai") _install("graphviz") importar re, json, textwrap, os, getpass de colecciones importar defaultdict de clases de datos importar clase de datos, campo al escribir importar Opcional desde fecha y hora importar fecha y hora intentar: desde google.colab importar datos de usuario OPENAI_API_KEY = userdata.get("OPENAI_API_KEY") si no es OPENAI_API_KEY: aumente ValueError print("✅ OPENAI_API_KEY cargado de los secretos de Colab.") excepto excepción: OPENAI_API_KEY = getpass.getpass("🔑 Ingrese su clave API de OpenAI: ") print("✅ Clave API recibida.") os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY from openai import OpenAI client = OpenAI(api_key=OPENAI_API_KEY) print("n" + "=" * 72) print(" Tutorial avanzado de IWE: gráfico de conocimiento + agentes de IA") print("=" * 72) @dataclass class Sección: nivel: int título: str contenido: str niños: lista = campo(default_factory=list) @dataclass clase Documento: clave: str título: str raw_content: str secciones: lista = campo(default_factory=list) enlaces_salientes: lista = campo(default_factory=list) etiquetas: lista = campo(default_factory=list) creado: str = "" modificado: str = "" clase KnowledgeGraph: def __init__(self): self.documents: dict[str, Documento] = {} self.backlinks: dict[str, set] = defaultdict(set) _WIKI_LINK = re.compile(r"[[([^]|]+)(?:|([^]]+))?]]") _MD_LINK = re.compile(r"[([^]]+)](([^)]+))") _HEADER = re.compile(r"^(#{1,6})s+(.+)", re.MULTILINE) _TAG = re.compile(r"#([a-zA-Z][w/-]*)") def _extract_links(self, text: str) -> list[str]: links =[]para coincidencia en self._WIKI_LINK.finditer(text): links.append(match.group(1).strip()) para coincidencia en self._MD_LINK.finditer(text): target = match.group(2).strip() si no es target.startswith("http"): target = target.replace(".md", "") links.append(target) enlaces de retorno def _parse_sections(self, text: str) -> list[Section]: secciones =[]partes = self._HEADER.split(texto) i = 1 while i < len(partes) – 1: nivel = len(partes[i]) título = partes[i + 1].strip() cuerpo = partes[i + 2] si i + 2 < len(partes) else "" secciones.append(Section(nivel=nivel, título=título, contenido=cuerpo.strip())) i += 3 devolver secciones def _extract_tags(self, text: str) -> list[str]: tags = set() para la línea en text.split("n"): if line.strip().startswith("#") y " " en line.strip(): stripped = re.sub(r"^#{1,6}s+.*", "", línea) para m en self._TAG.finditer(stripped): tags.add(m.group(1)) else: para m en self._TAG.finditer(line): tags.add(m.group(1)) return sorted(tags) def add_document(self, key: str, content: str) -> Documento: secciones = self._parse_sections(content) title = secciones[0].título si las secciones más enlaces clave = self._extract_links(contenido) etiquetas = self._extract_tags(contenido) ahora = datetime.now().strftime("%Y-%m-%d %H:%M") doc = Documento( clave=clave, título=título, contenido_sin formato=contenido, secciones=secciones, enlaces_salientes=enlaces, etiquetas=etiquetas, creado=ahora, modificado=ahora, ) self.documents[clave] = documento para el destino en enlaces: self.backlinks[destino].add(clave) return doc def get(self, clave: str) -> Opcional[Documento]: return self.documents.get(clave) def find(self, consulta: str, root_only: bool = False, límite: int = 10) -> lista[str]: q = query.lower() anotó =[]para clave, doc en self.documents.items(): puntuación = 0 si q en doc.title.lower(): puntuación += 10 si q en doc.raw_content.lower(): puntuación += doc.raw_content.lower().count(q) si q en key.lower(): puntuación += 5 para etiqueta en doc.tags: si q en tag.lower(): puntuación += 3 si puntuación > 0: anotado.append((clave, puntuación)) anotado.sort(clave=lambda x: -x[1]) resultados = [k para k, _ en puntuación[:límite]] si solo raíces: resultados = [k para k en resultados si no es self.backlinks.get(k)] devuelve resultados def recuperar(self, clave: str, profundidad: int = 1, contexto: int = 1, excluir: establecer = Ninguno) -> str: excluir = excluir o establecer() partes =[]si contexto > 0: padres_de = lista(self.backlinks.get(clave, set()) – excluir) for p en padres_de[:contexto]: pdoc = self.get(p) if pdoc: parts.append(f"[CONTEXTO: {pdoc.title}]n{pdoc.raw_content[:300]}….n") excluir.add(p) doc = self.get(clave) si no doc: return f"⚠ Documento '{key}' no encontrado." parts.append(doc.raw_content) excluir.add(clave) si profundidad > 0: para enlace en doc.outgoing_links: si enlace no está excluido: niño = self.get(enlace) si niño: partes.append(f"n—n[LINKED: {child.title}]n") parts.append( self.retrieve(enlace, profundidad=profundidad – 1, contexto=0, excluir=excluir) ) return "n".join(parts) def tree(self, key: str, indent: int = 0, _visited: set = Ninguno) -> str: _visited = _visited if _visited no es Ninguno más set() doc = self.get(key) if not doc: return "" prefix = " " * indent + ("└─ " if indent else "") if key in _visited: return f"{prefix}{doc.title} ({key}) ↩ (referencia circular)" _visited.add(key) líneas = [f"{prefix}{doc.title} ({key})"] para el enlace en doc.outgoing_links: if self.get(link):lines.append(self.tree(link, indent + 1, _visited)) return "n".join(lines) def squash(self, clave: str, visitado: establecer = Ninguno) -> str: visitado = visitado o set() doc = self.get(clave) si no es doc o ingresa visitado: return "" visitado.add(clave) parts = [doc.raw_content] para el enlace en doc.outgoing_links: child_content = self.squash(enlace, visitado) si child_content: parts.append(f"n{'─' * 40}n") parts.append(child_content) return "n".join(parts) def stats(self) -> dict: total_words = sum(len(d.raw_content.split()) for d in self.documents.values()) total_links = sum(len(d.outgoing_links) for d in self.documents.values()) huérfanos = [k for k in self.documents si no es self.backlinks.get(k) y no self.documents[k].outgoing_links] all_tags = set() for d in self.documents.values(): all_tags.update(d.tags) return { "total_documents": len(self.documents), "total_words": total_words, "total_links": total_links, "unique_tags": len(all_tags), "tags": ordenado(all_tags), "orphan_notes": huérfanos, "avg_words_per_doc": total_words // max(len(self.documents), 1), } def export_dot(self, Highlight_key: str = Ninguno) -> str: líneas = ['digraph KnowledgeGraph {', ' rankingdir=LR;', ' nodo [forma=cuadro, estilo="redondeado, relleno", fillcolor="#f0f4ff", ' 'fontname="Helvetica", fontsize=10];', ' edge [color="#666666", arrowsize=0.7];'] para clave, doc en self.documents.items(): etiqueta = doc.title[:30] color="#ffe4b5" if highlights_key == clave else '#f0f4ff'lines.append(f' "{key}" [label="{label}", fillcolor="{color}"];') para clave, doc en self.documents.items(): para enlace en doc.outgoing_links: si enlace en self.documents:lines.append(f' "{key}" -> "{link}";')lines.append("}") return "n".join(lines) print("n✅ Sección 1 completa: clase KnowledgeGraph definida.n")
Instalamos las dependencias requeridas, aceptamos de forma segura la clave API de OpenAI a través de secretos de Colab o una solicitud de contraseña e inicializamos el cliente OpenAI. Luego definimos las tres clases de datos fundamentales, Sección, Documento y KnowledgeGraph, que reflejan la arquitectura gráfica basada en arena de IWE, donde cada archivo de rebajas es un nodo y cada enlace es un borde dirigido. Implementamos el conjunto completo de operaciones IWE CLI en la clase KnowledgeGraph, incluido el análisis de rebajas para enlaces y encabezados wiki, búsqueda difusa con búsqueda, recuperación contextual con recuperación, visualización de jerarquía segura para ciclos con árbol, consolidación de documentos con squash, análisis de la base de conocimientos con estadísticas y exportación de gráficos DOT para visualización Graphviz.
kg = KnowledgeGraph() kg.add_document("project-index", """# Proyecto de aplicación web Este es el **Mapa de contenido** para nuestro proyecto de aplicación web. ## Arquitectura – [Sistema de autenticación](autenticación) – [Diseño de base de datos](diseño de base de datos) – [Diseño de API](diseño de API) ## Desarrollo – [Pila de interfaz de usuario](pila de interfaz de usuario) – [Canal de implementación](implementación) ## Investigación – [[estrategias de almacenamiento en caché]] – [[notas de rendimiento]] """) kg.add_document("authentication", """# Sistema de autenticación Nuestra aplicación utiliza **autenticación basada en JWT** con tokens de actualización. ## Flujo 1. El usuario envía las credenciales a `/api/auth/login` 2. El servidor valida con la tabla de usuario [Diseño de base de datos](diseño de base de datos) 3. Devuelve un token de acceso de corta duración (15 min) + token de actualización (7 días) 4. El cliente almacena el token de actualización en una cookie solo HTTP ## Consideraciones de seguridad – Contraseñas codificadas con bcrypt (factor de costo 12) – Límite de velocidad en el punto final de inicio de sesión: 5 intentos/minuto – Rotación de token de actualización en cada uso – Consulte [[caching-strategies]] para el almacenamiento en caché de sesiones #security #jwt #auth """) kg.add_document("database-design", """# Diseño de base de datos que utilizamos **PostgreSQL 16** con las siguientes tablas principales. ## Tabla de usuarios “`sql CREATE TABLE usuarios (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), correo electrónico VARCHAR(255) UNIQUE NOT NULL, contraseña VARCHAR(255) NOT NULL, create_at TIMESTAMPTZ DEFAULT NOW() ); UUID PRIMARY KEY, user_id UUID REFERENCES usuarios(id), token_hash VARCHAR(255) NOT NULL, expires_at TIMESTAMPTZ NOT NULL ); “` ## Estrategia de indexación – Árbol B en `users.email` para búsquedas de inicio de sesión – Árbol B en `sessions.token_hash` para validación de tokens – Consulte [[notas de rendimiento]] para optimización de consultas #database #postgresql #schema """) kg.add_document("api-design", """# API RESTful de diseño API siguiendo la especificación OpenAPI 3.0. ## Puntos finales | Método | Ruta | Descripción | |——–|——|————-| | POST | /api/auth/login | Autenticar usuario | | POST | /api/auth/refresh | Actualizar token de acceso | | GET | /api/users/me | Obtener perfil de usuario actual | | PUT | /api/users/me | Actualizar perfil | ## Manejo de errores Todos los errores devuelven JSON con [Diseño de base de datos] (diseño de base de datos). kg.add_document("frontend-stack", """# Frontend Stack ## Opciones de tecnología – **Framework**: React 19 con componentes de servidor – **Styling**: Tailwind CSS v4 – **State Management**: Zustand para el estado del cliente – **Data Fetching**: TanStack Query v5 ## Integración de autenticación El frontend consume los puntos finales [API Design](api-design). Los tokens de acceso se almacenan en la memoria (no localStorage) para seguridad manejada de forma transparente a través de interceptores de Axios. #frontend #react #tailwind """) kg.add_document("deployment", """# Deployment Pipeline ## Infraestructura – **Tiempo de ejecución de contenedor**: Docker con compilaciones de varias etapas – **Orquestación**: Kubernetes en GKE – **CI/CD**: Acciones de GitHub → Registro de artefactos de Google → GKE ##. Etapas de canalización 1. Lint y verificación de tipos 2. Pruebas unitarias (Jest + pytest) 3. Crear imágenes de Docker 4. Enviar al registro de artefactos 5. Implementar en preparación (automático) 6. Implementar en producción (aprobación manual) ## Monitoreo – Prometheus + Grafana para métricas – Registro estructurado con ID de correlación – Consulte [[notas de rendimiento]] para SLO #devops #kubernetes #cicd """) kg.add_document("caching-strategies", """# Estrategias de almacenamiento en caché ## Almacenamiento en caché a nivel de aplicación – **Redis** para almacenamiento de sesión y limitación de velocidad – Patrón de caché aparte para perfiles de usuario a los que se accede con frecuencia – TTL: 5 minutos para perfiles, 15 minutos para configuración ## Almacenamiento en caché HTTP – `Cache-Control: privado, max-age=0` para puntos finales autenticados – `Cache-Control: público, max-age=3600` para recursos estáticos – Compatibilidad con ETag para solicitudes condicionales ## Invalidación de caché – Invalidación basada en eventos a través de pub/sub – Claves de caché versionadas: `user:{id}:v{version}` Relacionado: [Sistema de autenticación](autenticación) usa Redis para actualizar tokens #caching #redis #rendimiento """) kg.add_document("notas de rendimiento", """# Notas de rendimiento ## Base de datos. Optimización de consultas: utilice `EXPLAIN ANALYZE` antes de implementar nuevas consultas: agrupación de conexiones con PgBouncer (máximo 50 conexiones): evite consultas N+1: use JOIN o el patrón DataLoader ## Métrica de SLO |——–|——–|———| 180 ms | | Disponibilidad | 99,9 % | 99,95 % | Tasa de error | < 0,1 % | ## Pruebas de carga: scripts k6 en `/tests/load/` – Línea de base: 1000 RPS sostenidos – Pico: 5000 RPS durante 60 segundos #rendimiento #slo #monitoring """) print("✅ Sección 2 completa: 8 documentos cargados en el gráfico de conocimiento.n") print("─" * 72) print(" 3A · encontramos – Buscar en el gráfico de conocimiento") print("─" * 72) resultados = kg.find("autenticación") print(f"n🔍 find('autenticación'): {resultados}") resultados = kg.find("rendimiento") print(f"🔍 buscar('rendimiento'): {resultados}") resultados = kg.find("cache", root_only=True) print(f"🔍 find('cache', root_only=True): {resultados}") print("n" + "─" * 72) print(" 3B · iwe tree — Jerarquía de documentos") print("─" * 72) print() print(kg.tree("indice-proyecto")) print("n" + "─" * 72) print(" 3C · iwe stats — Estadísticas de la base de conocimientos") print("─" * 72) stats = kg.stats() for k, v in stats.items(): print(f" {k:>25s}: {v}") print("n" + "─" * 72) print(" 3D · recuperamos – Recuperación consciente del contexto") print("─" * 72) print("n📄 Recuperando 'autenticación' con profundidad=1, contexto=1:n") recuperado = kg.retrieve("autenticación", profundidad=1, contexto=1) print(recuperado[:800] + "n… (truncado)") print("n" + "─" * 72) print(" 3E · iwe squash — Combinar documentos") print("─" * 72) aplastado = kg.squash("indice-proyecto") print(f"n📋 'índice-proyecto' aplastado: {len(aplastado)} caracteres, " f"{len(squashed.split())} palabras") print("n" + "─" * 72) print(" 3F · exportamos punto – Visualización de gráficos") print("─" * 72) dot_output = kg.export_dot(highlight_key="project-index") print(f"n🎨 Salida DOT ({len(dot_output)} caracteres):n") print(dot_output[:500] + "n…") intenta: importar Graphviz src = graphviz.Source(dot_output) src.render("knowledge_graph", format="png", cleanup=True) print("n✅ Gráfico renderizado en 'knowledge_graph.png'") intente: desde IPython.display import Image, display display(Image("knowledge_graph.png")) excepto ImportError: print(" (Ejecute en Colab/Jupyter para ver la imagen) inline)") excepto excepción como e: print(f" ⚠ Se omitió la representación de Graphviz: {e}") print("n✅ Sección 3 completa: se demuestran todas las operaciones gráficas.n")
Creamos una instancia de KnowledgeGraph y lo completamos con ocho documentos de rebajas interconectados que forman una base de conocimientos realista para desarrolladores, que abarca autenticación, diseño de bases de datos, diseño de API, interfaz, implementación, almacenamiento en caché y rendimiento, todo organizado bajo un punto de entrada de Mapa de contenido, exactamente como estructuraríamos notas en IWE. Luego ejercitamos cada operación de gráfico en esta base de conocimiento: buscamos con buscar, mostramos la jerarquía de documentos completa con un árbol, extraemos estadísticas con estadísticas, realizamos una recuperación consciente del contexto que sigue enlaces con recuperar, consolidamos todo el gráfico en un solo documento con squash y exportamos la estructura como un gráfico DOT. Representamos el gráfico visualmente usando Graphviz y lo mostramos en línea, lo que nos brinda una imagen clara de cómo todas nuestras notas se conectan entre sí.
print("─" * 72) print(" 4 · Transformaciones de documentos impulsadas por IA") print("─" * 72) def ai_transform(text: str, action: str, context: str = "", model: str = "gpt-4o-mini") -> str: Prompts = { "rewrite": ( "Reescribe el siguiente texto para mejorar la claridad y legibilidad. " "Mantén el formato de rebajas. Devuelve SÓLO el texto reescrito." ), "summarize": ( "Resuma el siguiente texto en 2 o 3 viñetas concisas. " "Céntrese en las decisiones clave y opciones técnicas." ), "expand": ( "Amplíe el siguiente texto con más detalles técnicos y ejemplos. " "Mantenga la misma estructura y agregue profundidad." ), "extract_todos": ( "Extraiga todos los elementos procesables de este texto y " "formatéelos como una lista de tareas pendientes " "rebajadas. Si no hay elementos procesables, sugiera " "relevantes siguientes pasos según el contenido." ), "generate_links": ( "Analice la siguiente nota y sugiera temas relacionados que " "deberían vincularse. Formatee como una lista rebajada de enlaces wiki: [[topic-name]]. " "Sólo sugiera temas que estén genuinamente relacionados." ), } system_msg = Prompts.get(action, Prompts["rewrite"]) if context: system_msg += f"nnDocumento contexto:n{context[:500]}" mensajes = [ {"role": "sistema", "contenido": system_msg}, {"role": "usuario", "contenido": texto}, ] respuesta = cliente.chat.completions.create( modelo=modelo, mensajes=mensajes, temperatura=0.3, max_tokens=1000, ) devuelve respuesta.opciones[0].message.content.strip() auth_doc = kg.get("authentication") print("n🔄 Transformar: SUMMARIZE — Sistema de autenticaciónn") resumen = ai_transform(auth_doc.raw_content, "summarize") print(summary) print("nn🔗 Transformar: GENERATE_LINKS — Sistema de autenticaciónn") links = ai_transform(auth_doc.raw_content, "generate_links") print(links) print("nn✅ Transform: EXTRACT_TODOS — Notas de rendimienton") perf_doc = kg.get("notas de rendimiento") todos = ai_transform(perf_doc.raw_content, "extract_todos") print(todos) print("n✅ Sección 4 completa: transformaciones de IA demostradas.n")
Definimos la función ai_transform que refleja el sistema de acción config.toml de IWE y admite cinco tipos de transformación: reescribir, resumir, expandir, extraer_todos y generar_enlaces, cada uno respaldado por un mensaje del sistema personalizado enviado a OpenAI. Realizamos tres demostraciones en vivo de nuestra base de conocimientos: resumimos el documento del Sistema de autenticación en viñetas concisas, lo analizamos en busca de enlaces wiki sugeridos a temas relacionados y extraemos tareas pendientes procesables del documento Notas de rendimiento. Vemos cómo el patrón de acción de IA de IWE, seleccionar un documento, elegir una transformación y aplicarla en el lugar, se traduce directamente en una función Python reutilizable que funciona con cualquier nota en nuestro gráfico.
print("─" * 72) print(" 5 · Agentic RAG — AI navega por su gráfico de conocimiento") print("─" * 72) AGENT_TOOLS = [ { "type": "function", "function": { "name": "iwe_find", "description": "Busca en el gráfico de conocimiento documentos que coincidan con una consulta. Devuelve una lista de claves de documentos.", "parameters": { "type": "object", "properties": { "query": {"type": "string", "description": "Consulta de búsqueda"}, "roots_only": {"type": "boolean", "description": "Solo devolver documentos raíz/MOC", "default": False}, }, "required": ["query"], }, }, }, { "type": "function", "function": { "name": "iwe_retrieve", "description": "Recuperar un documento contenido con contexto vinculado Utilice profundidad>0 para seguir enlaces salientes, contexto>0 para incluir documentos principales.", "parameters": { "type": "object", "properties": { "key": {"type": "string", "description": "Clave del documento a recuperar"}, "profundidad": {"type": "integer", "description": "Cuántos niveles de enlaces secundarios seguir (0-2)", "default": 1}, "context": {"type": "integer", "description": "Cuántos niveles de contexto principal (0-1)", "default": 0}, }, "required": ["key"], }, }, }, { "type": "function", "function": { "name": "iwe_tree", "description": "Mostrar la jerarquía del documento a partir de una clave determinada.", "parameters": { "type": "object", "properties": { "key": {"type": "string", "description": "Clave del documento raíz"}, }, "required": ["key"], }, }, }, { "type": "function", "function": { "name": "iwe_stats", "description": "Obtener estadísticas sobre toda la base de conocimientos.", "parameters": {"type": "object", "properties": {}}, }, }, ] def ejecutar_tool(nombre: str, args: dict) -> str: if nombre == "iwe_find": resultados = kg.find(args["query"], root_only=args.get("roots_only", False)) return json.dumps({"resultados": resultados}) elif nombre == "iwe_retrieve": contenido = kg.retrieve( args["key"], profundidad=args.get("profundidad", 1), contexto=args.get("context", 0), ) return content[:3000] elif name == "iwe_tree": return kg.tree(args["key"]) elif name == "iwe_stats": return json.dumps(kg.stats(), indent=2) return "Herramienta desconocida" def run_agent(question: str, max_turns: int = 6, model: str = "gpt-4o-mini") -> str: system_prompt = textwrap.dedent(""" Usted es un asistente de IA con acceso a un gráfico de conocimiento personal (IWE). Utilice las herramientas proporcionadas para navegar por el gráfico y responder preguntas. Flujo de trabajo: 1. Utilice iwe_find para descubrir documentos relevantes 2. Utilice iwe_retrieve para leer contenido (establezca profundidad = 1 para seguir enlaces) 3. Siga las relaciones para desarrollar una comprensión integral 4. Sintetice información de múltiples documentos Sea específico y cite en qué documentos encontró información. Si no puede encontrar suficiente información, dígalo claramente """) mensajes = [ {"role": "system", "content": system_prompt}, {"role": "user", "content": question}, ] for turn in range(max_turns): respuesta = client.chat.completions.create( model=model, message=messages, tools=AGENT_TOOLS, tool_choice="auto", ) msg =. opciones de respuesta[0].message if msg.tool_calls: mensajes.append(msg) para tc en msg.tool_calls: fn_name = tc.function.name fn_args = json.loads(tc.function.arguments) print(f" 🔧 Llamadas del agente: {fn_name}({fn_args})") resultado = ejecutar_tool(fn_name, fn_args) message.append({ "role": "tool", "tool_call_id": tc.id, "content": result, }) else: return msg.content return "El agente alcanzó el máximo de turnos sin completar". preguntas = [ "¿Cómo funciona nuestro sistema de autenticación y de qué tablas de base de datos depende?", "¿Cuál es nuestro proceso de implementación y cuáles son los objetivos de SLO de rendimiento?", "Dame una descripción general de alto nivel de toda la arquitectura del proyecto.", ] para i, q en enumerate(questions, 1): print(f"n{'═' * 72}") print(f" Pregunta {i}: {q}") print(f"{'═' * 72}n") respuesta = run_agent(q) print(f"n💡 Respuesta del agente:n{answer}n") print("n✅ Sección 5 completa: demostración de Agentic RAG.n")
Construimos el canal completo de recuperación de agentes que incorpora el concepto de "Puente de contexto" de IWE: un agente de IA que navega por nuestro gráfico de conocimiento utilizando llamadas a funciones OpenAI con cuatro herramientas: iwe_find para descubrimiento, iwe_retrieve para búsqueda de contenido consciente del contexto, iwe_tree para exploración de jerarquía e iwe_stats para análisis de la base de conocimientos. Conectamos el ejecutor de la herramienta que envía cada llamada de función a nuestra instancia de KnowledgeGraph e implementamos el bucle del agente que itera a través de ciclos de búsqueda, recuperación y síntesis hasta que reúne una respuesta completa. Luego ejecutamos tres preguntas de demostración progresivamente complejas, preguntando sobre las dependencias de autenticación, la implementación y los objetivos de SLO, y una descripción general completa de la arquitectura del proyecto, y observamos al agente llamar de forma autónoma a las herramientas, seguir enlaces entre documentos y producir respuestas integrales basadas en nuestras notas.
print("─" * 72) print(" 6 · Mantenimiento del gráfico de conocimiento impulsado por IA") print("─" * 72) def analyse_knowledge_gaps(modelo: str = "gpt-4o-mini") -> str: stats_info = json.dumps(kg.stats(), indent=2) titles = [f"- {d.title} ({k}): enlaces a {d.outgoing_links}" para k, d en kg.documents.items()] graph_overview = "n".join(titles) respuesta = client.chat.completions.create( model=model, message=[ {"role": "system", "content": ( "Es un consultor de gestión del conocimiento. Analice este " "gráfico de conocimiento e identifique: (1) temas faltantes que " "deberían existir, (2) documentos que deberían estar vinculados pero no lo están, " "(3) áreas que necesitan más detalles. Sea específico y procesable." )}, {"role": "user", "content": ( f"Estadísticas de la base de conocimientos:n{stats_info}nn" f"Estructura del documento:n{graph_overview}" )}, ], temperatura=0.4, max_tokens=1000, ) return respuesta.opciones[0].message.content.strip() def generate_new_note(tema: str, claves_relacionadas: lista[str], modelo: str = "gpt-4o-mini") -> str: context_parts =[]for key in related_keys[:3]: doc = kg.get(key) if doc: context_parts.append(f"## {doc.title}n{doc.raw_content[:400]}") context = "nn".join(context_parts) respuesta = client.chat.completions.create( model=model, message=[ {"role": "system", "content": ( "Eres un redactor técnico. Genera un nueva nota de rebajas " "sobre el tema determinado. Utilice enlaces wiki [[como-este]] para " "hacer referencia a documentos existentes relacionados. Incluya encabezados " "relevantes, ejemplos de código cuando corresponda y etiquetas hashtag." )}, {"role": "usuario", "contenido": ( f"Tema: {tema}nn" f"Notas existentes relacionadas para el contexto:n{context}nn" f"Documentos disponibles para vincular: {list(kg.documents.keys())}" )}, ], temperatura=0.5, max_tokens=1200, ) devuelve respuesta.opciones[0].message.content.strip() print("n🔍 Analizando brechas de conocimiento…n") brechas = analice_knowledge_gaps() print(brechas) print("nn📝 Generando una nueva nota: 'Estrategia de manejo de errores'…n") new_note = generate_new_note( "Estrategia de manejo de errores", related_keys=["api-design", "authentication", "frontend-stack"], ) print(new_note[:1000] + "n… (truncado)") kg.add_document("error-handling", new_note) print(f"n✅ Se agregó 'control de errores' al gráfico de conocimiento. " f"Total de documentos: {len(kg.documents)}") dot_output = kg.export_dot(highlight_key="error-handling") intente: importar Graphviz src = graphviz.Source(dot_output) src.render("knowledge_graph_v2", format="png", cleanup=True) print("✅ Gráfico actualizado representado en 'knowledge_graph_v2.png'") intente: desde IPython.display importar imagen, mostrar mostrar(Image("knowledge_graph_v2.png")) excepto ImportError: pasar excepto excepción como e: print(f" ⚠ Se omitió la representación de Graphviz: {e}") print("n✅ Sección 6 completa: mantenimiento impulsado por IA demostrado.n") print("─" * 72) print(" 7 · Razonamiento de múltiples saltos en el gráfico de conocimiento") print("─" * 72) complex_question = ("Si aumentamos nuestro tráfico de 1000 RPS a 5000 RPS sostenidos, " "qué cambios serían necesarios en todo el pila — desde la base de datos " "agrupación de conexiones, al almacenamiento en caché, al manejo de tokens de autenticación, " "a la infraestructura de implementación?" ) print(f"n🧠 Pregunta compleja de múltiples saltos:n {complex_question}n") respuesta = run_agent(complex_question, max_turns=8) print(f"n💡 Respuesta del agente:n{answer}") print("nn" + "=" * 72) print(" ✅ TUTORIAL COMPLETO") print("=" * 72) print(""" Ha explorado todos los conceptos centrales de IWE: 1. Gráfico de conocimiento: documentos como nodos, enlaces como bordes 2. Análisis de Markdown: enlaces wiki, encabezados, etiquetas 3. Mapas de contenido: organización jerárquica (MOC) 4. Operaciones gráficas: buscar, recuperar, árbol, aplastar, estadísticas, exportar 5. Transformaciones de IA: reescribir, resumir, expandir, extraer todos 6. Recuperación agente: agente de IA navegando por su gráfico de conocimiento 7. Mantenimiento de gráficos: análisis de brechas y generación de notas impulsado por IA 8. Razonamiento de saltos múltiples: síntesis entre documentos Para usar IWE de verdad (con su editor): → https://github.com/iwe-org/iwe → https://iwe.md/quick-start IWE admite VS Code, Neovim, Zed y Helix a través de LSP.
Usamos IA para analizar nuestro gráfico de conocimiento en busca de brechas estructurales, identificando temas faltantes, documentos desvinculados y áreas que necesitan más profundidad. Luego generamos automáticamente una nueva nota de "Estrategia de manejo de errores" que hace referencia a documentos existentes a través de enlaces wiki y la agregamos al gráfico en vivo. Volvemos a renderizar la visualización Graphviz actualizada, resaltando el nuevo nodo para mostrar cómo la base de conocimientos crece orgánicamente a medida que la IA y las contribuciones humanas se superponen. Cerramos con un complejo desafío de razonamiento de múltiples saltos, preguntando qué cambios se necesitan en toda la pila si escalamos de 1000 a 5000 RPS, donde el agente debe atravesar documentos de base de datos, almacenamiento en caché, autenticación e implementación para sintetizar una respuesta transversal que ninguna nota podría proporcionar por sí sola.
En conclusión, ahora tenemos una implementación completa y funcional de las ideas centrales de IWE ejecutándose en el entorno Colab. Hemos visto cómo estructurar las notas como un gráfico, en lugar de tratarlas como archivos planos, desbloquea capacidades poderosas: las relaciones se convierten en caminos navegables, el contexto fluye naturalmente de los documentos principales a los secundarios y los agentes de IA pueden descubrir, recorrer y sintetizar el conocimiento exactamente como lo organizamos. Hemos creado el proceso completo, desde el análisis de rebajas y la indexación de vínculos de retroceso hasta operaciones de recorrido de gráficos, transformaciones de documentos impulsadas por IA, recuperación de agentes con llamada de herramientas, análisis de brechas de conocimiento y razonamiento de múltiples saltos que abarca toda la base de conocimientos. Todo lo que construimos aquí se asigna directamente a las características reales de IWE: los comandos de búsqueda, recuperación, árbol, aplastamiento y exportación, las acciones de IA config.toml y la filosofía Context Bridge, que posiciona su gráfico de conocimiento personal como memoria compartida entre usted y sus agentes de IA.
Consulta el cuaderno completo aquí. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 120.000 ML y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.
Michal Sutter es un profesional de la ciencia de datos con una Maestría en Ciencia de Datos de la Universidad de Padua. Con una base sólida en análisis estadístico, aprendizaje automático e ingeniería de datos, Michal se destaca en transformar conjuntos de datos complejos en conocimientos prácticos.