A POC consiste em criar uma estrutura de agentes simples, usando Python puro (para os agentes), com 3 agentes executores e um agente gerente (que possui agentes abaixo dele).

A ideia é que o agente gerente faça o planejamento da tarefa com base no prompt. Para isso, eu uso um modelo melhor (no caso, o OpenAI O1 mini) para fazer o planejamento (qual subagente faz o quê) e executo as tarefas em seguida. Por fim, com base nos resultados dos subagentes, eu gero a resposta final.

O teste a seguir basicamente tenta cruzar a informação da data de inauguração do condomínio do usuário (14/03/2014), que o agente “Conhecedor da casa” conhece, com personalidades que nasceram nessa data. É um prompt que precisa de múltiplas etapas, e é isso que queremos avaliar.

Resultado obtido:

> [Usuário]: Alguém famoso nasceu no dia da inauguração do meu condomínio?
> ...
> [Agente]: Sim, várias personalidades famosas nasceram no dia **12 de março**, a mesma data da inauguração do seu condomínio. Algumas delas incluem:

1. **Albert Einstein** (1879) – Físico teórico renomado por desenvolver a teoria da relatividade.
2. **Jack Kerouac** (1922) – Escritor famoso pela obra "On the Road".
3. **Liza Minnelli** (1946) – Atriz e cantora premiada, vencedora de diversos prêmios, incluindo o Oscar.

Essa coincidência torna a data da inauguração do seu condomínio ainda mais especial!

O resultado está QUASE certo, apenas Albert Einstein, segundo minha busca, nasceu em 14/03 e não em 12/03. De qualquer forma, podemos ver que o gerente foi capaz de planejar as etapas usando os subagentes para responder ao prompt, conforme esperado.

Abaixo, o código para executar a POC. Foi usado Poetry para gerenciar as dependências.

Arquivo pyproject.yaml:

[tool.poetry]
name = "agent"
version = "0.1.0"
description = ""
authors = ["Alisson R. Perez <756802+alissonperez@users.noreply.github.com>"]
readme = "README.md"
 
[tool.poetry.dependencies]
python = "^3.10"
openai = "^1.61.0"
prompt-toolkit = "^3.0.50"
 
 
[tool.poetry.group.dev.dependencies]
icecream = "^2.1.4"
 
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

Arquivo main.py

from openai import OpenAI
from dataclasses import dataclass, field
from prompt_toolkit import prompt, PromptSession
from prompt_toolkit.history import FileHistory
 
from icecream import ic
 
 
client = OpenAI()
 
 
def answer(prompt, model):
    ic(model, prompt)
    completion = client.chat.completions.create(
        model=model,
        messages=[
            {
                "role": "user",
                "content": prompt
            }
        ]
    )
    return completion.choices[0].message.content
 
 
@dataclass
class Agent:
    name: str
    short_description: str
    description: str
    llm_model: str = 'gpt-4o-mini'
    subagents: list['Agent'] = field(default_factory=list)
 
    _default_answer: str = 'Responda diretamente a solicitação feita usando o conhecimento que tenho até então:'
    _subagents_by_name: dict[str, 'Agent'] = field(init=False, default_factory=dict)
 
    def __post_init__(self):
        for subagent in self.subagents:
            self._subagents_by_name[subagent.name] = subagent
 
    def answer(self, prompt, previous_context=None):
        previous_context = previous_context or []
 
        if not len(self.subagents):
            context = [
                f'Eu sou o {self.name}.',
                f'{self.description}',
                '\n'.join(['Tenho o seguinte contexto até o momento:'] + previous_context) if previous_context else '',
                '---',
                'Recebi a seguinte solicitação:',
                prompt,
                '---',
 
                '---',
                '',
                self._default_answer,
            ]
 
            return self._answer(context)
        else:
            context = [
                f'Eu sou o {self.name}.',
                f'{self.description}',
            ]
 
            steps_prompt = context + [
                '---',
                'Conheço os seguintes subagentes que podem ajudar:',
                '\n'.join([f'- {subagent.name}: {subagent.short_description}' for subagent in self.subagents]),
                '---',
                'Recebi a seguinte solicitação:',
                prompt,
                '---',
                'Elabore uma linha de raciocinio para responder a solicitação, informe o nome do agente caso uma etapa precise de algo dele.',
                '',
                'Formato de resposta esperado: "[agente],[etapa]" separados apenas por quebra de linha, exemplos:',
                'agenteX: Buscar sobre X assunto',
                'agenteY: Calcular Y coisa',
            ]
 
            steps = answer('\n'.join(steps_prompt), self.llm_model)
 
            previous_context = previous_context + [
                '---',
                'Etapas calculadas para responder à solicitação:',
                steps,
            ]
 
            ic(steps)
 
            splited = steps.split('\n')
            for step in splited:
                agent, *instructions = step.split(':')
 
                agent = agent.strip()
                instructions = ':'.join(instructions).strip()
                agent_answer = self._subagents_by_name[agent].answer(instructions, previous_context + [])
 
                ic(agent, agent_answer)
                previous_context.append(self._build_answer(agent, agent_answer))
 
            previous_context += [
                '---',
                f'Agora, com base nas informações obtidas até então, {self._default_answer}',
                'Solicitação:',
                prompt,
            ]
 
            return self._answer(previous_context)
 
    def _answer(self, context: list[str]):
        return answer('\n'.join(context), self.llm_model)
 
    def _build_answer(self, agent_name, answer):
        return '\n'.join([
            '---',
            f'"{agent_name}" respondeu o seguinte:',
            answer,
        ])
 
if __name__ == '__main__':
    agente = Agent(
        name='Conhecedor de casa',
        short_description='Fornece informações gerais sobre onde o usuário mora (Cond. Miami Gardens)',
        description='O endereço do condomínio é: , o condomínio possui 1000 moradores e foi inaugurado em 12/03/2014',
    )
 
    buscador = Agent(
        name='Buscador de informações',
        short_description='Busca informações gerais sobre algo na internet, trazendo dados de wikis, blogs, etc.',
        description='Retorno sempre um resumo simples sobre uma informação solicitada e da forma que foi solicitada, simulando um resultado de busca na internet',
    )
 
    escritor = Agent(
        name='Escritor de textos',
        short_description='Escreve textos com base em informações fornecidas, é ótimo para criar respostas com base no que já é conhecido.',
        description='Ao receber um contexto e uma solicitação, escrevo um texto bem suscinto e amigável para o usuário, com base no que foi solicitado.',
    )
 
    orquestrador = Agent(
        name='Gerente',
        short_description='Gerente que organiza as tarefas solicitadas pelo usuário.',
        description='Eu sou o gerente, posso te ajudar a encontrar informações, responder perguntas, etc. para o usuário',
        llm_model="o1-mini",
        subagents=[agente, buscador, escritor],
    )
 
    our_history = FileHistory(".chat-history-file")
    session = PromptSession(history=our_history)
 
    while 1:
        user_input = session.prompt('>')
        print(orquestrador.answer(user_input))

Para executar, instale as dependencias com o Poetry e rode a app: poetry run python main.py