2023년에는 생성적 AI의 인기로 인해 AI 지원 코딩을 도입하는 조직이 점점 더 많아질 것입니다. 2021년에 출시된 GitHub Copilot과 약간 다른 점은 코드 완성이 여러 시나리오 중 하나일 뿐이라는 것입니다. 많은 기업들이 요구 사항에 따라 완전한 코드 생성 및 코드 검토와 같은 시나리오를 탐색하고 있으며 개발 효율성을 높이기 위해 생성 AI도 도입하고 있습니다.
이러한 맥락에서 우리(Thoughtworks 오픈 소스 커뮤니티)는 더 많은 조직이 자체 AI 지원 코딩 도우미를 구축할 수 있도록 일련의 AI 보조 도구를 오픈 소스로 제공했습니다.
AutoDev를 설계할 당시에는 다양한 오픈 소스 모델이 끊임없이 진화하고 있었기 때문입니다. 이러한 맥락에서 해당 단계는 다음과 같습니다.
따라서 이 튜토리얼도 이 세 단계를 중심으로 진행됩니다. 또한 우리의 경험을 바탕으로 이 튜토리얼의 샘플 기술 스택은 다음과 같습니다.
AI에 대한 우리의 경험은 상대적으로 제한되어 있기 때문에 필연적으로 실수가 있을 수 있습니다. 따라서 우리는 이 오픈 소스 프로젝트를 구축하기 위해 더 많은 개발자와 협력하기를 희망합니다.
JetBrains 2023 "개발자 생태계" 보고서의 인공 지능 부분과 결합하여 생성 AI가 개발 프로세스에서 역할을 할 수 있는 영역을 반영하는 몇 가지 일반적인 시나리오를 요약할 수 있습니다. 주요 시나리오 중 일부는 다음과 같습니다.
AutoDev를 구축할 때 SQL DDL 생성, 요구 사항 생성, TDD 등과 같은 시나리오도 발견했습니다. 그래서. 개발자가 자신의 AI 기능을 사용자 정의할 수 있도록 시나리오를 사용자 정의하는 기능을 제공합니다. 자세한 내용은 https://ide.unitmesh.cc/customize를 참조하세요.
일일 코딩에는 AI 응답 속도에 대한 요구 사항이 서로 다른 여러 가지 시나리오가 있습니다(예를 들어).
장면 | 응답 속도 | 품질 요구 사항 생성 | 예상되는 크기 | 설명하다 |
---|---|---|---|---|
코드 완성 | 빠른 | 가운데 | 1~6B | 코드 완성은 일상적인 코딩에서 가장 일반적인 시나리오이며 응답 속도가 중요합니다. |
문서 생성 | 가운데 | 가운데 | 1 | 문서를 생성하려면 코드 구조에 대한 완전한 이해가 필요하며 속도와 품질도 똑같이 중요합니다. |
코드 검토 | 빠른 | 가운데 | 1 | 코드 검토에는 고품질의 조언이 필요하지만 최대한 신속하게 대응해야 합니다. |
단위 테스트 생성 | 빠른 | 가운데 | 6B~ | 단위 테스트는 컨텍스트를 덜 생성하며 응답성과 AI 품질도 똑같이 중요합니다. |
코드 리팩토링 | 가운데 | 높은 | 32B~ | 코드 리팩토링에는 더 많은 상황별 이해가 필요할 수 있으며 응답 시간이 적당히 느려질 수 있습니다. |
수요 창출 | 가운데 | 높은 | 32B~ | 수요 창출은 상대적으로 복잡한 시나리오이므로 정확성을 보장하기 위해 응답 속도를 적당히 늦출 수 있습니다. |
자연어 코드 검색 및 해석 | 중간-낮음 | 높은 | 32B~ | 자연어 코드 검색 및 해석은 비교적 복잡한 시나리오이므로 정확성을 보장하기 위해 응답 속도를 적당히 늦출 수 있습니다. |
추신: 여기서 32B는 크기의 순서로만 표현되었습니다. 모델이 클수록 효과가 더 좋아지기 때문입니다.
따라서 우리는 이를 종합적인 AI 지원 코딩을 제공하는 대형 1개, 중간 1개, 마이크로 1개 및 3개 모델로 요약합니다.
AI 코드 완성은 IDE 도구를 결합하여 프로그래밍 언어의 코드 컨텍스트와 규칙을 분석할 수 있으며, AI는 자동으로 코드 조각을 생성하거나 제안합니다. 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)
상황 학습(In-Context Learning)을 통해 달성된 완성 내용은 현재 펑션 블록에 있습니다. 예를 들어 원래 코드는 다음과 같습니다.
fun createBlog ( blogDto : CreateBlogDto ): BlogPost {
}
완성된 코드는 다음과 같습니다.
val blogPost = BlogPost (
title = blogDto.title,
content = blogDto.content,
author = blogDto.author
)
return blogRepository.save(blogPost)
애프터블록
상황 학습(In-Context Learning)을 통해 현재 기능 블록 이후 완료(예: 현재 기능 블록 이후 새 기능 완료)를 통해 달성됩니다. 예를 들어 원래 코드는 다음과 같습니다.
fun createBlog ( blogDto : CreateBlogDto ): BlogPost {
// ...
}
완성된 코드는 다음과 같습니다.
fun updateBlog ( id : Long , blogDto : CreateBlogDto ): BlogPost {
// ...
}
fun deleteBlog ( id : Long ) {
// ...
}
해당 AI 완성 기능을 구축할 때 완성 품질을 향상하고 더 나은 사용자 경험을 제공하기 위해 해당 패턴 데이터 세트에 적용하는 것도 고려해야 합니다.
이 기사 작성과 관련된 일부 관련 리소스:
코드 설명은 개발자가 대규모 코드 베이스를 보다 효과적으로 관리하고 이해할 수 있도록 설계되었습니다. 이러한 보조자는 코드 베이스에 대한 질문에 답변하고, 문서를 제공하고, 코드를 검색하고, 오류 원인을 식별하고, 코드 중복을 줄이는 등의 작업을 수행함으로써 개발 효율성을 높이고 오류율을 줄이며 개발자의 작업량을 줄일 수 있습니다.
이 시나리오에서는 우리가 기대하는 세대의 품질에 따라 일반적으로 대형 모델과 마이크로 모델, 중형 모델과 마이크로 모델로 구성됩니다. Chocolate Factory 도구의 디자인 경험과 결합하여 일반적으로 이러한 기능은 여러 단계로 나눌 수 있습니다.
RAG 애플리케이션은 인덱싱과 쿼리라는 두 부분으로 나뉩니다.
인덱싱 단계에서는 텍스트 분할, 벡터화, 데이터베이스 인덱싱 및 기타 기술이 포함된 코드 베이스를 인덱싱해야 합니다. 가장 어려운 요소 중 하나는 분할입니다. 우리가 참조하는 분할 규칙은 https://docs.sweep.dev/blogs/chunking-2m-files입니다. 지금 바로:
다양한 시나리오에서는 다양한 방식으로 분할할 수도 있습니다. 예를 들어 Chocolate Factory에서는 생성된 컨텍스트의 품질을 보장하기 위해 AST를 통해 분할합니다.
쿼리 단계에서는 벡터화 검색, 경로 검색 등과 같은 기존 검색 기술 중 일부를 결합하여 검색 품질을 보장해야 합니다. 동시에 중국어 시나리오에서는 검색 품질을 보장하기 위해 영어를 중국어로 변환하는 등 중국어로 변환하는 문제도 고려해야 합니다.
일상적인 지원을 위해 SQL DDL 자동 생성, 테스트 케이스 자동 생성, 요구 사항 자동 생성 등과 같은 생성 AI를 통해 이를 달성할 수도 있습니다. 이는 프롬프트 단어를 사용자 정의하고 특정 도메인 지식을 결합해야만 달성할 수 있으므로 여기서는 자세히 설명하지 않겠습니다.
모델 외에도 컨텍스트도 AI 지원 기능에 영향을 미치는 중요한 요소입니다. AutoDev를 구축할 때 우리는 두 가지 다른 컨텍스트 모드도 발견했습니다.
간단한 비교는 다음과 같습니다.
관련 맥락 | 비슷한 맥락 | |
---|---|---|
검색 기술 | 정적 코드 분석 | 유사성 검색 |
데이터 구조 정보 | AST, CFG | 유사한 청크 |
크로스 플랫폼 기능 | IDE 또는 독립 파서에 따라 다름 | 특정 플랫폼에 종속되지 않음 |
상황에 맞는 품질 | 매우 높음 | 높은 |
결과 생성 | 매우 높음 | 높은 |
건설 비용 | 언어와 플랫폼에 따라 다름 | 낮은 |
IDE에 대한 지원이 제한되면 컨텍스트 관련이 더 높은 비용 성능을 가져올 것입니다.
GitHub Copilot은 유사한 컨텍스트 아키텍처 패턴을 채택하며 세부 아키텍처는 다음과 같이 계층화됩니다.
"공개" Copilot-Explorer 프로젝트의 연구 자료에서 Prompt가 어떻게 구축되었는지 확인할 수 있습니다. 다음은 전송된 프롬프트 요청입니다.
{
"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
및 기타 유형을 포함하는 프롬프트Elements에서 작성됩니다. 여러 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 클래스 정보를 가져옵니다. 이는 컨텍스트로 모델에 제공됩니다(주석에 제공됨). 이 시점에서 모델은 더 정확한 생성자와 더 정확한 테스트 사례를 생성합니다.
관련 컨텍스트는 다양한 언어의 정적 코드 분석과 다양한 IDE의 API에 의존하기 때문에 다양한 언어와 다양한 IDE에도 적응해야 합니다. 공사비 측면에서는 비슷한 맥락에 비해 가격이 더 비싸다.
IDE와 편집기는 개발자의 주요 도구이며 설계 및 학습 비용이 상대적으로 높습니다. 먼저 공식 템플릿을 사용하여 다음을 생성할 수 있습니다.
그런 다음 그 위에 기능을 추가합니다(매우 간단하지 않습니까). 물론 그렇지 않습니다. 다음은 참조용 IDEA 플러그인 리소스입니다.
물론 AutoDev 플러그인을 참고하는 것이 더 적절합니다.
공식 템플릿을 직접 사용하여 해당 플러그인을 생성할 수 있습니다: https://github.com/JetBrains/intellij-platform-plugin-template
IDEA 플러그인 구현을 위해서는 주로 Action과 Listener를 통해 구현하는데, plugin.xml
에만 등록하면 된다. 자세한 내용은 공식 문서인 IntelliJ 플랫폼 플러그인 SDK를 참조하세요.
초기 단계에서는 AutoDev의 IDE 버전과의 호환성 문제를 고려하지 않았으므로, 나중에 이전 버전의 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 구현을 참조하며 논리가 복잡하지 않습니다.
단축키를 사용하여 트리거
주로 Action에서 사용자 입력을 모니터링하고 다음을 수행합니다.
기능 | 단축키 | 설명하다 |
---|---|---|
요청 완료 | Alt + / | 현재 컨텍스트를 얻은 다음 모델을 통해 완료 결과를 얻습니다. |
인레이 적용 | TAB | IDE에 완료 결과 표시 |
인레이 처리 | ESC | 완료 취소 |
사이클다음인레이 | Alt + ] | 다음 완료 결과로 전환 |
주기이전인레이 | 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)
}
}
}
그런 다음 다양한 입력에 따라 다양한 완료 결과가 트리거되고 구조가 처리됩니다.
렌더링 완료 코드
그런 다음 EditorCustomElementRenderer
에서 상속되는 Inlay Render를 구현해야 합니다.
IDE의 인터페이스 기능과 결합하여 해당 작업, 해당 그룹 및 해당 아이콘을 추가해야 합니다. 다음은 Action의 예입니다.
<add-to-group group-id="ShowIntentionsGroup" relative-to-action="ShowIntentionActions" anchor="after"/>
다음은 AutoDev의 일부 ActionGroup입니다.
그룹 ID | AI가 사용하는 | 설명 |
---|---|---|
표시 의도 그룹 | 코드 리팩토링, 코드 해석, 코드 생성, 코드 테스트 | 코드 컨텍스트에서 힌트를 표시하는 데 사용되며 macOS에서 Alt + Enter 및 ⌥ + Enter 단축키를 통해 액세스됩니다. |
콘솔편집기팝업메뉴 | 오류 수정 | 프로그램 실행 구조의 콘솔 등 콘솔에 표시되는 메뉴입니다. |
Vcs.MessageActionGroup | 코드 정보 생성 | VCS에 커밋 메시지를 작성하는 메뉴입니다. |
Vcs.Log.ContextMenu | 코드 리뷰, 코드 해석, 코드 생성 | VCS에서 로그를 보기 위한 메뉴, 사용 가능한 기능: 코드의 AI 검사, 릴리스 로그 생성. |
편집기팝업메뉴 | 모두 허용됩니다 | 마우스 오른쪽 버튼 클릭 메뉴에서 해당 ActionGroup을 추가할 수도 있습니다. |
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)와 같은 다른 IDE에서 실행하는 것의 차이는 더욱 커집니다. 자세한 소개는 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를 참조하세요.
TODO
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" ,
] ] ,
} ;
청크 코드
다음은 Tree-Sitter CST를 정리하여 LlamaIndex의 코드 청커 개선에 TreeSitter가 사용되는 방법입니다.
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