paint-brush
Cách tôi xây dựng một lập trình viên AI để tự động sửa lỗi GitHub 🤯từ tác giả@sunilkumardash9
486 lượt đọc
486 lượt đọc

Cách tôi xây dựng một lập trình viên AI để tự động sửa lỗi GitHub 🤯

từ tác giả Sunil Kumar Dash16m2024/08/01
Read on Terminal Reader

dài quá đọc không nổi

Khám phá cách tạo tác nhân SWE được hỗ trợ bởi AI để tự động hóa quy trình công việc GitHub bằng Typescript. Hướng dẫn này đề cập đến việc thiết lập, các thành phần và triển khai một tác nhân tự trị có thể xử lý các tác vụ như cập nhật tài liệu, sửa lỗi và đưa ra các bản vá, giúp nhà phát triển có thể tập trung vào công việc sáng tạo hơn.
featured image - Cách tôi xây dựng một lập trình viên AI để tự động sửa lỗi GitHub 🤯
Sunil Kumar Dash HackerNoon profile picture


Trong vài tuần qua, chúng tôi đã làm việc chăm chỉ trên kho lưu trữ đang phát triển nhanh chóng tại Composio . Chúng tôi sớm nhận ra rằng các tác vụ như cập nhật ReadMe, sửa chuỗi tài liệu và sửa các lỗi nhỏ—mặc dù lặp đi lặp lại và nhàm chán—đang tiêu tốn nhiều băng thông của chúng tôi.


Vì vậy, tôi nghĩ, tại sao không xây dựng một tác nhân tự trị được hỗ trợ bởi AI để xử lý những công việc khó khăn này?

Chúng tôi muốn một đặc vụ AI có thể làm được điều đó.


  • Truy cập kho lưu trữ GitHub.
  • Hãy lấy bất kỳ vấn đề nào và viết ra giải pháp cho nó.
  • Nếu cần, hãy chạy mã để kiểm tra xem nó có tốt không.
  • Cuối cùng, đẩy các tập tin vá vào kho lưu trữ từ xa.


Cuối cùng, chúng tôi đã xây dựng một siêu khung đơn giản và có thể mở rộng để xây dựng các tác nhân kỹ thuật phần mềm.

Các tác nhân này có thể thực hiện tương tự như các tác nhân con người trong nhiều nhiệm vụ như vậy. Việc giảm tải các nhiệm vụ nhàm chán cho các tác nhân này là điều hợp lý để giải phóng các nhà phát triển của bạn để tập trung vào các nhiệm vụ sáng tạo hơn.


jerry


Trong bài viết này, tôi sẽ chỉ cho bạn cách xây dựng tác nhân SWE trong Typescript để tự động hóa quy trình công việc GitHub của bạn.

Nhưng trước đó, chúng ta hãy hiểu ngay cả đặc vụ AI và SWE là gì.


Đại lý AI là gì?

Tác nhân AI là các hệ thống được hỗ trợ bởi các mô hình AI có thể tự động thực hiện các nhiệm vụ, tương tác với môi trường của chúng và đưa ra quyết định dựa trên chương trình và dữ liệu chúng xử lý.

Các thành phần của Tác nhân AI

Một tác nhân AI bao gồm ba thành phần quan trọng:

  • LLM : Mô hình ngôn ngữ lớn chịu trách nhiệm lý luận, ra quyết định, gọi công cụ, v.v.
  • Bộ nhớ : Quản lý thông tin ngắn hạn và dài hạn để theo dõi quy trình công việc.
  • Công cụ : Cho phép tương tác với môi trường bên ngoài, chẳng hạn như công cụ GitHub để trích xuất thông tin từ kho lưu trữ và thực hiện các thay đổi cần thiết.

Đại lý SWE là gì?

Vậy khi nào bạn gọi một đại lý AI là đại lý SWE?

