Getting Started With AI Agents

Getting Started With AI Agents

juil. 27, 2024 · by ChiefKemist

Description of Getting Started With AI Agents

Introduction

What the heck is an Agent?

Concepts related to Agents and LLMs

ReAct (Reason + Act): Synergizing Reasoning and Acting in Language Models (Github)

Think about the Prompt and Do something about it (or not?).

Self-Refine: Iterative Refinement with Self-Feedback (Github)

Upon receiving a prompt, iteratively take an action, inspect its results and take further actions as needed to improve results until no further improvements is needed or some other constraint is met to force a stop, then return the final result.

Flow Engineering: Code Generation with AlphaCodium: From Prompt Engineering to Flow Engineering (Github)

A test-based, multi-stage, code-oriented iterative flow, that improves the performances of LLMs on code problems.

Agent Frameworks

CrewAI: AI Agents for real use cases

Most AI agent frameworks are hard to use. We provide power with simplicity. Automate your most important workflows quickly.

LangChain: Agents

Build the right cognitive architecture for your application. Identify and implement the best prompting strategies 
and architectures so that your LLMs perform as intended.

Let's build an Agent

What's needed to build an LLM Powered Agent?

  • Context: Information for the Agent (Role, Goal, Initial Data etc)

  • Prompt: Message in Natural language to address the Agent (Text, Audio etc)

  • Python: Code the magic using Instructor

  • Read: Do more reading to learn more about Agents and code away

Information Retrieval vs Data Generation vs Function Calling with the Python Instructor library

So far, we have been using Instructor for both Information Retrieval and Data Generation as Structured Data. We will now dig deeper into the more advanced Function Calling Capabilities of the Python Instructor library. For scratpad, we'll use Marimo Notebooks.

So where's the A?

An agent is anything that can be viewed as perceiving its environment through sensors and Sensor acting upon that environment through actuators -- Stuart Russel and Peter Norvig.

Borrowed from Artificial Intelligence: A Modern Approach Fourth Edition

Percepts


class SystemMessage(BaseModel):
    role: Literal["system"] = "system"
    content: str

class UserMessage(BaseModel):
    role: Literal["user"] = "user"
    content: str

class AssistantMessage(BaseModel):
    role: Literal["assistant"] = "assistant"
    content: str


percept_seq: List[Union[SystemMessage, UserMessage, AssistantMessage]] = []

Table of actions


class DefaultFunc(OpenAISchema):
    response: str
    def run(self):
        msg = f"DefaultFunc->run: {self.response}"
        return msg

class UserFunc(OpenAISchema):
    name: str
    age: int

    def run(self):
        msg = f"UserFunc->run: User's name is {self.name} and age is {self.age}"
        return msg

toolbox = [DefaultFunc, UserFunc]

Actions and Actuator


def actuate(self, tool_call: ChatCompletionMessageToolCall):
        Func = next(iter([func for func in toolbox if func.__name__ == tool_call.function.name]))

        if not Func:
            available_function_names = [func.__name__ for func in toolbox]
            err_msg = f"Error: Function {tool_call.function.name} not found. Available functions: {available_function_names}"
            console.error(err_msg)
            return err_msg

        try:
            console.log(f"Tool Call -> {tool_call.function.name} ->with {tool_call.function.arguments} of type {type(tool_call.function.arguments)}", style="bold blue")
            args = from_json(tool_call.function.arguments)
            console.log(f"Args -> {args} of type {type(args)}", style="bold blue")
            func = Func.model_validate(args)
            console.log(f"Func -> {repr(func)}", style="bold blue")
            output = func.run()
            return output
        except Exception as e:
            return f"Error: {e}"

An attempt at Agent Design

This diagram helps visualize the flow of actions and thoughts through the agents, ensuring a structured and iterative approach to reaching the final outcome.


graph TD
    A[Start: Think about the prompt] --> B[Derive inputs]
    B --> T1{Toolbox}
    T1 -->|Tool 1| T2[Think about results of Tool 1]
    T1 -->|Tool 2| T3[Think about results of Tool 2]
    T1 -->|Tool 3| T4[Think about results of Tool 3]
    T1 -->|Tool 4| T5[Think about results of Tool 4]
    T1 -->|Tool 5| T6[Think about results of Tool 5]

    T2 --> T7{Toolbox}
    T3 --> T7
    T4 --> T7
    T5 --> T7
    T6 --> T7

    T7 -->|Tool 1| T8[Think about results of Tool 1]
    T7 -->|Tool 2| T9[Think about results of Tool 2]
    T7 -->|Tool 3| T10[Think about results of Tool 3]
    T7 -->|Tool 4| T11[Think about results of Tool 4]
    T7 -->|Tool 5| T12[Think about results of Tool 5]

    T8 --> T13{Toolbox}
    T9 --> T13
    T10 --> T13
    T11 --> T13
    T12 --> T13

    T13 -->|Tool 1| T14[Final result from Tool 1]
    T13 -->|Tool 2| T15[Final result from Tool 2]
    T13 -->|Tool 3| T16[Final result from Tool 3]
    T13 -->|Tool 4| T17[Final result from Tool 4]
    T13 -->|Tool 5| T18[Final result from Tool 5]

    T14 --> F[Final Result]
    T15 --> F
    T16 --> F
    T17 --> F
    T18 --> F

    style A fill:#f9f,stroke:#333,stroke-width:2px;
    style B fill:#bbf,stroke:#333,stroke-width:2px;
    style T1 fill:#bfb,stroke:#333,stroke-width:2px;
    style T2 fill:#ff9,stroke:#333,stroke-width:2px;
    style T3 fill:#ff9,stroke:#333,stroke-width:2px;
    style T4 fill:#ff9,stroke:#333,stroke-width:2px;
    style T5 fill:#ff9,stroke:#333,stroke-width:2px;
    style T6 fill:#ff9,stroke:#333,stroke-width:2px;
    style T7 fill:#bfb,stroke:#333,stroke-width:2px;
    style T8 fill:#ff9,stroke:#333,stroke-width:2px;
    style T9 fill:#ff9,stroke:#333,stroke-width:2px;
    style T10 fill:#ff9,stroke:#333,stroke-width:2px;
    style T11 fill:#ff9,stroke:#333,stroke-width:2px;
    style T12 fill:#ff9,stroke:#333,stroke-width:2px;
    style T13 fill:#bfb,stroke:#333,stroke-width:2px;
    style T14 fill:#f99,stroke:#333,stroke-width:2px;
    style T15 fill:#f99,stroke:#333,stroke-width:2px;
    style T16 fill:#f99,stroke:#333,stroke-width:2px;
    style T17 fill:#f99,stroke:#333,stroke-width:2px;
    style T18 fill:#f99,stroke:#333,stroke-width:2px;
    style F fill:#9f9,stroke:#333,stroke-width:2px;

