paint-brush
Wie ich einen KI-Programmierer zur Automatisierung der Behebung von GitHub-Problemen entwickelt habe 🤯von@sunilkumardash9
542 Lesungen
542 Lesungen

Wie ich einen KI-Programmierer zur Automatisierung der Behebung von GitHub-Problemen entwickelt habe 🤯

von Sunil Kumar Dash16m2024/08/01
Read on Terminal Reader

Zu lang; Lesen

Entdecken Sie, wie Sie einen KI-gestützten SWE-Agenten erstellen, um GitHub-Workflows mit Typescript zu automatisieren. Dieser Leitfaden behandelt die Einrichtung, Komponenten und Implementierung eines autonomen Agenten, der Aufgaben wie das Aktualisieren von Dokumentationen, das Beheben von Fehlern und das Bereitstellen von Patches übernehmen kann, sodass Entwickler sich auf kreativere Aufgaben konzentrieren können.
featured image - Wie ich einen KI-Programmierer zur Automatisierung der Behebung von GitHub-Problemen entwickelt habe 🤯
Sunil Kumar Dash HackerNoon profile picture


In den letzten Wochen haben wir fleißig an einem schnell wachsenden Repository bei Composio gearbeitet. Wir erkannten bald, dass Aufgaben wie das Aktualisieren von ReadMes, das Korrigieren von Docstrings und das Vornehmen kleinerer Fehlerbehebungen – obwohl repetitiv und banal – einen Großteil unserer Bandbreite beanspruchten.


Also dachte ich mir: Warum nicht einen KI-gestützten autonomen Agenten bauen, der diese Routinearbeiten übernimmt?

Wir wollten einen KI-Agenten, der das kann.


  • Greifen Sie auf das GitHub-Repository zu.
  • Nehmen Sie ein beliebiges Problem und schreiben Sie eine Lösung dafür.
  • Führen Sie den Code bei Bedarf aus, um zu überprüfen, ob er in Ordnung ist.
  • Pushen Sie abschließend die Patchdateien in das Remote-Repository.


Schließlich haben wir ein einfaches und erweiterbares Meta-Framework zum Erstellen von Software-Engineering-Agenten erstellt.

Diese Agenten können bei vielen dieser Aufgaben eine ähnliche Leistung erbringen wie ihre menschlichen Kollegen. Es ist sinnvoll, alltägliche Aufgaben an diese Agenten auszulagern, damit Ihre Entwickler sich auf kreativere Aufgaben konzentrieren können.


Jerry - Ein Lied


In diesem Artikel zeige ich Ihnen, wie Sie einen SWE-Agenten in Typescript erstellen, um Ihre GitHub-Workflows zu automatisieren.

Aber vorher wollen wir verstehen, was KI- und SWE-Agenten sind.


Was sind KI-Agenten?

KI-Agenten sind von KI-Modellen betriebene Systeme, die auf der Grundlage ihrer Programmierung und der von ihnen verarbeiteten Daten autonom Aufgaben ausführen, mit ihrer Umgebung interagieren und Entscheidungen treffen können.

Komponenten von KI-Agenten

Ein KI-Agent besteht aus drei entscheidenden Komponenten:

  • LLMs : Das Large Language Model ist für das Denken, die Entscheidungsfindung, den Aufruf von Tools usw. verantwortlich.
  • Speicher : Verwaltet kurzfristige und langfristige Informationen, um Arbeitsabläufe zu verfolgen.
  • Tools : Ermöglichen die Interaktion mit der externen Umgebung, beispielsweise einem GitHub-Tool zum Extrahieren von Informationen aus Repositories und zum Vornehmen der erforderlichen Änderungen.

Was sind SWE-Agenten?

Wann also bezeichnen Sie einen KI-Agenten als SWE-Agenten?

SWE-Agenten sind KI-Agenten, die die Qualitäten und Merkmale eines menschlichen Softwareentwicklers nachahmen, wie zum Beispiel

  • Langfristige Planung und Argumentation.
  • Verwenden von Standardentwicklertools.
  • Verbesserung der Codequalität durch Tests und Feedback.
  • Selbstständiges Debuggen und Lösen von Problemen.

