Nie przestawaj kodować

https://aie24.pl/

Chociaż AI może generować kompletne funkcje, a nawet działające programy, działa najlepiej, gdy przejmujesz inicjatywę. Utrzymuj swoje umiejętności na bieżąco i używaj AI jako narzędzia, które pomoże Ci pisać więcej kodu, a nie jako narzędzia, które napisze kod za Ciebie. AI nie tylko będzie się od Ciebie uczyć i pisać lepszy kod, ale będziesz także w stanie w pełni zrozumieć kod, który piszesz, co jest niezbędne do tworzenia wysokiej jakości oprogramowania.

Wskazówki i triki dotyczące generowania kodu

https://aie24.pl/

Wyniki uzyskane z generatywnego modelu AI będą się znacznie różnić w zależności od podpowiedzi, kontekstu i danych wejściowych dostarczonych do modelu, konkretnego używanego LLM, temperatury i innych ustawień dostarczonych do modelu i nie tylko. W miarę jak będziesz coraz bardziej komfortowo pracować z asystentem kodowania AI, zaczniesz coraz lepiej poznawać, co może, a czego nie może zrobić. Jeśli jednak zastosujesz się do pewnych praktyk, możesz sprawić, że LLM będzie generował dobry kod bardziej niezawodnie. W tej sekcji przedstawiam kilka wskazówek i trików, które okazały się najbardziej pomocne, a także kilka praktyk, które ostatecznie będą kosztować Cię więcej czasu, niż jest to warte, aby korzystać ze sztucznej inteligencji. Wspominam i stosuję wiele z tych najlepszych praktyk w innych miejscach tej książki, więc potraktuj tę sekcję jako przydatne odniesienie, gdy zaczynasz przygodę z kodowaniem wspomaganym przez AI.

Przenoszenie logiki z AI do klienta

https://aie24.pl/

Podczas integrowania odpowiedzi z AI do aplikacji, zastanów się, czy części odpowiedzi z AI można wykonać w kodzie po stronie klienta lub serwera. Jeśli tak, możesz zmniejszyć złożoność instrukcji dla AI, a także liczbę interakcji między aplikacją a AI. Będzie to miało wiele korzyści, w tym niższe koszty korzystania z AI, lepszą wydajność i umożliwienie AI skupienia się na mniejszej liczbie zadań, co może poprawić jej dokładność. Ponieważ poziom trudności po prostu dostosowuje liczbę losowych odpowiedzi, wykonywanie losowych ruchów wydaje się naturalną rzeczą do zdjęcia z talerza AI. Mój pomysł polegał na obsłudze poziomu trudności po stronie klienta, a nawet nie podpowiadaniu AI losowych ruchów. Aby to osiągnąć, wróciłem do całkowicie losowego kodu ruchu kółko i krzyżyk, który wygenerował ChatGPT i zmodyfikowałem go tak, aby wybrany poziom trudności określał, jak często ruchy są generowane losowo. Najpierw poprawiłem monit systemowy, aby wyeliminować opis poziomów trudności. Oto mój nowy monit:

{

rola: ‘system’,

treść:

“Jesteś graczem w kółko i krzyżyk AI. Zawsze jesteś

‘O’, a ja zawsze jestem ‘X’.\n\nPrzekażę ci

mój ruch jako liczbę na tej siatce:\n\n0 | 1 | 2\n3

| 4 | 5\n6 | 7 | 8\n\nOdpowiesz tylko

swoim ruchem, który nie może być liczbą, która

została już zagrana w bieżącej grze.\n\nKiedy powiem

‘new()’, rozpocznij nową grę.”,

}

Następnie, aby rozpocząć pisanie funkcji losowego ruchu, wprowadziłem następujący komentarz na początku funkcji getAIMove():

/*

Użyj wartości trudności, aby zdecydować, czy

zapytać API o ruch, czy użyć ruchu losowego.

Jeśli trudność wynosi 10, zawsze zapytaj API

Jeśli trudność wynosi 0, zawsze użyj ruchu losowego

Jeśli trudność wynosi od 0 do 10, użyj ruchu losowego

10 – procent trudności czasu

i użyj najlepszego ruchu procent trudności czasu

*/

Zmodyfikowana funkcja, którą napisałem przy pomocy Copilota, jest pokazana na Liście 5-4.

