[NB: un suivi de ce dépôt à l'aide de GPT-4 peut être trouvé ici.]
Des modèles de grands langues (LLM) tels que Chatgpt et Claude ont démontré des capacités de programmation impressionnantes et sont capables de résoudre des problèmes dans un large éventail de langues et de leurs taxonomies. Malgré ces succès, un certain scepticisme persiste dans la mesure dans laquelle ces modèles présentent une appréciation sous-jacente des règles syntaxiques et opérationnelles sous-jacentes à ces langues ( par rapport à la mémorisation des modèles à partir des données de formation).
Dans ce référentiel d'ingénierie invite, les capacités de programmation de Chatgpt sont explorées à l'aide d'un langage arbitraire spécifique au domaine (DSL). Les DSL représentent un substrat attractif pour étudier les capacités d'inférence des LLMS car ils sont nouveaux et moins susceptibles d'avoir été largement rencontrés et mémorisés pendant la formation 1 . En tant que tels, ils permettent un test plus direct de la mesure dans laquelle les LLM peuvent déduire les règles des nouveaux langages de programmation de manière à quelques coups .
Ici, le langage spécifique au domaine SIL (langage d'intégration de symétrie) a été sélectionné pour deux raisons. Premièrement, il est extrêmement improbable que Chatgpt ait été exposé à tout code SIL pendant la formation, car il s'agit d'un DSL interne développé par un fonds de spéléané chargé de la technologie appelée Symetry Investments. Deuxièmement, en tant que langage de programmation, il a des caractéristiques intéressantes pour le modèle à reproduire (par exemple, c'est un langage fonctionnel qui met l'accent sur l'expressivité, mais manque let expressions comme dans Haskell ou OCAML).

Après quelques exemples d'invites de code, Chatgpt tente d'écrire du code dans un nouveau DSL fonctionnel connu sous le nom de «SIL».
Vous trouverez ci-dessous une collection d'invites composées de courts exemples de code SIL qui mettent en évidence ses fonctionnalités. Après avoir incité Chatgpt avec la tâche et fourni des échantillons de code SIL (voir la figure ci-dessous; l'historique complet et les exemples de code SIL sont également dans ce repo), je lui ai demandé d'implémenter un certain nombre de tâches de programmation traditionnelles dans SIL.
Dans les sections ci-dessous, je montre certains des exemples de scripts de code SIL avec lesquels le modèle a été invité (l'ensemble complet d'exemples peut être trouvé ici) et ses tentatives d'implémentation de divers problèmes dans SIL.
La première invite est un script pour gérer certaines fonctionnalités de serveur de messagerie. En tant que tel, il diffère considérablement du type de problème que je vais par la suite inciter Chatgpt à résoudre, mais il illustre une partie de la syntaxe, des structures de données et des aspects fonctionnels de SIL (par exemple |> ).
// example of using IMAP IDLE to run rules on new mail
import imap
moveMessages(session,ids,target) => if (ids.length > 0 ) then imap.moveUIDs(session,ids,target) else false
login = imap.ImapLogin(environment( " IMAP_USER " ),environment( " IMAP_PASS " ))
server = imap.ImapServer( " imap.fastmail.com " , " 993 " )
session = imap.Session(server,login, true ,imap.Options(debugMode: true )) | > imap.openConnection | > imap.login
rules=[ [
[ " INBOX/0receipts " ,
[
" FROM [email protected] " ,
" FROM interactivebrokers.com " ,
]],
[ " Junk " ,
[
" FROM Tapatalk " ,
]],
[ " INBOX/newsletters " ,
[
" FROM [email protected] " , " HEADER X-mailer mailgun " , " HEADER X-mailer WPMailSMTP/Mailer/mailgun 2.4.0 " , " HEADER X-mailer nlserver " ,
" FROM hbr.org " , " FROM elliottwave.com " , " OR FROM cio.com FROM cio.co.uk " , " FROM substack.com " , " FROM eaglealpha.com " ,
" FROM haaretz.com " , " FROM gavekal.com " , " FROM go.weka.io " , " FROM marketing.weka.io " , ` HEADER list-unsubscribe "" ` ,
` HEADER list-Id "" ` , ` HEADER list-Post "" ` , ` HEADER list-owner"" ` , ` HEADER Precedence bulk ` , ` HEADER Precedence list ` ,
` HEADER list-bounces "" ` , ` HEADER list-help "" ` , ` HEADER List-Unsubscribe "" ` , " FROM no-reply " ,
]],
[ " INBOX/notifications " ,
[
` KEYWORD "$IsNotification" ` , " FROM [email protected] " , " FROM [email protected] " , " FROM skillcast.com " ,
" FROM reedmac.co.uk " , " FROM [email protected] " , " FROM [email protected] " , " FROM [email protected] " ,
]],
]
runRules(Session,Rules) => Rules
| > map(target => [target[ 0 ],(target[ 1 ] | > map(term => imap.search(Session,term).ids))])
| > mapa(set => moveMessages(Session,set[ 1 ] | > join,set[ 0 ]))
runRulesBox(Session,Rules,Mailbox) => {
imap.select(Session,Mailbox)
in runRules (Session,Rules)
}
inboxes=[ " INBOX " ]
result = inboxes | > mapa(inbox => runRulesBox(session,rules,imap.Mailbox(session,inbox)))
print(result)
import parallel;
threadFunction(x) => {
imap.idle(session)
in inboxes | > mapa(inbox => runRulesBox(session,rules,imap.Mailbox(session,inbox)))
}
parallel.runEvents((x) => false ,[threadFunction]) Le deuxième exemple de code invite vise de manière similaire à mettre en évidence certaines fonctionnalités de ce DSL pour le modèle et introduit de nouvelles fonctions de bibliothèque standard telles que iota et fold .
import imap
import imap_config
import string
// Get the configuration from the environment and command line.
config = imap_config.getConfig(commandLineArguments)
// -------------------------------------------------------------------------------------------------
// Some helper functions.
//
// Firstly, a function to join an array of strings.
joinFields(flds, sep) => {
len(flds) > 0 | > enforce( " Cannot join an empty array. " )
in fold (flds[ 1 :$], (str, fld) => str ~ sep ~ fld, flds[ 0 ])
}
// Secondly, a field formatter which strips the field prefix and pads to a fixed width.
// E.g., ("From: [email protected]" |> fmtField(20)) == "[email protected] "
fmtField(field, width) => {
pad(str) => iota(width - len(str)) | > fold((a, i) => a ~ " " , str)
in field
| > string .split( " : " )[ 1 :$]
| > joinFields( " : " )
| > pad
}
// And thirdly, a function which concatenates the headers into a formatted string.
fmtHeaders(outStr, headers) => {
outStr ~ " " ~ joinFields(headers, " | " ) ~ " n "
}
// -------------------------------------------------------------------------------------------------
// Connect to the inbox.
creds = imap.ImapLogin(config.user, config.pass)
server = imap.ImapServer(config.host, config.port)
session =
imap.Session(server, creds)
| > imap.openConnection()
| > imap.login()
inbox = imap.Mailbox(session, " INBOX " )
// Get the number of messages in the inbox.
msgCount = imap.status(session, inbox).messages
// Select the default inbox.
inbox | > imap.examine(session, _)
// Get the headers (date, from and subject) for each message, from oldest to newest, format and
// print them.
headers =
iota(msgCount)
| > map(id => " # " ~ toString(id + 1 ))
| > map(id =>
imap.fetchFields(session, id, " date from subject " ).lines
| > map(hdr => fmtField(hdr, 40 )))
| > fold(fmtHeaders, " INBOX: n " )
print(headers)L'échantillon de troisième code illustre en outre certaines des fonctionnalités inhabituelles de ce DSL, dans le but que Chatgpt les utilisera dans ses propres implémentations ensuite.
// This script will search for emails and match new issue numbers with resolutions to report the
// number of outstanding alerts.
import imap
import * from imap.query
import imap_config
import dates
import string
// Get the configuration from the environment and command line.
config = imap_config.getConfig(commandLineArguments)
// Connect to the inbox.
creds = imap.ImapLogin(config.user, config.pass)
server = imap.ImapServer(config.host, config.port)
session =
imap.Session(server, creds)
| > imap.openConnection()
| > imap.login()
inbox = imap.Mailbox(session, " support " )
// Select the default inbox.
inbox | > imap.examine(session, _)
// These criteria are common for both our searches.
commonCrit = imap.Query()
| > and(from( ` [email protected] ` ))
| > and(sentSince(dates. Date ( 2020 , 5 , 13 )))
// Get each of the alerts and resolutions from the past week (13-19 May 2020).
alertMsgIds =
imap.search(session, imap.Query(subject( " Alert: new issue " )) | > and(commonCrit)).ids
resolutionMsgIds =
imap.search(session, imap.Query(subject( " Resolution: issue " )) | > and(commonCrit)).ids
// A function to get the alert ID from a message subject.
getAlertId(msgId) => {
imap.fetchFields(session, toString (msgId), " subject " ).lines[ 0 ]
| > string .split()[$ - 1 ]
}
// A function to remove an entry from a table whether it's there or not.
removeIfExists(tbl, key) => {
if find( keys (tbl), key) == [] then
tbl
else
removeEntry(tbl, key)
}
// Now find those alerts which have no resolution. Firstly the subject for each alert, get the
// issue number end and store it in a table.
allAlertTable = alertMsgIds | > fold((tbl, msgId) => addEntry(tbl, getAlertId(msgId), msgId), {})
// Go through the resolutions and remove their corresponding alerts from the table.
unresolvedAlertTable =
resolutionMsgIds | > fold((tbl, msgId) => removeIfExists(tbl, getAlertId(msgId)), allAlertTable)
// Create a report with the date of the unresolved alerts.
report =
keys (unresolvedAlertTable)
| > map(alertId => {
msgId = unresolvedAlertTable[alertId] | > toString
in [ alertId
, imap.fetchFields(session, msgId, " date " ).lines[ 0 ]
, imap.fetchText(session, msgId).lines[ 0 ]
]
})
| > fold((outStr, tuple) => {
outStr ~ " Issue: " ~ tuple[ 0 ] ~ " n " ~ tuple[ 1 ] ~ " n Summary: " ~ tuple[ 2 ] ~ " n "
}, " n UNRESOLVED ISSUES FROM THE PAST 10 DAYS: nn " )
// Close the session.
imap.closeConnection(session)
// Print the report last.
print(report) En tant que premier exercice, j'ai demandé au modèle d'implémenter une base de données relationnelle très simple dans SIL, avec des fonctionnalités de base telles que la comparaison et l'impression de la différence entre deux structures de données (notez que // indique des commentaires dans SIL).
// Here, ChatGPT was prompted to write a a very simple, schematic relational database in SIL
// This prompt proved too general, perhaps, and after requesting further information, I prompted the model to generate a simple, schematic database with functionalities such as "compare" and "diff" for two or more tables
users = [ { " id " : 1 , " name " : " John Doe " , " email " : " [email protected] " }, { " id " : 2 , " name " : " Jane Smith " , " email " : " [email protected] " }, { " id " : 3 , " name " : " Bob Johnson " , " email " : " [email protected] " },]
orders = [ { " id " : 1 , " user_id " : 1 , " product " : " Widget " , " quantity " : 2 , " price " : 19.99 }, { " id " : 2 , " user_id " : 2 , " product " : " Gadget " , " quantity " : 1 , " price " : 29.99 }, { " id " : 3 , " user_id " : 3 , " product " : " Thingamajig " , " quantity " : 3 , " price " : 39.99 },]
// Compare two tables
compareTables(table1, table2) => {
table1 == table2
}
// Diff two tables
diffTables(table1, table2) => {
table1.filter(i => ! table2.includes(i))
}
print(compareTables(users, users)) // true
print(diffTables(users, orders)) // [{"id": 1, "name": "John Doe", "email": "[email protected]"}]La mise en œuvre d'algorithmes de tri est une question d'interview de l'ingénierie logicielle archétypale. Ici, j'ai demandé à Chatgpt d'implémenter Bubblesort (conceptuellement l'algorithme de tri le plus élémentaire) dans SIL. Vous trouverez ci-dessous la réponse:
bubbleSort(arr) => {
for i in 1 ... len(arr) {
for j in 1 ... len(arr) - i {
if (arr[j] > arr[j + 1 ]) {
temp = arr[j]
arr[j] = arr[j + 1 ]
arr[j + 1 ] = temp
}
}
}
arr
}
arr = [ 4 , 6 , 1 , 3 , 5 , 2 ]
print(bubbleSort(arr)) Bien que ce script soit, en effet, une implémentation fonctionnelle de Bubblesort, le modèle ignore largement les aspects fonctionnels de la SIL. Par exemple, cette implémentation de Bubblesort en utilisant for des boucles au lieu de la récursivité.
Ensuite, j'ai incité le modèle à écrire un script pour calculer la médiane d'une liste (réponse générée ci-dessous). C'était peut-être l'une des réponses les moins satisfaisantes. Premièrement, malgré les instructions if/else figurant dans l'exemple de code SIL avec lequel j'ai invité le modèle, la syntaxe de ce code généré est incorrecte. Deuxièmement, Chatgpt hallucine (ou suppose) la présence d'un module appelé math et un sort de fonction de construction (ce sont des hypothèses raisonnables, mais cela réduit considérablement la complexité du problème invité).
// Notably, despite if/else statements featuring in the code prompts used for training, ChatGPT gets the syntax wrong here
import math
// Function to compute the median of a list of integers
median(numbers) => {
// Sort the list of numbers
sortedNumbers = sort(numbers)
// Get the middle index of the sorted list
middleIndex = math.floor(len(sortedNumbers) / 2 )
// If the list has an odd number of elements, return the middle element
if len(sortedNumbers) % 2 == 1 :
return sortedNumbers[middleIndex]
// Otherwise, return the average of the two middle elements
else :
return (sortedNumbers[middleIndex - 1 ] + sortedNumbers[middleIndex]) / 2
}
Chatgpt a ensuite été invité à implémenter l'algorithme K -Nearest Neighbors dans SIL. Ceci est en fait le résultat de deux invites (comme décrit dans le bloc de code généré ci-dessous), car le modèle hallucine / suppose un module appelé distances dans la première tentative. Dans la deuxième tentative, je l'ai incité à implémenter l'algorithme sans supposer l'existence de ce module.
Les performances de Chatgpt ici sont mitigées. Premièrement, dans certains cas, il ignore les aspects fonctionnels du SIL (comme la mise en œuvre let for boucle pour la première tentative. Dans d'autres parties de l'implémentation, cependant, il apprécie que le SIL soit fonctionnel, et utilise |> et les opérateurs de map avec précision.
// Note that this is the fusion of two separate scripts generated by ChatGPT to solve the k-nearest neighbours algorithm, as described in the comments below
// Overall, these attempts seem reasonable, but it does appear to hallucinate "let" bindings which do not feature in the example code with which it was prompted. This is likely due to conflating the functionality of SIL code with OCaml or F#
// Attempt 1:
// This is the first ChatGPT attempt when prompted to generate a script for the k-nearest neighbours algorithm
// In this case, it assumes (or hallucinates) a library called "distances"
import distances
// Define a point as a tuple of x and y coordinates
type Point = (num, num)
// Find the distance between two points
distance(p1: Point, p2: Point) => {
let xDistance = p1[ 0 ] - p2[ 0 ]
let yDistance = p1[ 1 ] - p2[ 1 ]
in sqrt (xDistance * xDistance + yDistance * yDistance)
}
// Find the k nearest points to a given point
kNearest(point: Point, points: [Point], k: num) => {
// Compute the distances between the given point and all other points
let distances = points | > map(p => (p, distance(point, p)))
// Sort the points by distance
distances = distances | > sort((d1, d2) => d1[ 1 ] < d2[ 1 ])
// Return the k closest points
in distances[:k] | > map(d => d[ 0 ])
}
// Attempt 2:
// Next, ChatGPT is prompted to generate a script without importing the questionable "distances" library
// Again, it assumes some in-built functions, such as "sort" or "sqrt"
// Function to calculate the distance between two points
distance(p1, p2) => {
sum = 0
for i in 0 .. len(p1) - 1 {
sum = sum + (p1[i] - p2[i]) ^ 2
}
sqrt(sum)
}
// Function to find the k nearest neighbors
k_nearest_neighbors(data, point, k) => {
distances = data | > map(p => (p, distance(p, point)))
distances | > sort((a, b) => a[ 1 ] < b[ 1 ]) | > take(k) | > map(p => p[ 0 ])
}
L'une des caractéristiques intéressantes des langues fonctionnelles est qu'elles peuvent être très expressives. Ici, j'ai incité ChatGpt à implémenter la map de base des opérateurs fonctionnels, filter et reduce (ou fold dans le contexte des langages fonctionnels). Pour les premiers cas, le modèle semblait perdre la trace des aspects fonctionnels de la SIL, mais pourrait réimplémenter le code de manière plus fonctionnelle lorsqu'elle est invitée.
// map
map(fn, list) => {
result = []
for i in list {
result.append(fn(i))
}
return result
}
// filter
filter(fn, list) => {
result = []
for i in list {
if (fn(i)) {
result.append(i)
}
}
return result
}
// reduce
reduce(fn, list, initial) => {
result = initial
for i in list {
result = fn(result, i)
}
return result
}
// reduce (prompted to use functional operators such as pipe |>, in addition to the 'map' and 'filter' functions defined previously)
reduce(fn, list, initial) =>
list | >
filter(fn, _, initial) =>
map(fn, _, initial) =>
initialProject Euler est une collection de problèmes mathématiques et de programmation (qui, je pense, font des invites intéressantes pour les LLM). Ici, j'invite Chatgpt pour résoudre les deux premiers problèmes d'Euler du projet à l'aide de SIL. À ce stade, les répétitions de Chatgpt se sont quelque peu réunies; Aucune de ces solutions ne ressemble beaucoup à la fonctionnalité de haut niveau de SIL ni à sa syntaxe Exaxct. Il pourrait être intéressant de fournir d'autres exemples de code SIL à ce stade, puis de reproduire le modèle pour résoudre ces problèmes.
// Project Euler problem #1:
// Here, my prompt was to solve the (easy) first problem of the Project Euler series (https://projecteuler.net/problem=1)
// Notably, despite if/else statements featuring in the code prompts used for training, ChatGPT gets the syntax wrong here
result = 0
for i in 1 ... 1000 {
if (i % 3 == 0 || i % 5 == 0 ) {
result += i
}
}
print(result) // Project Euler problem #2:
// This prompt is to solve the second Project Euler problem (https://projecteuler.net/problem=2)
// The solution proposed by ChatGPT is sensible, but potentially inefficient, and ignores the recursion of functional languages
// Notably, despite if/else statements featuring in the code prompts used for training, ChatGPT gets the syntax wrong here
result = 0
fib(n) => {
if (n <= 1 ) {
return n
}
return fib (n - 1 ) + fib(n - 2 )
}
for i in 1 ... 100 {
val = fib(i)
if (val > 4000000 ) {
break
}
if (val % 2 == 0 ) {
result += val
}
}
print(result)
Explorer la capacité des LLM à déduire et à comprendre les caractéristiques d'un nouveau langage de programmation de manière à quelques tirs reste une question ouverte et intéressante. Ici, ces capacités ont été explorées dans le contexte de l'incitation à Chatgpt à résoudre des problèmes provoqués dans un nouveau langage spécifique au domaine (DSL) appelé SIL. Les DSL sont un cas de test potentiellement utile pour explorer l'inférence par rapport à la mémorisation dans les LLM, car ils ont souvent des caractéristiques distinctifs et sont moins susceptibles d'avoir été largement rencontrés pendant l'entraînement (le cas échéant).
Dans l'ensemble, les performances ont été mitigées: Chatgpt a correctement compris que ce DSL est un langage de programmation fonctionnel (bien qu'il ait parfois dû être ré-réparti), et a mis en œuvre ses solutions en conséquence. Cependant, il n'a pas réussi à capturer les règles syntaxiques de cette DSL à partir des cinq exemples de scripts fournis. Des investigations plus approfondies (comme l'utilisation d'autres DSL, le développement d'une métrique plus formelle pour évaluer le code généré par la LLM ou quantifier la dynamique d'apprentissage du modèle) ferait une extension intrigante de ce repos. Enfin, dans une session distincte de Chatgpt, j'ai fourni ce méta-prospère pour déterminer comment le modèle a interprété son propre code SIL:

Chatgpt examine sa propre tentative d'écriture de code dans la langue spécifique au domaine SIL et décrit certaines de ses fonctionnalités.
Il est prouvé que la mémorisation LLM est promue par la fréquence de la présentation de l'exemple de formation et le nombre de jetons pertinents utilisés pour provoquer le modèle. ↩