Explanation

  • Start: Think about the prompt: The process begins with the agent considering the given prompt.

  • Derive inputs: The agent derives the necessary inputs to decide which tool(s) to use from the toolbox.

  • Toolbox: The agent has access to a toolbox containing 5 different tools. Based on the derived inputs, the agent selects a tool.

  • Think about results of Tool: After using the first tool, the agent thinks about the results it obtained.

  • Sequential tool invocation: The agent may decide to invoke a second and even a third tool based on the intermediate results.

  • Final result: Eventually, the agent yields the final result after processing through the necessary tools and intermediate thinking steps.

    #+begin_src python

from instructor import OpenAISchema

from getting_started_with_ai_agents.agents.club_bouncer import ( ClubBouncer, Person, Guest, ) from getting_started_with_ai_agents.llm import gen_client, SystemMessage, UserMessage

client = gen_client()

class ClubSecurity(OpenAISchema): """ Security agent for Club UbuntuTechHive. Helps the bouncer manage the line and check the guest list following the club's rules. if the person is at least 21 years old and has at least $20 in cash, they can enter the club. if the person is on the guest list, they can enter the club. if the person is a VIP, they must have at least $1000 in cash to get table service. """

name: str # name of the person age: int # age of the person cash: float # amount of cash the person has is_on_guest_list: bool # whether the person is on the guest list

def run(self): person = Person( name=self.name, age=self.age, cash=self.cash, is_on_guest_list=self.is_on_guest_list, ) return person

class ClubHost(OpenAISchema): """ Hostess for Club UbuntuTechHive. Collects the cover charge and assigns tickets to guests. Checks the guest list and assigns guests to the VIP line. Manages the VIPs and table service. Accepts VIPs with at least $1000 in cash and table service requests. Accepts payment for table service and assigns tables to VIPs. if the person has money for a ticket, let them buy the ticket and mark them as having a ticket using a wristband. if guest is on the guest list, mark them as present on the club's copy of the guest list and let them in. if guest has $1000 in cash, they are a VIP, mark them as VIP, then lead them to a free table. """

name: str # name of the person age: int # age of the person cash: float # amount of cash the person has has_ticket: bool = False # whether the person has a ticket is_on_guest_list: bool = False # whether the person is on the guest list is_vip: bool = False # whether the person is a VIP

def run(self): guest = Guest( name=self.name, age=self.age, cash=self.cash, has_ticket=self.has_ticket, is_on_guest_list=self.is_on_guest_list, is_vip=self.is_vip, ) return guest

bouncer = ClubBouncer( context=[ SystemMessage( content=""" Welcome to Club UbuntuTechHive! I am the Club Bouncer. I will be managing the line tonight for the club tonight. I will be deciding who can enter the club. My decisions are based on the following criteria:

  • The person must be at least 21 years old.

  • The person must have at least $20 in cash.

  • The person must have a ticket or be on the guest list.

  • There will be a $20 cover charge for those without a ticket or not on the guest list.

  • VIPs must have at least $1000 in cash to get table service.

  • Table service is available for VIPs only if there are a limited number of tables available.

  • When no tables are available, VIPs must wait on the VIP line for a table to become available. """ ) ], capacity=100, table_count=10, client=client, toolbox=[ClubSecurity, ClubHost],

)

if name == "__main__": bouncer.manage_line( UserMessage( content=""" The person is John Doe and is 25 years old with $50 in cash. They on the guest list. """ ) )

bouncer.manage_line( UserMessage( content=""" Lambert is 21 years old with $20 in cash. They want to purchase a ticket. """ ) )

#+end_src

Conclusion

This field is moving fast and there are few experts. Don't be afraid to read and learn and code to gain understanding. Then decide for yourself wether to adopt a library that suits your needs or make up your own!

References

["Python" "LLMs" "Agentic Search" "Human in the loop" "ReAct (Reason + Act)" "Self-Refine" "Flow Engineering" "Natural language processing" "Text generation" "Information retrieval"]