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

En Parte 1 De esta serie tutorial, presentamos Agentes de IAprogramas autónomos que realizan tareas, toman decisiones y se comunican con otros.

Los agentes realizan acciones a través de herramientas. Puede suceder que una herramienta no funcione en el primer intento, o que múltiples herramientas deben activarse en secuencia. Los agentes deben poder organizar las tareas en una progresión lógica y cambiar sus estrategias en un entorno dinámico.

En pocas palabras, la estructura del agente debe ser sólida y el comportamiento debe ser confiable. La forma más común de hacerlo es a través de:

  • Iteraciones – Repetir una cierta acción varias veces, a menudo con ligeros cambios o mejoras en cada ciclo. Cada vez podría involucrar al agente que revisa ciertos pasos para refinar su salida o alcanzar una solución óptima.
  • Cadenas Una serie de acciones que se unen en una secuencia. Cada paso en la cadena depende de la anterior, y la salida de una acción se convierte en la entrada para la siguiente.

En este tutorial, voy a mostrar cómo usar iteraciones y cadenas para agentes. 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 (enlace al código completo al final del artículo).

Configuración

Consulte Parte 1 para la configuración de Ollama y el principal LLM.

import Ollama
llm = "qwen2.5" 

Usaremos el Yahoofinance API público con el Pitón biblioteca (pip install yfinance==0.2.55) para descargar datos financieros.

import yfinance as yf

stock = "MSFT"
yf.Ticker(ticker=stock).history(period='5d') #1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max

Incrustamos eso en una herramienta.

import matplotlib.pyplot as plt

def get_stock(ticker:str, period:str, col:str):
    data = yf.Ticker(ticker=ticker).history(period=period)
    if len(data) > 0:
        data[col].plot(color="black", legend=True, xlabel='', title=f"{ticker.upper()} ({period})").grid()
        plt.show()
        return 'ok'
    else:
        return 'no'

tool_get_stock = {'type':'function', 'function':{
  'name': 'get_stock',
  'description': 'Download stock data',
  'parameters': {'type': 'object',
                'required': ['ticker','period','col'],
                'properties': {
                    'ticker': {'type':'str', 'description':'the ticker symbol of the stock.'},
                    'period': {'type':'str', 'description':"for 1 month input '1mo', for 6 months input '6mo', for 1 year input '1y'. Use '1y' if not specified."},
                    'col': {'type':'str', 'description':"one of 'Open','High','Low','Close','Volume'. Use 'Close' if not specified."},
}}}}

## test
get_stock(ticker="msft", period="1y", col="Close")

Además, tomando el código del artículo anterior como referencia, escribiré una función general para procesar la respuesta del modelo, como cuando el agente quiere usar una herramienta o cuando solo devuelva texto.

def use_tool(agent_res:dict, dic_tools:dict) -> dict:
    ## use tool
    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")
                ### tool output
                t_output = f(**tool["function"]["arguments"])
                print(t_output)
                ### final res
                res = t_output
            else:
                print('🤬 >', f"x1b[1;31m{t_name} -> NotFoundx1b[0m")
    ## don't use tool
    if agent_res['message']['content'] != '':
        res = agent_res["message"]["content"]
        t_name, t_inputs = '', ''
    return {'res':res, 'tool_used':t_name, 'inputs_used':t_inputs}

Comencemos una conversación rápida con nuestro agente. Por ahora, voy a usar un mensaje genérico simple.

indic = '' 'Usted es un analista financiero, ayude al usuario a usar sus herramientas disponibles.' '' Messages = [{"role":"system", "content":prompt}]
Dic_Tools = {'get_stock': get_stock} while true: ## input de usuario intente: q = input ('🙂>') excepto eoferror: ruptura si q == "quit": rompiendo si q.strip () == "": continuar mensajes.append ({"roy": "usuarios", "contenido": q}) ## agente agente = rollama = ollama. mensajes = mensajes, herramientas =[tool_get_stock]) Dic_res = Use_Tool (agente_res, dic_tools) res, herramienta_used, inputs_used = DIC_RES["res"]Dic_Res["tool_used"]Dic_Res["inputs_used"]
   
    ## Impresión de respuesta final ("👽>", f "x1b[1;30m{res}x1b[0m")
    messages.append( {"role":"assistant", "content":res} )

As you can see, I started by asking an “easy” question. The LLM already knows that the symbol of Microsoft stock is MSFT, therefore the Agent was able to activate the Tool with the right inputs. But what if I ask something that might not be included in the LLM knowledge base? 

Seems that the LLM doesn’t know that Facebook changed its name to META, so it used the Tool with the wrong inputs. I will enable the Agent to try an action several times through iterations.

Iterations