Abonnieren

Übersicht SWE Agent

Hier sind einige der Eigenschaften des SWE-Agenten, den wir erstellen werden:

  • Es ist Framework-agnostisch, sodass Sie jedes Framework verwenden können, z. B. LangChain, OpenAI SDK usw.
  • Sie können Tools hinzufügen, um die Vielseitigkeit zu erweitern, beispielsweise Tavily für den Internetzugang.


Die SWE-Agenten können auf Ihre öffentlichen und privaten Repositories zugreifen, an bereitgestellten Problemen arbeiten und Änderungen an den Repositories übertragen.

Es kann Codes mithilfe des Hostcomputers, Docker oder einer anderen Cloud-Umgebung (E2B, FlyIo) ausführen. Für die Sandboxing-Codeausführung sollten Sie jedoch die beiden letzteren verwenden.


Sandboxing hilft, unbeabsichtigte Folgen der Ausführung willkürlichen Codes zu verhindern.


Voraussetzungen für SWE Agent

Dies sind die Voraussetzungen für die erfolgreiche Erstellung des Agenten:

  1. OpenAI API-Schlüssel: Wir verwenden OpenAI SDK, um auf GPT-Modelle zuzugreifen und Tool-Aufrufe zu orchestrieren.
  2. GitHub Access Token: Sie müssen Ihr GitHub-Konto mit einem Persönlicher Zugriffstoken um dem SWE-Agenten den Zugriff auf Ihr Code-Repository und dessen Änderung zu ermöglichen.
  3. Composio API-Schlüssel: Sie benötigen außerdem einen API-Schlüssel von Composio. Um einen zu erhalten, erstellen Sie einen Benutzer Konto mit Composio und navigieren Sie zur Registerkarte „Einstellungen“ im Dashboard.

Loginseite


Lass uns anfangen 🔥

Abhängigkeiten

Beginnen Sie mit der Installation von Abhängigkeiten mit Ihrem bevorzugten Paketmanager. Die empfohlene Methode ist pnpm , Sie können aber auch npm oder yarn verwenden.

 pnpm install -g composio-core

Umgebungsvariablen einrichten 🌐

Sie benötigen ein GITHUB_ACCESS_TOKEN, OPENAI_API_KEY, COMPOSIO_API_KEY, GITHUB_USERNAME und GITHUB_USER_EMAIL, um das Projekt abzuschließen.


Erstellen Sie also eine .env Datei und fügen Sie die oben genannten Variablen hinzu.

 GITHUB_ACCESS_TOKEN="your access token" OPENAI_API_KEY="openai_key" COMPOSIO_API_KEY="composio-api-key" GITHUB_USER_NAME="GitHub username" GITHUB_USER_EMAIL="GitHub user email"

Projektstruktur 📁

Das Projekt ist wie folgt organisiert:

Quelle
├── Agenten
│ └── swe.ts
├── app.ts
├── prompts.ts
└── utils.ts

Hier ist eine kurze Beschreibung der Dateien.

  • agents/swe.ts : Enthält die Implementierung des SWE-Agenten.
  • app.ts : Der Haupteinstiegspunkt der Anwendung.
  • prompts.ts : Definiert die von den Agenten verwendeten Eingabeaufforderungen.
  • utils.ts : Im gesamten Projekt verwendete Dienstprogrammfunktionen.

Um schnell zu beginnen, klonen Sie dieses Repository und installieren Sie die restlichen Abhängigkeiten.

 git clone https://github.com/ComposioHQ/swe-js-template.git swe-js cd swe-js && pnpm i


Nachdem Sie nun die gesamte Einrichtung abgeschlossen haben, können wir unseren KI-Agenten codieren.


Programmierer Pepe



Definieren von Eingabeaufforderungen und Zielen 🎯

Wir beginnen mit der Definition der Eingabeaufforderungen und Ziele für den SWE-Agenten. Es ist wichtig, jeden Schritt im Detail zu erklären, da diese Definitionen die Leistung und Ausführung des Agenten erheblich beeinflussen.

Erstellen Sie also eine Datei prompts.ts , falls Sie dies noch nicht getan haben.

