Agentes de IA de cero a héroe – Parte 1

Introducción

Agentes de IA son programas autónomos que realizan tareas, toman decisiones y se comunican con otros. Normalmente, usan un conjunto de herramientas para ayudar a completar las tareas. En aplicaciones Genai, estos agentes procesan razonamiento secuencial y pueden usar herramientas externas (como búsquedas web o consultas de bases de datos) cuando el conocimiento de LLM no es suficiente. A diferencia de un chatbot básico, que genera texto aleatorio cuando es incierto, un agente de IA activa herramientas para proporcionar respuestas más precisas y específicas.

Nos estamos acercando cada vez más al concepto de AI agente: Los sistemas que exhiben un mayor nivel de autonomía y capacidad de toma de decisiones, sin intervención humana directa. Si bien los agentes de IA de hoy responden de manera reactiva a las entradas humanas, los AI agentes de mañana participan proactivamente en la resolución de problemas y pueden ajustar su comportamiento en función de la situación.

Hoy, los agentes de construcción desde cero se están volviendo tan fácil como entrenar un modelo de regresión logística hace 10 años. En aquel entonces, Lear Proporcionó una biblioteca directa para entrenar rápidamente modelos de aprendizaje automático con solo unas pocas líneas de código, abstraiendo gran parte de la complejidad subyacente.

En este tutorial, voy a mostrar cómo construir desde cero diferentes tipos de agentes de IAde sistemas simples a más avanzados. Presentaré un código de Python útil que se puede aplicar fácilmente en otros casos similares (solo copie, pegue, ejecute) y camine por cada línea de código con comentarios para que pueda replicar este ejemplo.

Configuración

Como dije, cualquiera puede tener un agente personalizado que se ejecute localmente de forma gratuita sin GPU o claves API. La única biblioteca necesaria es Ollama (PIP install Ollama == 0.4.7), ya que permite a los usuarios ejecutar LLM localmente, sin necesidad de servicios basados ​​en la nube, dando más control sobre la privacidad y el rendimiento de los datos.

En primer lugar, necesitas descargar Ollama del sitio web.

Luego, en el shell de inmediato de su computadora portátil, use el comando para descargar el LLM seleccionado. Voy con Alibaba’s Qwencomo es inteligente y lite.

Después de completar la descarga, puede pasar a Python y comenzar a escribir código.

import ollama
llm = "qwen2.5"

Probemos el LLM:

stream = ollama.generate(model=llm, prompt=""'what time is it?''', stream=True)
for chunk in stream:
    print(chunk['response'], end='', flush=True)

Obviamente, el LLM per se es muy limitado y no puede hacer mucho además de chatear. Por lo tanto, necesitamos proporcionarle la posibilidad de tomar medidas, o en otras palabras, a activar herramientas.

Una de las herramientas más comunes es la capacidad de buscar en Internet. En Python, la forma más fácil de hacerlo es con el famoso navegador privado Duckduckgo (pip install duckduckgo-search==6.3.5). Puede usar directamente la biblioteca original o importar el Langchain envoltura (pip install langchain-community==0.3.17).

Con Ollamapara usar una herramienta, la función debe describirse en un diccionario.

from langchain_community.tools import DuckDuckGoSearchResults
def search_web(query: str) -> str:
  return DuckDuckGoSearchResults(backend="news").run(query)

tool_search_web = {'type':'function', 'function':{
  'name': 'search_web',
  'description': 'Search the web',
  'parameters': {'type': 'object',
                'required': ['query'],
                'properties': {
                    'query': {'type':'str', 'description':'the topic or subject to search on the web'},
}}}}
## test
search_web(query="nvidia")

Las búsquedas en Internet podrían ser muy amplias, y quiero darle al agente la opción de ser más precisos. Digamos que estoy planeando usar este agente para aprender sobre las actualizaciones financieras, para que pueda darle una herramienta específica para ese tema, como buscar solo un sitio web financiero en lugar de toda la web.

def search_yf(query: str) -> str:  engine = DuckDuckGoSearchResults(backend="news")
  return engine.run(f"site:finance.yahoo.com {query}")

