Euquipe: o tão sonhado pesadelo está chegando! Você está preparado?

Luciano Santos Borges
8 min readMay 13, 2024

--

TL;DR: Se você chegou até aqui, é porque o título provavelmente despertou sua curiosidade. Não se preocupe, não estou aqui para vender susto 👻, mas sim para demonstrar como as ferramentas de IA, que eu costumava criticar e agora reconheço seu valor e potencial, podem ser úteis em suas atividades diárias. Neste artigo, vou apresentar como agentes inteligentes podem ser aliados em diversas tarefas.

https://br.freepik.com/vetores-gratis/conceito-de-multitarefa-masculino-adulto_6703745.htm#fromView=search&page=1&position=31&uuid=4a4d5a93-28a4-44f2-93d2-732e9f65e3a9

Como mencionei na minha série de artigos “Da Ideia ao MVP com Auxílio do ChatGPT”, precisei deixar de lado meus preconceitos em relação às ferramentas de IA como o ChatGPT, Groq, entre outras. Anteriormente, eu acreditava que elas não seriam úteis, porém estava completamente enganado. Na verdade, elas são extremamente úteis e, quando utilizadas corretamente, podem até mesmo substituir o trabalho de uma equipe inteira.

Recentemente, assisti a um vídeo no Canal Sandeco que abordava a ferramenta crewAI, e fiquei bastante entusiasmado em testá-la. De acordo com informações disponíveis no site da ferramenta, ela possibilita a orquestração de agentes de IA autônomos para lidar com tarefas complexas.

Como ainda não possuo a versão paga nem do ChatGPT nem do Groq, pretendo desenvolver uma estrutura mínima para evitar ficar limitado ao número de tokens disponíveis na versão gratuita do Groq.

No exemplo que apresentarei, vou simular uma equipe Scrum composta por pelo menos um Product Owner e dois desenvolvedores, um especialista em Java e outro em Angular. Para executar o script a seguir, utilizarei o Google Colab. É importante ressaltar que será necessário criar uma chave no Groq para este propósito.

!pip install --q crewai
!pip install --q langchain_groq
!pip install --q 'crewai[tools]'

import os
from crewai_tools import tools
from crewai import Agent, Task, Crew, Process
from langchain_groq import ChatGroq

llama3 = ChatGroq(
api_key="<cole_sua_chave_aqui>",
model="llama3-70b-8192"
)

#Agents
product_owner = Agent(
role='Product Owner',
goal='Definir a user story',
backstory="""Como Product Owner, estou profundamente comprometido em
entender as necessidades do usuário que quer manter um cadastro de
estados brasileiros (uf e descrição) em um banco de dados.
Com uma solida formação em Scrum, tenho experiência em trabalhar em
equipes multifuncionais na entrega de produtos de alta qualidade.
Meu objetivo é garantir que o produto atenda às expectativas do usuário.""",
llm=llama3,
max_iter=10,
max_rpm=50
)

java = Agent(
role='Desenvolvedor de Software Especialista em Java',
goal='Desenvolver a lógica de negócios utilizando Java.',
backstory="""Como Desenvolvedor de Software Especialista em Java, sou
apaixonado por escrever código limpo, eficiente e escalável. Com uma
sólida formação em ciência da computação e anos de experiência no
desenvolvimento de aplicativos empresariais, estou familiarizado com os
princípios de design e desenvolvimento de software.""",
llm=llama3,
max_iter=10,
max_rpm=50
)

angular = Agent(
role='Desenvolvedor de Software Especialista em Angular',
goal='Desenvolver e manter a interface do usuário da plataforma utilizando
o framework Angular.',
backstory="""Como Desenvolvedor de Software Especialista em Angular,
tenho uma paixão por criar interfaces de usuário modernas e responsivas.
Com amplo conhecimento em desenvolvimento front-end e experiência
significativa com o framework Angular, estou bem equipado para transformar
os requisitos em interfaces intuitivas e visualmente atraentes.""",
llm=llama3,
max_iter=10,
max_rpm=50
)

#Tasks
definir_user_story = Task(
description=f"""Definir a user story e seus critérios de aceitação.""",
expected_output=f"""User story com critério de aceitação""",
agent=product_owner
)