Definieren Sie als Nächstes die Rolle und das Ziel des Agenten.

 export const ROLE = "Software Engineer"; export const GOAL = "Fix the coding issues given by the user, and finally generate a patch with the newly created files using `filetool_git_patch` tool";


Hier haben wir die Rolle als SWE definiert und das Ziel ist es, alle Codierungsprobleme zu beheben und mit filetool_git_patch einen Patch für die Korrektur zu erstellen. Dies ist eine Compsoio-Aktion für die GitHub-Integration zum Erstellen von Patchdateien.

Definieren Sie nun die Hintergrundgeschichte und eine Beschreibung des Swe-Agenten.

 export const BACKSTORY = `You are an autonomous programmer; your task is to solve the issue given in the task with the tools in hand. Your mentor gave you the following tips. 1. Please clone the github repo using the 'FILETOOL_GIT_CLONE' tool, and if it already exists - you can proceed with the rest of the instructions after going into the directory using \`cd\` shell command. 2. PLEASE READ THE CODE AND UNDERSTAND THE FILE STRUCTURE OF THE CODEBASE USING GIT REPO TREE ACTION. 3. POST THAT READ ALL THE RELEVANT READMES AND TRY TO LOOK AT THE FILES RELATED TO THE ISSUE. 4. Form a thesis around the issue and the codebase. Think step by step. Form pseudocode in case of large problems. 5. THEN TRY TO REPLICATE THE BUG THAT THE ISSUES DISCUSS. - If the issue includes code for reproducing the bug, we recommend that you re-implement that in your environment, and run it to make sure you can reproduce the bug. - Then start trying to fix it. - When you think you've fixed the bug, re-run the bug reproduction script to make sure that the bug has indeed been fixed. - If the bug reproduction script does not print anything when it is successfully runs, we recommend adding a print("Script completed successfully, no errors.") command at the end of the file so that you can be sure that the script indeed, it ran fine all the way through. 6. If you run a command that doesn't work, try running a different one. A command that did not work once will not work the second time unless you modify it! 7. If you open a file and need to get to an area around a specific line that is not in the first 100 lines, say line 583, don't just use the scroll_down command multiple times. Instead, use the goto 583 command. It's much quicker. 8. If the bug reproduction script requires inputting/reading a specific file, such as buggy-input.png, and you'd like to understand how to input that file, conduct a search in the existing repo code to see whether someone else has I've already done that. Do this by running the command find_file "buggy-input.png" If that doesn't work, use the Linux 'find' command. 9. Always make sure to look at the currently open file and the current working directory (which appears right after the currently open file). The currently open file might be in a different directory than the working directory! Some commands, such as 'create', open files, so they might change the currently open file. 10. When editing files, it is easy to accidentally specify a wrong line number or write code with incorrect indentation. Always check the code after You issue an edit to ensure it reflects what you want to accomplish. If it didn't, issue another command to fix it. 11. When you FINISH WORKING on the issue, USE THE 'filetool_git_patch' ACTION with the new files using the "new_file_paths" parameters created to create the final patch to be submitted to fix the issue. Example, if you add \`js/src/app.js\`, then pass \`new_file_paths\` for the action like below, { "new_file_paths": ["js/src/app.js"] } `; export const DESCRIPTION = `We're solving the following issue within our repository. Here's the issue text: ISSUE: {issue} REPO: {repo} Now, you're going to solve this issue on your own. When you're satisfied with all your changes, you can submit them to the code base by simply running the submit command. Note, however, that you cannot use any interactive session commands (eg python, vim) in this environment, but you can write scripts and run them. Eg you can write a Python script and then run it with \`python </path/to/script>.py\`. If you face a "module not found error", you can install dependencies. Example: in case the error is "pandas not found", install pandas like this \`pip install pandas\` Respond to the human as helpfully and accurately as possible`;

Im obigen Codeblock haben wir die Schritte, die der Agent ausführen muss, um die Aufgabe zu erfüllen, sorgfältig und klar definiert. Dies ist wichtig, um sicherzustellen, dass der Agent weiß, was zu tun ist, wenn er mit üblichen Programmierhürden konfrontiert wird.


Definieren von Hilfsfunktionen 🛠️