Tác nhân SWE là tác nhân AI bắt chước những phẩm chất và đặc điểm của kỹ sư phần mềm con người, chẳng hạn như

  • Lập kế hoạch và lý luận dài hạn.
  • Sử dụng các công cụ dành cho nhà phát triển tiêu chuẩn.
  • Cải thiện chất lượng mã thông qua thử nghiệm và phản hồi.
  • Gỡ lỗi và giải quyết vấn đề một cách tự động.

skit

Tổng quan Đại lý SWE

Dưới đây là một số đặc điểm của tác nhân SWE mà chúng tôi sẽ xây dựng:

  • Đó là bất khả tri về khung, vì vậy bạn có thể sử dụng bất kỳ khung nào, chẳng hạn như LangChain, OpenAI SDK, v.v.
  • Bạn có thể thêm các công cụ để mở rộng tính linh hoạt của nó, chẳng hạn như Tavily để truy cập internet.


Các đại lý SWE có thể truy cập vào kho lưu trữ công khai và riêng tư của bạn, xử lý các vấn đề được cung cấp và đẩy các thay đổi vào kho lưu trữ.

Nó có thể thực thi mã bằng máy chủ, Docker hoặc bất kỳ môi trường đám mây nào khác (E2B, FlyIo). Tuy nhiên, sẽ tốt nhất nếu bạn thích sử dụng hai cái sau để thực thi mã hộp cát.


Hộp cát giúp ngăn chặn mọi hậu quả không mong muốn của việc thực thi mã tùy ý.


Điều kiện tiên quyết cho Đại lý SWE

Dưới đây là những điều kiện tiên quyết để xây dựng thành công tác nhân:

  1. Khóa API OpenAI: Chúng tôi sẽ sử dụng OpenAI SDK để truy cập các mô hình GPT và điều phối các lệnh gọi công cụ.
  2. Mã thông báo truy cập GitHub: Bạn phải liên kết tài khoản GitHub của mình bằng mã thông báo mã thông báo truy cập cá nhân để cho phép tác nhân SWE truy cập và sửa đổi kho lưu trữ mã của bạn.
  3. Khóa API Composio: Bạn cũng sẽ cần khóa API từ Composio. Để có được một cái, hãy tạo một người dùng tài khoản bằng Composio và điều hướng đến tab Cài đặt trên trang tổng quan.

trang đăng nhập


Hãy bắt đầu 🔥

phụ thuộc

Bắt đầu bằng cách cài đặt các phần phụ thuộc bằng trình quản lý gói yêu thích của bạn. Phương pháp được đề xuất là pnpm , nhưng bạn cũng có thể sử dụng npm hoặc yarn .

 pnpm install -g composio-core

Thiết lập biến môi trường 🌐

Bạn sẽ cần GITHUB_ACCESS_TOKEN, OPENAI_API_KEY, COMPOSIO_API_KEY, GITHUB_USERNAME và GITHUB_USER_EMAIL để hoàn thành dự án.


Vì vậy, hãy tạo tệp .env và thêm các biến trên.

 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"

Cấu trúc dự án 📁

Dự án được tổ chức như sau:

src
├── đại lý
│ └── thật tuyệt vời
├── ứng dụng.ts
├── nhắc nhở.ts
└── utils.ts

Đây là mô tả ngắn gọn về các tập tin.

  • Agent/swe.ts : Chứa việc triển khai tác nhân SWE.
  • app.ts : Điểm vào chính của ứng dụng.
  • Lời nhắc.ts : Xác định lời nhắc được sử dụng bởi các tác nhân.
  • utils.ts : Các hàm tiện ích được sử dụng xuyên suốt dự án.

Để bắt đầu nhanh chóng, hãy sao chép kho lưu trữ này và cài đặt phần phụ thuộc còn lại.

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


Bây giờ bạn đã hoàn thành toàn bộ thiết lập. Hãy mã hóa tác nhân AI của chúng ta.


lập trình viên



Xác định lời nhắc và mục tiêu 🎯

Chúng tôi bắt đầu bằng việc xác định các lời nhắc và mục tiêu cho tác nhân SWE. Việc giải thích chi tiết từng bước là rất quan trọng vì những định nghĩa này ảnh hưởng đáng kể đến hiệu suất và việc thực thi của tác nhân.