Nowa funkcja getAIMove()

async function getAIMove(message) {

/*

Use the value of difficulty to decide whether to

query the API for a move or use a random move.

If difficulty is 10, always query the API

If difficulty is 0, always use a random move

If difficulty is between 0 and 10, use a random move

10 – difficulty percent of the time

and use the best move difficulty percent of the time

*/

let random = Math.random();

if (random < difficulty / 10) {

const response = await fetch(‘http://localhost:3000/chat’, {

method: ‘POST’,

headers: {

‘Content-Type’: ‘application/json’,

},

body: JSON.stringify({

messages: message,

}),

});

const data = await response.json();

document.getElementById(‘message’).innerHTML =

data.response.choices[0].message.content;

return data.response.choices[0].message.content;

}

let move = Math.floor(Math.random() * 8);

while (board[move] !== ”) {

move = Math.floor(Math.random() * 8);

}

document.getElementById(‘message’).innerHTML = move.toString();

return move.toString();

}

Dzięki temu nowemu monitowi i funkcji mogłem ustawić poziom trudności (najpierw w skrypcie, a następnie za pomocą suwaka wejściowego w HTML) i dostosować procent ruchów wykonywanych przez GPT-4 w porównaniu z ruchami generowanymi losowo w aplikacji klienckiej. Jednak AI nadal nie potrafiła grać w kółko i krzyżyk, nawet przy poziomie trudności ustawionym na 10. Możliwe, choć lepsze podpowiedzi, aby sprawić, by GPT-4 był dobry w kółko i krzyżyk.

Ulepszanie klienta

https://aie24.pl/

Aby aplikacja kliencka wysyłała moje ruchy do serwera i otrzymywała ruchy AI z powrotem z serwera, napisałem nową funkcję o nazwie getAIMove(). Ta funkcja wysyła serwerowi API wszystkie ruchy w bieżącej grze i otrzymuje nowy ruch AI. Stworzyłem również funkcję o nazwie startNewGame(), która pobiera poziom trudności i przekazuje polecenie do serwera, aby rozpocząć nową grę. Pełny kod skryptu po stronie klienta pokazano naListingu.

JavaScript po stronie klienta

let board = [”, ”, ”, ”, ”, ”, ”, ”, ”];

let currentPlayer = ‘X’;

let isGameOver = false;

let messageHistory = [];

function startNewGame(levelOfDifficulty) {

board = [”, ”, ”, ”, ”, ”, ”, ”, ”];

currentPlayer = ‘X’;

isGameOver = false;

messageHistory = [];

messageHistory.push({

role: ‘user’,

content: ‘new(‘ + levelOfDifficulty + ‘)’,

});

document.querySelectorAll(‘.cell’).forEach((cell) => (cell.

innerHTML = ”));

const response = getAIMove(messageHistory);

return response;

}

function makeMove(index) {

if (board[index] === ” && !isGameOver) {

board[index] = currentPlayer;

document.getElementsByClassName(‘cell’)[index].innerHTML =

currentPlayer;

messageHistory.push({

role: ‘user’,

content: index.toString(),

});

if (checkWin()) {

alert(currentPlayer + ‘ Wins!’);

isGameOver = true;

return;

}

if (checkDraw()) {

alert(‘Draw!’);

isGameOver = true;

return;

}

aiMove(messageHistory); // Player is X, AI is O

}

}

async function getAIMove(message) {

// This function will send a message to the API server

// The message will contain each previous move and the user’s

latest move

// The API server will return the AI’s next move

const response = await fetch(‘http://localhost:3000/chat’, {

method: ‘POST’,

headers: {

‘Content-Type’: ‘application/json’,

},

body: JSON.stringify({

messages: message,

}),

});

const data = await response.json();

document.getElementById(‘message’).innerHTML =

data.response.choices[0].message.content;

return data.response.choices[0].message.content;

}

async function aiMove(messageHistory) {

let move = await getAIMove(messageHistory);

messageHistory.push({

role: ‘assistant’,

content: move.toString(),

});

board[move] = ‘O’;

document.getElementsByClassName(‘cell’)[move].innerHTML = ‘O’;

if (checkWin()) {

alert(‘O Wins!’);

isGameOver = true;

}

}