In diesem Abschnitt definieren wir zwei Hauptfunktionen, from GitHub und getBranchNameFromIssue , die Informationen zu einem Problem extrahieren.

 import * as fs from 'fs'; import * as path from 'path'; import * as readline from 'readline'; import { ComposioToolSet } from "composio-core/lib/sdk/base.toolset"; import { nanoid } from "nanoid"; type InputType = any; function readUserInput( prompt: string, metavar: string, validator: (value: string) => InputType ): InputType { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise<InputType>((resolve, reject) => { rl.question(`${prompt} > `, (value) => { try { const validatedValue = validator(value); rl.close(); resolve(validatedValue); } catch (e) { console.error(`Invalid value for \`${metavar}\` error parsing \`${value}\`; ${e}`); rl.close(); reject(e); } }); }); } function createGithubIssueValidator(owner: string, name: string, toolset: ComposioToolSet) { return async function githubIssueValidator(value: string): Promise<string> { const resolvedPath = path.resolve(value); if (fs.existsSync(resolvedPath)) { return fs.readFileSync(resolvedPath, 'utf-8'); } if (/^\d+$/.test(value)) { const responseData = await toolset.executeAction('github_issues_get', { owner, repo: name, issue_number: parseInt(value, 10), }); return responseData.body as string; } return value; }; } export async function fromGithub(toolset: ComposioToolSet): Promise<{ repo: string; issue: string }> { const owner = await readUserInput( 'Enter github repository owner', 'github repository owner', (value: string) => value ); const name = await readUserInput( 'Enter github repository name', 'github repository name', (value: string) => value ); const repo = `${owner}/${name.replace(",", "")}`; const issue = await readUserInput( 'Enter the github issue ID or description or path to the file containing the description', 'github issue', createGithubIssueValidator(owner, name, toolset) ); return { repo, issue }; }

Folgendes passiert im obigen Codeblock.

  • readUserInput : Diese Funktion liest Benutzereingaben von der Befehlszeile. Wir benötigen nur die GitHub-Benutzer-ID, den Repository-Namen und die Problemnummer oder -beschreibung.
  • createGithubIssueValidator : Diese Funktion gibt einen Validator für GitHub-Probleme zurück. Sie kann Eingaben als Dateipfad, numerische Problem-ID oder einfache Zeichenfolgenbeschreibung verarbeiten. Wenn die Eingabe eine numerische Problem-ID ist, ruft sie die Problemdetails mithilfe github_issues_get Aktion von Composio von GitHub ab.
  • fromGitHub : Diese Funktion kombiniert diese Elemente, um die erforderlichen Informationen zu einem GitHub-Repository und einem Problem zu sammeln und zu validieren.

Definieren Sie nun getBranchNameFromIssue , um aus der Problembeschreibung einen Branch-Namen zu erstellen.

 export function getBranchNameFromIssue(issue: string): string { return "swe/" + issue.toLowerCase().replace(/\s+/g, '-') + "-" + nanoid(); }

Definition des Swe-Agenten 🤖

Dies ist der wichtigste Abschnitt, in dem Sie den Swe-Agenten mithilfe der OpenAI-Assistenten und Composio-Toolsets definieren.

Importieren Sie also zuerst die Bibliotheken und definieren Sie LLM und Tools.

 import { OpenAIToolSet, Workspace } from 'composio-core'; import { BACKSTORY, DESCRIPTION, GOAL } from '../prompts'; import OpenAI from 'openai'; // Initialize tool. const llm = new OpenAI({ apiKey: process.env.OPENAI_API_KEY, }); const composioToolset = new OpenAIToolSet({ workspaceConfig: Workspace.Docker({}) }); // To use E2B Code interpreter /* const composioToolset = new OpenAIToolSet({ workspaceConfig: Workspace.E2B({ apiKey: process.env.E2B_API_KEY, }) }); */

Im obigen Codeblock

  • Wir haben mit dem API-Schlüssel eine Instanz von OpenAI erstellt.
  • Wir haben auch eine Instanz von OpenAIToolSet erstellt, bei der workspaceConfig auf Docker eingestellt ist. Dies dient dazu, Docker zu verwenden, um die Codierungsumgebung für den Swe-Agenten in einer Sandbox zu betreiben. Sie können auch Cloud-Code-Interpreter wie E2B und FlyIo verwenden.