desenvolver_logica_de_negocios = Task(
description=f"""Escrever código Java seguindo padrão MVC para implementar
a user story.""",
expected_output=f"""Código fonte Java que implementa a API que permitirá
o frontend manter os estados.""",
agent=java
)

implementar_interface_do_usuario = Task(
description=f"""Desenvolver componentes de interface do usuário usando o
framework Angular com base nos requisitos fornecidos.""",
expected_output=f"""Componentes Angular funcionais que correspondem ao
requisito de interface do usuário.""",
agent=angular
)

integrar_com_o_backend = Task(
description=f"""Integrar os componentes front-end desenvolvidos com o
backend da aplicação, garantindo comunicação eficaz entre eles.""",
expected_output=f"""Front-end integrado com o backend, funcionalidades de
comunicação implementadas.""",
agent=angular
)

#Crew
equipe_scrum = Crew(
agents=[product_owner, java, angular],
tasks=[
definir_user_story,
desenvolver_logica_de_negocios,
implementar_interface_do_usuario,
integrar_com_o_backend
],
process=Process.sequential,
manager_llm=llama3,
full_output=True,
verbose=True
)

resultado = equipe_scrum.kickoff()
print(resultado)

Antes que os críticos comecem a atacar, gostaria de salientar que este é meu primeiro contato com essa ferramenta, apesar de achá-la extremamente promissora. Observo que muitas pessoas a utilizam para gerar conteúdo textual, mas raramente a vejo sendo empregada para auxiliar no trabalho de uma equipe de desenvolvimento. Por essa razão, decidi realizar uma simulação do cenário no qual estou inserido atualmente, com o intuito de identificar as vantagens que posso aproveitar em prol da minha equipe.

Os principais resultados produzidos pelo crewAI podem ser visualizados neste repositório. Aqui no artigo, vou focar apenas nos pontos mais relevantes.

A primeira coisa que podemos observar é a saída da tarefa “Definir a user story e seus critérios de aceitação” que o agente Product Owner ficou responsável.

**User Story:**
As a system administrator,
I want to store Brazilian states with their UF and description in a database,
so that I can easily manage and retrieve information about the states.

**Acceptance Criteria:**

1. The system shall store each Brazilian state with a unique identifier, UF,
and description.
* The unique identifier shall be a numeric value.
* The UF shall be a two-character code (e.g., SP for São Paulo).
* The description shall be a string that describes the state.
2. The system shall allow for the creation of new Brazilian states with
their respective UF and description.
* The system shall validate the input data to ensure that the UF
is a valid two-character code and the description is not empty.
3. The system shall allow for the retrieval of a list of all Brazilian states,
including their UF and description.
* The system shall return a list of states in alphabetical order by
state name.
4. The system shall allow for the retrieval of a specific Brazilian state
by its unique identifier, UF, or description.
* The system shall return the state's information, including its
unique identifier, UF, and description.
5. The system shall prevent duplicate states from being stored in the database.
* The system shall check for duplicate states based on the UF and description.

Logo em seguida é possível ver a saída da tarefa “Escrever código Java seguindo padrão MVC para implementar a user story” que ficou responsável pelo agente Desenvolver Java ficou responsável.

@Path("/brazilian-states")
public class BrazilianStatesAPI {

private Connection conn;

public BrazilianStatesAPI() {
// Initialize database connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
}

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getAllStates() {
List<State> states = new ArrayList<>();
try (PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM brazilian_states")) {
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
State state = new State(rs.getInt("id"), rs.getString("name"), rs.getString("abbreviation"));
states.add(state);
}
} catch (SQLException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
return Response.ok(states).build();
}

@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getStateById(@PathParam("id") int id) {
State state = null;
try (PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM brazilian_states WHERE id = ?")) {
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
state = new State(rs.getInt("id"), rs.getString("name"), rs.getString("abbreviation"));
}
} catch (SQLException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
return Response.ok(state).build();
}

@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createState(State state) {
try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO brazilian_states (name, abbreviation) VALUES (?, ?)")) {
pstmt.setString(1, state.getName());
pstmt.setString(2, state.getAbbreviation());
pstmt.executeUpdate();
} catch (SQLException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
return Response.ok().build();
}

@PUT
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public Response updateState(@PathParam("id") int id, State state) {
try (PreparedStatement pstmt = conn.prepareStatement("UPDATE brazilian_states SET name = ?, abbreviation = ? WHERE id = ?")) {
pstmt.setString(1, state.getName());
pstmt.setString(2, state.getAbbreviation());
pstmt.setInt(3, id);
pstmt.executeUpdate();
} catch (SQLException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
return Response.ok().build();
}

@DELETE
@Path("/{id}")
public Response deleteState(@PathParam("id") int id) {
try (PreparedStatement pstmt = conn.prepareStatement("DELETE FROM brazilian_states WHERE id = ?")) {
pstmt.setInt(1, id);
pstmt.executeUpdate();
} catch (SQLException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage()).build();
}
return Response.ok().build();
}
}