function checkWin() {

let winCombos = [

[0, 1, 2],

[3, 4, 5],

[6, 7, 8],

[0, 3, 6],

[1, 4, 7],

[2, 5, 8],

[0, 4, 8],

[2, 4, 6],

];

for (let i = 0; i < winCombos.length; i++) {

if (

board[winCombos[i][0]] &&;

board[winCombos[i][0]] === board[winCombos[i][1]] &&;

board[winCombos[i][0]] === board[winCombos[i][2]]

) {

return true;

}

}

return false;

}

function checkDraw() {

return board.every((cell) => cell !== ”);

}

Po zakończeniu pisania skryptu po stronie klienta i aktualizacji strony HTML w celu dodania przycisku Rozpocznij grę, przetestowałem grę. Po krótkim debugowaniu gra działała i mogłem grać w kółko i krzyżyk z GPT-4 przez moją przeglądarkę internetową. Jednak po kilku grach stało się jasne, że chociaż GPT-4 zna zasady gry w kółko i krzyżyk, jest kiepski w strategii. Wygrałem każdą grę, nawet gdy ustawiłem poziom trudności na 10 i grałem źle, jak pokazano na rysunku . Modele GPT są modelami językowymi i nie są dobrze wyposażone do obsługi rozumowania.

Implementacja podpowiedzi z kilkoma strzałami na serwerze

https://aie24.pl/

Aby nadać AI więcej kontekstu, napisałem serię wiadomości (używając placu zabaw OpenAI), aby symulować poprawnie rozegraną grę między asystentem AI a użytkownikiem. Następnie zakodowałem je na stałe na serwerze, jak pokazano na rysunku

Następnie ponownie uruchomiłem serwer i spróbowałem rozegrać nową grę za pomocą polecenia curl. Nowa gra rozpoczęła się poprawnie, ale przy pierwszym ruchu AI odpowiedziała, że ​​powinienem rozpocząć nową grę, aby kontynuować grę, jak pokazano na rysunku.

Stało się tak, ponieważ poprzednie monity w konwersacji nie są wysyłane do serwera. Ponieważ serwer nie ma możliwości śledzenia sesji między indywidualnym użytkownikiem a AI, najlepszym miejscem do wdrożenia stanu sesji jest klient. Zanim jednak zacznę pracę nad klientem, muszę skonfigurować serwer tak, aby łączył monity zakodowane na stałe na serwerze z monitami pochodzącymi od klienta. Zapisałem monit systemowy i przykładową grę w zmiennej, a następnie dodałem to do wiadomości, które aplikacja kliencka wysyła do serwera. Uprościłem również przykładową grę, tak aby AI zwracała tylko numer pola, na którym chce umieścić O. Ta zmiana ułatwia kodowanie aplikacji klienckiej i ma dodatkową zaletę w postaci znacznego zmniejszenia liczby tokenów niezbędnych do rozegrania gry. Gotowa aplikacja serwerowa jest pokazana .na listingu

Ukończony API Server

import express from ‘express’;

import OpenAI from ‘openai’;

import ‘dotenv/config’;

import cors from ‘cors’;

const app = express();

const openai = new OpenAI({

apiKey: process.env.OPENAI_API_KEY,

});

app.use(express.json());

app.use(cors());

app.post(‘/chat’, async (req, res) => {

const context = [

{

role: ‘system’,

content:

“You are an AI tic-tac-toe player. You are always ‘O’ and

I’m always ‘X’.\n\nI’ll provide you with my move as a number

on this grid:\n\n0 | 1 | 2\n3 | 4 | 5\n6 | 7 | 8\n\nYou’ll

respond with only your move, which must not be a number that

has already been played in the current game.\n\nWhen I say

‘new(10)’ start a new game and set the difficulty level to

10, meaning that you will always choose the best move. If I

set the difficulty to a lower level, you will sometimes make

random moves. At difficulty 1, you will always choose

randomly from the available squares.”,

},

{

role: ‘user’,

content: ‘new(10)’,

},

{

role: ‘assistant’,

content: ‘new game, level 10’,

},

{

role: ‘user’,

content: ‘0’,

},

{

role: ‘assistant’,

content: ‘4’,

},

{

role: ‘user’,

content: ‘1’,

},

{

role: ‘assistant’,

content: ‘2’,

},

{

role: ‘user’,

content: ‘6’,

},

{

role: ‘assistant’,

content: ‘8’,

},

{

role: ‘user’,

content: ‘5’,

},

{

role: ‘assistant’,

content: ‘3’,

},

{

role: ‘user’,

content: ‘7’,

},

];

const newMessage = req.body.messages;

const messages = […context, …newMessage];

const response = await openai.chat.completions.create({

model: ‘gpt-4’,

messages: messages,

temperature: 0.5,

max_tokens: 255,

top_p: 1,

frequency_penalty: 0,

presence_penalty: 0,

});

res.json({ response });

});

