2021년 Microsoft, OpenAI, Github가 공동으로 유용한 코드 완성 및 제안 도구인 Copilot을 만들었습니다.
개발자의 코드 편집기 내에서 코드 줄을 추천합니다. 예를 들어 개발자가 Visual Studio Code, Neovim 및 JetBrains IDE와 같은 통합 개발 환경에 코드를 입력하면 다음 코드 줄을 추천할 수 있습니다. 또한 Copilot은 완전한 방법과 복잡한 알고리즘에 대한 조언은 물론 템플릿 코드 및 단위 테스트에 대한 지원도 제공할 수 있습니다.
1년 이상이 지난 지금, 이 도구는 많은 프로그래머들에게 뗄래야 뗄 수 없는 "프로그래밍 파트너"가 되었습니다. 전 테슬라 인공지능 이사 안드레이 카르파티(Andrej Karpathy)는 "코파일럿 덕분에 내 프로그래밍 속도가 크게 빨라졌고, 어떻게 '수동 프로그래밍'으로 돌아가는지는 상상하기 어렵다. 현재는 아직 사용법을 배우는 단계이고, 코드의 정확도는 80%에 가깝습니다. "
익숙해지는 것 외에도 Copilot의 프롬프트가 어떻게 생겼는지 등 Copilot에 대한 몇 가지 질문도 있습니다. 모델을 어떻게 호출하나요? 추천 성공률은 어떻게 측정되나요? 사용자 코드 조각을 수집하여 자체 서버로 보내나요? 코파일럿 뒤에 있는 모델은 대형 모델인가요, 소형 모델인가요?
이 질문에 답하기 위해 일리노이 대학 어바나-샴페인 캠퍼스의 한 연구원이 대략적으로 Copilot을 리버스 엔지니어링하고 그의 관찰 내용을 블로그에 올렸습니다.
Andrej Karpathy는 자신의 트윗에서 이 블로그를 추천했습니다.
다음은 블로그 원문입니다.
Github Copilot은 저에게 매우 유용합니다. 그것은 종종 마술처럼 내 마음을 읽고 유용한 제안을 하기도 합니다. 가장 놀랐던 점은 주변 코드(다른 파일의 코드 포함)에서 함수/변수를 정확하게 "추측"하는 능력이었습니다. 이는 Copilot 확장이 주변 코드에서 Codex 모델로 중요한 정보를 보낼 때만 발생합니다. 어떻게 작동하는지 궁금해서 소스코드를 살펴보기로 했습니다.
이 게시물에서는 Copilot의 내부에 대한 구체적인 질문에 답하는 동시에 코드를 샅샅이 조사하면서 발견한 몇 가지 흥미로운 관찰 사항을 설명하려고 합니다.
이 프로젝트의 코드는 여기에서 찾을 수 있습니다:
코드 주소: https://github.com/thakkarparth007/copilot-explorer
전체 기사는 다음과 같이 구성됩니다. :
몇 달 전 저는 Copilot 확장의 매우 피상적인 "리버스 엔지니어링"을 수행했으며 그 이후로 더 깊이 파고들고 싶었습니다. 나는 지난 몇 주 동안 마침내 이 일을 하게 되었습니다. 기본적으로 Copilot에 포함된 Extension.js 파일을 사용하여 모듈의 자동 추출을 단순화하기 위해 몇 가지 사소한 수동 변경을 수행했으며 각 모듈을 "아름답게" 지정하고 모듈 이름을 지정하기 위해 여러 개의 AST 변환을 작성했습니다. 또한 분류하고 수동으로 주석을 달았습니다. 가장 흥미로운 부분 중 일부.
내가 만든 도구를 통해 리버스 엔지니어링된 부조종사 코드 베이스를 탐색할 수 있습니다. 포괄적이고 세련되지는 않을 수 있지만 여전히 Copilot의 코드를 탐색하는 데 사용할 수 있습니다.
도구 링크: https://thakkarparth007.github.io/copilot-explorer/
Github Copilot은 다음 두 가지 주요 부분으로 구성됩니다.
이제 Codex는 대량의 공개 Github 코드에 대해 교육을 받았으므로 유용한 제안을 할 수 있다는 것이 합리적입니다. 하지만 Codex는 현재 프로젝트에 어떤 기능이 있는지 알 수 없습니다. 그럼에도 불구하고 Codex는 여전히 프로젝트 기능과 관련된 제안을 제공할 수 있습니다.
이에 대한 답은 두 부분으로 나누어 보겠습니다. 먼저 Copilot에서 생성한 실제 프롬프트 예를 살펴본 다음 이것이 어떻게 생성되는지 살펴보겠습니다.
프롬프트는 다음과 같습니다
Copilot 확장 프로그램은 프롬프트에서 프로젝트와 관련된 많은 정보를 인코딩합니다. Copilot에는 상당히 복잡한 프롬프트 엔지니어링 파이프라인이 있습니다. 다음은 프롬프트의 예입니다.
{"prefix": "# Path: codeviz\app.pyn# Compare this snippet from codeviz\predictions.py:n# import jsonn# import sysn# import timen# from manifest import Manifestn# n# sys.path.append(__file__ + "/..")n# from common import module_codes, module_deps, module_categories, data_dir, cur_dirn# n# gold_annots = json.loads(open(data_dir / "gold_annotations.js").read().replace("let gold_annotations = ", ""))n# n# M = Manifest(n# client_name = "openai",n# client_connection = open(cur_dir / ".openai-api-key").read().strip(),n# cache_name = "sqlite",n# cache_connection = "codeviz_openai_cache.db",n# engine = "code-davinci-002",n# )n# n# def predict_with_retries(*args, **kwargs):n# for _ in range(5):n# try:n# return M.run(*args, **kwargs)n# except Exception as e:n# if "too many requests" in str(e).lower():n# print("Too many requests, waiting 30 seconds...")n# time.sleep(30)n# continuen# else:n# raise en# raise Exception("Too many retries")n# n# def collect_module_prediction_context(module_id):n# module_exports = module_deps[module_id]["exports"]n# module_exports = [m for m in module_exports if m != "default" and "complex-export" not in m]n# if len(module_exports) == 0:n# module_exports = ""n# else:n# module_exports = "It exports the following symbols: " + ", ".join(module_exports)n# n# # get module snippetn# module_code_snippet = module_codes[module_id]n# # snip to first 50 lines:n# module_code_snippet = module_code_snippet.split("\n")n# if len(module_code_snippet) > 50:n# module_code_snippet = "\n".join(module_code_snippet[:50]) + "\n..."n# else:n# module_code_snippet = "\n".join(module_code_snippet)n# n# return {"exports": module_exports, "snippet": module_code_snippet}n# n# #### Name prediction ####n# n# def _get_prompt_for_module_name_prediction(module_id):n# context = collect_module_prediction_context(module_id)n# module_exports = context["exports"]n# module_code_snippet = context["snippet"]n# n# prompt = f"""\n# Consider the code snippet of an unmodule named.n# nimport jsonnfrom flask import Flask, render_template, request, send_from_directorynfrom common import *nfrom predictions import predict_snippet_description, predict_module_namennapp = Flask(__name__)nn@app.route('/')ndef home():nreturn render_template('code-viz.html')nn@app.route('/data/<path:filename>')ndef get_data_files(filename):nreturn send_from_directory(data_dir, filename)nn@app.route('/api/describe_snippet', methods=['POST'])ndef describe_snippet():nmodule_id = request.json['module_id']nmodule_name = request.json['module_name']nsnippet = request.json['snippet']ndescription = predict_snippet_description(nmodule_id,nmodule_name,nsnippet,n)nreturn json.dumps({'description': description})nn# predict name of a module given its idn@app.route('/api/predict_module_name', methods=['POST'])ndef suggest_module_name():nmodule_id = request.json['module_id']nmodule_name = predict_module_name(module_id)n","suffix": "if __name__ == '__main__':rnapp.run(debug=True)","isFimEnabled": true,"promptElementRanges": [{ "kind": "PathMarker", "start": 0, "end": 23 },{ "kind": "SimilarFile", "start": 23, "end": 2219 },{ "kind": "BeforeCursor", "start": 2219, "end": 3142 }]}
보시다시피 위 프롬프트에는 접두사와 접미사가 포함되어 있습니다. 그런 다음 Copilot은 이 프롬프트(일부 형식 지정 후)를 모델에 보냅니다. 이 경우 접미사가 비어 있지 않기 때문에 Copilot은 "삽입 모드", 즉 FIM(Fill-in-Middle) 모드에서 Codex를 호출합니다.
접두사를 보면 프로젝트의 다른 파일에 있는 일부 코드가 포함되어 있음을 알 수 있습니다. # codevizpredictions.py의 이 스니펫 비교를 참조하세요. 코드 줄과 그 뒤의 줄
프롬프트는 어떻게 준비되어 있나요?
대략 다음과 같은 일련의 단계가 프롬프트를 생성하기 위해 실행됩니다.
일반적으로 프롬프트는 다음과 같은 일련의 단계를 통해 단계별로 생성됩니다.
1. 진입점: 프롬프트 추출. 지정된 문서와 커서 위치가 주어지면 발생합니다. 생성된 주요 진입점은 extractPrompt(ctx, doc, insertPos)
2입니다. VSCode에서 문서의 상대 경로와 언어 ID를 쿼리합니다. 참조: getPromptForRegularDoc(ctx, doc, insertPos)
3. 관련 문서: 그런 다음 VSCode에서 동일한 언어로 가장 최근에 액세스한 파일 20개를 쿼리합니다. getPromptHelper(ctx, docText, insertOffset, docRelPath, docUri, docLangId) 를 참조하세요. 이러한 파일은 나중에 프롬프트에 포함될 유사한 조각을 추출하는 데 사용됩니다. 저는 개인적으로 다중 언어 개발이 매우 일반적이기 때문에 동일한 언어를 필터로 사용하는 것이 이상하다고 생각합니다. 그러나 나는 그것이 여전히 대부분의 경우에 적용된다고 생각합니다.
4. 구성: 다음으로 몇 가지 옵션을 설정합니다. 구체적으로 다음을 포함합니다:
5. 접두사 계산: 이제 "프롬프트 위시리스트"를 생성하여 프롬프트의 접두사 부분을 계산합니다. 여기에는 다양한 "요소"와 해당 우선순위를 추가합니다. 예를 들어 요소는 "의 이 조각 비교"와 같거나 로컬 가져오기의 컨텍스트 또는 각 파일의 언어 ID 및/또는 경로일 수 있습니다. 이 모든 작업은 getPrompt (fs, curFile, PromptOpts = {}, 관련Docs = []) 에서 발생합니다.
6. 접미사 계산: 이전 단계는 접두사에 대한 것이지만 접미사의 논리는 비교적 간단합니다. 커서에서 사용 가능한 접미사로 토큰 예산을 채우면 됩니다. 이것이 기본값이지만, AB 실험 프레임워크에서도 제어되는 SuffixStartMode 옵션에 따라 접미사의 시작 위치가 약간 달라집니다. 예를 들어, SuffixStartMode가 SiblingBlock인 경우 Copilot은 먼저 편집 중인 함수의 가장 가까운 기능적 형제를 찾아 거기에서 접미사를 작성합니다.
조각 추출 자세히 살펴보기
나에게 있어 프롬프트 생성의 가장 완벽한 부분은 다른 파일에서 조각을 추출하는 것 같습니다. 여기에서 호출되며 neighbor-snippet-selector.getNeighbourSnippets에 의해 정의됩니다. 옵션에 따라 "Fixed window Jaccard Matcher" 또는 "Indentation based Jaccard Matcher"가 사용됩니다. 100% 확신할 수는 없지만 Indentation 기반 Jaccard Matcher가 실제로 사용되는 것 같지는 않습니다.
기본적으로 고정 창 Jaccard Matcher를 사용합니다. 이 경우, 주어진 파일(조각이 추출될 파일)은 고정 크기 슬라이딩 창으로 분할됩니다. 그런 다음 각 창과 참조 파일(입력 중인 파일) 간의 Jaccard 유사성을 계산합니다. 각 "관련 파일"에 대해 최적의 창만 반환됩니다(상위 K 조각을 반환해야 한다는 요구 사항이 있지만 이를 따르지 않습니다). 기본적으로 FixWindowJaccardMatcher는 "Eager 모드"(즉, 창 크기는 60줄)에서 사용됩니다. 그러나 이 모드는 AB 실험 프레임워크에 의해 제어되므로 다른 모드를 사용할 수도 있습니다.
Copilot은 Inline/GhostText 및 Copilot 패널이라는 두 가지 UI를 통해 완성 기능을 제공합니다. 두 경우 모두 모델 호출 방식에 약간의 차이가 있습니다.
Inline/GhostText
메인 모듈: https://thakkarparth007.github.io/copilot-explorer/codedeviz/templates/code-viz.html#m9334&pos=301:14
In 그중 Copilot 확장에서는 모델이 속도를 높이기 위해 매우 적은 제안(1~3개)을 제공해야 합니다. 또한 모델의 결과를 적극적으로 캐시합니다. 또한 사용자가 계속 입력하는 경우 제안 사항을 조정합니다. 사용자가 매우 빠르게 입력하는 경우 모델에 디바운싱 기능을 켜도록 요청합니다.
이 UI는 특정 상황에서 요청이 전송되지 않도록 일부 논리도 설정합니다. 예를 들어, 사용자 커서가 줄 중간에 있는 경우 오른쪽에 있는 문자가 공백, 닫는 중괄호 등인 경우에만 요청이 전송됩니다.
1. 상황별 필터를 통해 잘못된 요청 차단
더 흥미로운 점은 프롬프트를 생성한 후 모듈이 프롬프트가 모델을 호출하기에 "충분히 좋은"지 확인한다는 것입니다. 이는 "컨텍스트 필터링 점수"를 계산하여 달성됩니다. 이 점수는 언어, 이전 제안의 수락/거절 여부, 이전 수락/거부 사이의 기간, 프롬프트의 마지막 줄 길이, 마지막 제안의 길이 등 11가지 기능을 포함하는 단순 로지스틱 회귀 모델을 기반으로 하는 것으로 보입니다. 캐릭터 등 이 모델 가중치는 확장 코드 자체에 포함되어 있습니다.
점수가 임계값(기본값 15%) 미만인 경우 요청이 이루어지지 않습니다. 이 모델을 탐색하는 것은 흥미로울 것입니다. 일부 언어는 다른 언어보다 가중치가 더 높다는 것을 관찰했습니다(예: php > js > python > Rust > dart…php). 또 다른 직관적인 관찰은 프롬프트가 ) 또는 ]로 끝나는 경우 점수가 ( 또는 [로 끝나는 경우보다 낮다는 것입니다. 전자는 이미 "완료"되었음을 나타낼 가능성이 높기 때문에 의미가 있습니다. 후자는 사용자가 자동 완성의 이점을 누릴 수 있음을 명확하게 나타냅니다.
Copilot 패널
기본 모듈: https://thakkarparth007.github.io/copilot-explorer/codedeviz/templates/code-viz.html#m2990&pos =12: 1
핵심 논리 1: https://thakkarparth007.github.io/copilot-explorer/codeviz/templates/code-viz.html#m893&pos=9:1
핵심 논리 2: https ://thakkarparth007.github.io/copilot-explorer/codedeviz/templates/code-viz.html#m2388&pos=67:1
이 UI는 인라인 UI에 비해 모델에서 더 많은 샘플을 요청합니다(기본적으로 10). . 이 UI에는 상황별 필터링 논리가 없는 것 같습니다(사용자가 명시적으로 호출하는 경우 모델에 메시지를 표시하지 않는 것이 합리적입니다).
여기에는 두 가지 주요 흥미로운 사항이 있습니다.
제안을 표시하기 전에(UI를 통해) Copilot은 두 가지 확인을 수행합니다.
출력이 중복되는 경우(예: foo = foo = foo = foo...), 이는 언어 모델의 일반적인 실패 모드이며, 이 제안은 Copilot 프록시 서버 또는 클라이언트에서 발생할 수 있습니다.사용자가 입력한 경우 제안, 이 제안도 폐기됩니다
팁 3: 원격 측정
Github은 이전 블로그에서 프로그래머가 작성한 코드의 40%가 Copilot에서 작성되었다고 주장했습니다(Python과 같은 인기 언어의 경우). ). 이 숫자를 어떻게 측정했는지 궁금해서 원격 측정 코드에 뭔가를 삽입하고 싶었습니다.
특히 코드 조각을 수집하는 경우 어떤 원격 측정 데이터를 수집하는지 알고 싶습니다. 이것이 궁금합니다. Copilot 확장을 Github 백엔드 대신 오픈 소스 FauxPilot 백엔드로 쉽게 지정할 수 있지만 확장은 여전히 원격 측정을 통해 코드 조각을 Github에 보낼 수 있기 때문에 코드 개인 정보 보호에 대해 우려하는 일부 사람들이 이를 사용하지 못하게 될 수 있습니다. 부조종사. 이것이 사실인지 궁금합니다.
질문 1: 40% 수치는 어떻게 측정되나요?
Copilot의 성공률을 측정하는 것은 단순히 수락/거부 횟수를 세는 문제가 아닙니다. 사람들은 일반적으로 권장 사항을 수락하고 약간의 수정을 가하기 때문입니다. 따라서 Github 직원은 수락된 제안이 코드에 여전히 존재하는지 확인합니다. 구체적으로는 추천 코드가 삽입된 후 15초, 30초, 2분, 5분, 10분 후에 확인합니다.
허용된 제안에 대한 정확한 검색은 이제 너무 제한적이므로 제안된 텍스트와 삽입 지점 주변 창 사이의 편집 거리(문자 수준 및 단어 수준에서)를 측정합니다. 삽입과 창 사이의 단어 수준 편집 거리가 50%(제안 크기로 정규화됨) 미만인 경우 제안은 "아직 코드에 있음"으로 간주됩니다.물론, 이 모든 것은 허용된 코드에만 적용됩니다. 질문 2: 원격 측정 데이터에 코드 조각이 포함되어 있나요? 예, 포함되어 있습니다. 제안을 수락하거나 거부한 후 30초 후에 부조종사는 삽입 지점 근처의 스냅샷을 "캡처"합니다. 특히 확장 프로그램은 프롬프트 추출 메커니즘을 호출하여 해당 삽입 지점에서 제안을 하는 데 사용할 수 있는 "가설 프롬프트"를 수집합니다. Copilot은 또한 삽입 지점과 "추측된" 끝점 사이의 코드를 캡처하여 "가설적 완성"을 캡처합니다. 이 끝점을 어떻게 추측하는지 잘 모르겠습니다. 앞에서 언급했듯이 이는 승인 또는 거부 후에 발생합니다. 이 스냅샷이 모델을 더욱 개선하기 위한 훈련 데이터로 사용될 수 있다고 생각합니다. 그러나 30초는 코드가 "정착"되었다고 가정하기에는 너무 짧은 시간인 것 같습니다. 하지만 원격 측정에 사용자 프로젝트에 해당하는 github 저장소가 포함되어 있다는 점을 고려하면 30초 기간에 시끄러운 데이터 포인트가 생성되더라도 GitHub 직원은 상대적으로 시끄러운 이 데이터를 오프라인에서 정리할 수 있습니다. 물론 이 모든 것은 제 추측일 뿐입니다. GitHub에서는 "제품 개선"을 위해 코드 조각을 사용하는 데 동의할지 여부를 선택할 수 있습니다. 동의하지 않으면 이러한 조각이 포함된 원격 분석이 서버로 전송되지 않습니다(적어도 v1에서는). .57 에서 확인했는데 v1.65에서도 확인했습니다. 네트워크를 통해 전송되기 전에 코드를 보고 원격 측정 데이터 포인트를 기록하여 이를 확인합니다. 자세한 로깅을 활성화하도록 확장 코드를 약간 수정했습니다(구성 가능한 매개변수를 찾을 수 없음). 이 모델이 "cushman-ml"이라는 것을 알게 되었는데, 이는 Copilot이 175B 매개변수 모델 대신 12B 매개변수 모델을 사용하고 있을 수 있음을 강력하게 시사합니다. 오픈 소스 작업자에게 이는 매우 고무적인 일입니다. 즉, 적당한 규모의 모델이 훌륭한 조언을 제공할 수 있다는 의미입니다. 물론 Github가 보유하고 있는 막대한 양의 데이터는 여전히 오픈 소스 작업자가 접근할 수 없습니다. 이 글에서는 확장자로 배포되는 Worker.js 파일에 대해서는 다루지 않았습니다. 언뜻 보면 기본적으로 프롬프트 추출 논리의 병렬 버전만 제공하는 것처럼 보이지만 더 많은 기능이 있을 수 있습니다. 파일 주소: https://thakkarparth007.github.io/copilot-explorer/muse/github.copilot-1.57.7193/dist/worker_expanded.js 자세한 로깅 활성화 if 자세한 로깅을 활성화하려면 확장 코드를 수정하여 활성화할 수 있습니다. 기성 패치를 원한다면 확장 코드 https://thakkarparth007.github.io/copilot-explorer/muse/github를 복사하세요. copilot-1.57.7193 /dist/extension.js 이 버전은 1.57.7193 원본 텍스트에 더 자세한 링크가 있으므로 관심 있는 독자는 원문을 확인할 수 있습니다.기타 관찰
위 내용은 Copilot을 리버스 엔지니어링한 후 12B 매개변수가 있는 작은 모델만 사용할 수 있다는 사실을 발견했습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!