Приложение React/Redux представляет собой односторонний цикл потока данных. Пользователь взаимодействует с пользовательским интерфейсом, который производит действия Redux, которые отправляются в функции reducer, которые обновляют объект состояния приложения, который передается обратно в React для повторного отображения пользовательского интерфейса.

Разговор с ChatGPT также представляет собой односторонний цикл. Человек посылает запрос (действие), который передается языковой модели (редуктор), которая обновляет разговор (состояние) своим ответом.

Можем ли мы использовать последнюю модель ChatGPT для этих целей? Спойлер: да, можем! ChatGPT с радостью выступит в роли универсального редьюсера, так что вы сможете прототипировать фронтенд вашего приложения без необходимости сразу же создавать бэкенд или бизнес-логику. А после того, как вы немного поработаете с фронтендом, чтобы научить ИИ работать с приложением, вы можете даже попросить его реализовать функцию редьюсера за вас.

Вот краткая демонстрация базового приложения-счетчика с моим плагином react-redux-chatgpt, выполняющим функцию reduce:

А вот ChatGPT, работающий на примере Redux TodoMVC, без изменений, за исключением удаления редьюсеров и замены их на ChatGPT:

Код на гитхабе

Базовый пример: Счетчик


Давайте начнем с приложения счетчика:

import { Provider, useSelector, useDispatch } from 'react-redux';

const initState = { count: 0 };

const App = () => {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      counter: {count}
      &nbsp;
      <button onClick={() => dispatch({ type: 'increment' })}>
        increment
      </button>
    </div>
  );
};

Наше начальное состояние - это просто { count: 0 } - проще не бывает! Нажатие на кнопку приведет к выполнению действия { type: 'increment' }, и мы ожидаем, что наш редьюсер обновит состояние до { count: 1 }, { count: 2 }, { count: 3 } и т.д.

Давайте перейдем в ChatGPT и расскажем ему, что мы делаем:

Я: 
I’m a React/Redux app. I have a single data store.

Here’s the initial state of my store, in JSON form: {"count":0}
AI: 
Great! It looks like you have a simple initial state for your Redux store, with a single property called “count” that has a value of 0.

In Redux, the store is the central place where the application’s state is stored. It is created by passing a reducer function to the createStore function. The reducer function is responsible for updating the state based on actions that are dispatched to the store.

<snip>

Хорошо, круто. ChatGPT знает, что такое Redux. Может ли он обрабатывать действия для нас?

Я: 
My Redux reducers received an action, here encoded in JSON form: {"type":"increment"}

What should the new state of my store be after this action?

AI: 
If your Redux reducer receives an action with a type of 'increment', it should update the state of the store by incrementing the value of the count property by 1.

<snip>

When the reducer function receives an action with a type of 'increment', it will return a new state object with the count property incremented by 1. The rest of the state will be unchanged.

For example, if the initial state of the store is { count: 0 }, and the reducer function receives an action with a type of 'increment', the new state of the store will be { count: 1 }.

It’s important to note that the reducer function should be a pure function, meaning that it should not have any side effects and should always return the same output for a given set of inputs. This makes it easier to test and reason about the behavior of your application.

Ответ там есть, но он похоронен в куче объяснений, которые не поддаются машинному чтению. Это общий паттерн для ChatGPT. Давайте попробуем еще раз и посмотрим, сможем ли мы заставить ИИ перейти сразу к делу.

Я: 
My Redux reducers received an action, here encoded in JSON form: {"type":"increment"}

What should the new state of my store be after this action? Provide your answer in JSON form. Reply with only the answer in JSON form and include no other commentary.
AI: 
{"count":1}

Идеально.

Теперь мы можем начинать новый разговор каждый раз, когда получаем новое действие, каждый раз повторяя наше текущее состояние для ИИ. Но помимо того, что это будет стоить больше токенов, когда OpenAI наконец-то начнет взимать плату за эту магию, использование модели непрерывного разговора, на которой построен ChatGPT, поможет сохранить внутреннюю согласованность его ответов. С помощью небольшой техники подсказок мы можем заставить ИИ извлекать текущее состояние из журнала разговора и направлять свои ответы на основе предыдущих действий и состояний, которые он наблюдал.

Я: 
I’m a React/Redux app. I have a single data store.

Here’s the initial state of my store, in JSON form: {"count":0}

My Redux reducers received an action, here encoded in JSON form: {"type":"increment"}