app.listen(3000, () => {

console.log(‘Server listening on port 3000’);

});

Testowanie serwera

https://aie24.pl/

Uzbrojony w sugestie Copilota dotyczące testowania i obsługi błędów, miałem kilka zadań do wykonania, zanim mogłem sprawić, by serwer działał z aplikacją kliencką. Najpierw musiałem ustalić, czy serwer w ogóle działa. Przeglądając kod, wygląda to jak standardowy serwer API Node.js, który powinien być w stanie odebrać wiadomość, przekazać ją do serwera OpenAI i zwrócić odpowiedź w formacie JSON. Korzystając z sugestii testowania od Copilota, uruchomiłem serwer (używając node server.js) i wprowadziłem następujące polecenie curl w nowym oknie terminala (wszystko w jednym wierszu):

curl -X POST -H “Content-Type: application/json” -d ‘

{“message”: “new(5)”}’ http://localhost:3000/chat

Ta komenda powinna nakazać grze w kółko i krzyżyk AI rozpoczęcie nowej gry o poziomie trudności 5. A odpowiedź, którą otrzymałem od serwera, pokazuje, że właśnie to zrobiła:

“message”:{“role”:”assistant”,”content”:”Rozumiem.

Rozpoczynamy nową grę z poziomem trudności 5, co

oznacza mieszankę optymalnych i losowych ruchów. Twój

ruch!”},”finish_reason”:”stop”}],”usage”:{“prompt_tokens”:

199,”completion_tokens”:29,”total_tokens”:228}}}%

Następnie spróbowałem przesłać mój pierwszy ruch, a AI odpowiedziała odpowiednio. Jednak po kilku ruchach mój przeciwnik AI wydawał się tracić kontrolę nad grą, a odpowiedzi zajmowały o wiele za dużo czasu. Moja pierwsza próba gry w kółko i krzyżyk przeciwko AI przy użyciu mojego serwera API jest pokazana na rysunku .

Problem polega na tym, że chociaż ten kod działa, nie ma sposobu, aby śledzić stan gry. Każde żądanie będzie wysyłać tylko najnowszy ruch od klienta do OpenAI. Ponadto AI skorzystałoby z kilku przykładów formatu odpowiedzi, którego szukam. Podawanie sztucznej inteligencji przykładów prawidłowych odpowiedzi nazywa się podpowiadaniem za pomocą niewielu odpowiedzi.

Wysyłanie monitów kontynuacyjnych

https://aie24.pl/

Na dole odpowiedzi Copilot wyjaśnił wygenerowany kod i zasugerował monit kontynuacyjny:

Jak mogę przetestować serwer API?

Tak czy inaczej, to miał być mój następny monit, więc kliknąłem sugerowany monit, a Copilot (poprawnie) zasugerował użycie curl lub Postman do jego przetestowania i podał instrukcje dotyczące korzystania z obu, jak pokazano na rysunku 5-9, po lewej stronie. Model zasugerował również kolejny monit kontynuacyjny, co wydało mi się doskonałym pomysłem, więc kliknąłem go, aby poprosić o kod do obsługi błędów zwracanych przez serwer API. Odpowiedź na ten monit pokazano na rysunku , po prawej stronie.

Pisanie serwera

https://aie24.pl/

