Как разработчик, я устал использовать Github Copilot, когда я хочу использовать новый пакет Python, а он понятия не имеет, что это за пакет, и предлагает фиктивный код.

Я думаю, что ОЧЕНЬ БЛИЖАЙШЕЕ будущее разработки и программирования заключается в том, что ИИ может получать последние изменения кода и документацию для ЛЮБЫХ пакетов и языков программирования и предлагать это вам, не выходя из IDE и материалов Google, и что Github Copilot лучше всего подходит для этого.

Как это будет работать, так это при наличии подсказки или комментария, например:

# Process document and extract fields using veryfi-python OCR package

Боту нужно только получить наиболее подходящую документацию для этого варианта использования и использовать ее в качестве контекста для запроса LLM. Нет необходимости переобучать LLM вообще, проблема сводится к поиску и извлечению наиболее релевантного контекста.

Я создал игрушечное приложение, размещенное бесплатно на Gradio https://huggingface.co/spaces/daidao-ai-100/veryfi-bot только для этого конкретного пакета `veryfi-python`, чтобы продемонстрировать этот вариант использования. Самая сложная часть этого приложения была связана с анализом кода (имени функции, подписи и строки документации) для каждого метода, индексированием его в векторной базе данных и получением с помощью запроса.

https://github.com/dai-dao/veryfi-bot/blob/main/preprocess.py

""" 
Assume that the docstring is inside the function body, as in the case with Veryfi code
Input:
    def test(a : str, b : str) -> str:
        \"\"\"
            this is docstring
        \"\"\"
        a = 1
        
Output:
    this is docstring
"""
def parse_function_docstring(function_body : str) -> Optional[str]:
    if '"""' in function_body:
        start_docstring = function_body.index('"""')
        end_docstring = function_body[start_docstring + 3 : ].index('"""')
        return function_body[start_docstring + 3 : start_docstring + 3 + end_docstring].strip().replace("  ", "")
    return None

"""
- Need to use custom parsing function because using AST doesn't include type hinting 
    - Best way is to look for the opening and closing bracket of the method
- Can not parse just looking for the first closing colon ":\n" because this could occur in function type hinting
    - Also can not just look for the last occuring ":\n" either because it could be in the function body
Input:
    def test(a :
            str = '()', b : str) -> str:
        a = '''
            b :
            1
        '''
        
Output:
    def test(a :
            str = '()', b : str) -> str:
"""
def parse_function_definition(function_body : str) -> Optional[str]:
    if function_body.strip().startswith("def "):
        open_bracket_offset = function_body.index("(")
        close_bracket_offset = -1
        open_bracket_count = 1
        encounter_quote = False
        for idx, char in enumerate(function_body[open_bracket_offset + 1 : ]):
            # In the odd case that characters ( and ) appear in a string as default arguments
            if char in ["'", "\""]:
                encounter_quote = not encounter_quote                
            if char == "(" and not encounter_quote:
                open_bracket_count += 1
            elif char == ")" and not encounter_quote:
                open_bracket_count -= 1
                # Found the first matching closing bracket for the first opening bracket which is the function definition
                if open_bracket_count == 0:
                    close_bracket_offset = idx + open_bracket_offset + 1
                    break

        # Could not find corresponding closing bracket, probably syntax error
        if close_bracket_offset == -1:
            return None
        
        # Now we can find the first colon offset starting from the end of function definition 
        colon_offset = function_body[close_bracket_offset : ].index(":\n") + close_bracket_offset
        return function_body[: colon_offset + 1].strip()

Бот также должен иметь возможность определить, относится ли запрос к этому конкретному пакету или нет. Эту проблему можно решить либо с помощью нескольких запросов к LLM, либо путем точной настройки LLM, когда объем данных становится больше.

https://github.com/dai-dao/veryfi-bot/blob/main/predict.py

def call_gpt_turbo(messages, token_len=300) -> Optional[str]:
  response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=messages,
    temperature=0.,
    max_tokens=token_len,
  )
  if "choices" in response:
    if len(response["choices"]) > 0:
      out = response["choices"][0]["message"]["content"]
      return out.strip()
  return None


"""
Using chatgpt, provide a list of example prompts asking for help on using the veryfi-python package
Take the first sentence of all the docstrings in the code to use as prompts. This represents what user might ask
Prompt format: 
    input: {first_sentence_docstring}
    output: yes
This provides chatgpt with some context to which questions might be relevant to the 
veryfi-python package and which questions are not relevant
Currently this will use the first sentence of all docstrings as input to chatgpt, 
which will become a bottleneck if the package has hundreds of methods.
To scale this approach, the best solution is to fine-tune a custom model, and at inference time only need to 
input the question without the need to provide any examples.
"""
def is_veryfi_python_help_intent(question : str) -> bool:
    def build_prompt_with_samples_for_intent_detection() -> List[dict]:
        out = [{"role" : "system", "content" : """You are a helpful Python developer. 
                                                Please check if the following prompt is asking for help with the veryfi-python package.
                                                I give you a few examples.
                                                """}]
        # Take only the first sentence of the docstring as sample
        with jsonlines.open(f"data/{INDEX_NAME}_doc.jsonl", "r") as reader:
            for sample in reader:
                docstring = sample["docstring"]
                if docstring:
                    first_sentence = docstring.split("\n")[0].strip()
                    out.append({"role" : "user", "content" : first_sentence})
                    out.append({"role" : "assistant", "content" : "yes"})
        return out    

    instruction = \
    """
    Is this question asking for help on using the veryfi-python package?
    """
    messages = build_prompt_with_samples_for_intent_detection()
    messages.append({"role" : "user", "content" : f"{instruction}\n{question.strip()}"})
    out = call_gpt_turbo(messages, token_len=10)
    if out and "yes" in out.lower():
        return True
    return False

Результат? Чертовски хорошо, я надеюсь, что это скоро будет включено в Github Copilot, поэтому мне больше не нужно гуглить и читать документацию :)

Если вам нужна помощь в создании бота для кода на основе вашей документации по коду, обратитесь по адресу [email protected]. Ваше здоровье :)