tool_search_yf = {'type':'function', 'function':{
  'name': 'search_yf',
  'description': 'Search for specific financial news',
  'parameters': {'type': 'object',
                'required': ['query'],
                'properties': {
                    'query': {'type':'str', 'description':'the financial topic or subject to search'},
}}}}

## test
search_yf(query="nvidia")

Agente simple (WebSearch)

En mi opinión, el agente más básico debe al menos poder elegir entre una o dos herramientas y reelaborar la salida de la acción para dar al usuario una respuesta adecuada y concisa.

Primero, debe escribir un mensaje para describir el propósito del agente, cuanto más detallado, mejor (el mío es muy genérico), y ese será el primer mensaje en el historial de chat con el LLM.

prompt=""'You are an assistant with access to tools, you must decide when to use tools to answer user message.''' 
messages = [{"role":"system", "content":prompt}]

Para mantener viva el chat con la IA, usaré un bucle que comience con la entrada del usuario y luego se invoca el agente para responder (que puede ser un texto del LLM o la activación de una herramienta).

while True:
    ## user input
    try:
        q = input('🙂 >')
    except EOFError:
        break
    if q == "quit":
        break
    if q.strip() == "":
        continue
    messages.append( {"role":"user", "content":q} )
   
    ## model
    agent_res = ollama.chat(
        model=llm,
        tools=[tool_search_web, tool_search_yf],
        messages=messages)

Hasta este punto, el historial de chat podría verse algo así:

Si el modelo quiere usar una herramienta, la función apropiada debe ejecutarse con los parámetros de entrada sugeridos por el LLM en su objeto de respuesta:

Por lo tanto, nuestro código necesita obtener esa información y ejecutar la función de herramienta.

## Respuesta DIC_TOOLS = {'Search_web': search_web, 'search_yf': search_yf} if "tool_calls" en agent_res["message"].Keys (): para la herramienta en agente_res["message"]["tool_calls"]: t_name, t_inputs = herramienta["function"]["name"]herramienta["function"]["arguments"]

            Si F: = Dic_tools.get (t_name): ### PRISMA DE HERRAMIENTA DE CALIDA ('🔧>', F "\ x1B[1;31m{t_name} -> Inputs: {t_inputs}\x1b[0m")
                messages.append( {"role":"user", "content":"use tool '"+t_name+"' with inputs: "+str(t_inputs)} )
                ### tool output
                t_output = f(**tool["function"]["arguments"]) print (t_output) ### Final Res P = f '' 'Resumir esto para responder la pregunta del usuario, sea lo más conciso posible: {t_output}' '' res = ollama.generate (model = llm, pronto = Q+". "+P)["response"]
            else: imprimir ('🤬>', f "\ x1b[1;31m{t_name} -> NotFound\x1b[0m")
 
    if agent_res['message']['content']  ! = '': res = agente_res["message"]["content"]

     
    imprimir ("👽>", f "\ x1b[1;30m{res}\x1b[0m")
    messages.append( {"role":"assistant", "content":res} )

Now, if we run the full code, we can chat with our Agent.

Advanced Agent (Coding)

LLMs know how to code by being exposed to a large corpus of both code and natural language text, where they learn patterns, syntax, and semantics of Programming languages. The model learns the relationships between different parts of the code by predicting the next token in a sequence. In short, LLMs can generate Python code but can’t execute it, Agents can.

I shall prepare a Tool allowing the Agent to execute code. In Python, you can easily create a shell to run code as a string with the native command exec().

import io
import contextlib

def code_exec(code: str) -> str:\
    output = io.StringIO()
    with contextlib.redirect_stdout(output):
        try:
            exec(code)
        except Exception as e:
            print(f"Error: {e}")
    return output.getvalue()

tool_code_exec = {'type':'function', 'function':{
  'name': 'code_exec',
  'description': 'execute python code',
  'parameters': {'type': 'object',
                'required': ['code']'Propiedades': {'Código': {'Tipo': 'Str', 'Descripción': 'Código para ejecutar'},}}}} ## Test Code_Exec ("a = 1+1; imprime (a) ")

Al igual que antes, escribiré un aviso, pero esta vez, al comienzo del bucle de chat, le pediré al usuario que proporcione una ruta de archivo.