Postanowiłem napisać serwer Node.js, aby komunikować się z API OpenAI, aby nie musieć przechowywać klucza API w mojej aplikacji klienckiej. Utworzyłem nowy katalog dla mojego serwera, zainicjowałem katalog jako pakiet Node.js (używając npm init) i utworzyłem plik o nazwie server.js. Aby rozpocząć pisanie server.js, kliknąłem przycisk Wyświetl kod w prawym górnym rogu placu zabaw i wybrałem Node.js jako moją bibliotekę. Otworzyło się okno Wyświetl kod z niezbędnym kodem do wysyłania bieżących ustawień i monitów do API oraz pobierania następnego uzupełnienia. Skopiowałem ten kod z placu zabaw i wkleiłem go do server.js. Dodałem również kod do importowania klucza API z pliku .env. Plik .env jest używany w Node.js do przechowywania zmiennych środowiskowych poza programem głównym. Zmienne środowiskowe przechowują informacje, które są specyficzne dla instalacji oprogramowania (takie jak klucze API) i nie powinny być dystrybuowane wraz z oprogramowaniem (ponieważ nie chcesz, aby inne osoby używały Twojego klucza API). Początek mojego serwera pokazany jest na rysunku .

Następnym krokiem było stworzenie własnego serwera API, który pobiera wyniki z OpenAI i zwraca je do mojej aplikacji klienckiej. Ponieważ pisanie serwera API jest dość standardowym zadaniem, postanowiłem użyć Copilot Chat, aby go wygenerować. Zacząłem od następującego monitu:

Chcę przekształcić kod w server.js w serwer API, do którego mogę wysłać nową wiadomość i dodać ją do tablicy messages przed przesłaniem jej do API OpenAI. Serwer powinien zwrócić odpowiedź z API OpenAI.

Kod wygenerowany przez Copilot był dobrym początkiem. Cały kod jest pokazany w Listingu .

import express from ‘express’;

import OpenAI from ‘openai’;

import ‘dotenv/config’;

const app = express();

const openai = new OpenAI({

apiKey: process.env.OPENAI_API_KEY,

});

app.use(express.json());

app.post(‘/chat’, async (req, res) => {

const { message } = req.body;

const response = await openai.chat.completions.create({

model: ‘gpt-4’,

messages: [

{

role: ‘system’,

content:

“You are an AI tic-tac-toe player. You are always ‘O’ and

I’m always ‘X’.\n\nI’ll provide you with my move as a number

on this grid:\n\n0 | 1 | 2\n3 | 4 | 5\n6 | 7 | 8\n\nYou’ll

respond with only an array with an X in the position of my

move, followed by your move, followed by an array with an O

in the position of your move.\n\nIf one of us wins or it’s a

draw, tell me ‘you win’, ‘I win’, or ‘draw’.\n\nWhen I say

‘new(10)’ start a new game and set the difficulty level to

10, meaning that you will always choose the best move. If I

set the difficulty to a lower level, you will sometimes make

random moves. At difficulty 1, you will always choose

randomly from the available squares.”,

},

{

role: ‘user’,

content: message,

},

],

});

res.json({ response });

});

app.listen(3000, () => {

console.log(‘Server listening on port 3000’);

});

Wysyłanie monitów kontynuacyjnych

Na dole odpowiedzi Copilot wyjaśnił wygenerowany kod i zasugerował monit kontynuacyjny:

J

Pisanie monitu

https://aie24.pl/

Po zastanowieniu się nad problemem, jak stworzyć gracza w kółko i krzyżyk z AI, postanowiłem spróbować użyć interfejsu API OpenAI i modelu GPT-4. Przez chwilę eksperymentowałem z placem zabaw OpenAI i skończyłem z następującym monitem systemowym:

Korzystając z tego monitu systemowego i ustawiając temperaturę na 0,5 (aby model był kreatywny, ale nie za bardzo), mogłem wprowadzić swoje ruchy i uzyskać odpowiedzi, które mogłem wykorzystać w moim programie, jak pokazano na rysunku.

Łączenie kodu napisanego ręcznie i wygenerowanego przez AI

https://aie24.pl/

Gdy masz kod wygenerowany przez AI, który spełnia przynajmniej niektóre wymagania oprogramowania, czas zacząć pisać kod ręcznie. Dobrą strategią przejścia od podstawowej aplikacji, takiej jak ta wygenerowana przez AI w poprzedniej sekcji, do czegoś, co działa poprawnie, jest rozpoczęcie od opracowania zaplecza. Zaplecze definiuje logikę biznesową i dane w aplikacji, a także sposób, w jaki interfejs użytkownika będzie oddziaływać z logiką i danymi. W przypadku gry w kółko i krzyżyk z AI, pisanie zaplecza rozpocznie się od stworzenia monitu, którego użyję, aby model AI zagrał ze mną w kółko i krzyżyk.