Iterations refer to the repetition of a process until a certain condition is met. We can let the Agent try a specific number of times, but we need to let it know that the previous parameters didn’t work, by adding the details in the message history.

    max_i, i = 3, 0
    while res == 'no' and i < max_i:
        comment = f'''I used tool '{tool_used}' with inputs {inputs_used}. But it didn't work, so I must try again with different inputs'''
        messages.append( {"role":"assistant", "content":comment} )
        agent_res = ollama.chat(model=llm, messages=messages,
                                tools=[tool_get_stock]) Dic_res = Use_Tool (agente_res, dic_tools) res, herramienta_used, inputs_used = DIC_RES["res"]Dic_Res["tool_used"]Dic_Res["inputs_used"]
       
        i += 1 si i == max_i: res = f'i probado {i} veces pero algo está mal '## Respuesta final imprime ("👽>", f "x1b[1;30m{res}x1b[0m")
    messages.append( {"role":"assistant", "content":res} )

The Agent tried 3 times with different inputs but it couldn’t find a solution because there is a gap in the LLM knowledge base. In this case, the model needed human input to understand how to use the Tool.

Next, we’re going to enable the Agent to fill the knowledge gap by itself.

Chains

A chain refers to a linear sequence of actions where the output of one step is used as the input for the next step. In this example, I will add another Tool that the Agent can use in case the first one fails.

We can use the web-searching Tool from the previous article.

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']'Propiedades': {'Query': {'type': 'str', 'descripción': 'el tema o sujeto a la búsqueda en la web'},}}}} ## test search_web (Query = "Facebook stock")

Hasta ahora, siempre he usado indicaciones muy genéricas, ya que las tareas fueron relativamente simples. Ahora, quiero asegurarme de que el agente comprenda cómo usar las herramientas en el orden correcto, así que voy a escribir un aviso adecuado. Así es como se debe hacer un aviso:

  1. El objetivo del agente
  2. Qué debe devolver (formato, es decir, contenido)
  3. Cualquier advertencia relevante que pueda afectar la salida
  4. Volcado de contexto
prompt = '''
[GOAL] You are a financial analyst, assist the user using your available tools.

[RETURN] You must return the stock data that the user asks for.

[WARNINGS] In order to retrieve stock data, you need to know the ticker symbol of the company.

[CONTEXT] First ALWAYS try to use the tool 'get_stock'.
If it doesn't work, you can use the tool 'search_web' and search 'company name stock'.
Get information about the stock and deduct what is the right ticker symbol of the company.
Then, you can use AGAIN the tool 'get_stock' with the ticker you got using the previous tool.
'''

Simplemente podemos agregar la cadena al bucle de iteración que ya tenemos. Esta vez, el agente tiene dos herramientas, y cuando el primero falla, el modelo puede decidir si volver a intentarlo o usar el segundo. Entonces, si se usa la segunda herramienta, El agente debe procesar la salida y aprender ¿Cuál es la entrada correcta para la primera herramienta que inicialmente falló?

    max_i, i = 3, 0
    while res in ['no',''] and i < max_i:
        comment = f'''I used tool '{tool_used}' with inputs {inputs_used}. But it didn't work, so I must try a different way.'''
        messages.append( {"role":"assistant", "content":comment} )
        agent_res = ollama.chat(model=llm, messages=messages,
                                tools=[tool_get_stock, tool_search_web])
        dic_res = use_tool(agent_res, dic_tools)
        res, tool_used, inputs_used = dic_res["res"], dic_res["tool_used"], dic_res["inputs_used"]

        ## chain: output of previous tool = input of next tool
        if tool_used == 'search_web':
            query = q+". You must return just the compay ticker.nContext: "+res
            llm_res = ollama.generate(model=llm, prompt=query)["response"]
            messages.append( {"role":"user", "content":f"try ticker: {llm_res}"} )
           
            print("👽 >", f"x1b[1;30mI can try with {llm_res}x1b[0m")
           
            agent_res = ollama.chat(model=llm, messages=messages, tools=[tool_get_stock])
            dic_res = use_tool(agent_res, dic_tools)
            res, tool_used, inputs_used = dic_res["res"], dic_res["tool_used"], dic_res["inputs_used"]
        i += 1        if i == max_i:            res = f'I tried {i} times but something is wrong'
    
 ## final response    
 print("👽 >", f"x1b[1;30m{res}x1b[0m")    
 messages.append( {"role":"assistant", "content":res} )

Como se esperaba, el agente intentó usar la primera herramienta con las entradas incorrectas, pero en lugar de intentar la misma acción nuevamente que antes, decidió usar la segunda herramienta. Al consumir información, debe comprender la solución sin la necesidad de aportes humanos.

En resumen, la IA intentó hacer una acción pero falló debido a una brecha en su base de conocimiento. Por lo tanto, activó las herramientas para llenar ese vacío y entregar la salida solicitada por el usuario … esa es la verdadera esencia de los agentes de IA.

Conclusión

Este artículo ha cubierto formas más estructuradas de hacer que los agentes sean más confiables, utilizando iteraciones y cadenas. 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 3donde 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 👈