Штучний інтелект (ШІ) став незамінним інструментом у циклі розробки програмного забезпечення. Однак його корисність залежить не лише від його можливостей, а й від того, як розробники взаємодіють з ним. У цій статті я поділюсь практичним випадком використання та ілюструю найкращі практики використання ШІ як спільного помічника, а не як рішення для повного зняття складних завдань. Приклад включає сортування 200 складних SQL-виразів AWS Redshift за їх залежностями, завдання, яке значно виграє від співпраці людини та ШІ.
Виклик
Уявіть, що ви маєте справу з 200 вкладеними SQL-виразами для AWS Redshift. Ці представлення мають складні залежності між собою, і вам потрібно визначити правильний порядок для їх обробки. Це вимагає топологічного сортування представлень на основі їх залежностей, які вбудовані в SQL-вирази.
Ось спрощений приклад одного з таких виразів:
CREATE OR REPLACE VIEW claims_preprocessing.generate_encounter_id AS
SELECT nd.patient_data_source_id, nd.start_date, nd.end_date, nd.claim_id,
ne.encounter_id AS old_encounter_id
FROM (
SELECT fd.patient_data_source_id, fd.start_date, fd.end_date, fd.claim_id,
fd.previous_max_end_date, fd.new_group_flag,
SUM(fd.new_group_flag) OVER(
PARTITION BY fd.patient_data_source_id
ORDER BY fd.start_date, fd.claim_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS encounter_group
FROM (
SELECT gd.patient_data_source_id, gd.start_date, gd.end_date, gd.claim_id,
gd.previous_max_end_date,
CASE WHEN (gd.start_date > COALESCE(gd.previous_max_end_date, '1900-01-01'::date))
THEN 1 ELSE 0 END AS new_group_flag
FROM (
SELECT bd.patient_data_source_id, bd.start_date, bd.end_date, bd.claim_id,
MAX(bd.end_date) OVER(
PARTITION BY bd.patient_data_source_id
ORDER BY bd.start_date, bd.claim_id
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS previous_max_end_date
FROM (
SELECT DISTINCT m.patient_data_source_id, m.start_date, m.end_date, m.claim_id
FROM claims_preprocessing.encounters__stg_medical_claim m
JOIN claims_preprocessing.asc__anchor_events u
ON (m.claim_id = u.claim_id)
) bd
) gd
) fd
) nd
JOIN (
SELECT unique_encounters.patient_data_source_id, unique_encounters.encounter_group,
ROW_NUMBER() OVER(
ORDER BY unique_encounters.patient_data_source_id,
unique_encounters.encounter_start_date) AS encounter_id
FROM (
SELECT numbered_data.patient_data_source_id, numbered_data.encounter_group,
MIN(numbered_data.start_date) AS encounter_start_date
FROM (
SELECT fd.patient_data_source_id, fd.start_date, fd.end_date, fd.claim_id,
fd.previous_max_end_date, fd.new_group_flag,
SUM(fd.new_group_flag) OVER(
PARTITION BY fd.patient_data_source_id
ORDER BY fd.start_date, fd.claim_id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS encounter_group
FROM (
SELECT gd.patient_data_source_id, gd.start_date, gd.end_date, gd.claim_id,
gd.previous_max_end_date,
CASE WHEN (gd.start_date > COALESCE(gd.previous_max_end_date, '1900-01-01'::date))
THEN 1 ELSE 0 END AS new_group_flag
FROM (
SELECT bd.patient_data_source_id, bd.start_date, bd.end_date, bd.claim_id,
MAX(bd.end_date) OVER(
PARTITION BY bd.patient_data_source_id
ORDER BY bd.start_date, bd.claim_id
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS previous_max_end_date
FROM (
SELECT DISTINCT m.patient_data_source_id, m.start_date, m.end_date, m.claim_id
FROM claims_preprocessing.encounters__stg_medical_claim m
JOIN claims_preprocessing.asc__anchor_events u
ON (m.claim_id = u.claim_id)
) bd
) gd
) fd
) numbered_data
GROUP BY numbered_data.patient_data_source_id, numbered_data.encounter_group
) unique_encounters
) ne
ON (nd.patient_data_source_id = ne.patient_data_source_id
AND nd.encounter_group = ne.encounter_group);
Небезпеки повного покладання на ШІ
Ваше перше бажання може бути покласти на ШІ все завдання — створити порядок представлень, що враховує залежності. Однак цей підхід має суттєві недоліки. Моделі ШІ часто:
- Генерують некоректні або неповні результати, особливо якщо логіка парсингу залежностей має помилки.
- Не дають можливості для ітерацій, що ускладнює виправлення помилок.
Наприклад, ШІ спочатку спробував створити регулярний вираз для парсингу SQL-виразів на предмет залежностей. Однак складність вкладених запитів призвела до того, що він неодноразово зазнавав невдачі.
Конкретні помилки включали:
- Невірне тлумачення вкладених запитів і нездатність точно захопити всі залежності.
- Перевантаження логіки регулярних виразів без врахування альтернативних підходів.
- Зосередження на регулярних виразах замість адаптації до простішого і більш ефективного рішення.
Крім того, ШІ мало успіху в обробці специфічних конструкцій SQL, таких як:
- Окреме управління різними типами клауз
JOIN
з їх специфічним синтаксисом, включаючи:
- Стандартний
JOIN ... ON
LEFT/RIGHT/INNER/CROSS JOIN
- Клаузи
FROM
- Не оброблено:
- Дужкові вирази
FROM
/JOIN
- Глибоко вкладені підзапити
- Багато умов з’єднання
ШІ зосередилося на вдосконаленні регулярних виразів — шлях, який виявився надмірно складним і часозатратним. Визначивши це, я зміг змінити курс, порадивши ШІ зовсім відмовитися від регулярних виразів. Замість цього я націлив ШІ працювати спільно для створення програм, які можуть розв’язати задачу ітераційно. Наприклад, я інструктував ШІ шукати шаблони, такі як x.y
, де x.y
відповідало б імені подання зі списку відомих подань. Ця зміна значно спростила процес парсингу.
Основний висновок? Сліпе покладання на ШІ для складних і нюансованих завдань часто призводить до не оптимальних результатів. Натомість працюйте з ШІ для створення інструментів і рішень спільно, ітераційно коригуючи їх за потреби.
Рішення: Спільний підхід
Щоб вирішити цю проблему, ви можете працювати з ШІ структуровано. Ось як:
Крок 1: Розбийте задачу на керовані частини
Замість того, щоб повністю покладатися на ШІ, розбийте її на менші, простіші завдання:
- Ідентифікуйте всі імена подань.
- Виділіть залежності для кожного подання.
- Виконайте топологічне сортування.
- Підготуйте відповідний порядок подань у SQL-виразах.
Крок 2: Керуйте ШІ
Почніть з чітких інструкцій для кожного кроку. Наприклад:
- Завдання: Напишіть Python-скрипт для виділення залежностей.
- Очікування: Скрипт повинен ідентифікувати посилання у форматі
schema.view
і зіставити їх зі списком імен подань.
Крок 3: Перевірка та ітерації
Згенеровані ШІ скрипти часто потребують коригувань. Проблеми з парсингом, такі як помилки в захопленні вкладених залежностей або надмірно складна логіка регулярних виразів, розглянуті в розділі "Підводні камені". Можливо, вам доведеться уточнити або адаптувати логіку парсингу, щоб врахувати ці питання і узгодити її з конкретними потребами завдання.
Крок 4: Збірка рішення
Якщо залежності точно виділено та перевірено, ви можете використати інший скрипт з ШІ для виконання топологічного сортування і перевірки результатів.
Практична реалізація: Триетапний робочий процес
Ось як виглядатиме процес:
- Програма 1: Скласти список усіх імен подань з SQL-скриптів.
- Програма 2: Виділити залежності для кожного подання та вивести їх у зручному для людини форматі для перевірки.
- Програма 3: Виконати топологічне сортування і зіставити відсортований порядок з SQL-виразами.
Кожна програма має свою чітку роль, і перевірка результатів на кожному етапі є важливою для виявлення помилок, таких як неправильне виділення залежностей або помилки сортування, на ранніх етапах.
Основний висновок: Співпрацюйте, а не делегуйте
Ключовий урок — це співпраця з ШІ, а не повне делегування завдань. Ви можете ефективно використовувати його потужність, розбиваючи проблеми на менші частини, направляючи ШІ, коли воно робить помилки, і перевіряючи результати.
У цьому випадку, працюючи ітераційно з ШІ, ми змогли розв’язати складну задачу з визначенням залежностей. ШІ прискорило рішення, дозволивши людині контролювати процес, забезпечуючи точність і гнучкість.
Цей спільний підхід до використання ШІ в розробці програмного забезпечення дозволяє вам залишатися в контролі, використовуючи ШІ як потужного помічника, а не як крихку автоматизовану систему.
Перекладено з: Effectively Using AI as a Buddy for Software Development: A Case Study