Vì vậy, hãy tạo tệp prompts.ts nếu bạn chưa làm như vậy.

Tiếp theo, xác định vai trò và mục tiêu của tác nhân.

 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";


Ở đây, chúng tôi đã xác định vai trò là SWE và mục tiêu là khắc phục mọi sự cố mã hóa và tạo bản vá để khắc phục bằng cách sử dụng filetool_git_patch . Đây là Compsoio Action để tích hợp GitHub nhằm tạo các tệp bản vá.

Bây giờ, hãy xác định cốt truyện và mô tả về đặc vụ Swe.

 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`;

Trong khối mã trên, chúng tôi đã xác định cẩn thận và rõ ràng các bước mà tác nhân cần thực hiện để hoàn thành nhiệm vụ. Điều này rất quan trọng để đảm bảo tổng đài viên biết phải làm gì khi gặp phải những trở ngại lập trình thông thường.


Định nghĩa các hàm tiện ích 🛠️

Trong phần này, chúng ta sẽ xác định hai hàm chính, from GitHubgetBranchNameFromIssue , sẽ trích xuất thông tin về một vấn đề.

 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 }; }

Vì vậy, đây là những gì đang diễn ra trong khối mã trên.

  • readUserInput : Hàm này đọc đầu vào của người dùng từ dòng lệnh. Chúng tôi chỉ cần ID người dùng GitHub, tên kho lưu trữ và số phát hành hoặc mô tả.
  • createGithubIssueValidator : Hàm này trả về trình xác thực cho các sự cố GitHub. Nó có thể xử lý đầu vào dưới dạng đường dẫn tệp, ID vấn đề dạng số hoặc mô tả chuỗi đơn giản. Nếu thông tin đầu vào là ID vấn đề dạng số thì nó sẽ tìm nạp chi tiết vấn đề từ GitHub bằng hành động github_issues_get của Composio.
  • fromGitHub : Hàm này kết hợp các thành phần này để thu thập và xác thực thông tin cần thiết về kho lưu trữ GitHub và một vấn đề.

Bây giờ, hãy xác định getBranchNameFromIssue để tạo tên nhánh từ mô tả vấn đề.

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

Định nghĩa Swe Agent 🤖

Đây là phần quan trọng nhất, nơi bạn sẽ xác định tác nhân Swe bằng cách sử dụng bộ công cụ Composio và trợ lý OpenAI.

Vì vậy, trước tiên, hãy nhập các thư viện và xác định LLM cũng như các công cụ.

 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, }) }); */

Trong khối mã trên,

  • Chúng tôi đã tạo một phiên bản OpenAI bằng khóa API.
  • Chúng tôi cũng đã tạo một phiên bản OpenAIToolSet với workspaceConfig được đặt thành Docker. Mục đích là sử dụng Docker để sandbox môi trường mã hóa cho tác nhân Swe. Bạn cũng có thể sử dụng trình thông dịch mã đám mây như E2B và FlyIo.

Bây giờ chúng ta sẽ định nghĩa Swe Agent.

 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 }; }

Đây là những gì đang diễn ra trong khối mã trên.

  • Get Tools : Tìm nạp các công cụ từ bộ công cụ Composio cho filetool , file edit toolshelltool . Như tên cho thấy, chúng sẽ được sử dụng để truy cập tệp, chỉnh sửa tệp và sử dụng shell để thực thi lệnh.
  • Trim Tool Descriptions : Giới hạn mô tả công cụ ở mức tối đa 1024 ký tự.
  • Cập nhật giá trị Null : Thay thế giá trị null trong cấu hình công cụ bằng mảng trống.
  • Tạo chuỗi trợ lý : Bắt đầu chuỗi trợ lý OpenAI với các lời nhắc được xác định trước.
  • Câu lệnh trả về : Cung cấp các công cụ được khởi tạo, luồng trợ lý, phiên bản OpenAI và bộ công cụ Composio.

Xác định điểm đầu vào của ứng dụng 🚀

Đây là phần cuối cùng, nơi chúng tôi xác định điểm vào của ứng dụng. Do đó, hãy tải các biến môi trường và nhập các mô-đun cần thiết.

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

Khối mã

  • Tải các biến môi trường.
  • Nhập các chức năng tiện ích cần thiết.
  • Nhập Tác nhân Swe và Mục tiêu tác nhân mà chúng tôi đã xác định trước đó.

Bây giờ, xác định chức năng 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();


Đây là tệp app.ts hoàn chỉnh của chúng tôi, tệp này sẽ được sử dụng để thực thi quy trình làm việc tổng đài.


Vì vậy, đây là những gì đang xảy ra trong đoạn mã trên.

  • Khởi tạo SWE Agent : Gọi initSWEAgent để lấy chuỗi trợ lý, phiên bản OpenAI, các công cụ và bộ công cụ Composio.
  • Tìm nạp kho lưu trữ và vấn đề : Tìm nạp chi tiết kho lưu trữ và vấn đề từ fromGithub .
  • Tạo Trợ lý : Khởi tạo trợ lý OpenAI với các hướng dẫn, công cụ và mô hình ngôn ngữ.
  • Gửi vấn đề tới Trợ lý : Gửi nội dung vấn đề dưới dạng tin nhắn đến chuỗi trợ lý.
  • Chạy Trợ lý và Thăm dò ý kiến : Chạy trợ lý và thăm dò ý kiến để phản hồi cuộc gọi công cụ. Để biết thêm thông tin về phản hồi bỏ phiếu, hãy tham khảo Kho lưu trữ SDK OpenAI.
  • Thực thi hành động vá lỗi : Thực thi filetool_git_patch để tạo một bản vá.
  • Xử lý phản hồi bản vá : Nếu một bản vá được tạo, hãy ghi nhật ký nó, tạo một nhánh, cam kết và đẩy các thay đổi. Đợi 2 giây trước khi tạo yêu cầu kéo. Tạo yêu cầu kéo trên GitHub.
  • Close Workspace : Đóng không gian làm việc của bộ công cụ Composio.
  • Run Main Function : Gọi main() để thực hiện các bước trên.


Bây giờ, hãy chạy ứng dụng bằng cách sử dụng pnpm start .


Điều này sẽ nhắc bạn nhập ID người dùng GitHub, tên kho lưu trữ và ID vấn đề hoặc mô tả về vấn đề bạn muốn giải quyết.

Sau khi hoàn tất, nó sẽ lấy vùng chứa Composio Docker từ sổ đăng ký và bắt đầu khắc phục sự cố.


Cuối cùng, khi quy trình làm việc hoàn tất, bản vá sẽ được đẩy đến kho lưu trữ từ xa. Bây giờ, khi mở kho GitHub, bạn sẽ thấy một nhánh mới có đề xuất khắc phục sự cố. Bạn có thể so sánh nó với nhánh chính và tạo yêu cầu kéo.
cảm ơn bạn, Mike Scott

Bạn có thể tìm thấy mã hoàn chỉnh ở đây trên GitHub .


Các bước tiếp theo ⏭️

Điều tốt nhất về tác nhân SWE này là bạn có thể mở rộng khả năng của nó bằng cách sử dụng các công cụ và tích hợp Composio.

Bạn có thể thêm Slack hoặc Discord vào đại lý của mình để thông báo cho bạn khi quá trình thực thi hoàn tất. Bạn cũng có thể kết nối Jira hoặc Linear để tự động tạo và cập nhật nhiệm vụ dựa trên hoạt động của tác nhân.


Hãy kết nối! 🔌

Bạn có thể tham gia cộng đồng của chúng tôi để tương tác với những người bảo trì và đóng góp với tư cách là nhà phát triển nguồn mở. Đừng ngần ngại truy cập kho lưu trữ GitHub của chúng tôi để đóng góp và giải quyết các vấn đề liên quan đến Composio. Dev.

Gắn dấu sao cho bản soạn. kho lưu trữ dev ⭐

gắn dấu sao cho repo


Cảm ơn bạn đã đọc!