في عام 2023، ستقود شعبية الذكاء الاصطناعي التوليدي المزيد والمزيد من المؤسسات إلى تقديم البرمجة بمساعدة الذكاء الاصطناعي. ما يختلف قليلاً عن GitHub Copilot الذي تم إصداره في عام 2021 هو أن إكمال التعليمات البرمجية ليس سوى واحد من العديد من السيناريوهات. يستكشف عدد كبير من الشركات سيناريوهات مثل إنشاء تعليمات برمجية كاملة ومراجعة التعليمات البرمجية بناءً على المتطلبات، كما تقدم أيضًا الذكاء الاصطناعي التوليدي لتحسين كفاءة التطوير.
في هذا السياق، قمنا (مجتمع Thinkworks مفتوح المصدر) أيضًا بفتح سلسلة من الأدوات المساعدة للذكاء الاصطناعي لمساعدة المزيد من المؤسسات على بناء مساعدي الترميز بمساعدة الذكاء الاصطناعي:
لأنه عندما قمنا بتصميم AutoDev، كانت العديد من النماذج مفتوحة المصدر تتطور باستمرار. وفي هذا السياق، تتمثل خطواته في:
لذلك، يتمحور هذا البرنامج التعليمي أيضًا حول هذه الخطوات الثلاث. بالإضافة إلى ذلك، واستنادًا إلى خبرتنا، فإن حزمة التكنولوجيا النموذجية لهذا البرنامج التعليمي:
نظرًا لأن خبرتنا في الذكاء الاصطناعي محدودة نسبيًا، فمن المؤكد أنه سيكون هناك بعض الأخطاء، لذلك نأمل أيضًا أن نعمل مع المزيد من المطورين لبناء هذا المشروع مفتوح المصدر.
بالجمع مع جزء الذكاء الاصطناعي في تقرير JetBrains 2023 "النظام البيئي للمطورين"، يمكننا تلخيص بعض السيناريوهات العامة التي تعكس المجالات التي يمكن أن يلعب فيها الذكاء الاصطناعي التوليدي دورًا في عملية التطوير. فيما يلي بعض السيناريوهات الرئيسية:
عندما قمنا ببناء AutoDev، اكتشفنا أيضًا سيناريوهات مثل إنشاء SQL DDL، وإنشاء المتطلبات، وTDD، وما إلى ذلك. لذا. نحن نقدم القدرة على تخصيص السيناريوهات حتى يتمكن المطورون من تخصيص قدرات الذكاء الاصطناعي الخاصة بهم للحصول على التفاصيل، راجع: https://ide.unitmesh.cc/customize.
في الترميز اليومي، هناك عدة سيناريوهات مختلفة مع متطلبات مختلفة لسرعة استجابة الذكاء الاصطناعي (على سبيل المثال):
مشهد | سرعة الاستجابة | توليد متطلبات الجودة | الحجم المتوقع | يوضح |
---|---|---|---|---|
إكمال الكود | سريع | وسط | 1 ~ 6 ب | يعد إكمال التعليمات البرمجية هو السيناريو الأكثر شيوعًا في البرمجة اليومية، وتعد سرعة الاستجابة أمرًا بالغ الأهمية. |
توليد الوثائق | وسط | وسط | 1 | يتطلب إنشاء الوثائق فهمًا كاملاً لبنية التعليمات البرمجية، كما أن السرعة والجودة لهما نفس القدر من الأهمية. |
مراجعة الكود | سريع | وسط | 1 | تتطلب مراجعات الكود مشورة عالية الجودة ولكن يجب أيضًا أن تكون سريعة الاستجابة قدر الإمكان. |
توليد اختبار الوحدة | سريع | وسط | 6ب~ | تولد اختبارات الوحدة سياقًا أقل، كما أن الاستجابة وجودة الذكاء الاصطناعي لهما نفس القدر من الأهمية. |
إعادة هيكلة التعليمات البرمجية | وسط | عالي | 32ب~ | قد تتطلب إعادة بناء التعليمات البرمجية مزيدًا من الفهم السياقي، وقد تتباطأ أوقات الاستجابة إلى حد ما. |
توليد الطلب | وسط | عالي | 32ب~ | يعد توليد الطلب سيناريو معقدًا نسبيًا، ويمكن إبطاء سرعة الاستجابة بشكل معتدل لضمان الدقة. |
البحث عن رموز اللغة الطبيعية وتفسيرها | متوسطة ومنخفضة | عالي | 32ب~ | يعد البحث عن رموز اللغة الطبيعية وتفسيرها سيناريوهات معقدة نسبيًا، ويمكن إبطاء سرعة الاستجابة بشكل معتدل لضمان الدقة. |
ملاحظة: يتم التعبير عن 32B هنا فقط كترتيب من حيث الحجم، لأن التأثير سيكون أفضل مع نموذج أكبر.
لذلك، نلخصها على النحو التالي: نموذج واحد كبير، ونموذج متوسط، ونموذج صغير، وثلاثة نماذج، توفر ترميزًا شاملاً بمساعدة الذكاء الاصطناعي:
يمكن أن يجمع إكمال كود الذكاء الاصطناعي بين أدوات IDE لتحليل سياق الكود وقواعد لغة البرمجة، وسيقوم الذكاء الاصطناعي تلقائيًا بإنشاء مقتطفات من التعليمات البرمجية أو اقتراحها. في أدوات إكمال التعليمات البرمجية المشابهة لـ GitHub Copilot، يتم تقسيمها عادةً إلى ثلاثة أوضاع تقسيم فرعية:
الإكمال المضمن (المضمن)
كما هو الحال في وضع FIM (ملء الوسط)، يكون المحتوى المكتمل موجودًا في السطر الحالي. على سبيل المثال: BlotPost blogpost = new
، الإكمال هو: BlogPost();
لتحقيق: BlogPost blogpost = new BlogPost();
يمكننا استخدام Deepseek Coder كمثال لرؤية التأثير في هذا السيناريو:
< |fim▁begin| > def quick_sort(arr):
if len(arr) < = 1:
return arr
pivot = arr[0]
left = []
right = []
< |fim▁hole| >
if arr[i] < pivot:
left.append(arr[i])
else:
right.append(arr[i])
return quick_sort(left) + [pivot] + quick_sort(right) < |fim▁end| >
هنا، نحتاج إلى دمج الكود قبل المؤشر وبعده.
الإكمال داخل الكتلة (InBlock)
يتم تحقيقه من خلال التعلم السياقي (التعلم في السياق)، ويكون محتوى الإكمال موجودًا في كتلة الوظيفة الحالية. على سبيل المثال، الكود الأصلي هو:
fun createBlog ( blogDto : CreateBlogDto ): BlogPost {
}
الكود المكتمل هو:
val blogPost = BlogPost (
title = blogDto.title,
content = blogDto.content,
author = blogDto.author
)
return blogRepository.save(blogPost)
AfterBlock
يتم تحقيقه من خلال التعلم السياقي (In-Context Learning)، والإكمال بعد كتلة الوظيفة الحالية، مثل: إكمال وظيفة جديدة بعد كتلة الوظيفة الحالية. على سبيل المثال، الكود الأصلي هو:
fun createBlog ( blogDto : CreateBlogDto ): BlogPost {
// ...
}
الكود المكتمل هو:
fun updateBlog ( id : Long , blogDto : CreateBlogDto ): BlogPost {
// ...
}
fun deleteBlog ( id : Long ) {
// ...
}
عندما نبني وظيفة إكمال الذكاء الاصطناعي المقابلة، نحتاج أيضًا إلى النظر في تطبيقها على مجموعة بيانات النمط المقابلة لتحسين جودة الإكمال وتوفير تجربة أفضل للمستخدم.
بعض الموارد ذات الصلة لكتابة هذا المقال:
تم تصميم تفسيرات التعليمات البرمجية لمساعدة المطورين على إدارة وفهم قواعد التعليمات البرمجية الكبيرة بشكل أكثر فعالية. يمكن لهؤلاء المساعدين الإجابة على الأسئلة المتعلقة بقاعدة التعليمات البرمجية، وتوفير الوثائق، ورمز البحث، وتحديد مصادر الأخطاء، وتقليل تكرار التعليمات البرمجية، وما إلى ذلك، وبالتالي تحسين كفاءة التطوير، وتقليل معدلات الخطأ، وتقليل عبء العمل على المطورين.
في هذا السيناريو، اعتمادًا على جودة التوليد الذي نتوقعه، عادة ما يتكون من نموذجين: أحدهما كبير والآخر ميكرو أو النموذج الأكبر والنموذج الأكبر له نتائج أفضل من حيث جودة التوليد. بالاشتراك مع خبرتنا في التصميم في أداة Chocolate Factory، عادةً ما يمكن تقسيم هذه الوظيفة إلى عدة خطوات:
باعتباره تطبيق RAG، فهو مقسم إلى قسمين: الفهرسة والاستعلام.
في مرحلة الفهرسة، نحتاج إلى فهرسة قاعدة التعليمات البرمجية، والتي تتضمن تجزئة النص، وتوجيهه، وفهرسة قاعدة البيانات وغيرها من التقنيات. أحد العناصر الأكثر تحديًا هو التقسيم. قواعد التقسيم التي نشير إليها هي: https://docs.sweep.dev/blogs/chunking-2m-files. الآن:
في سيناريوهات مختلفة، يمكننا أيضًا القسمة بطرق مختلفة، على سبيل المثال، في Chocolate Factory، نقوم بالتقسيم عبر AST لضمان جودة السياق الذي تم إنشاؤه.
في مرحلة الاستعلام، نحتاج إلى الجمع بين بعض تقنيات البحث التقليدية لدينا، مثل البحث الموجه، والبحث عن المسار، وما إلى ذلك، لضمان جودة البحث. وفي الوقت نفسه، في السيناريو الصيني، نحتاج أيضًا إلى النظر في مسألة التحويل إلى الصينية، مثل تحويل الإنجليزية إلى الصينية لضمان جودة البحث.
للحصول على المساعدة اليومية، يمكننا أيضًا تحقيق ذلك من خلال الذكاء الاصطناعي التوليدي، مثل إنشاء SQL DDL تلقائيًا، وإنشاء حالات الاختبار تلقائيًا، وإنشاء المتطلبات تلقائيًا، وما إلى ذلك. ولا يمكن تحقيق ذلك إلا من خلال تخصيص الكلمات السريعة والجمع بين المعرفة بمجال معين، لذلك لن أخوض في التفاصيل هنا.
بالإضافة إلى النموذج، يعد السياق أيضًا عاملاً مهمًا يؤثر على قدرات مساعدة الذكاء الاصطناعي. عندما قمنا ببناء AutoDev، اكتشفنا أيضًا وضعين مختلفين للسياق:
مقارنة بسيطة هي كما يلي:
السياق ذو الصلة | سياق مماثل | |
---|---|---|
تكنولوجيا البحث | تحليل الكود الثابت | بحث التشابه |
معلومات هيكل البيانات | أست، سي إف جي | قطعة مماثلة |
قدرات عبر منصة | يعتمد على IDE، أو محلل مستقل | لا تعتمد على منصات محددة |
الجودة السياقية | عالية للغاية | عالي |
توليد النتائج | عالية للغاية | عالي |
تكلفة البناء | يعتمد على اللغة والمنصة | قليل |
عندما يكون دعم IDE محدودًا، فإن الارتباط بالسياق سيؤدي إلى أداء أعلى من حيث التكلفة .
يتبنى GitHub Copilot نمطًا معماريًا مشابهًا للسياق، ويتم تقسيم بنيته التفصيلية على النحو التالي:
في المواد البحثية لمشروع Copilot-Explorer "العامة"، يمكنك رؤية كيفية إنشاء المطالبة. وفيما يلي الطلب الفوري الذي تم إرساله إلى:
{
"prefix" : " # Path: codeviz \ app.py n #.... " ,
"suffix" : " if __name__ == '__main__': rn app.run(debug=True) " ,
"isFimEnabled" : true ,
"promptElementRanges" : [
{
"kind" : " PathMarker " ,
"start" : 0 ,
"end" : 23
},
{
"kind" : " SimilarFile " ,
"start" : 23 ,
"end" : 2219
},
{
"kind" : " BeforeCursor " ,
"start" : 2219 ,
"end" : 3142
}
]
}
في:
prefix
المستخدم لإنشاء الموجه من عناصر موجه، والتي تتضمن: BeforeCursor
و AfterCursor
و SimilarFile
و ImportedFile
و LanguageMarker
و PathMarker
و RetrievalSnippet
وأنواع أخرى. من أسماء العديد من PromptElementKind
، يمكننا أيضًا رؤية معناها الحقيقي.suffix
المستخدم لإنشاء الموجه من خلال الجزء الذي يوجد به المؤشر، وعدد المواضع المتبقية ليتم حسابها بناءً على الحد الأعلى للرموز المميزة (2048). حساب الرمز المميز هنا هو حساب رمز LLM الحقيقي في Copilot، ويتم حسابه بواسطة Cushman002. يختلف طول الرمز المميز للأحرف الصينية، مثل: { context: "console.log('你好,世界')", lineCount: 1, tokenLength: 30 }
، حيث يكون طول المحتوى في السياق 20، ولكن tokenLength هو 30، وطول الأحرف الصينية هو 5 (بما في ذلك ,
)، والرمز المميز الذي يشغله حرف واحد هو 3.فيما يلي مثال أكثر تفصيلاً لسياق تطبيق Java:
// Path: src/main/cc/unitmesh/demo/infrastructure/repositories/ProductRepository.java
// Compare this snippet from src/main/cc/unitmesh/demo/domain/product/Product.java:
// ....
// Compare this snippet from src/main/cc/unitmesh/demo/application/ProductService.java:
// ...
// @Component
// public class ProductService {
// //...
// }
//
package cc . unitmesh . demo . repositories ;
// ...
@ Component
public class ProductRepository {
//...
في سياق الحوسبة، يستخدم GitHub Copilot معامل Jaccard (تشابه Jaccard). لمزيد من المنطق التفصيلي، يرجى الرجوع إلى: بعد قضاء أكثر من نصف شهر، قمت أخيرًا بإجراء هندسة عكسية لـ Github Copilot.
الموارد ذات الصلة:
كما هو مذكور أعلاه، يعتمد الكود ذو الصلة على تحليل الكود الثابت ، بشكل أساسي بمساعدة المعلومات الهيكلية للكود، مثل AST، وCFG، وDDG، وما إلى ذلك. في سيناريوهات ومنصات مختلفة، يمكننا الجمع بين أدوات تحليل التعليمات البرمجية الثابتة المختلفة، وفيما يلي بعض أدوات تحليل التعليمات البرمجية الثابتة الشائعة:
في سيناريو الإكمال، من خلال تحليل التعليمات البرمجية الثابتة، يمكننا الحصول على السياق الحالي، مثل: الوظيفة الحالية، والفئة الحالية، والملف الحالي، وما إلى ذلك. فيما يلي مثال على سياق AutoDev لإنشاء اختبارات الوحدة:
// here are related classes:
// 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/service/BlogService.java
// class BlogService {
// blogRepository
// + public BlogPost createBlog(BlogPost blogDto)
// + public BlogPost getBlogById(Long id)
// + public BlogPost updateBlog(Long id, BlogPost blogDto)
// + public void deleteBlog(Long id)
// }
// 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/dto/CreateBlogRequest.java
// class CreateBlogRequest ...
// 'filePath: /Users/phodal/IdeaProjects/untitled/src/main/java/cc/unitmesh/untitled/demo/entity/BlogPost.java
// class BlogPost {...
@ ApiOperation ( value = "Create a new blog" )
@ PostMapping ( "/" )
public BlogPost createBlog ( @ RequestBody CreateBlogRequest request ) {
في هذا المثال، يتم تحليل سياق وظيفة createBlog
للحصول على فئات الإدخال والإخراج الخاصة بالوظيفة: CreateBlogRequest
ومعلومات BlogPost
ومعلومات فئة BlogService، والتي يتم توفيرها للنموذج كسياق (مقدم في التعليقات). في هذه المرحلة، يقوم النموذج بإنشاء مُنشئات أكثر دقة، بالإضافة إلى حالات اختبار أكثر دقة.
نظرًا لأن السياق ذي الصلة يعتمد على تحليل التعليمات البرمجية الثابتة للغات وواجهات برمجة التطبيقات المختلفة لمعرفات التطوير المختلفة، فإننا نحتاج أيضًا إلى التكيف مع لغات مختلفة وبيئات تطوير متكاملة مختلفة. ومن حيث تكلفة البناء، فهي أكثر تكلفة مقارنة بالسياقات المماثلة.
تعد بيئة التطوير المتكاملة والمحررات الأدوات الرئيسية للمطورين، كما أن تكاليف تصميمها وتعلمها مرتفعة نسبيًا. أولاً، يمكننا استخدام القالب الرسمي لإنشاء:
ثم أضف وظيفة في الأعلى (أليس الأمر بسيطًا جدًا)، بالطبع لا. فيما يلي بعض موارد البرنامج الإضافي لـ IDEA التي يمكنك الرجوع إليها:
بالطبع، من الأنسب الرجوع إلى المكون الإضافي AutoDev.
يمكنك استخدام القالب الرسمي مباشرة لإنشاء المكون الإضافي المقابل: https://github.com/JetBrains/intellij-platform-plugin-template
بالنسبة لتنفيذ المكون الإضافي لـ IDEA، يتم تنفيذه بشكل أساسي من خلال Action وListener، اللذين يحتاجان فقط إلى التسجيل في plugin.xml
. للحصول على التفاصيل، يرجى الرجوع إلى الوثائق الرسمية: IntelliJ Platform Plugin SDK
نظرًا لأننا لم نأخذ في الاعتبار مشكلة التوافق مع إصدارات IDE من AutoDev في المرحلة المبكرة، لكي نكون متوافقين مع الإصدارات الأقدم من IDE لاحقًا، نحتاج إلى إجراء معالجة التوافق على المكون الإضافي. لذلك، كما هو موضح في الوثيقة الرسمية: نطاقات أرقام البناء، يمكننا أن نرى أن الإصدارات المختلفة لها متطلبات مختلفة لـ JDK. وفيما يلي متطلبات الإصدارات المختلفة:
رقم الفرع | إصدار منصة IntelliJ |
---|---|
233 | 2023.3 |
232 | 2023.2 |
231 | 2023.1 |
223 | 2022.3 |
222 | 2022.2 ملاحظة: Java 17 مطلوب الآن (منشور مدونة) |
221 | 2022.1 |
213 | 2021.3 |
212 | 2021.2 |
211 | 2021.1 |
203 | 2020.3 ملاحظة: Java 11 مطلوب الآن (منشور مدونة) |
وقم بتكوينه في gradle.properties
:
pluginSinceBuild = 223
pluginUntilBuild = 233.*
يعد تكوين التوافق اللاحق أمرًا مزعجًا، لذا يمكنك الرجوع إلى تصميم AutoDev.
فيما يتعلق بإكمال التعليمات البرمجية تلقائيًا، يشير المصنعون المحليون بشكل أساسي إلى تنفيذ GitHub Copilot، والمنطق ليس معقدًا.
الزناد باستخدام مفاتيح الاختصار
فهو يراقب بشكل أساسي إدخال المستخدم في الإجراء، ثم:
وظيفة | مفتاح الاختصار | يوضح |
---|---|---|
requestCompletions | Alt + / | احصل على السياق الحالي ثم احصل على نتائج الإكمال من خلال النموذج |
ApplyInlays | TAB | عرض نتائج الإكمال على IDE |
DisposInlays | ESC | إلغاء الإكمال |
CycleNextInlays | Alt + ] | قم بالتبديل إلى نتيجة الإكمال التالية |
CyclePrevInlays | Alt + [ | التبديل إلى نتيجة الإكمال السابقة |
استخدم طريقة التشغيل التلقائي
يقوم بشكل رئيسي بمراقبة مدخلات المستخدم من خلال EditorFactoryListener
، ثم يقوم بتشغيل نتائج إكمال مختلفة بناءً على مدخلات مختلفة. الكود الأساسي هو كما يلي:
class AutoDevEditorListener : EditorFactoryListener {
override fun editorCreated ( event : EditorFactoryEvent ) {
// ...
editor.document.addDocumentListener( AutoDevDocumentListener (editor), editorDisposable)
editor.caretModel.addCaretListener( AutoDevCaretListener (editor), editorDisposable)
// ...
}
class AutoDevCaretListener ( val editor : Editor ) : CaretListener {
override fun caretPositionChanged ( event : CaretEvent ) {
// ...
val wasTypeOver = TypeOverHandler .getPendingTypeOverAndReset(editor)
// ...
llmInlayManager.disposeInlays(editor, InlayDisposeContext . CaretChange )
}
}
class AutoDevDocumentListener ( val editor : Editor ) : BulkAwareDocumentListener {
override fun documentChangedNonBulk ( event : DocumentEvent ) {
// ...
val llmInlayManager = LLMInlayManager .getInstance()
llmInlayManager
.editorModified(editor, changeOffset)
}
}
}
ثم وفقًا لمدخلات مختلفة، يتم تشغيل نتائج إكمال مختلفة وتتم معالجة الهيكل.
تقديم رمز الانتهاء
بعد ذلك، نحتاج إلى تنفيذ Inlay Render، الذي يرث من EditorCustomElementRenderer
.
إلى جانب إمكانيات الواجهة الخاصة بـ IDE، نحتاج إلى إضافة الإجراء المقابل والمجموعة المقابلة والأيقونة المقابلة. فيما يلي مثال على الإجراء:
<add-to-group group-id="ShowIntentionsGroup" relative-to-action="ShowIntentionActions" anchor="after"/>
فيما يلي بعض مجموعات العمل الخاصة بـ AutoDev:
معرف المجموعة | استخدامات الذكاء الاصطناعي | وصف |
---|---|---|
ShowIntentionsGroup | إعادة هيكلة الكود، وتفسير الكود، وتوليد الكود، واختبار الكود | يُستخدم لعرض التلميحات في سياق التعليمات البرمجية ويمكن الوصول إليها عبر الاختصارات Alt + Enter ⌥ + Enter على نظام التشغيل macOS. |
ConsoleEditorPopupMenu | إصلاح الأخطاء | القائمة المعروضة في وحدة التحكم، مثل وحدة التحكم الخاصة ببنية تنفيذ البرنامج. |
Vcs.MessageActionGroup | توليد معلومات الكود | قائمة لكتابة رسائل الالتزام في VCS. |
Vcs.Log.ContextMenu | مراجعة الكود، تفسير الكود، إنشاء الكود | قائمة لعرض السجلات في VCS، والوظائف المتاحة: فحص الكود بالذكاء الاصطناعي، وإنشاء سجلات الإصدار. |
EditorPopupMenu | كلها مقبولة | انقر بزر الماوس الأيمن على القائمة، ويمكنك أيضًا إضافة مجموعة العمل المقابلة |
عند كتابة ShowIntentionsGroup، يمكننا الرجوع إلى تنفيذ AutoDev لبناء المجموعة المقابلة:
< group id = " AutoDevIntentionsActionGroup " class = " cc.unitmesh.devti.intentions.IntentionsActionGroup "
icon = " cc.unitmesh.devti.AutoDevIcons.AI_COPILOT " searchable = " false " >
< add-to-group group-id = " ShowIntentionsGroup " relative-to-action = " ShowIntentionActions " anchor = " after " />
</ group >
نظرًا لاستراتيجية منصة Intellij، يصبح الفرق بين التشغيل في Java IDE (Intellij IDEA) وبيئات التطوير المتكاملة الأخرى مثل Python IDE (Pycharm) أكبر. نحتاج إلى توفير التوافق بناءً على المنتجات متعددة المنصات للحصول على مقدمة تفصيلية، يرجى الرجوع إلى: توافق المكونات الإضافية مع منتجات منصة IntelliJ
أولاً، تم تصميم بنية المكونات الإضافية بشكل أكبر، أي أنه يتم توفير وحدات مختلفة للغات مختلفة. فيما يلي البنية المعيارية لـ AutoDev:
java/ # Java 语言插件
src/main/java/cc/unitmesh/autodev/ # Java 语言入口
src/main/resources/META-INF/plugin.xml
plugin/ # 多平台入口
src/main/resources/META-INF/plugin.xml
src/ # 即核心模块
main/resource/META-INF/core.plugin.xml
في plugin/plugin.xml
، نحتاج إلى إضافة depends
extensions
المقابلة، وفيما يلي مثال على ذلك:
< idea-plugin package = " cc.unitmesh " xmlns : xi = " http://www.w3.org/2001/XInclude " allow-bundled-update = " true " >
< xi : include href = " /META-INF/core.xml " xpointer = " xpointer(/idea-plugin/*) " />
< content >
< module name = " cc.unitmesh.java " />
<!-- 其它模块 -->
</ content >
</ idea-plugin >
في java/plugin.xml
، نحتاج إلى إضافة depends
extensions
المقابلة، وفيما يلي مثال على ذلك:
< idea-plugin package = " cc.unitmesh.java " >
<!-- suppress PluginXmlValidity -->
< dependencies >
< plugin id = " com.intellij.modules.java " />
< plugin id = " org.jetbrains.plugins.gradle " />
</ dependencies >
</ idea-plugin >
بعد ذلك، سيقوم Intellij تلقائيًا بتحميل الوحدة المقابلة لتحقيق دعم متعدد اللغات. اعتمادًا على اللغات المختلفة التي نتوقع دعمها، نحتاج إلى plugin.xml
المطابق، مثل:
cc.unitmesh.javascript.xml
cc.unitmesh.rust.xml
cc.unitmesh.python.xml
cc.unitmesh.kotlin.xml
cc.unitmesh.java.xml
cc.unitmesh.go.xml
cc.unitmesh.cpp.xml
وأخيرًا، ما عليك سوى تنفيذ الوظائف المقابلة في وحدات اللغة المختلفة.
لتبسيط هذه العملية، نستخدم Unit Eval لإظهار كيفية إنشاء سياقين متشابهين.
من خلال تحليل الكود الثابت، يمكننا الحصول على الوظيفة الحالية والفئة الحالية والملف الحالي وما إلى ذلك. بالاشتراك مع تشابه المسار، ابحث عن السياق الأكثر صلة.
private fun findRelatedCode ( container : CodeContainer ): List < CodeDataStruct > {
// 1. collects all similar data structure by imports if exists in a file tree
val byImports = container. Imports
.mapNotNull {
context.fileTree[it. Source ]?.container?. DataStructures
}
.flatten()
// 2. collects by inheritance tree for some node in the same package
val byInheritance = container. DataStructures
.map {
(it. Implements + it. Extend ).mapNotNull { i ->
context.fileTree[i]?.container?. DataStructures
}.flatten()
}
.flatten()
val related = (byImports + byInheritance).distinctBy { it. NodeName }
// 3. convert all similar data structure to uml
return related
}
class RelatedCodeStrategyBuilder ( private val context : JobContext ) : CodeStrategyBuilder {
override fun build (): List < TypedIns > {
// ...
val findRelatedCodeDs = findRelatedCode(container)
val relatedCodePath = findRelatedCodeDs.map { it. FilePath }
val jaccardSimilarity = SimilarChunker .pathLevelJaccardSimilarity(relatedCodePath, currentPath)
val relatedCode = jaccardSimilarity.mapIndexed { index, d ->
findRelatedCodeDs[index] to d
}.sortedByDescending {
it.second
}.take( 3 ).map {
it.first
}
// ...
}
}
بالنسبة للكود أعلاه، يمكننا استخدام معلومات الاستيراد الخاصة بالكود كجزء من الكود ذي الصلة. ثم ابحث عن الكود ذي الصلة من خلال علاقة الميراث للكود. وأخيرا، تم العثور على السياق الأقرب من خلال تشابه المسار.
ابحث أولاً، ثم ابحث عن الكود ذي الصلة من خلال تشابه الكود. يظهر المنطق الأساسي:
fun pathLevelJaccardSimilarity ( chunks : List < String >, text : String ): List < Double > {
// ...
}
fun tokenize ( chunk : String ): List < String > {
return chunk.split( Regex ( " [^a-zA-Z0-9] " )).filter { it.isNotBlank() }
}
fun similarityScore ( set1 : Set < String >, set2 : Set < String >): Double {
// ...
}
لمزيد من التفاصيل، راجع: مماثل Chunker
المهام
TreeSitter هو إطار عمل لإنشاء محللين مخصصين فعالين، تم تطويره بواسطة GitHub. يستخدم محلل LR(1)، مما يعني أنه يمكنه تحليل أي لغة في وقت O(n) بدلاً من وقت O(n²). ويستخدم أيضًا تقنية تسمى "إعادة استخدام شجرة بناء الجملة" والتي تسمح له بتحديث أشجار بناء الجملة دون إعادة تحليل الملف بأكمله.
نظرًا لأن TreeSitter يوفر بالفعل دعمًا متعدد اللغات، فيمكنك استخدام Node.js وRust ولغات أخرى لإنشاء المكونات الإضافية المقابلة. انظر: TreeSitter للحصول على التفاصيل.
اعتمادًا على نوايانا، هناك طرق مختلفة لاستخدام TreeSitter:
رمز التحليل
في محرك بحث اللغة الطبيعية للكود Bloop، نستخدم TreeSitter لتحليل الرموز لتحقيق جودة بحث أفضل.
; ; methods
(method_declaration
name: (identifier) @hoist.definition.method)
ثم قرر كيفية عرضه بناءً على النوع:
pub static JAVA : TSLanguageConfig = TSLanguageConfig {
language_ids : & [ "Java" ] ,
file_extensions : & [ "java" ] ,
grammar : tree_sitter_java :: language ,
scope_query : MemoizedQuery :: new ( include_str ! ( "./scopes.scm" ) ) ,
hoverable_query : MemoizedQuery :: new (
r#"
[(identifier)
(type_identifier)] @hoverable
"# ,
) ,
namespaces : & [ & [
// variables
"local" ,
// functions
"method" ,
// namespacing, modules
"package" ,
"module" ,
// types
"class" ,
"enum" ,
"enumConstant" ,
"record" ,
"interface" ,
"typedef" ,
// misc.
"label" ,
] ] ,
} ;
كود القطعة
فيما يلي كيفية استخدام TreeSitter في تحسين مقطع الكود الخاص بـ LlamaIndex عن طريق تنظيف Tree-Sitter CSTs:
from tree_sitter import Tree
def chunker (
tree : Tree ,
source_code : bytes ,
MAX_CHARS = 512 * 3 ,
coalesce = 50 # Any chunk less than 50 characters long gets coalesced with the next chunk
) -> list [ Span ]:
# 1. Recursively form chunks based on the last post (https://docs.sweep.dev/blogs/chunking-2m-files)
def chunk_node ( node : Node ) -> list [ Span ]:
chunks : list [ Span ] = []
current_chunk : Span = Span ( node . start_byte , node . start_byte )
node_children = node . children
for child in node_children :
if child . end_byte - child . start_byte > MAX_CHARS :
chunks . append ( current_chunk )
current_chunk = Span ( child . end_byte , child . end_byte )
chunks . extend ( chunk_node ( child ))
elif child . end_byte - child . start_byte + len ( current_chunk ) > MAX_CHARS :
chunks . append ( current_chunk )
current_chunk = Span ( child . start_byte , child . end_byte )
else :
current_chunk += Span ( child . start_byte , child . end_byte )
chunks . append ( current_chunk )
return chunks
chunks = chunk_node ( tree . root_node )
# 2. Filling in the gaps
for prev , curr in zip ( chunks [: - 1 ], chunks [ 1 :]):
prev . end = curr . start
curr . start = tree . root_node . end_byte
# 3. Combining small chunks with bigger ones
new_chunks = []
current_chunk = Span ( 0 , 0 )
for chunk in chunks :
current_chunk += chunk
if non_whitespace_len ( current_chunk . extract ( source_code )) > coalesce