코드 검토는 표준을 유지하고 프로젝트에서 코드의 모범 사례를 강조하는 데 항상 중요했습니다. 이 게시물은 개발자가 코드를 어떻게 검토해야 하는지에 대한 게시물이 아니라 코드의 일부를 AI에 위임하는 것에 관한 것입니다.
Michael Lynch가 자신의 게시물인 "인간처럼 코드 검토를 수행하는 방법"에서 언급했듯이 코드 검토의 지루한 부분을 컴퓨터가 처리하도록 해야 합니다. Michael이 서식 지정 도구를 강조하는 동안 저는 한 단계 더 나아가 인공 지능이 이를 알아내도록 하고 싶습니다. 내 말은, 업계의 AI 붐을 활용해 보는 것은 어떨까요?
이제 저는 서식 도구와 린터 대신 AI를 사용해야 한다고 말하는 것이 아닙니다. 대신, 인간이 놓칠 수 있는 사소한 것들을 잡아내기 위해 추가적으로 사용됩니다.
그래서 저는 코드가 풀 요청 차이점을 검토하고 AI를 사용하여 제안을 생성하는 github 액션을 만들기로 결정했습니다. 자세히 안내해 드리겠습니다.
? 메모
- 이 GitHub 작업은 이제 GitHub 마켓플레이스에서 사용할 수 있습니다.
- 자바스크립트 작업입니다. 자바스크립트 github 작업 생성에 대해 자세히 알아보세요.
github API와 상호작용하기 위해 github API와 관용적인 방식으로 상호작용하기 위한 일종의 SDK 또는 클라이언트 라이브러리인 octokit을 사용했습니다.
풀 요청의 diff를 가져오려면 필수 매개변수와 함께 application/vnd.github.diff 값이 포함된 Accept 헤더를 전달해야 합니다.
async function getPullRequestDetails(octokit, { mode }) { let AcceptFormat = "application/vnd.github.raw json"; if (mode === "diff") AcceptFormat = "application/vnd.github.diff"; if (mode === "json") AcceptFormat = "application/vnd.github.raw json"; return await octokit.rest.pulls.get({ owner: github.context.repo.owner, repo: github.context.repo.repo, pull_number: github.context.payload.pull_request.number, headers: { accept: AcceptFormat, }, }); }
github 액션에 전혀 익숙하지 않다면 여기 Victoria Lo의 github 액션 101 시리즈가 있으니 좋은 시작이 될 것입니다.
차이점을 얻으면 이를 구문 분석하고 원치 않는 변경 사항을 제거한 다음 아래에 표시된 스키마로 반환합니다.
/** using zod */ schema = z.object({ path: z.string(), position: z.number(), line: z.number(), change: z.object({ type: z.string(), add: z.boolean(), ln: z.number(), content: z.string(), relativePosition: z.number(), }), previously: z.string().optional(), suggestions: z.string().optional(), })
파일을 무시하는 것은 매우 간단합니다. 사용자 입력 목록에는 세미콜론으로 구분된 glob 패턴 문자열이 필요합니다. 그런 다음 구문 분석되어 무시된 파일의 기본 목록과 연결되고 중복이 제거됩니다.
**/*.md; **/*.env; **/*.lock;
const filesToIgnoreList = [ ...new Set( filesToIgnore .split(";") .map(file => file.trim()) .filter(file => file !== "") .concat(FILES_IGNORED_BY_DEFAULT) ), ];
그런 다음 무시된 파일 목록을 사용하여 무시된 파일을 참조하는 차이점 변경 사항을 제거합니다. 그러면 원하는 변경 사항만 포함된 원시 페이로드가 제공됩니다.
차이점을 구문 분석한 후 원시 페이로드를 얻으면 이를 플랫폼 API에 전달합니다. 다음은 OpenAI API의 구현입니다.
async function useOpenAI({ rawComments, openAI, rules, modelName, pullRequestContext }) { const result = await openAI.beta.chat.completions.parse({ model: getModelName(modelName, "openai"), messages: [ { role: "system", content: COMMON_SYSTEM_PROMPT, }, { role: "user", content: getUserPrompt(rules, rawComments, pullRequestContext), }, ], response_format: zodResponseFormat(diffPayloadSchema, "json_diff_response"), }); const { message } = result.choices[0]; if (message.refusal) { throw new Error(`the model refused to generate suggestions - ${message.refusal}`); } return message.parsed; }
API 구현에서 응답 형식이 사용되는 것을 볼 수 있습니다. 이는 많은 LLM 플랫폼에서 제공하는 기능으로, 특정 스키마/형식으로 응답을 생성하도록 모델에 지시할 수 있습니다. 모델이 풀 요청의 잘못된 파일이나 위치에 대해 환각을 느끼고 제안을 생성하거나 응답 페이로드에 새 속성을 추가하는 것을 원하지 않기 때문에 이 경우 특히 유용합니다.
시스템 프롬프트는 코드 검토를 수행하는 방법과 염두에 두어야 할 사항에 대해 모델에 더 많은 컨텍스트를 제공하기 위해 존재합니다. github.com/murtuzaalisurti/better.
에서 시스템 프롬프트를 볼 수 있습니다.사용자 프롬프트에는 풀 요청의 실제 차이점, 규칙 및 컨텍스트가 포함됩니다. 이것이 코드 검토를 시작하는 것입니다.
이 github 작업은 OpenAI 및 Anthropic 모델을 모두 지원합니다. Anthropic API를 구현하는 방법은 다음과 같습니다.
async function useAnthropic({ rawComments, anthropic, rules, modelName, pullRequestContext }) { const { definitions } = zodToJsonSchema(diffPayloadSchema, "diffPayloadSchema"); const result = await anthropic.messages.create({ max_tokens: 8192, model: getModelName(modelName, "anthropic"), system: COMMON_SYSTEM_PROMPT, tools: [ { name: "structuredOutput", description: "Structured Output", input_schema: definitions["diffPayloadSchema"], }, ], tool_choice: { type: "tool", name: "structuredOutput", }, messages: [ { role: "user", content: getUserPrompt(rules, rawComments, pullRequestContext), }, ], }); let parsed = null; for (const block of result.content) { if (block.type === "tool_use") { parsed = block.input; break; } } return parsed; }
마지막으로 제안 사항을 검색한 후 이를 정리하고 github API에 전달하여 검토의 일부로 댓글을 추가합니다.
새 리뷰를 작성하면 한 번에 하나의 댓글을 추가하는 대신 한 번에 모든 댓글을 추가할 수 있기 때문에 아래 댓글 추가 방법을 선택했습니다. 댓글을 하나씩 추가하면 알림이 트리거되고 사용자에게 스팸 알림을 보내는 것을 원하지 않기 때문에 속도 제한이 트리거될 수도 있습니다.
function filterPositionsNotPresentInRawPayload(rawComments, comments) { return comments.filter(comment => rawComments.some(rawComment => rawComment.path === comment.path && rawComment.line === comment.line) ); } async function addReviewComments(suggestions, octokit, rawComments, modelName) { const { info } = log({ withTimestamp: true }); // eslint-disable-line no-use-before-define const comments = filterPositionsNotPresentInRawPayload(rawComments, extractComments().comments(suggestions)); try { await octokit.rest.pulls.createReview({ owner: github.context.repo.owner, repo: github.context.repo.repo, pull_number: github.context.payload.pull_request.number, body: `Code Review by ${modelName}`, event: "COMMENT", comments, }); } catch (error) { info(`Failed to add review comments: ${JSON.stringify(comments, null, 2)}`); throw error; } }
Github 작업을 개방적이고 통합에 개방적으로 유지하고 싶었습니다. 그렇기 때문에 원하는 모델을 선택하여 사용할 수 있습니다. (지원되는 모델 목록 참조) 또는 미세 조정하고 빌드할 수 있습니다. 지원되는 기본 모델 위에 사용자 정의 모델을 추가하고 이 github 작업과 함께 사용하세요.
토큰 문제나 속도 제한이 발생하는 경우 해당 플랫폼의 설명서를 참조하여 모델 제한을 업그레이드할 수 있습니다.
그래서 무엇을 기다리고 계시나요? github에 저장소가 있다면 지금 작업을 시도해 보세요. github 작업 마켓플레이스에 있습니다.
AI로 구동되는 코드 검토자 Github 작업으로 워크플로에 바로 사용할 수 있습니다.
다음 콘텐츠를 사용하여 저장소의 .github/workflows 폴더(없는 경우 생성) 내에 워크플로 파일을 생성합니다.
name: Code Review
on
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
branches:
- main # change this to your target branch
workflow_dispatch: # Allows you to run the workflow manually from the Actions tab
permissions: # necessary permissions
pull-requests: write
contents: read
jobs:
your-job-name:
runs-on: ubuntu-latest
name: your-job-name
steps:
- name: step-name
id: step-id
uses: murtuzaalisurti/better@v2 # this is
…부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.
Copyright© 2022 湘ICP备2022001581号-3