paint-brush
Лучше — Рецензент кода на базе искусственного интеллектак@murtuzaalisurti
Новая история

Лучше — Рецензент кода на базе искусственного интеллекта

к Murtuza8m2024/11/20
Read on Terminal Reader
Read this story w/o Javascript

Слишком долго; Читать

Обзоры кода всегда были важны для поддержания стандарта и акцентирования внимания на лучших практиках кода в проекте. Это не пост о том, как разработчики должны просматривать код, а скорее о делегировании его части ИИ. Вот почему я решил создать действие github (github.com/murtuzaalisurti/better), которое проверяет разницу в запросе на извлечение и генерирует предложения с использованием ИИ.

People Mentioned

Mention Thumbnail
Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Лучше — Рецензент кода на базе искусственного интеллекта
Murtuza HackerNoon profile picture
0-item
1-item

Обзоры кода всегда были важны для поддержания высоких стандартов и закрепления лучших практик в проекте кодирования. Это не пост о том, как разработчикам следует просматривать код, а скорее о делегировании его части ИИ.


Как упоминает Майкл Линч в своем посте «Как делать обзоры кода, как человек» , мы должны позволить компьютерам заботиться о скучных частях обзора кода. В то время как Майкл подчеркивает инструмент форматирования, я хотел бы пойти дальше и позволить искусственному интеллекту разобраться с этим. Я имею в виду, почему бы не воспользоваться бумом ИИ в отрасли?


Я не говорю, что ИИ следует использовать вместо инструментов форматирования и линтеров. Вместо этого его следует использовать поверх всего этого, чтобы отлавливать тривиальные вещи, которые может пропустить человек. Вот почему я решил создать действие github , которое просматривает код diff pull request и генерирует предложения с помощью ИИ. Позвольте мне провести вас через это.


🚨 ПРИМЕЧАНИЕ:


Получение разницы

Для взаимодействия с API github я использовал octokit , который представляет собой своего рода SDK или клиентскую библиотеку для взаимодействия с API github идиоматическим способом.


Чтобы получить разницу в запросе на извлечение, вам необходимо передать заголовок Accept со значением application/vnd.github.diff вместе с требуемыми параметрами.


 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, вот серия «101 действие GitHub» от Виктории Ло , и это хорошее начало.



Получив разницу, я анализирую ее и удаляю нежелательные изменения, а затем возвращаю ее в виде схемы, показанной ниже:


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


Список игнорируемых файлов затем используется для удаления изменений diff, которые ссылаются на эти игнорируемые файлы. Это дает вам сырую полезную нагрузку, содержащую только нужные вам изменения.

Генерация предложений

Как только я получаю сырую полезную нагрузку после разбора diff, я передаю ее в API платформы. Вот реализация API OpenAI.


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

Добавление комментариев

Наконец, после получения предложений я очищаю их и передаю в API GitHub для добавления комментариев как части обзора.

Я выбрал следующий способ добавления комментариев, потому что, создавая новый отзыв, вы можете добавить все комментарии за один раз, а не добавлять по одному комментарию за раз. Добавление комментариев по одному может также вызвать ограничение скорости, потому что добавление комментариев вызывает уведомления, а вы не хотите спамить пользователей уведомлениями.


 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 action marketplace .