Jetzt definieren wir den Swe-Agenten.

 export async function initSWEAgent(): Promise<{composioToolset: OpenAIToolSet; assistantThread: OpenAI.Beta.Thread; llm: OpenAI; tools: Array<any>}> { let tools = await composioToolset.getTools({ apps: [ "filetool", "fileedittool", "shelltool" ], }); tools = tools.map((a) => { if (a.function?.description?.length || 0 > 1024) { a.function.description = a.function.description?.substring(0, 1024); } return a; }); tools = tools.map((tool) => { const updateNullToEmptyArray = (obj) => { for (const key in obj) { if (obj[key] === null) { obj[key] = []; } else if (typeof obj[key] === 'object' && !Array.isArray(obj[key])) { updateNullToEmptyArray(obj[key]); } } }; updateNullToEmptyArray(tool); return tool; }); const assistantThread = await llm.beta.threads.create({ messages: [ { role: "assistant", content:`${BACKSTORY}\n\n${GOAL}\n\n${DESCRIPTION}` } ] }); return { assistantThread, llm, tools, composioToolset }; }

Folgendes passiert im obigen Codeblock.

  • Tools abrufen : Ruft Tools aus dem Composio-Toolset für filetool , file edit tool und shelltool ab. Wie der Name schon sagt, werden diese verwendet, um auf Dateien zuzugreifen, Dateien zu bearbeiten und die Shell zum Ausführen von Befehlen zu verwenden.
  • Werkzeugbeschreibungen kürzen : Beschränkt Werkzeugbeschreibungen auf maximal 1024 Zeichen.
  • Nullwerte aktualisieren : Ersetzt Nullwerte in Toolkonfigurationen durch leere Arrays.
  • Assistent-Thread erstellen : Initiiert einen OpenAI-Assistent-Thread mit vordefinierten Eingabeaufforderungen.
  • Rückgabeanweisung : Stellt die initialisierten Tools, den Assistenzthread, die OpenAI-Instanz und das Composio-Toolset bereit.

Definieren des Einstiegspunkts zur Anwendung 🚀

Dies ist der letzte Abschnitt, in dem wir den Einstiegspunkt der Anwendung definieren. Laden Sie daher die Umgebungsvariablen und importieren Sie die erforderlichen Module.

 import dotenv from "dotenv"; dotenv.config(); import { fromGithub, getBranchNameFromIssue } from './utils'; import { initSWEAgent } from './agents/swe'; import { GOAL } from './prompts';

Der Codeblock

  • Lädt Umgebungsvariablen.
  • Importiert die notwendigen Hilfsfunktionen.
  • Importiert den Swe-Agenten und das Agentenziel, die wir zuvor definiert haben.