prompt=""'You are an expert data scientist, and you have tools to execute python code.
First of all, execute the following code exactly as it is: 'df=pd.read_csv(path); print(df.head())'
If you create a plot, ALWAYS add 'plt.show()' at the end.
'''
messages = [{"role":"system", "content":prompt}]
start = True

while True:
    ## user input
    try:
        if start is True:
            path = input('📁 Provide a CSV path >')
            q = "path = "+path
        else:
            q = input('🙂 >')
    except EOFError:
        break
    if q == "quit":
        break
    if q.strip() == "":
        continue
   
    messages.append( {"role":"user", "content":q} )

Dado que las tareas de codificación pueden ser un poco más complicadas para los LLM, voy a agregar también refuerzo de memoria. Por defecto, durante una sesión, no hay una verdadera memoria a largo plazo. Los LLM tienen acceso al historial de chat, por lo que pueden recordar información temporalmente y rastrear el contexto y las instrucciones que ha dado anteriormente en la conversación. Sin embargo, la memoria no siempre funciona como se esperaba, especialmente si el LLM es pequeño. Por lo tanto, una buena práctica es reforzar la memoria del modelo agregando recordatorios periódicos en el historial de chat.

prompt=""'You are an expert data scientist, and you have tools to execute python code.
First of all, execute the following code exactly as it is: 'df=pd.read_csv(path); print(df.head())'
If you create a plot, ALWAYS add 'plt.show()' at the end.
'''
messages = [{"role":"system", "content":prompt}]
memory = '''Use the dataframe 'df'.'''
start = True

while True:
    ## user input
    try:
        if start is True:
            path = input('📁 Provide a CSV path >')
            q = "path = "+path
        else:
            q = input('🙂 >')
    except EOFError:
        break
    if q == "quit":
        break
    if q.strip() == "":
        continue
   
    ## memory
    if start is False:
        q = memory+"\n"+q
    messages.append( {"role":"user", "content":q} )

Tenga en cuenta que la longitud de memoria predeterminada en Ollama es de 2048 caracteres. Si su máquina puede manejarla, puede aumentarla cambiando el número cuando se invoca el LLM:

    ## model
    agent_res = ollama.chat(
        model=llm,
        tools=[tool_code_exec],
        options={"num_ctx":2048},
        messages=messages)

En este USECase, la salida del agente es principalmente código y datos, por lo que no quiero que la LLM vuelva a elaborar las respuestas.

    ## response
    dic_tools = {'code_exec':code_exec}
   
    if "tool_calls" in agent_res["message"].keys():
        for tool in agent_res["message"]["tool_calls"]:
            t_name, t_inputs = tool["function"]["name"], tool["function"]["arguments"]
            if f := dic_tools.get(t_name):
                ### calling tool
                print('🔧 >', f"\x1b[1;31m{t_name} -> Inputs: {t_inputs}\x1b[0m")
                messages.append( {"role":"user", "content":"use tool '"+t_name+"' with inputs: "+str(t_inputs)} )
                ### tool output
                t_output = f(**tool["function"]["arguments"])
                ### final res
                res = t_output
            else:
                print('🤬 >', f"\x1b[1;31m{t_name} -> NotFound\x1b[0m")
 
    if agent_res['message']['content'] != '':
        res = agent_res["message"]["content"]
     
    print("👽 >", f"\x1b[1;30m{res}\x1b[0m")
    messages.append( {"role":"assistant", "content":res} )
    start = False

Ahora, si ejecutamos el código completo, podemos chatear con nuestro agente.

Conclusión

Este artículo ha cubierto los pasos fundamentales de crear agentes desde cero usando solo Ollama. Con estos bloques de construcción en su lugar, ya está equipado para comenzar a desarrollar sus propios agentes para diferentes casos de uso.

Estén atentos para la Parte 2donde nos sumergiremos más profundamente en ejemplos más avanzados.

Código completo para este artículo: Github

¡Espero que lo hayas disfrutado! No dude en ponerse en contacto conmigo para obtener preguntas y comentarios o simplemente para compartir sus interesantes proyectos.

👉 Vamos a conectarnos 👈