Buckets:
| <meta charset="utf-8" /><meta name="hf:doc:metadata" content="{"title":"Construyendo Tu Primer LangGraph","local":"construyendo-tu-primer-langgraph","sections":[{"title":"Configurando Nuestro Entorno","local":"configurando-nuestro-entorno","sections":[],"depth":2},{"title":"Paso 1: Definir Nuestro Estado","local":"paso-1-definir-nuestro-estado","sections":[],"depth":2},{"title":"Paso 2: Definir Nuestros Nodos","local":"paso-2-definir-nuestros-nodos","sections":[],"depth":2},{"title":"Paso 3: Definir Nuestra L贸gica de Enrutamiento","local":"paso-3-definir-nuestra-l贸gica-de-enrutamiento","sections":[],"depth":2},{"title":"Paso 4: Crear el StateGraph y Definir Aristas(Edges)","local":"paso-4-crear-el-stategraph-y-definir-aristasedges","sections":[],"depth":2},{"title":"Paso 5: Ejecutar la Aplicaci贸n","local":"paso-5-ejecutar-la-aplicaci贸n","sections":[],"depth":2},{"title":"Paso 6: Inspeccionando Nuestro Agente de Clasificaci贸n de Correo con Langfuse 馃摗","local":"paso-6-inspeccionando-nuestro-agente-de-clasificaci贸n-de-correo-con-langfuse-","sections":[],"depth":2},{"title":"Lo Que Hemos Construido","local":"lo-que-hemos-construido","sections":[],"depth":2},{"title":"Puntos Clave","local":"puntos-clave","sections":[],"depth":2},{"title":"驴Qu茅 Sigue?","local":"qu茅-sigue","sections":[],"depth":2}],"depth":1}"> | |
| <link href="/docs/agents-course/pr_545/es/_app/immutable/assets/0.e3b0c442.css" rel="modulepreload"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/entry/start.63ebcd5b.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/scheduler.37c15a92.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/singletons.4b25defb.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/index.18351ede.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/paths.6deaf1b7.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/entry/app.b37467ae.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/index.2bf4358c.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/nodes/0.147ec314.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/each.e59479a4.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/nodes/40.82e9b180.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/Tip.363c041f.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/CodeBlock.4e987730.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/Heading.8ada512a.js"> | |
| <link rel="modulepreload" href="/docs/agents-course/pr_545/es/_app/immutable/chunks/getInferenceSnippets.031140c2.js"><!-- HEAD_svelte-u9bgzb_START --><meta name="hf:doc:metadata" content="{"title":"Construyendo Tu Primer LangGraph","local":"construyendo-tu-primer-langgraph","sections":[{"title":"Configurando Nuestro Entorno","local":"configurando-nuestro-entorno","sections":[],"depth":2},{"title":"Paso 1: Definir Nuestro Estado","local":"paso-1-definir-nuestro-estado","sections":[],"depth":2},{"title":"Paso 2: Definir Nuestros Nodos","local":"paso-2-definir-nuestros-nodos","sections":[],"depth":2},{"title":"Paso 3: Definir Nuestra L贸gica de Enrutamiento","local":"paso-3-definir-nuestra-l贸gica-de-enrutamiento","sections":[],"depth":2},{"title":"Paso 4: Crear el StateGraph y Definir Aristas(Edges)","local":"paso-4-crear-el-stategraph-y-definir-aristasedges","sections":[],"depth":2},{"title":"Paso 5: Ejecutar la Aplicaci贸n","local":"paso-5-ejecutar-la-aplicaci贸n","sections":[],"depth":2},{"title":"Paso 6: Inspeccionando Nuestro Agente de Clasificaci贸n de Correo con Langfuse 馃摗","local":"paso-6-inspeccionando-nuestro-agente-de-clasificaci贸n-de-correo-con-langfuse-","sections":[],"depth":2},{"title":"Lo Que Hemos Construido","local":"lo-que-hemos-construido","sections":[],"depth":2},{"title":"Puntos Clave","local":"puntos-clave","sections":[],"depth":2},{"title":"驴Qu茅 Sigue?","local":"qu茅-sigue","sections":[],"depth":2}],"depth":1}"><!-- HEAD_svelte-u9bgzb_END --> <p></p> <h1 class="relative group"><a id="construyendo-tu-primer-langgraph" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#construyendo-tu-primer-langgraph"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Construyendo Tu Primer LangGraph</span></h1> <p data-svelte-h="svelte-doeu2a">Ahora que entendemos los componentes b谩sicos, vamos a ponerlos en pr谩ctica construyendo nuestro primer grafo funcional. Implementaremos el sistema de procesamiento de correos electr贸nicos de Alfred, donde necesita:</p> <ol data-svelte-h="svelte-18e50hn"><li>Leer correos electr贸nicos entrantes</li> <li>Clasificarlos como spam o leg铆timos</li> <li>Redactar una respuesta preliminar para correos leg铆timos</li> <li>Enviar informaci贸n al Sr. Wayne cuando son leg铆timos (solo impresi贸n)</li></ol> <p data-svelte-h="svelte-1rwed3n">Este ejemplo demuestra c贸mo estructurar un flujo de trabajo con LangGraph que involucra toma de decisiones basada en LLM. Aunque esto no puede considerarse un Agente ya que no se involucra ninguna herramienta, esta secci贸n se enfoca m谩s en aprender el marco de trabajo de LangGraph que en Agentes.</p> <div class="course-tip bg-gradient-to-br dark:bg-gradient-to-r before:border-green-500 dark:before:border-green-800 from-green-50 dark:from-gray-900 to-white dark:to-gray-950 border border-green-50 text-green-700 dark:text-gray-400">Puedes seguir el c贸digo en <a href="https://huggingface.co/agents-course/notebooks/resolve/main/unit2/langgraph/mail_sorting.ipynb" target="_blank" data-svelte-h="svelte-l7p6s8">este notebook</a> que puedes ejecutar usando Google Colab.</div> | |
| ## Nuestro Flujo de Trabajo | |
| <p data-svelte-h="svelte-8xoej8">Aqu铆 est谩 el flujo de trabajo que construiremos:</p> <img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/first_graph.png" alt="First LangGraph"> <h2 class="relative group"><a id="configurando-nuestro-entorno" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#configurando-nuestro-entorno"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Configurando Nuestro Entorno</span></h2> <p data-svelte-h="svelte-1tpjtub">Primero, instalemos los paquetes necesarios:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START -->%pip install langgraph langchain_openai<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-1khiaor">A continuaci贸n, importemos los m贸dulos necesarios:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> os | |
| <span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> TypedDict, <span class="hljs-type">List</span>, <span class="hljs-type">Dict</span>, <span class="hljs-type">Any</span>, <span class="hljs-type">Optional</span> | |
| <span class="hljs-keyword">from</span> langgraph.graph <span class="hljs-keyword">import</span> StateGraph, END | |
| <span class="hljs-keyword">from</span> langchain_openai <span class="hljs-keyword">import</span> ChatOpenAI | |
| <span class="hljs-keyword">from</span> langchain_core.messages <span class="hljs-keyword">import</span> HumanMessage, AIMessage<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="paso-1-definir-nuestro-estado" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-1-definir-nuestro-estado"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 1: Definir Nuestro Estado</span></h2> <p data-svelte-h="svelte-1tph6bi">Definamos qu茅 informaci贸n necesita rastrear Alfred durante el flujo de trabajo de procesamiento de correos electr贸nicos:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">class</span> <span class="hljs-title class_">EmailState</span>(<span class="hljs-title class_ inherited__">TypedDict</span>): | |
| <span class="hljs-comment"># El correo electr贸nico que se est谩 procesando</span> | |
| email: <span class="hljs-type">Dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>] <span class="hljs-comment"># Contiene asunto, remitente, cuerpo, etc.</span> | |
| <span class="hljs-comment"># An谩lisis y decisiones</span> | |
| is_spam: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">bool</span>] | |
| <span class="hljs-comment"># Generaci贸n de respuesta</span> | |
| draft_response: <span class="hljs-type">Optional</span>[<span class="hljs-built_in">str</span>] | |
| <span class="hljs-comment"># Metadatos de procesamiento</span> | |
| messages: <span class="hljs-type">List</span>[<span class="hljs-type">Dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-type">Any</span>]] <span class="hljs-comment"># Rastrea la conversaci贸n con LLM para an谩lisis</span><!-- HTML_TAG_END --></pre></div> <blockquote data-svelte-h="svelte-19va48"><p>馃挕 <strong>Tip:</strong> Haz que tu estado sea lo suficientemente completo para rastrear toda la informaci贸n importante, pero evita sobrecargarlo con detalles innecesarios.</p></blockquote> <h2 class="relative group"><a id="paso-2-definir-nuestros-nodos" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-2-definir-nuestros-nodos"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 2: Definir Nuestros Nodos</span></h2> <p data-svelte-h="svelte-wfblmo">Ahora, creemos las funciones de procesamiento que formar谩n nuestros nodos:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-comment"># Inicializar nuestro LLM</span> | |
| model = ChatOpenAI(temperature=<span class="hljs-number">0</span>) | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">read_email</span>(<span class="hljs-params">state: EmailState</span>): | |
| <span class="hljs-string">"""Alfred lee y registra el correo electr贸nico entrante"""</span> | |
| email = state[<span class="hljs-string">"email"</span>] | |
| <span class="hljs-comment"># Aqu铆 podr铆amos hacer alg煤n preprocesamiento inicial</span> | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Alfred est谩 procesando un correo electr贸nico de <span class="hljs-subst">{email[<span class="hljs-string">'sender'</span>]}</span> con asunto: <span class="hljs-subst">{email[<span class="hljs-string">'subject'</span>]}</span>"</span>) | |
| <span class="hljs-comment"># No se necesitan cambios de estado aqu铆</span> | |
| <span class="hljs-keyword">return</span> {} | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">classify_email</span>(<span class="hljs-params">state: EmailState</span>): | |
| <span class="hljs-string">"""Alfred usa un LLM para determinar si el correo es spam o leg铆timo"""</span> | |
| email = state[<span class="hljs-string">"email"</span>] | |
| <span class="hljs-comment"># Preparar nuestro prompt para el LLM</span> | |
| prompt = <span class="hljs-string">f""" | |
| Como Alfred el mayordomo, analiza este correo electr贸nico y determina si es spam o leg铆timo. | |
| Email: | |
| De: <span class="hljs-subst">{email[<span class="hljs-string">'sender'</span>]}</span> | |
| Asunto: <span class="hljs-subst">{email[<span class="hljs-string">'subject'</span>]}</span> | |
| Body: <span class="hljs-subst">{email[<span class="hljs-string">'body'</span>]}</span> | |
| Primero, determina si este correo es spam. Si es spam, explica por qu茅. | |
| Si es leg铆timo, categor铆zalo (consulta, queja, agradecimiento, etc.). | |
| """</span> | |
| <span class="hljs-comment"># Llamar al LLM</span> | |
| messages = [HumanMessage(content=prompt)] | |
| response = model.invoke(messages) | |
| <span class="hljs-comment"># L贸gica simple para analizar la respuesta (en una aplicaci贸n real, querr铆as un an谩lisis m谩s robusto)</span> | |
| response_text = response.content.lower() | |
| is_spam = <span class="hljs-string">"spam"</span> <span class="hljs-keyword">in</span> response_text <span class="hljs-keyword">and</span> <span class="hljs-string">"not spam"</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> response_text | |
| <span class="hljs-comment"># Extraer una raz贸n si es spam</span> | |
| spam_reason = <span class="hljs-literal">None</span> | |
| <span class="hljs-keyword">if</span> is_spam <span class="hljs-keyword">and</span> <span class="hljs-string">"reason:"</span> <span class="hljs-keyword">in</span> response_text: | |
| spam_reason = response_text.split(<span class="hljs-string">"reason:"</span>)[<span class="hljs-number">1</span>].strip() | |
| <span class="hljs-comment"># Determinar categor铆a si es leg铆timo</span> | |
| email_category = <span class="hljs-literal">None</span> | |
| <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> is_spam: | |
| categories = [<span class="hljs-string">"consulta"</span>, <span class="hljs-string">"queja"</span>, <span class="hljs-string">"agradecimiento"</span>, <span class="hljs-string">"solicitud"</span>, <span class="hljs-string">"informaci贸n"</span>] | |
| <span class="hljs-keyword">for</span> category <span class="hljs-keyword">in</span> categories: | |
| <span class="hljs-keyword">if</span> category <span class="hljs-keyword">in</span> response_text: | |
| email_category = category | |
| <span class="hljs-keyword">break</span> | |
| <span class="hljs-comment"># Actualizar mensajes para seguimiento</span> | |
| new_messages = state.get(<span class="hljs-string">"messages"</span>, []) + [ | |
| {<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt}, | |
| {<span class="hljs-string">"role"</span>: <span class="hljs-string">"assistant"</span>, <span class="hljs-string">"content"</span>: response.content} | |
| ] | |
| <span class="hljs-comment"># Regresar actualizaciones de estado</span> | |
| <span class="hljs-keyword">return</span> { | |
| <span class="hljs-string">"is_spam"</span>: is_spam, | |
| <span class="hljs-string">"spam_reason"</span>: spam_reason, | |
| <span class="hljs-string">"email_category"</span>: email_category, | |
| <span class="hljs-string">"messages"</span>: new_messages | |
| } | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">handle_spam</span>(<span class="hljs-params">state: EmailState</span>): | |
| <span class="hljs-string">"""Alfred descarta el correo spam con una nota"""</span> | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Alfred ha marcado el correo como spam. Raz贸n: <span class="hljs-subst">{state[<span class="hljs-string">'spam_reason'</span>]}</span>"</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"El correo ha sido movido a la carpeta de spam."</span>) | |
| <span class="hljs-comment"># Hemos terminado de procesar este correo</span> | |
| <span class="hljs-keyword">return</span> {} | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">draft_response</span>(<span class="hljs-params">state: EmailState</span>): | |
| <span class="hljs-string">"""Alfred redacta una respuesta preliminar para correos leg铆timos"""</span> | |
| email = state[<span class="hljs-string">"email"</span>] | |
| category = state[<span class="hljs-string">"email_category"</span>] <span class="hljs-keyword">or</span> <span class="hljs-string">"general"</span> | |
| <span class="hljs-comment"># Preparar nuestro prompt para el LLM</span> | |
| prompt = <span class="hljs-string">f""" | |
| Como Alfred el mayordomo, redacta una respuesta preliminar cort茅s a este correo. | |
| Email: | |
| From: <span class="hljs-subst">{email[<span class="hljs-string">'sender'</span>]}</span> | |
| Subject: <span class="hljs-subst">{email[<span class="hljs-string">'subject'</span>]}</span> | |
| Body: <span class="hljs-subst">{email[<span class="hljs-string">'body'</span>]}</span> | |
| ste correo ha sido categorizado como: <span class="hljs-subst">{category}</span> | |
| Redacta una respuesta breve y profesional que el Sr. Hugg pueda revisar y personalizar antes de enviar. | |
| """</span> | |
| <span class="hljs-comment"># Llamar al LLM</span> | |
| messages = [HumanMessage(content=prompt)] | |
| response = model.invoke(messages) | |
| <span class="hljs-comment"># Actualizar mensajes para seguimiento</span> | |
| new_messages = state.get(<span class="hljs-string">"messages"</span>, []) + [ | |
| {<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt}, | |
| {<span class="hljs-string">"role"</span>: <span class="hljs-string">"assistant"</span>, <span class="hljs-string">"content"</span>: response.content} | |
| ] | |
| <span class="hljs-comment"># Regresar actualizaciones de estado</span> | |
| <span class="hljs-keyword">return</span> { | |
| <span class="hljs-string">"draft_response"</span>: response.content, | |
| <span class="hljs-string">"messages"</span>: new_messages | |
| } | |
| <span class="hljs-keyword">def</span> <span class="hljs-title function_">notify_mr_hugg</span>(<span class="hljs-params">state: EmailState</span>): | |
| <span class="hljs-string">"""Alfred notifica al Sr. Hugg sobre el correo y presenta el borrador de respuesta"""</span> | |
| email = state[<span class="hljs-string">"email"</span>] | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"\n"</span> + <span class="hljs-string">"="</span>*<span class="hljs-number">50</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Se帽or, ha recibido un correo electr贸nico de <span class="hljs-subst">{email[<span class="hljs-string">'sender'</span>]}</span>."</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Subject: <span class="hljs-subst">{email[<span class="hljs-string">'subject'</span>]}</span>"</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">f"Categor铆a: <span class="hljs-subst">{state[<span class="hljs-string">'email_category'</span>]}</span>"</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"\nHe preparado un borrador de respuesta para su revisi贸n:"</span>) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"-"</span>*<span class="hljs-number">50</span>) | |
| <span class="hljs-built_in">print</span>(state[<span class="hljs-string">"draft_response"</span>]) | |
| <span class="hljs-built_in">print</span>(<span class="hljs-string">"="</span>*<span class="hljs-number">50</span> + <span class="hljs-string">"\n"</span>) | |
| <span class="hljs-comment"># Hemos terminado de procesar este correo</span> | |
| <span class="hljs-keyword">return</span> {}<!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="paso-3-definir-nuestra-l贸gica-de-enrutamiento" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-3-definir-nuestra-l贸gica-de-enrutamiento"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 3: Definir Nuestra L贸gica de Enrutamiento</span></h2> <p data-svelte-h="svelte-jowye">Necesitamos una funci贸n para determinar qu茅 camino tomar despu茅s de la clasificaci贸n:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">def</span> <span class="hljs-title function_">route_email</span>(<span class="hljs-params">state: EmailState</span>) -> <span class="hljs-built_in">str</span>: | |
| <span class="hljs-string">"""Determinar el siguiente paso basado en la clasificaci贸n de spam"""</span> | |
| <span class="hljs-keyword">if</span> state[<span class="hljs-string">"is_spam"</span>]: | |
| <span class="hljs-keyword">return</span> <span class="hljs-string">"spam"</span> | |
| <span class="hljs-keyword">else</span>: | |
| <span class="hljs-keyword">return</span> <span class="hljs-string">"legitimate"</span><!-- HTML_TAG_END --></pre></div> <blockquote data-svelte-h="svelte-1jznpp5"><p>馃挕 <strong>Nota:</strong> Esta funci贸n de enrutamiento es llamada por LangGraph para determinar qu茅 arista(edge) seguir despu茅s del nodo de clasificaci贸n. El valor de retorno debe coincidir con una de las claves en nuestro mapeo de aristas(edges) condicionales.</p></blockquote> <h2 class="relative group"><a id="paso-4-crear-el-stategraph-y-definir-aristasedges" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-4-crear-el-stategraph-y-definir-aristasedges"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 4: Crear el StateGraph y Definir Aristas(Edges)</span></h2> <p data-svelte-h="svelte-1u3rwx9">Ahora conectamos todo:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-comment"># Crear el grafo</span> | |
| email_graph = StateGraph(EmailState) | |
| <span class="hljs-comment"># A帽adir nodos</span> | |
| email_graph.add_node(<span class="hljs-string">"read_email"</span>, read_email) | |
| email_graph.add_node(<span class="hljs-string">"classify_email"</span>, classify_email) | |
| email_graph.add_node(<span class="hljs-string">"handle_spam"</span>, handle_spam) | |
| email_graph.add_node(<span class="hljs-string">"draft_response"</span>, draft_response) | |
| email_graph.add_node(<span class="hljs-string">"notify_mr_hugg"</span>, notify_mr_hugg) | |
| <span class="hljs-comment"># A帽adir aristas(edges) - definiendo el flujo</span> | |
| email_graph.add_edge(<span class="hljs-string">"read_email"</span>, <span class="hljs-string">"classify_email"</span>) | |
| <span class="hljs-comment"># A帽adir ramificaci贸n condicional desde classify_email</span> | |
| email_graph.add_conditional_edges( | |
| <span class="hljs-string">"classify_email"</span>, | |
| route_email, | |
| { | |
| <span class="hljs-string">"spam"</span>: <span class="hljs-string">"handle_spam"</span>, | |
| <span class="hljs-string">"legitimate"</span>: <span class="hljs-string">"draft_response"</span> | |
| } | |
| ) | |
| <span class="hljs-comment"># A帽adir las aristas(edges) finales</span> | |
| email_graph.add_edge(<span class="hljs-string">"handle_spam"</span>, END) | |
| email_graph.add_edge(<span class="hljs-string">"draft_response"</span>, <span class="hljs-string">"notify_mr_hugg"</span>) | |
| email_graph.add_edge(<span class="hljs-string">"notify_mr_hugg"</span>, END) | |
| <span class="hljs-comment"># Compilar el grafo</span> | |
| compiled_graph = email_graph.<span class="hljs-built_in">compile</span>()<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-1panad1">Observa c贸mo usamos el nodo especial <code>END</code> proporcionado por LangGraph. Esto indica estados terminales donde el flujo de trabajo se completa.</p> <h2 class="relative group"><a id="paso-5-ejecutar-la-aplicaci贸n" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-5-ejecutar-la-aplicaci贸n"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 5: Ejecutar la Aplicaci贸n</span></h2> <p data-svelte-h="svelte-19tq2o0">Probemos nuestro grafo con un correo leg铆timo y un correo spam:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-comment"># Ejemplo de correo leg铆timo</span> | |
| legitimate_email = { | |
| <span class="hljs-string">"sender"</span>: <span class="hljs-string">"john.smith@example.com"</span>, | |
| <span class="hljs-string">"subject"</span>: <span class="hljs-string">"Pregunta sobre sus servicios"</span>, | |
| <span class="hljs-string">"body"</span>: <span class="hljs-string">"Estimado Sr. Hugg, un colega me recomend贸 contactarle y estoy interesado en conocer m谩s sobre sus servicios de consultor铆a. 驴Podr铆amos programar una llamada la pr贸xima semana? Saludos cordiales, John Smith"</span> | |
| } | |
| <span class="hljs-comment"># Ejemplo de correo spam</span> | |
| spam_email = { | |
| <span class="hljs-string">"sender"</span>: <span class="hljs-string">"winner@lottery-intl.com"</span>, | |
| <span class="hljs-string">"subject"</span>: 隆隆隆HAS GANADO $<span class="hljs-number">5</span>,<span class="hljs-number">000</span>,<span class="hljs-number">000</span>!!!<span class="hljs-string">", | |
| "</span>body<span class="hljs-string">": "</span>隆FELICIDADES! 隆Has sido seleccionado como el ganador de nuestra loter铆a internacional! Para reclamar tu premio de $<span class="hljs-number">5</span>,<span class="hljs-number">000</span>,<span class="hljs-number">000</span>, por favor env铆anos tus datos bancarios y una tarifa de procesamiento de $<span class="hljs-number">100.</span><span class="hljs-string">" | |
| } | |
| # Procesar el correo leg铆timo | |
| print("</span>\nProcesando correo leg铆timo...<span class="hljs-string">") | |
| legitimate_result = compiled_graph.invoke({ | |
| "</span>email<span class="hljs-string">": legitimate_email, | |
| "</span>is_spam<span class="hljs-string">": None, | |
| "</span>spam_reason<span class="hljs-string">": None, | |
| "</span>email_category<span class="hljs-string">": None, | |
| "</span>draft_response<span class="hljs-string">": None, | |
| "</span>messages<span class="hljs-string">": [] | |
| }) | |
| # Procesar el correo spam | |
| print("</span>\nProcesando correo spam...<span class="hljs-string">") | |
| spam_result = compiled_graph.invoke({ | |
| "</span>email<span class="hljs-string">": spam_email, | |
| "</span>is_spam<span class="hljs-string">": None, | |
| "</span>spam_reason<span class="hljs-string">": None, | |
| "</span>email_category<span class="hljs-string">": None, | |
| "</span>draft_response<span class="hljs-string">": None, | |
| "</span>messages<span class="hljs-string">": [] | |
| })</span><!-- HTML_TAG_END --></pre></div> <h2 class="relative group"><a id="paso-6-inspeccionando-nuestro-agente-de-clasificaci贸n-de-correo-con-langfuse-" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#paso-6-inspeccionando-nuestro-agente-de-clasificaci贸n-de-correo-con-langfuse-"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Paso 6: Inspeccionando Nuestro Agente de Clasificaci贸n de Correo con Langfuse 馃摗</span></h2> <p data-svelte-h="svelte-1qbfxk3">Mientras Alfred perfecciona el Agente de Clasificaci贸n de Correo, se est谩 cansando de depurar sus ejecuciones. Los agentes, por naturaleza, son impredecibles y dif铆ciles de inspeccionar. Pero como su objetivo es construir el mejor Agente de Detecci贸n de Spam y desplegarlo en producci贸n, necesita una trazabilidad robusta para el monitoreo y an谩lisis futuros.</p> <p data-svelte-h="svelte-pa0rsu">Para hacer esto, Alfred puede usar una herramienta de observabilidad como <a href="https://langfuse.com/" rel="nofollow">Langfuse</a> para rastrear y monitorear el agente.</p> <p data-svelte-h="svelte-1mkf60j">Primero, instalamos Langfuse con pip:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START -->%pip install -q langfuse<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-1tnragj">Luego, agregamos las claves API de Langfuse y la direcci贸n del host como variables de entorno. Puedes obtener tus credenciales de Langfuse registr谩ndote en <a href="https://cloud.langfuse.com" rel="nofollow">Langfuse Cloud</a> o <a href="https://langfuse.com/self-hosting" rel="nofollow">self-host Langfuse</a>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">import</span> os | |
| <span class="hljs-comment"># Obt茅n las claves para tu proyecto desde la p谩gina de configuraci贸n del proyecto: https://cloud.langfuse.com</span> | |
| os.environ[<span class="hljs-string">"LANGFUSE_PUBLIC_KEY"</span>] = <span class="hljs-string">"pk-lf-..."</span> | |
| os.environ[<span class="hljs-string">"LANGFUSE_SECRET_KEY"</span>] = <span class="hljs-string">"sk-lf-..."</span> | |
| os.environ[<span class="hljs-string">"LANGFUSE_HOST"</span>] = <span class="hljs-string">"https://cloud.langfuse.com"</span> <span class="hljs-comment"># 馃嚜馃嚭 regi贸n de la UE</span> | |
| <span class="hljs-comment"># os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 馃嚭馃嚫 regi贸n de EE.U</span><!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-jls7vc">Luego, configuramos el <a href="https://langfuse.com/docs/integrations/langchain/tracing#add-langfuse-to-your-langchain-application" rel="nofollow">Langfuse <code>callback_handler</code></a> instrumentamos el agente a帽adiendo el <code>langfuse_callback</code> a la invocaci贸n del grafo: <code>config={"callbacks": [langfuse_handler]}</code>.</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START --><span class="hljs-keyword">from</span> langfuse.callback <span class="hljs-keyword">import</span> CallbackHandler | |
| <span class="hljs-comment"># Inicializar CallbackHandler de Langfuse para LangGraph/Langchain (trazado)</span> | |
| langfuse_handler = CallbackHandler() | |
| <span class="hljs-comment"># Procesar correo leg铆timo</span> | |
| legitimate_result = compiled_graph.invoke( | |
| <span class="hljs-built_in">input</span>={<span class="hljs-string">"email"</span>: legitimate_email, <span class="hljs-string">"is_spam"</span>: <span class="hljs-literal">None</span>, <span class="hljs-string">"spam_reason"</span>: <span class="hljs-literal">None</span>, <span class="hljs-string">"email_category"</span>: <span class="hljs-literal">None</span>, <span class="hljs-string">"draft_response"</span>: <span class="hljs-literal">None</span>, <span class="hljs-string">"messages"</span>: []}, | |
| config={<span class="hljs-string">"callbacks"</span>: [langfuse_handler]} | |
| )<!-- HTML_TAG_END --></pre></div> <p data-svelte-h="svelte-nazjpc">隆Alfred est谩 ahora conectado 馃攲! Las ejecuciones de LangGraph se est谩n registrando en Langfuse, d谩ndole visibilidad completa del comportamiento del agente. Con esta configuraci贸n, est谩 listo para revisar ejecuciones anteriores y refinar a煤n m谩s su Agente de Clasificaci贸n de Correo.</p> <p data-svelte-h="svelte-3rgcl6"><img src="https://langfuse.com/images/cookbook/huggingface-agent-course/langgraph-trace-legit.png" alt="Example trace in Langfuse"></p> <p data-svelte-h="svelte-1kmw2a1"><em><a href="https://cloud.langfuse.com/project/cloramnkj0002jz088vzn1ja4/traces/f5d6d72e-20af-4357-b232-af44c3728a7b?timestamp=2025-03-17T10%3A13%3A28.413Z&observation=6997ba69-043f-4f77-9445-700a033afba1" rel="nofollow">Public link to the trace with the legit email</a></em></p> <p data-svelte-h="svelte-12rx4fm">Visualizando Nuestro Grafo</p> <p data-svelte-h="svelte-fg4lxm">LangGraph nos permite visualizar nuestro flujo de trabajo para entender y depurar mejor su estructura:</p> <div class="code-block relative "><div class="absolute top-2.5 right-4"><button class="inline-flex items-center relative text-sm focus:text-green-500 cursor-pointer focus:outline-none transition duration-200 ease-in-out opacity-0 mx-0.5 text-gray-600 " title="code excerpt" type="button"><svg class="" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" fill="currentColor" focusable="false" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 32 32"><path d="M28,10V28H10V10H28m0-2H10a2,2,0,0,0-2,2V28a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V10a2,2,0,0,0-2-2Z" transform="translate(0)"></path><path d="M4,18H2V4A2,2,0,0,1,4,2H18V4H4Z" transform="translate(0)"></path><rect fill="none" width="32" height="32"></rect></svg> <div class="absolute pointer-events-none transition-opacity bg-black text-white py-1 px-2 leading-tight rounded font-normal shadow left-1/2 top-full transform -translate-x-1/2 translate-y-2 opacity-0"><div class="absolute bottom-full left-1/2 transform -translate-x-1/2 w-0 h-0 border-black border-4 border-t-0" style="border-left-color: transparent; border-right-color: transparent; "></div> Copied</div></button></div> <pre class=""><!-- HTML_TAG_START -->compiled_graph.get_graph().draw_mermaid_png()<!-- HTML_TAG_END --></pre></div> <img src="https://huggingface.co/datasets/agents-course/course-images/resolve/main/en/unit2/LangGraph/mail_flow.png" alt="Mail LangGraph"> <p data-svelte-h="svelte-pjb0mq">Esto produce una representaci贸n visual que muestra c贸mo est谩n conectados nuestros nodos y los caminos condicionales que se pueden tomar.</p> <h2 class="relative group"><a id="lo-que-hemos-construido" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#lo-que-hemos-construido"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Lo Que Hemos Construido</span></h2> <p data-svelte-h="svelte-bkdtjn">Hemos creado un flujo de trabajo completo de procesamiento de correos electr贸nicos que:</p> <ol data-svelte-h="svelte-17og569"><li>Toma un correo electr贸nico entrante</li> <li>Usa un LLM para clasificarlo como spam o leg铆timo</li> <li>Maneja el spam descart谩ndolo</li> <li>Para correos leg铆timos, redacta una respuesta y notifica al Sr. Hugg</li></ol> <p data-svelte-h="svelte-ntcgtk">Esto demuestra el poder de LangGraph para orquestar flujos de trabajo complejos con LLMs mientras mantiene un flujo claro y estructurado.</p> <h2 class="relative group"><a id="puntos-clave" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#puntos-clave"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>Puntos Clave</span></h2> <ul data-svelte-h="svelte-1wjpek5"><li><strong>Gesti贸n de Estado</strong>: Definimos un estado completo para rastrear todos los aspectos del procesamiento de correos electr贸nicos</li> <li><strong>Implementaci贸n de Nodos:</strong>: Creamos nodos funcionales que interact煤an con un LLM</li> <li><strong>Enrutamiento Condicional</strong>: Implementamos l贸gica de ramificaci贸n basada en la clasificaci贸n de correos</li> <li><strong>Estados Terminales:</strong>: Usamos el nodo END para marcar puntos de finalizaci贸n en nuestro flujo de trabajo</li></ul> <h2 class="relative group"><a id="qu茅-sigue" class="header-link block pr-1.5 text-lg no-hover:hidden with-hover:absolute with-hover:p-1.5 with-hover:opacity-0 with-hover:group-hover:opacity-100 with-hover:right-full" href="#qu茅-sigue"><span><svg class="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M167.594 88.393a8.001 8.001 0 0 1 0 11.314l-67.882 67.882a8 8 0 1 1-11.314-11.315l67.882-67.881a8.003 8.003 0 0 1 11.314 0zm-28.287 84.86l-28.284 28.284a40 40 0 0 1-56.567-56.567l28.284-28.284a8 8 0 0 0-11.315-11.315l-28.284 28.284a56 56 0 0 0 79.196 79.197l28.285-28.285a8 8 0 1 0-11.315-11.314zM212.852 43.14a56.002 56.002 0 0 0-79.196 0l-28.284 28.284a8 8 0 1 0 11.314 11.314l28.284-28.284a40 40 0 0 1 56.568 56.567l-28.285 28.285a8 8 0 0 0 11.315 11.314l28.284-28.284a56.065 56.065 0 0 0 0-79.196z" fill="currentColor"></path></svg></span></a> <span>驴Qu茅 Sigue?</span></h2> <p data-svelte-h="svelte-1fk3r9z">En la siguiente secci贸n, exploraremos caracter铆sticas m谩s avanzadas de LangGraph, incluyendo el manejo de interacci贸n humana en el flujo de trabajo y la implementaci贸n de l贸gica de ramificaci贸n m谩s compleja basada en m煤ltiples condiciones.</p> <a class="!text-gray-400 !no-underline text-sm flex items-center not-prose mt-4" href="https://github.com/huggingface/agents-course/blob/main/units/es/unit2/langgraph/first_graph.mdx" target="_blank"><span data-svelte-h="svelte-1kd6by1"><</span> <span data-svelte-h="svelte-x0xyl0">></span> <span data-svelte-h="svelte-1dajgef"><span class="underline ml-1.5">Update</span> on GitHub</span></a> <p></p> | |
| <script> | |
| { | |
| __sveltekit_1k0olmh = { | |
| assets: "/docs/agents-course/pr_545/es", | |
| base: "/docs/agents-course/pr_545/es", | |
| env: {} | |
| }; | |
| const element = document.currentScript.parentElement; | |
| const data = [null,null]; | |
| Promise.all([ | |
| import("/docs/agents-course/pr_545/es/_app/immutable/entry/start.63ebcd5b.js"), | |
| import("/docs/agents-course/pr_545/es/_app/immutable/entry/app.b37467ae.js") | |
| ]).then(([kit, app]) => { | |
| kit.start(app, element, { | |
| node_ids: [0, 40], | |
| data, | |
| form: null, | |
| error: null | |
| }); | |
| }); | |
| } | |
| </script> | |
Xet Storage Details
- Size:
- 58.7 kB
- Xet hash:
- e3d27aec3b6ff0f33ad51aaae15cf1500bd71a70434045a40ad95849c82fdff0
路
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.