Definieren Sie nun die main .

 async function main() { /**Run the agent.**/ const { assistantThread, llm, tools, composioToolset } = await initSWEAgent(); const { repo, issue } = await fromGithub(composioToolset); const assistant = await llm.beta.assistants.create({ name: "SWE agent", instructions: GOAL + `\nRepo is: ${repo} and your goal is to ${issue}`, model: "gpt-4o", tools: tools }); await llm.beta.threads.messages.create( assistantThread.id, { role: "user", content: issue } ); const stream = await llm.beta.threads.runs.createAndPoll(assistantThread.id, { assistant_id: assistant.id, instructions: `Repo is: ${repo} and your goal is to ${issue}`, tool_choice: "required" }); await composioToolset.waitAndHandleAssistantToolCalls(llm as any, stream, assistantThread, "default"); const response = await composioToolset.executeAction("filetool_git_patch", { }); if (response.patch && response.patch?.length > 0) { console.log('=== Generated Patch ===\n' + response.patch, response); const branchName = getBranchNameFromIssue(issue); const output = await composioToolset.executeAction("SHELL_EXEC_COMMAND", { cmd: `cp -r ${response.current_working_directory} git_repo && cd git_repo && git config --global --add safe.directory '*' && git config --global user.name ${process.env.GITHUB_USER_NAME} && git config --global user.email ${process.env.GITHUB_USER_EMAIL} && git checkout -b ${branchName} && git commit -m 'feat: ${issue}' && git push origin ${branchName}` }); // Wait for 2s await new Promise((resolve) => setTimeout(() => resolve(true), 2000)); console.log("Have pushed the code changes to the repo. Let's create the PR now", output); await composioToolset.executeAction("GITHUB_PULLS_CREATE", { owner: repo.split("/")[0], repo: repo.split("/")[1], head: branchName, base: "master", title: `SWE: ${issue}` }) console.log("Done! The PR has been created for this issue in " + repo); } else { console.log('No output available - no patch was generated :('); } await composioToolset.workspace.close(); } main();


Dies ist unsere vollständige app.ts Datei, die zum Ausführen des Agenten-Workflows verwendet wird.


Folgendes passiert im obigen Code.

  • SWE-Agent initialisieren : Ruft initSWEAgent auf, um den Assistenten-Thread, die OpenAI-Instanz, die Tools und das Composio-Toolset abzurufen.
  • Repository und Problem abrufen : Ruft Repository- und Problemdetails von fromGithub ab.
  • Assistent erstellen : Initialisiert einen OpenAI-Assistenten mit Anweisungen, Tools und dem Sprachmodell.
  • Problem an Assistenten senden : Sendet den Probleminhalt als Nachricht an den Assistenten-Thread.
  • Assistent ausführen und Umfrage durchführen : Führt den Assistenten aus und führt Umfragen für Antworten auf Tool-Aufrufe durch. Weitere Informationen zu Umfrageantworten finden Sie im OpenAI SDK-Repository.
  • Patch-Aktion ausführen : Führt filetool_git_patch aus, um einen Patch zu generieren.
  • Patch-Antwort verarbeiten : Wenn ein Patch generiert wird, protokollieren Sie ihn, erstellen Sie einen Zweig, führen Sie ein Commit durch und pushen Sie die Änderungen. Warten Sie 2 Sekunden, bevor Sie eine Pull-Anfrage erstellen. Erstellen Sie eine Pull-Anfrage auf GitHub.
  • Arbeitsbereich schließen : Schließt den Arbeitsbereich des Composio-Toolsets.
  • Hauptfunktion ausführen : Ruft main() auf, um die obigen Schritte auszuführen.


Führen Sie jetzt die Anwendung mit pnpm start aus.


Sie werden aufgefordert, die GitHub-Benutzer-ID, den Repository-Namen und die Problem-ID oder Beschreibung des Problems einzugeben, das Sie beheben möchten.

Sobald der Vorgang abgeschlossen ist, wird ein Composio-Docker-Container aus der Registrierung abgerufen und mit der Arbeit an dem Problem begonnen.


Wenn der Workflow abgeschlossen ist, wird der Patch schließlich in das Remote-Repository übertragen. Wenn Sie jetzt Ihr GitHub-Repository öffnen, sehen Sie einen neuen Zweig mit dem vorgeschlagenen Fix für das Problem. Sie können ihn mit dem Hauptzweig vergleichen und eine Pull-Anfrage erstellen.
Danke, Mike Scott

Den kompletten Code finden Sie hier auf GitHub .


Nächste Schritte ⏭️

Das Beste an diesem SWE-Agenten ist, dass Sie seine Funktionen mit Composio-Tools und -Integrationen erweitern können.

Sie können Ihrem Agenten Slack oder Discord hinzufügen, um benachrichtigt zu werden, wenn die Ausführung abgeschlossen ist. Sie können auch Jira oder Linear verbinden, um basierend auf den Aktivitäten des Agenten automatisch Aufgaben zu erstellen und zu aktualisieren.


Lass uns verbinden! 🔌

Sie können unserer Community beitreten, um mit den Betreuern zusammenzuarbeiten und als Open-Source-Entwickler mitzuwirken. Zögern Sie nicht, unser GitHub-Repository zu besuchen, um mitzuwirken und Probleme im Zusammenhang mit Composio. Dev zu melden.

Markieren Sie das Composio.dev-Repository mit einem Stern ⭐

Markieren Sie das Repo


Vielen Dank fürs Lesen!