What should the new state of my store be after this action? Provide your answer in JSON form. Reply with only the answer in JSON form and include no other commentary.
AI: 
{"count":1}
Я:
My Redux reducers received an action, here encoded in JSON form: {"type":"increment"}

Given the state of the store in your previous response, what should the new state of my store be after this action? Provide your answer in JSON form. Reply with only the answer in JSON form and include no other commentary.
AI: {"count":2}
Я: 
My Redux reducers received an action, here encoded in JSON form: {"type":"increment"}

Given the state of the store in your previous response, what should the new state of my store be after this action? Provide your answer in JSON form. Reply with only the answer in JSON form and include no other commentary.
AI: {"count":3}

Теперь, когда процесс запущен, давайте поэкспериментируем. Может ли ChatGPT синтезировать действия для нас?

Я:
The user has requested that I do the following: add 3 to the count

What action(s) should I dispatch to my Redux store in order to complete this request? Provide your answer in the form of a JSON array of actions. Reply with only the answer in JSON array form and include no other commentary.
AI:
{"type":"increment","payload":3}

Итак, ChatGPT придумал параметр "payload" для действия "increment. Креативно, но, возможно, не то, что нам нужно. Можем ли мы заставить его использовать только то, что мы продемонстрировали до сих пор?

Я:
The user has requested that I do the following: add 3 to the count

Given the state of the store in your previous response and the types of actions I have received so far, what action(s) should I dispatch to my Redux store in order to complete this request? Provide your answer in the form of a JSON array of actions. Reply with only the answer in JSON array form and include no other commentary.
AI:
[{"type":"increment"},{"type":"increment"},{"type":"increment"}]
Я:
My Redux reducers received some actions, here encoded in JSON form: [{"type":"increment"},{"type":"increment"},{"type":"increment"}]

Given the state of the store in your previous response, what should the new state of my store be after this action? Provide your answer in JSON form. Reply with only the answer in JSON form and include no other commentary.
AI:
{"count":6}

Прекрасно. ChatGPT даже может сократить последовательность действий с помощью одной подсказки. Это позволит сэкономить на обращении к OpenAI.

Мы можем полностью отказаться от ИИ, как только ChatGPT сможет сгенерировать сам reducer.

Я:
Give me JavaScript code that implements my reducer function. It should be able to handle all of the actions I told you I received, producing the states you told me would result from applying them.
AI: 
Sure, here is a JavaScript code that implements a reducer function that can handle the increment action:

const reducer = (state = {count: 0}, action) => {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    default:
      return state;
  }
};
This reducer function will initialize the state to {count: 0} if no state is provided. When it receives an increment action, it will return a new state object with the count increased by 1. If it receives any other action type, it will simply return the current state.

You can add additional actions by adding new cases to the switch statement. For example, if you wanted to handle a decrement action, you could add a case for it like this:

case 'decrement':
  return {count: state.count - 1};
I hope this helps! Let me know if you have any questions.

Официального API для ChatGPT пока нет, поэтому я использую неофициальный пакет chatgpt NPM для подключения приложения в демонстрационном видео. OpenAI имеет защиту Cloudflare; чтобы обойти ее, npm утилита запускает полный экземпляр Chrome (!) в фоновом режиме для передачи сообщений туда и обратно между сервером REST API, который я написал, и внутренним API ChatGPT.

Логика управления ChatGPT в разговоре, как описано выше, собрана в моей библиотеке react-redux-chatgpt. Она конструирует подсказки, отслеживает потоки разговора и даже может автоматически повторять подсказки в редких случаях, когда ChatGPT выдает неверные данные. В библиотеке также есть несколько стоковых компонентов для реализации экрана "размышлений..." и панели действий в демонстрации, а также методы synthesizeReducers и synthesizeActions для генерации кода. Подключить библиотеку к нашему приложению-счетчику очень просто:

import { createRoot } from 'react-dom/client';
import { AiButtons, ChatGptBackend, ThinkingScreen } from 'react-redux-chatgpt';

const backend = new ChatGptBackend(initState);

const root = createRoot(document.getElementById('root'));
root.render(
  <Provider store={backend.store}>
    <App />
    <AiButtons backend={backend} />
    <ThinkingScreen useSelector={useSelector} />
  </Provider>
);

Будущее написания кода уже здесь, и оно общается с нами.

Еще больше примеров использования ML в современных сервисах можно посмотреть в моем телеграм канале. Я пишу про ML, стартапы и релокацию в UK для IT специалистов.