class State {
private int id;
private String name;
private String abbreviation;

// getters and setters
}

Embora o código fornecido esteja incompleto e não siga as boas práticas recomendadas, e também não faça uso de especificações como JPA, é inegável o potencial da ferramenta. Acredito que, ao investirmos tempo para aprender a escrever bons agentes e a definir boas tarefas, provavelmente obteremos uma resposta mais próxima do que esperamos para uma aplicação Java moderna.

Por fim, podemos ver a saída da tarefa “Desenvolver componentes de interface do usuário usando o framework Angular com base nos requisitos fornecidos” que ficou responsável pelo agente Desenvolver Angular.

**app.component.ts**

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
selector: 'app-root',
template: `
<h1>Brazilian States</h1>
<ul>
<li *ngFor="let state of states">
{{ state.name }} ({{ state.uf }})
</li>
</ul>
<form (ngSubmit)="createState()">
<input type="text" [(ngModel)]="newState.name">
<input type="text" [(ngModel)]="newState.uf">
<button type="submit">Create State</button>
</form>
`,
})
export class AppComponent implements OnInit {
states: BrazilianState[] = [];
newState: BrazilianState = new BrazilianState(null, '', '', '');

constructor(private http: HttpClient) {}

ngOnInit(): void {
this.http.get<BrazilianState[]>('https://api.estados-brasileiros.com/v1/estados')
.subscribe(states => this.states = states);
}

createState(): void {
this.http.post<BrazilianState>('https://api.estados-brasileiros.com/v1/estados', this.newState)
.subscribe(state => this.states.push(state));
}

updateState(state: BrazilianState): void {
this.http.put<BrazilianState>(`https://api.estados-brasileiros.com/v1/estados/${state.uf}`, state)
.subscribe(() => console.log(`State updated: ${state.name}`));
}

deleteState(state: BrazilianState): void {
this.http.delete(`https://api.estados-brasileiros.com/v1/estados/${state.uf}`)
.subscribe(() => this.states = this.states.filter(s => s !== state));
}
}

interface BrazilianState {
id: number;
uf: string;
name: string;
region: string;
}


**app.module.ts**

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';

@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, HttpClientModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}

Ao contrário do código Java, este agente demonstra uma separação do código em arquivos. Embora não tenha executado o código, podemos ver que o template da página consiste em uma lista de estados e um formulário para cadastro, além dos métodos para as principais operações. No entanto, observei a falta do código para recuperar um único estado.

Minha visão é que não devemos ignorar as tecnologias de IA. Essas ferramentas podem nos ajudar a lidar com as tarefas mais tediosas do dia a dia, liberando tempo para nos dedicarmos às atividades que nos desafiam de verdade. Estou vendo o uso desses agentes em diversas áreas, especialmente no campo do marketing e geração de copies. Por isso, neste artigo, procurei trazer um cenário semelhante ao que eu vivo. Até agora, minha conclusão é que essas ferramentas podem ser extremamente úteis no desenvolvimento de software. No entanto, acredito que substituir completamente o papel do desenvolvedor ainda está longe de ser uma realidade. Ou talvez não! 😬

Espero que este artigo tenha sido útil para você, oferecendo uma visão interessante sobre como as ferramentas de IA podem ser aproveitadas no desenvolvimento de software. Se você gostou do conteúdo e achou útil, ficaria muito grato se pudesse deixar um “clap” aqui no Medium para que mais pessoas possam descobri-lo. Ficaria muito feliz em tê-lo como parte da minha rede de conexões no LinkedIn. Juntos, podemos continuar compartilhando conhecimento e experiências. Espero conectar-me em breve!

Da Ideia ao MVP com Auxílio do ChatGPT

10 stories

--

--