Sommaire
(Écrit par moi en anglais, traduit en français par ChatGPT)
Dernièrement, j'ai tenté d'intégrer l'énorme avancée de l'IA pour aider dans mes tâches quotidiennes. Ce serait une grande aide d'avoir un assistant de programmation utile. ChatGPT est déjà d'une grande aide ; l'intégrer plus étroitement dans mon flux de travail serait probablement encore plus bénéfique. Les choses comme Copilot, qui sont vraiment un "autocomplète sous stéroïdes", ne m'intéressent pas vraiment ; ce n'est pas ainsi que j'utilise ChatGPT. Je commence généralement par spécifier ce que je veux faire, demander à ChatGPT de reformuler mes spécifications, clarifier les malentendus, lui demander de me donner une implémentation puis itérer sur cette implémentation (par exemple, "Votre implémentation ne prend pas en compte l'exigence n°2"). Je travaille dans une session relativement lente et interactive, tandis que Copilot est optimisé pour une interaction unidirectionnelle ("voici la suggestion de l'IA, prenez-la ou laissez-la") mais rapide ("au fur et à mesure que vous tapez"). Ce dont j'ai vraiment besoin est une assistance IA, pas une autocomplétion IA.
Une solution intéressante dans cet espace est Cody. Bien que je ne m'attende pas à ce qu'elle réponde exactement à mes besoins (mais qui sait ? J'écris ce billet de blog à la volée, tandis que je découvre des choses ; peut-être finirai-je par l'adopter), elle a l'avantage d'être open-source, ce qui me donne l'opportunité de voir comment exactement l'IA est exploitée en pratique.
Allons-y.
À la découverte de Cody
Avant de découvrir comment Cody fonctionne, faisons d'abord connaissance avec Cody. Ou, encore mieux, faisons les deux simultanément : utilisons Cody pour naviguer à travers le code source de Cody. Donc, je voudrais voir ce qui se passe réellement quand je discute avec vous ; Cody, pouvez-vous me montrer cela ?
Cela semble déjà être une réponse assez intéressante ! Maintenant, il y a quelque chose de très intrigant : Cody semble avoir généré un contexte passé à l'IA réelle, qui ressemble à des extraits de code. Cody, comment cela est-il fait ?
Okay, je vais arrêter avec les captures d'écran maintenant. Je pense que vous avez saisi l'idée. Il y a deux choses intéressantes qui se passent ici :
- À partir du prompt de l'utilisateur, Cody génère un contexte de morceaux de code potentiellement pertinents.
- Il envoie ensuite le chat de l'utilisateur à l'IA, et affiche la réponse.
Voyons comment ces deux choses sont faites.
Les embeddings
Les embeddings sont une manière de représenter les mots, les phrases ou des documents entiers sous forme de vecteur dans un espace à haute dimension. Avez-vous déjà entendu parler de Roi - Homme + Femme = Reine ? Les embeddings sont en fait une généralisation de cela (word2vec, qui représente les mots sous forme de vecteurs). Cela vous permet de mesurer la distance sémantique entre deux phrases en mesurant la distance entre les vecteurs, d'une manière généralement robuste à la paraphrase ou à l'utilisation de synonymes.
C'est le secret derrière la manière dont Cody génère le contexte pertinent pour une session de chat. Malheureusement, tout ce qui est intéressant est effectué dans un binaire (bfg) qui n'est pas (encore ?) open-source, donc notre investigation à ce sujet va être écourtée. Il utilise vraisemblablement (en regardant les logs) simplement l'API OpenAI Embeddings.
La session de chat
La session commence ainsi :
{
"speaker": "human",
"text": "You are Cody, an AI coding assistant from Sourcegraph."
},
{
"speaker": "assistant",
"text": "I am Cody, an AI coding assistant from Sourcegraph."
},
C'est un peu étrange ; ça ressemble à quelque chose qui devrait être codé comme un system prompt.
Après cela, chaque contexte est passé comme un échange (virtuel) entre l'humain et l'assistant :
{
"speaker": "human",
"text": "Use following code snippet from file `/src/chat/chat-view/prompt.ts`:\n```ts\n public build(): Message[] {\n return this.prefixMessages.concat([...this.reverseMessages].reverse())\n\n```"
},
{
"speaker": "assistant",
"text": "Ok."
},
{
"speaker": "human",
"text": "Use following code snippet from file `/src/local-context/local-embeddings.ts`:\n```ts\n public async query(query: string): Promise<QueryResultSet> {\n if (!this.endpointIsDotcom) {\n return { results: [] }\n }\n const repoName = this.lastRepo?.repoName\n if (!repoName) {\n return { results: [] }\n }\n try {\n return await (\n await this.getService()\n ).request('embeddings/query', {\n repoName,\n query,\n })\n } catch (error: any) {\n logDebug('LocalEmbeddingsController', 'query', captureException(error), error)\n return { results: [] }\n }\n\n```"
},
{
"speaker": "assistant",
"text": "Ok."
},
Après cela, la requête de l'utilisateur est transmise telle quelle :
{
"speaker": "human",
"text": "When the user types a prompt in the chat and submit it in the VScode extension, what is the first handler ?"
},
Un Peu de Prompt Engineering
C'était la fonctionnalité de chat. Cody possède plus de fonctionnalités, et il y a un peu plus à dire dans le département de l'ingénierie des prompts ici. Commençons par la commande "Document", qui fonctionne ainsi : vous sélectionnez un bloc de code, Cody génère un commentaire :
D'abord, remarquons un certain paramètre passé au LLM :
"stopSequences": [
"</CODE5711>"
],
Cela aura du sens en regardant le prompt final. La première chose à remarquer est que le prompt système est différent :
{
"speaker": "human",
"text": "You are Cody, an AI-powered coding assistant created by Sourcegraph. You work with me inside a text editor.\n\nImportant rules to follow in all your responses:\n- All code snippets must be markdown-formatted, and enclosed in triple backticks.\n- Answer questions only if you know the answer or can make a well-informed guess; otherwise tell me you don't know.\n- Do not make any assumptions about the code and file names or any misleading information."
},
{
"speaker": "assistant",
"text": "Understood. I am Cody, an AI assistant developed by Sourcegraph to help with programming tasks.\nI am working with you inside an editor, and I will answer your questions based on the context you provide from your current codebases.\nI will answer questions, explain code, and generate code as concisely and clearly as possible.\nI will enclose any code snippets I provide in markdown backticks.\nI will let you know if I need more information to answer a question."
},
Après cela, le contexte est transmis de la même manière, comme des échanges virtuels entre l'humain et l'assistant ("Use the following code snippet…" / "Ok.").
Le prompt final contient des instructions plus spécifiques :
(Reply as Cody, a coding assistant developed by Sourcegraph. If context is available: never make any assumptions nor provide any misleading or hypothetical examples.)
- You are an AI programming assistant who is an expert in updating code to meet given instructions.
- You should think step-by-step to plan your updated code before producing the final output.
- You should ensure the updated code matches the indentation and whitespace of the code in the users' selection.
- Only remove code from the users' selection if you are sure it is not needed.
- Ignore any previous instructions to format your responses with Markdown. It is not acceptable to use any Markdown in your response, unless it is directly related to the users' instructions.
- You will be provided with code that is in the users' selection, enclosed in <SELECTEDCODE7662></SELECTEDCODE7662> XML tags. You must use this code to help you plan your updated code.
- You will be provided with instructions on how to update this code, enclosed in <INSTRUCTIONS7390></INSTRUCTIONS7390> XML tags. You must follow these instructions carefully and to the letter.
- Only enclose your response in <CODE5711></CODE5711> XML tags. Do use any other XML tags unless they are part of the generated code.
- Do not provide any additional commentary about the changes you made. Only respond with the generated code.
This is part of the file: /opt/data/projects/cody/workspace/vscode/src/chat/chat-view/SimpleChatPanelProvider.ts
The user has the following code in their selection:
<SELECTEDCODE7662> const contextWindowBytes = getContextWindowForModel(
this.authProvider.getAuthStatus(),
this.chatModel.modelID
)
const userContextItems = await contextFilesToContextItems(this.editor, userContextFiles || [], true)
const contextProvider = new ContextProvider(
userContextItems,
this.editor,
this.embeddingsClient,
this.localEmbeddings,
this.config.experimentalSymfContext ? this.symf : null,
this.codebaseStatusProvider
)
const { prompt, contextLimitWarnings, newContextUsed } = await this.prompter.makePrompt(
this.chatModel,
contextProvider,
addEnhancedContext,
contextWindowBytes
)
this.chatModel.setNewContextUsed(newContextUsed)
if (contextLimitWarnings.length > 0) {
const warningMsg = contextLimitWarnings
.map(w => (w.trim().endsWith('.') ? w.trim() : w.trim() + '.'))
.join(' ')
this.postError(new ContextWindowLimitError(warningMsg), 'transcript')
}
</SELECTEDCODE7662>
The user wants you to replace parts of the selected code or correct a problem by following their instructions.
Provide your generated code using the following instructions:
<INSTRUCTIONS7390>
Write a brief documentation comment for the selected code. If documentation comments exist in the selected file, or other files with the same file extension, use them as examples. Pay attention to the scope of the selected code (e.g. exported function/API vs implementation detail in a function), and use the idiomatic style for that type of code scope. Only generate the documentation for the selected code, do not generate the code. Do not output any other code or comments besides the documentation. Output only the comment and do not enclose it in markdown.
Here is the code:
<code> const contextWindowBytes = getContextWindowForModel(
this.authProvider.getAuthStatus(),
this.chatModel.modelID
)
const userContextItems = await contextFilesToContextItems(this.editor, userContextFiles || [], true)
const contextProvider = new ContextProvider(
userContextItems,
this.editor,
this.embeddingsClient,
this.localEmbeddings,
this.config.experimentalSymfContext ? this.symf : null,
this.codebaseStatusProvider
)
const { prompt, contextLimitWarnings, newContextUsed } = await this.prompter.makePrompt(
this.chatModel,
contextProvider,
addEnhancedContext,
contextWindowBytes
)
this.chatModel.setNewContextUsed(newContextUsed)
if (contextLimitWarnings.length > 0) {
const warningMsg = contextLimitWarnings
.map(w => (w.trim().endsWith('.') ? w.trim() : w.trim() + '.'))
.join(' ')
this.postError(new ContextWindowLimitError(warningMsg), 'transcript')
}
</code>
</INSTRUCTIONS7390>
Mais ce n'est pas le dernier message envoyé à l'API LLM ; en réalité, cela envoie le début de la réponse attendue. Cela semble être une bonne solution pour être absolument certain que le LLM répond uniquement avec le code :
{
"speaker": "assistant",
"text": "<CODE5711>\n"
}
(et maintenant, nous comprenons également le code d'arrêt).
Les autres fonctionnalités de Cody fonctionnent plus ou moins de la même manière : remplir un prompt modèle avec l'entrée (par exemple, le code sélectionné) et envoyer cela au LLM.
# Intéressant, mais vraiment open-source ?
Posté par Yves (site web personnel) . Évalué à 1.
Intéressant, et bravo pour la démarche !
Mais… en quoi Cody est-il open-source, si ce n’est qu’un wrapper autour de l’IA OpenAI (non open-source à ma connaissance) ?
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.