{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration","title":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","description":"Comparación técnica de frameworks de orquestación AI, arquitecturas de plugins, manejo de memoria, y construcción de un pipeline RAG con cada uno para identificar las diferencias prácticas.","language":"es","datePublished":"2026-01-18","dateModified":null,"contentFormat":"text/markdown","content":"AI/ML\n\n# Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex\n\n2026-01-187 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/semantic-kernel-langchain-ai-orchestration>)\n\n## El Panorama en 2026\n\nEl espacio de orquestación AI maduró significativamente. LangChain ya no es el problema complejo desparramado que era en 2023, la reescritura v0.3 con LCEL trajo composabilidad real. Semantic Kernel llegó a 1.0 y se convirtió en el SDK oficial de Microsoft para orquestación AI. LlamaIndex se talló un nicho fuerte como el framework de cabecera para retrieval-augmented generation.\n\nPero se solapan bastante. Los tres pueden construir un pipeline RAG. Los tres soportan tool calling. Los tres se conectan a vector stores. Las diferencias están en la filosofía, la experiencia de desarrollo y dónde son más fuertes.\n\n## Comparación de Arquitectura\n\n### LangChain: La Navaja Suiza\n\nLa abstracción core de LangChain es la cadena, una secuencia de operaciones pipeadas juntas. Con LCEL, esto se vuelve genuinamente elegante:\n\n```python\n# langchain_rag.py - Pipeline RAG moderno con LangChain\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain_community.vectorstores import Chroma\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langchain_core.runnables import RunnablePassthrough, RunnableParallel\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import DirectoryLoader, PyPDFLoader\n\n# 1. Cargar y splitear documentos\nloader = DirectoryLoader(\n './legal_docs/',\n glob=\"**/*.pdf\",\n loader_cls=PyPDFLoader,\n show_progress=True\n)\ndocs = loader.load()\n\nsplitter = RecursiveCharacterTextSplitter(\n chunk_size=512,\n chunk_overlap=50,\n separators=[\"\\n\\n\", \"\\n\", \". \", \" \", \"\"]\n)\nchunks = splitter.split_documents(docs)\n\n# 2. Crear vector store\nembeddings = OpenAIEmbeddings(model=\"text-embedding-3-small\")\nvectorstore = Chroma.from_documents(\n chunks,\n embeddings,\n collection_name=\"legal_docs\",\n persist_directory=\"./chroma_db\"\n)\nretriever = vectorstore.as_retriever(\n search_type=\"mmr\",\n search_kwargs={\"k\": 6, \"fetch_k\": 20, \"lambda_mult\": 0.7}\n)\n\n# 3. Definir la cadena RAG con LCEL\nllm = ChatAnthropic(model=\"claude-sonnet-4-20250514\", temperature=0)\n\nprompt = ChatPromptTemplate.from_messages([\n (\"system\", \"\"\"Usted es un asistente de investigación legal. Respondé preguntas\n basándote estrictamente en el contexto proporcionado. Si el contexto no\n tiene información suficiente, decilo explícitamente. Citá secciones específicas.\n\n Contexto:\n {context}\"\"\"),\n (\"human\", \"{question}\")\n])\n\ndef format_docs(docs):\n return \"\\n\\n---\\n\\n\".join(\n f\"[{doc.metadata.get('source', 'unknown')}] {doc.page_content}\"\n for doc in docs\n )\n\n# El pipeline LCEL - limpio, componible, debuggeable\nrag_chain = (\n RunnableParallel(\n context=retriever | format_docs,\n question=RunnablePassthrough()\n )\n | prompt\n | llm\n | StrOutputParser()\n)\n\n# Uso\nresponse = rag_chain.invoke(\"¿Cuáles son las limitaciones de responsabilidad en la sección 4.2?\")\n\n# Streaming - funciona nativamente con LCEL\nfor chunk in rag_chain.stream(\"Resumí las cláusulas de indemnización\"):\n print(chunk, end=\"\", flush=True)\n```\n\nLa fortaleza de LCEL es el operador pipe. ` retriever | format_docs ` se lee exactamente como lo que hace. Debuggear es directo porque es posible invocar cualquier paso independientemente. El ` RunnableParallel ` corre el retrieval y el passthrough de la pregunta concurrentemente.\n\nLa desventaja: el ecosistema es enorme y confuso. Hay ` langchain `, ` langchain-core `, ` langchain-community `, ` langchain-openai `, ` langchain-anthropic `, descifrar qué paquete tiene qué clase sigue siendo una búsqueda del tesoro.\n\n### Semantic Kernel: El Jugador Enterprise\n\nSemantic Kernel piensa en términos de plugins y planners. Un plugin es una colección de funciones que la AI puede llamar. Un planner decide qué plugins invocar y en qué orden.\n\n```python\n# semantic_kernel_rag.py\nimport semantic_kernel as sk\nfrom semantic_kernel.connectors.ai.open_ai import (\n AzureChatCompletion,\n AzureTextEmbedding\n)\nfrom semantic_kernel.connectors.memory.chroma import ChromaMemoryStore\nfrom semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin\nfrom semantic_kernel.functions import kernel_function\nfrom semantic_kernel.memory import SemanticTextMemory\n\n# 1. Inicializar el kernel\nkernel = sk.Kernel()\n\nkernel.add_service(AzureChatCompletion(\n service_id=\"chat\",\n deployment_name=\"gpt-4o\",\n endpoint=\"https://myinstance.openai.azure.com/\",\n api_key=\"your-key\"\n))\n\nembedding_service = AzureTextEmbedding(\n service_id=\"embedding\",\n deployment_name=\"text-embedding-3-small\",\n endpoint=\"https://myinstance.openai.azure.com/\",\n api_key=\"your-key\"\n)\n\n# 2. Configurar memoria\nmemory_store = ChromaMemoryStore(persist_directory=\"./chroma_sk\")\nmemory = SemanticTextMemory(\n storage=memory_store,\n embeddings_generator=embedding_service\n)\nkernel.add_plugin(TextMemoryPlugin(memory), \"memory\")\n\n# 3. Crear un plugin RAG custom\nclass LegalResearchPlugin:\n def __init__(self, memory: SemanticTextMemory):\n self._memory = memory\n\n @kernel_function(\n name=\"search_legal_docs\",\n description=\"Busca documentos legales por cláusulas y secciones relevantes. \"\n \"Usar cuando el usuario pregunta sobre contratos, cláusulas, \"\n \"responsabilidad, indemnización, o cualquier tema legal.\"\n )\n async def search_legal_docs(\n self,\n query: str,\n collection: str = \"legal_docs\"\n ) -> str:\n results = await self._memory.search(\n collection=collection,\n query=query,\n limit=6,\n min_relevance_score=0.7\n )\n if not results:\n return \"No se encontraron documentos relevantes para esta consulta.\"\n\n passages = []\n for result in results:\n source = result.metadata.get(\"source\", \"desconocido\")\n passages.append(\n f\"[Fuente: {source}, Relevancia: {result.relevance:.2f}]\\n\"\n f\"{result.text}\"\n )\n return \"\\n\\n---\\n\\n\".join(passages)\n\n# Registrar plugin\nlegal_plugin = LegalResearchPlugin(memory)\nkernel.add_plugin(legal_plugin, \"legal_research\")\n\n# 4. Crear la función de chat con function calling automático\nexecution_settings = sk.PromptExecutionSettings(\n service_id=\"chat\",\n function_choice_behavior=\"auto\",\n temperature=0\n)\n\nchat_function = kernel.add_function(\n plugin_name=\"chat\",\n function_name=\"legal_assistant\",\n prompt=\"\"\"Usted es un asistente de investigación legal con acceso a un corpus de documentos.\n Utilizar el plugin legal_research para encontrar información relevante antes de responder.\n\n Pregunta del usuario: {{$input}}\n\n Proporcioná una respuesta exhaustiva con citas.\"\"\",\n prompt_execution_settings=execution_settings\n)\n```\n\nEl setting ` function_choice_behavior=\"auto\" ` es la clave. El LLM ve todos los plugins registrados con sus descripciones y decide cuáles llamar. El decorator ` @kernel_function ` con una buena descripción es esencial, el LLM la lee para decidir cuándo usar la función.\n\nLa fortaleza de Semantic Kernel está en escenarios enterprise estructurados. El modelo de plugins mapea bien a límites organizacionales, el equipo legal es dueño del plugin legal, el equipo de finanzas del plugin de finanzas.\n\n### LlamaIndex: El Especialista en RAG\n\nLlamaIndex fue construido a propósito para retrieval-augmented generation, y se nota:\n\n```python\n# llamaindex_rag.py\nfrom llama_index.core import (\n VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext\n)\nfrom llama_index.core.node_parser import SentenceSplitter\nfrom llama_index.core.postprocessor import (\n SimilarityPostprocessor, LongContextReorder\n)\nfrom llama_index.core.query_engine import RetrieverQueryEngine\nfrom llama_index.core.retrievers import VectorIndexRetriever\nfrom llama_index.core.response_synthesizers import get_response_synthesizer\nfrom llama_index.llms.anthropic import Anthropic\nfrom llama_index.embeddings.openai import OpenAIEmbedding\nfrom llama_index.vector_stores.chroma import ChromaVectorStore\nimport chromadb\n\nSettings.llm = Anthropic(model=\"claude-sonnet-4-20250514\", temperature=0)\nSettings.embed_model = OpenAIEmbedding(model=\"text-embedding-3-small\")\nSettings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)\n\ndocuments = SimpleDirectoryReader(\n input_dir=\"./legal_docs/\",\n required_exts=[\".pdf\", \".docx\"],\n filename_as_id=True,\n).load_data()\n\nfor doc in documents:\n doc.metadata[\"doc_type\"] = \"legal_contract\"\n doc.metadata[\"jurisdiction\"] = \"AR\"\n\nchroma_client = chromadb.PersistentClient(path=\"./chroma_li\")\nchroma_collection = chroma_client.get_or_create_collection(\"legal_docs\")\nvector_store = ChromaVectorStore(chroma_collection=chroma_collection)\nstorage_context = StorageContext.from_defaults(vector_store=vector_store)\n\nindex = VectorStoreIndex.from_documents(\n documents, storage_context=storage_context, show_progress=True\n)\n\nretriever = VectorIndexRetriever(index=index, similarity_top_k=10)\n\nnode_postprocessors = [\n SimilarityPostprocessor(similarity_cutoff=0.7),\n LongContextReorder(), # Pone los más relevantes al principio y final\n]\n\nresponse_synthesizer = get_response_synthesizer(\n response_mode=\"tree_summarize\",\n use_async=True,\n)\n\nquery_engine = RetrieverQueryEngine(\n retriever=retriever,\n node_postprocessors=node_postprocessors,\n response_synthesizer=response_synthesizer,\n)\n\nresponse = query_engine.query(\n \"¿Cuáles son las limitaciones de responsabilidad en la sección 4.2?\"\n)\n```\n\nEl pipeline de LlamaIndex es más opinado sobre retrieval. El postprocessor ` LongContextReorder ` es un ejemplo perfecto, reordena los nodos retrieveados para que los más relevantes estén al principio y al final del contexto, basado en investigación que muestra que los LLMs prestan más atención a esas posiciones. Eso lo tendrías que construir usted mismo en LangChain.\n\nEl modo de respuesta ` tree_summarize ` es otra fortaleza. En vez de meter todos los chunks retrieveados en un solo prompt, construye un árbol de resúmenes, resumiendo grupos de chunks, después resumiendo los resúmenes.\n\n## Manejo de Memoria Comparado\n\nLa memoria es donde más divergen los frameworks:\n\n```python\n# LangChain - Objetos de memoria explícitos\nfrom langchain.memory import ConversationSummaryBufferMemory\nmemory = ConversationSummaryBufferMemory(\n llm=llm, max_token_limit=2000, return_messages=True\n)\n# Mantiene mensajes recientes verbatim, resume los más viejos\n\n# Semantic Kernel - ChatHistory con control manual\nfrom semantic_kernel.contents import ChatHistory\nhistory = ChatHistory()\nhistory.add_system_message(\"Usted es un asistente legal.\")\nhistory.add_user_message(\"¿Qué hay en la sección 4.2?\")\n# Se controla exactamente qué entra y qué se poda\n\n# LlamaIndex - Chat engine con memoria built-in\nfrom llama_index.core.memory import ChatMemoryBuffer\nmemory = ChatMemoryBuffer.from_defaults(token_limit=3000)\nchat_engine = index.as_chat_engine(\n chat_mode=\"condense_plus_context\",\n memory=memory,\n system_prompt=\"Usted es un asistente de investigación legal.\"\n)\n# Automáticamente condensa el historial en una query\nresponse = chat_engine.chat(\"¿Qué hay en la sección 4.2?\")\nresponse = chat_engine.chat(\"¿Y qué dice sobre indemnización?\")\n```\n\nLangChain te da más estrategias de memoria pero requiere cableado manual. Semantic Kernel te da más control pero menos magia. El modo ` condense_plus_context ` de LlamaIndex es el más turnkey.\n\n## Veredicto Después de Construir Tres Sistemas\n\n**LlamaIndex** es la opción más fuerte para plataformas de búsqueda de documentos legales. El pipeline de procesamiento de documentos, extracción de metadata y postprocessors de retrieval ahorran semanas.\n\n**LangChain** es la opción más fuerte para automatización de soporte al cliente. A menudo se necesitan comportamientos de agentes complejos, routing entre agentes humanos e AI, llamando APIs externas para estado de pedidos.\n\n**Semantic Kernel** es la opción más fuerte para bases de conocimiento internas en empresas heavy en Microsoft. Integración con Azure AD, autenticación con managed identity a Azure [OpenAI](<https://openai.com/>), y el modelo de plugins que mapeaba a su estructura organizacional.\n\nNinguno de estos frameworks es malo. Todos maduraron enormemente. Pero tienen fortalezas diferentes, y pretender que son intercambiables le hace un flaco favor a todos. Elegir el que matchee tu caso de uso principal, no el que tenga más estrellas en GitHub.\n\n## Preguntas frecuentes\n\n### ¿Qué framework debería usar para empezar con una aplicación RAG?\n\nLlamaIndex si RAG es tu caso de uso principal, tiene el pipeline de procesamiento de documentos y retrieval más maduro. LangChain si se necesita RAG como parte de un sistema de agentes más grande con herramientas y cadenas. Semantic Kernel si se está en el ecosistema Microsoft o se necesita soporte enterprise-grade en .NET.\n\n### ¿Puedo usar múltiples proveedores de LLM con estos frameworks?\n\nSí, los tres soportan múltiples proveedores. LangChain tiene el soporte más amplio de proveedores. Semantic Kernel tiene soporte de primera clase para Azure OpenAI y OpenAI con conectores de comunidad en crecimiento. LlamaIndex soporta todos los proveedores principales a través de su abstracción de LLM.\n\n### ¿Cómo manejan estos frameworks la memoria y el estado de conversación?\n\nLangChain tiene clases de memoria explícitas (ConversationBufferMemory, ConversationSummaryMemory). Semantic Kernel usa un objeto ChatHistory con memoria basada en plugins. LlamaIndex se enfoca en memoria basada en índices a través de sus abstracciones de chat engine.\n\n## Tips\n\n1. **Tip:** El ` LCEL ` (LangChain Expression Language) de LangChain es la forma moderna de encadenar componentes. Si seguís usando ` LLMChain `, se está en APIs deprecadas, migrá ya.\n2. **Tip:** El decorator ` KernelFunction ` de Semantic Kernel convierte cualquier método Python/C# en un plugin. Utilizar ` @kernel_function(description=...) ` con descripciones detalladas, el LLM las usa para selección de funciones.\n3. **Tip:** El chunk size default de ` VectorStoreIndex ` en LlamaIndex de 1024 tokens es demasiado grande para la mayoría de los casos. Iniciar con ` chunk_size=512, chunk_overlap=50 ` y tuneá desde ahí.\n\n- semantic-kernel\n- langchain\n- llamaindex\n- rag\n- ai-orchestration\n- llm\n- plugins\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#title"},{"level":2,"text":"El Panorama en 2026","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#el-panorama-en-2026"},{"level":2,"text":"Comparación de Arquitectura","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#comparacin-de-arquitectura"},{"level":3,"text":"LangChain: La Navaja Suiza","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#langchain-la-navaja-suiza"},{"level":3,"text":"Semantic Kernel: El Jugador Enterprise","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#semantic-kernel-el-jugador-enterprise"},{"level":3,"text":"LlamaIndex: El Especialista en RAG","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#llamaindex-el-especialista-en-rag"},{"level":2,"text":"Manejo de Memoria Comparado","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#manejo-de-memoria-comparado"},{"level":2,"text":"Veredicto Después de Construir Tres Sistemas","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#veredicto-despus-de-construir-tres-sistemas"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#questions"},{"level":3,"text":"¿Qué framework debería usar para empezar con una aplicación RAG?","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-1"},{"level":3,"text":"¿Puedo usar múltiples proveedores de LLM con estos frameworks?","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-2"},{"level":3,"text":"¿Cómo manejan estos frameworks la memoria y el estado de conversación?","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#article-tips"}],"sections":[{"heading":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","level":1,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration","content":"AI/ML"},{"heading":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","level":1,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#title","content":"2026-01-187 min lectura\n\nLucio Durán\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/semantic-kernel-langchain-ai-orchestration>)"},{"heading":"El Panorama en 2026","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#el-panorama-en-2026","content":"El espacio de orquestación AI maduró significativamente. LangChain ya no es el problema complejo desparramado que era en 2023, la reescritura v0.3 con LCEL trajo composabilidad real. Semantic Kernel llegó a 1.0 y se convirtió en el SDK oficial de Microsoft para orquestación AI. LlamaIndex se talló un nicho fuerte como el framework de cabecera para retrieval-augmented generation.\n\nPero se solapan bastante. Los tres pueden construir un pipeline RAG. Los tres soportan tool calling. Los tres se conectan a vector stores. Las diferencias están en la filosofía, la experiencia de desarrollo y dónde son más fuertes."},{"heading":"Comparación de Arquitectura","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#comparacin-de-arquitectura","content":""},{"heading":"LangChain: La Navaja Suiza","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#langchain-la-navaja-suiza","content":"La abstracción core de LangChain es la cadena, una secuencia de operaciones pipeadas juntas. Con LCEL, esto se vuelve genuinamente elegante:\n\n```python\n# langchain_rag.py - Pipeline RAG moderno con LangChain\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain_community.vectorstores import Chroma\nfrom langchain_core.prompts import ChatPromptTemplate\nfrom langchain_core.output_parsers import StrOutputParser\nfrom langchain_core.runnables import RunnablePassthrough, RunnableParallel\nfrom langchain_text_splitters import RecursiveCharacterTextSplitter\nfrom langchain_community.document_loaders import DirectoryLoader, PyPDFLoader\n\n# 1. Cargar y splitear documentos\nloader = DirectoryLoader(\n './legal_docs/',\n glob=\"**/*.pdf\",\n loader_cls=PyPDFLoader,\n show_progress=True\n)\ndocs = loader.load()\n\nsplitter = RecursiveCharacterTextSplitter(\n chunk_size=512,\n chunk_overlap=50,\n separators=[\"\\n\\n\", \"\\n\", \". \", \" \", \"\"]\n)\nchunks = splitter.split_documents(docs)\n\n# 2. Crear vector store\nembeddings = OpenAIEmbeddings(model=\"text-embedding-3-small\")\nvectorstore = Chroma.from_documents(\n chunks,\n embeddings,\n collection_name=\"legal_docs\",\n persist_directory=\"./chroma_db\"\n)\nretriever = vectorstore.as_retriever(\n search_type=\"mmr\",\n search_kwargs={\"k\": 6, \"fetch_k\": 20, \"lambda_mult\": 0.7}\n)\n\n# 3. Definir la cadena RAG con LCEL\nllm = ChatAnthropic(model=\"claude-sonnet-4-20250514\", temperature=0)\n\nprompt = ChatPromptTemplate.from_messages([\n (\"system\", \"\"\"Usted es un asistente de investigación legal. Respondé preguntas\n basándote estrictamente en el contexto proporcionado. Si el contexto no\n tiene información suficiente, decilo explícitamente. Citá secciones específicas.\n\n Contexto:\n {context}\"\"\"),\n (\"human\", \"{question}\")\n])\n\ndef format_docs(docs):\n return \"\\n\\n---\\n\\n\".join(\n f\"[{doc.metadata.get('source', 'unknown')}] {doc.page_content}\"\n for doc in docs\n )\n\n# El pipeline LCEL - limpio, componible, debuggeable\nrag_chain = (\n RunnableParallel(\n context=retriever | format_docs,\n question=RunnablePassthrough()\n )\n | prompt\n | llm\n | StrOutputParser()\n)\n\n# Uso\nresponse = rag_chain.invoke(\"¿Cuáles son las limitaciones de responsabilidad en la sección 4.2?\")\n\n# Streaming - funciona nativamente con LCEL\nfor chunk in rag_chain.stream(\"Resumí las cláusulas de indemnización\"):\n print(chunk, end=\"\", flush=True)\n```\n\nLa fortaleza de LCEL es el operador pipe. ` retriever | format_docs ` se lee exactamente como lo que hace. Debuggear es directo porque es posible invocar cualquier paso independientemente. El ` RunnableParallel ` corre el retrieval y el passthrough de la pregunta concurrentemente.\n\nLa desventaja: el ecosistema es enorme y confuso. Hay ` langchain `, ` langchain-core `, ` langchain-community `, ` langchain-openai `, ` langchain-anthropic `, descifrar qué paquete tiene qué clase sigue siendo una búsqueda del tesoro."},{"heading":"Semantic Kernel: El Jugador Enterprise","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#semantic-kernel-el-jugador-enterprise","content":"Semantic Kernel piensa en términos de plugins y planners. Un plugin es una colección de funciones que la AI puede llamar. Un planner decide qué plugins invocar y en qué orden.\n\n```python\n# semantic_kernel_rag.py\nimport semantic_kernel as sk\nfrom semantic_kernel.connectors.ai.open_ai import (\n AzureChatCompletion,\n AzureTextEmbedding\n)\nfrom semantic_kernel.connectors.memory.chroma import ChromaMemoryStore\nfrom semantic_kernel.core_plugins.text_memory_plugin import TextMemoryPlugin\nfrom semantic_kernel.functions import kernel_function\nfrom semantic_kernel.memory import SemanticTextMemory\n\n# 1. Inicializar el kernel\nkernel = sk.Kernel()\n\nkernel.add_service(AzureChatCompletion(\n service_id=\"chat\",\n deployment_name=\"gpt-4o\",\n endpoint=\"https://myinstance.openai.azure.com/\",\n api_key=\"your-key\"\n))\n\nembedding_service = AzureTextEmbedding(\n service_id=\"embedding\",\n deployment_name=\"text-embedding-3-small\",\n endpoint=\"https://myinstance.openai.azure.com/\",\n api_key=\"your-key\"\n)\n\n# 2. Configurar memoria\nmemory_store = ChromaMemoryStore(persist_directory=\"./chroma_sk\")\nmemory = SemanticTextMemory(\n storage=memory_store,\n embeddings_generator=embedding_service\n)\nkernel.add_plugin(TextMemoryPlugin(memory), \"memory\")\n\n# 3. Crear un plugin RAG custom\nclass LegalResearchPlugin:\n def __init__(self, memory: SemanticTextMemory):\n self._memory = memory\n\n @kernel_function(\n name=\"search_legal_docs\",\n description=\"Busca documentos legales por cláusulas y secciones relevantes. \"\n \"Usar cuando el usuario pregunta sobre contratos, cláusulas, \"\n \"responsabilidad, indemnización, o cualquier tema legal.\"\n )\n async def search_legal_docs(\n self,\n query: str,\n collection: str = \"legal_docs\"\n ) -> str:\n results = await self._memory.search(\n collection=collection,\n query=query,\n limit=6,\n min_relevance_score=0.7\n )\n if not results:\n return \"No se encontraron documentos relevantes para esta consulta.\"\n\n passages = []\n for result in results:\n source = result.metadata.get(\"source\", \"desconocido\")\n passages.append(\n f\"[Fuente: {source}, Relevancia: {result.relevance:.2f}]\\n\"\n f\"{result.text}\"\n )\n return \"\\n\\n---\\n\\n\".join(passages)\n\n# Registrar plugin\nlegal_plugin = LegalResearchPlugin(memory)\nkernel.add_plugin(legal_plugin, \"legal_research\")\n\n# 4. Crear la función de chat con function calling automático\nexecution_settings = sk.PromptExecutionSettings(\n service_id=\"chat\",\n function_choice_behavior=\"auto\",\n temperature=0\n)\n\nchat_function = kernel.add_function(\n plugin_name=\"chat\",\n function_name=\"legal_assistant\",\n prompt=\"\"\"Usted es un asistente de investigación legal con acceso a un corpus de documentos.\n Utilizar el plugin legal_research para encontrar información relevante antes de responder.\n\n Pregunta del usuario: {{$input}}\n\n Proporcioná una respuesta exhaustiva con citas.\"\"\",\n prompt_execution_settings=execution_settings\n)\n```\n\nEl setting ` function_choice_behavior=\"auto\" ` es la clave. El LLM ve todos los plugins registrados con sus descripciones y decide cuáles llamar. El decorator ` @kernel_function ` con una buena descripción es esencial, el LLM la lee para decidir cuándo usar la función.\n\nLa fortaleza de Semantic Kernel está en escenarios enterprise estructurados. El modelo de plugins mapea bien a límites organizacionales, el equipo legal es dueño del plugin legal, el equipo de finanzas del plugin de finanzas."},{"heading":"LlamaIndex: El Especialista en RAG","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#llamaindex-el-especialista-en-rag","content":"LlamaIndex fue construido a propósito para retrieval-augmented generation, y se nota:\n\n```python\n# llamaindex_rag.py\nfrom llama_index.core import (\n VectorStoreIndex, SimpleDirectoryReader, Settings, StorageContext\n)\nfrom llama_index.core.node_parser import SentenceSplitter\nfrom llama_index.core.postprocessor import (\n SimilarityPostprocessor, LongContextReorder\n)\nfrom llama_index.core.query_engine import RetrieverQueryEngine\nfrom llama_index.core.retrievers import VectorIndexRetriever\nfrom llama_index.core.response_synthesizers import get_response_synthesizer\nfrom llama_index.llms.anthropic import Anthropic\nfrom llama_index.embeddings.openai import OpenAIEmbedding\nfrom llama_index.vector_stores.chroma import ChromaVectorStore\nimport chromadb\n\nSettings.llm = Anthropic(model=\"claude-sonnet-4-20250514\", temperature=0)\nSettings.embed_model = OpenAIEmbedding(model=\"text-embedding-3-small\")\nSettings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=50)\n\ndocuments = SimpleDirectoryReader(\n input_dir=\"./legal_docs/\",\n required_exts=[\".pdf\", \".docx\"],\n filename_as_id=True,\n).load_data()\n\nfor doc in documents:\n doc.metadata[\"doc_type\"] = \"legal_contract\"\n doc.metadata[\"jurisdiction\"] = \"AR\"\n\nchroma_client = chromadb.PersistentClient(path=\"./chroma_li\")\nchroma_collection = chroma_client.get_or_create_collection(\"legal_docs\")\nvector_store = ChromaVectorStore(chroma_collection=chroma_collection)\nstorage_context = StorageContext.from_defaults(vector_store=vector_store)\n\nindex = VectorStoreIndex.from_documents(\n documents, storage_context=storage_context, show_progress=True\n)\n\nretriever = VectorIndexRetriever(index=index, similarity_top_k=10)\n\nnode_postprocessors = [\n SimilarityPostprocessor(similarity_cutoff=0.7),\n LongContextReorder(), # Pone los más relevantes al principio y final\n]\n\nresponse_synthesizer = get_response_synthesizer(\n response_mode=\"tree_summarize\",\n use_async=True,\n)\n\nquery_engine = RetrieverQueryEngine(\n retriever=retriever,\n node_postprocessors=node_postprocessors,\n response_synthesizer=response_synthesizer,\n)\n\nresponse = query_engine.query(\n \"¿Cuáles son las limitaciones de responsabilidad en la sección 4.2?\"\n)\n```\n\nEl pipeline de LlamaIndex es más opinado sobre retrieval. El postprocessor ` LongContextReorder ` es un ejemplo perfecto, reordena los nodos retrieveados para que los más relevantes estén al principio y al final del contexto, basado en investigación que muestra que los LLMs prestan más atención a esas posiciones. Eso lo tendrías que construir usted mismo en LangChain.\n\nEl modo de respuesta ` tree_summarize ` es otra fortaleza. En vez de meter todos los chunks retrieveados en un solo prompt, construye un árbol de resúmenes, resumiendo grupos de chunks, después resumiendo los resúmenes."},{"heading":"Manejo de Memoria Comparado","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#manejo-de-memoria-comparado","content":"La memoria es donde más divergen los frameworks:\n\n```python\n# LangChain - Objetos de memoria explícitos\nfrom langchain.memory import ConversationSummaryBufferMemory\nmemory = ConversationSummaryBufferMemory(\n llm=llm, max_token_limit=2000, return_messages=True\n)\n# Mantiene mensajes recientes verbatim, resume los más viejos\n\n# Semantic Kernel - ChatHistory con control manual\nfrom semantic_kernel.contents import ChatHistory\nhistory = ChatHistory()\nhistory.add_system_message(\"Usted es un asistente legal.\")\nhistory.add_user_message(\"¿Qué hay en la sección 4.2?\")\n# Se controla exactamente qué entra y qué se poda\n\n# LlamaIndex - Chat engine con memoria built-in\nfrom llama_index.core.memory import ChatMemoryBuffer\nmemory = ChatMemoryBuffer.from_defaults(token_limit=3000)\nchat_engine = index.as_chat_engine(\n chat_mode=\"condense_plus_context\",\n memory=memory,\n system_prompt=\"Usted es un asistente de investigación legal.\"\n)\n# Automáticamente condensa el historial en una query\nresponse = chat_engine.chat(\"¿Qué hay en la sección 4.2?\")\nresponse = chat_engine.chat(\"¿Y qué dice sobre indemnización?\")\n```\n\nLangChain te da más estrategias de memoria pero requiere cableado manual. Semantic Kernel te da más control pero menos magia. El modo ` condense_plus_context ` de LlamaIndex es el más turnkey."},{"heading":"Veredicto Después de Construir Tres Sistemas","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#veredicto-despus-de-construir-tres-sistemas","content":"**LlamaIndex** es la opción más fuerte para plataformas de búsqueda de documentos legales. El pipeline de procesamiento de documentos, extracción de metadata y postprocessors de retrieval ahorran semanas.\n\n**LangChain** es la opción más fuerte para automatización de soporte al cliente. A menudo se necesitan comportamientos de agentes complejos, routing entre agentes humanos e AI, llamando APIs externas para estado de pedidos.\n\n**Semantic Kernel** es la opción más fuerte para bases de conocimiento internas en empresas heavy en Microsoft. Integración con Azure AD, autenticación con managed identity a Azure [OpenAI](<https://openai.com/>), y el modelo de plugins que mapeaba a su estructura organizacional.\n\nNinguno de estos frameworks es malo. Todos maduraron enormemente. Pero tienen fortalezas diferentes, y pretender que son intercambiables le hace un flaco favor a todos. Elegir el que matchee tu caso de uso principal, no el que tenga más estrellas en GitHub."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#questions","content":""},{"heading":"¿Qué framework debería usar para empezar con una aplicación RAG?","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-1","content":"LlamaIndex si RAG es tu caso de uso principal, tiene el pipeline de procesamiento de documentos y retrieval más maduro. LangChain si se necesita RAG como parte de un sistema de agentes más grande con herramientas y cadenas. Semantic Kernel si se está en el ecosistema Microsoft o se necesita soporte enterprise-grade en .NET."},{"heading":"¿Puedo usar múltiples proveedores de LLM con estos frameworks?","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-2","content":"Sí, los tres soportan múltiples proveedores. LangChain tiene el soporte más amplio de proveedores. Semantic Kernel tiene soporte de primera clase para Azure OpenAI y OpenAI con conectores de comunidad en crecimiento. LlamaIndex soporta todos los proveedores principales a través de su abstracción de LLM."},{"heading":"¿Cómo manejan estos frameworks la memoria y el estado de conversación?","level":3,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#question-3","content":"LangChain tiene clases de memoria explícitas (ConversationBufferMemory, ConversationSummaryMemory). Semantic Kernel usa un objeto ChatHistory con memoria basada en plugins. LlamaIndex se enfoca en memoria basada en índices a través de sus abstracciones de chat engine."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#article-tips","content":"1. **Tip:** El ` LCEL ` (LangChain Expression Language) de LangChain es la forma moderna de encadenar componentes. Si seguís usando ` LLMChain `, se está en APIs deprecadas, migrá ya.\n2. **Tip:** El decorator ` KernelFunction ` de Semantic Kernel convierte cualquier método Python/C# en un plugin. Utilizar ` @kernel_function(description=...) ` con descripciones detalladas, el LLM las usa para selección de funciones.\n3. **Tip:** El chunk size default de ` VectorStoreIndex ` en LlamaIndex de 1024 tokens es demasiado grande para la mayoría de los casos. Iniciar con ` chunk_size=512, chunk_overlap=50 ` y tuneá desde ahí.\n\n- semantic-kernel\n- langchain\n- llamaindex\n- rag\n- ai-orchestration\n- llm\n- plugins\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"English","url":"https://lucioduran.com/blog/semantic-kernel-langchain-ai-orchestration"},{"text":"OpenAI","url":"https://openai.com/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration#article","mainEntityOfPage":{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration"},"headline":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","description":"Comparación técnica de frameworks de orquestación AI, arquitecturas de plugins, manejo de memoria, y construcción de un pipeline RAG con cada uno para identificar las diferencias prácticas.","datePublished":"2026-01-18","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"image":"https://statics.lucioduran.com/images/lucioduran-social-card.jpg","url":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/blog#blog"},"articleSection":"AI/ML","keywords":"semantic-kernel, langchain, llamaindex, rag, ai-orchestration, llm, plugins","wordCount":1304,"timeRequired":"PT7M","workTranslation":[{"@type":"BlogPosting","inLanguage":"en","url":"https://lucioduran.com/blog/semantic-kernel-langchain-ai-orchestration"}]},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"Frameworks de Orquestación AI: Semantic Kernel vs LangChain vs LlamaIndex","item":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration"}]},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Qué framework debería usar para empezar con una aplicación RAG?","acceptedAnswer":{"@type":"Answer","text":"LlamaIndex si RAG es tu caso de uso principal, tiene el pipeline de procesamiento de documentos y retrieval más maduro. LangChain si se necesita RAG como parte de un sistema de agentes más grande con herramientas y cadenas. Semantic Kernel si se está en el ecosistema Microsoft o se necesita soporte enterprise-grade en .NET."}},{"@type":"Question","name":"¿Puedo usar múltiples proveedores de LLM con estos frameworks?","acceptedAnswer":{"@type":"Answer","text":"Sí, los tres soportan múltiples proveedores. LangChain tiene el soporte más amplio de proveedores. Semantic Kernel tiene soporte de primera clase para Azure OpenAI y OpenAI con conectores de comunidad en crecimiento. LlamaIndex soporta todos los proveedores principales a través de su abstracción de LLM."}},{"@type":"Question","name":"¿Cómo manejan estos frameworks la memoria y el estado de conversación?","acceptedAnswer":{"@type":"Answer","text":"LangChain tiene clases de memoria explícitas (ConversationBufferMemory, ConversationSummaryMemory). Semantic Kernel usa un objeto ChatHistory con memoria basada en plugins. LlamaIndex se enfoca en memoria basada en índices a través de sus abstracciones de chat engine."}}]}]}],"contentHash":"369e1bdbc6fbb185ea371e660f153dd8da695a6f353a9788151006d48a819c20","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/semantic-kernel-langchain-ai-orchestration","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":67745,"contentBytes":14471}}
