कोडिंग प्रोजेक्ट में उच्च मानकों को बनाए रखने और सर्वोत्तम प्रथाओं को सुदृढ़ करने में कोड समीक्षा हमेशा महत्वपूर्ण रही है। यह पोस्ट इस बारे में नहीं है कि डेवलपर्स को कोड की समीक्षा कैसे करनी चाहिए, यह इसके एक हिस्से को AI को सौंपने के बारे में अधिक है।
जैसा कि माइकल लिंच ने अपनी पोस्ट - "मानव की तरह कोड समीक्षा कैसे करें" में उल्लेख किया है - हमें कंप्यूटर को कोड समीक्षा के उबाऊ हिस्सों का ध्यान रखने देना चाहिए। जबकि माइकल एक फ़ॉर्मेटिंग टूल पर जोर देते हैं, मैं इसे एक कदम आगे ले जाना चाहता हूँ और कृत्रिम बुद्धिमत्ता को इसे हल करने देना चाहता हूँ। मेरा मतलब है, उद्योग में एआई बूम का लाभ क्यों न उठाया जाए?
अब मैं यह नहीं कह रहा हूँ कि AI का इस्तेमाल फ़ॉर्मेटिंग टूल और लिंटर की जगह किया जाना चाहिए। इसके बजाय, इसका इस्तेमाल उसके ऊपर किया जाना चाहिए, ताकि छोटी-छोटी चीज़ों को पकड़ा जा सके जो शायद किसी इंसान से छूट जाएँ। इसीलिए मैंने एक github एक्शन बनाने का फ़ैसला किया जो पुल रिक्वेस्ट डिफ़ की कोड समीक्षा करता है और AI का इस्तेमाल करके सुझाव तैयार करता है। चलिए मैं आपको इसके बारे में बताता हूँ।
🚨 नोट:
- यह GitHub क्रिया अब GitHub बाज़ार पर उपलब्ध है।
- यह एक जावास्क्रिप्ट क्रिया है - जावास्क्रिप्ट गिटहब क्रियाएँ बनाने के बारे में अधिक जानें।
गिटहब एपीआई के साथ बातचीत करने के लिए, मैंने octokit
उपयोग किया है, जो कि एक एसडीके या क्लाइंट लाइब्रेरी है जो गिटहब एपीआई के साथ मुहावरेदार तरीके से बातचीत करता है ।
आपके द्वारा उठाए गए पुल अनुरोध का अंतर प्राप्त करने के लिए, आपको आवश्यक पैरामीटर के साथ 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, }, }); }
यदि आप गिटहब एक्शन्स से बिल्कुल भी परिचित नहीं हैं, तो यहां विक्टोरिया लो द्वारा गिटहब एक्शन्स 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(), })
फ़ाइलों को अनदेखा करना बहुत आसान है। उपयोगकर्ता इनपुट सूची के लिए ग्लोब पैटर्न की अर्धविराम से अलग की गई स्ट्रिंग की आवश्यकता होती है। फिर इसे पार्स किया जाता है, अनदेखा की गई फ़ाइलों की डिफ़ॉल्ट सूची के साथ संयोजित किया जाता है और डी-डुप किया जाता है।
**/*.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 । उपयोगकर्ता प्रॉम्प्ट में वास्तविक अंतर, नियम और पुल अनुरोध का संदर्भ शामिल है। यह वही है जो कोड समीक्षा को शुरू करता है।
यह गिटहब एक्शन OpenAI और एंथ्रोपिक दोनों मॉडल का समर्थन करता है। यहाँ बताया गया है कि यह एंथ्रोपिक 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 एक्शन मार्केटप्लेस पर है।