Языки программирования описывают процедуры. Они не могут описать мир.


Языки программирования — одно из величайших изобретений человечества

Языки программирования однозначны. x = 3 + 4 даёт 7 независимо от того, когда и где выполняется. Нет места для интерпретации.

Языки программирования верифицируемы. Синтаксические ошибки обнаруживаются до компиляции. Ошибки типов обнаруживаются до выполнения. Когда запускаются тесты, результат — либо успех, либо провал.

Языки программирования Тьюринг-полны. Они могут выразить всё вычислимое. При достаточном времени и памяти любую процедуру можно описать.

Всё, что в этой серии было указано как ограничения естественного языка — двусмысленность, невозможность верификации, отсутствие структуры — языки программирования решили.

Так почему бы не использовать язык программирования для представления контекста ИИ?

Не получится.


Языки программирования описывают процедуры

Ниже — валидный код на Python.

def calculate_revenue(units_sold, unit_price):
    return units_sold * unit_price

Этот код ясен, верифицируем и исполняем. Но что он выражает?

«Умножить количество проданных единиц на цену единицы, чтобы получить выручку.»

Это процедура. Метод. HOW. Он описывает, что делать, когда поступает входное значение.

Теперь попробуем выразить следующее.

«Выручка Samsung Electronics в третьем квартале 2024 года составила 79 триллионов вон.»

Это не процедура. Это факт. WHAT. Ничего не исполняется. Он описывает состояние мира.

Как выразить это на Python?

samsung_revenue_2024_q3 = 79_000_000_000_000

Число присвоено переменной. Работает. Но это не описание. Это хранение. Этот код не знает:

  • Что за сущность «Samsung Electronics».
  • Что означает «выручка». Это финансовый показатель? Физическая величина?
  • «Q3 2024» — это время, версия или метка?
  • Каков источник цифры 79 триллионов вон?
  • Насколько это значение достоверно?

Имя переменной samsung_revenue_2024_q3 позволяет человеку догадаться о значении. Для машины это произвольная строка. Переименуйте в xyzzy_42 — результат выполнения будет тем же.

В языках программирования имена переменных не несут смысла. Смысл живёт за пределами кода, в голове программиста.


Усложнение не помогает

А если создать класс?

class FinancialReport:
    def __init__(self, company, metric, period, value, currency):
        self.company = company
        self.metric = metric
        self.period = period
        self.value = value
        self.currency = currency

report = FinancialReport("삼성전자", "매출", "2024-Q3", 79_000_000_000_000, "KRW")

Лучше. Появилась структура. Но проблемы остались.

company — строка «삼성전자» (Samsung Electronics). «Samsung Electronics», «SEC» и «005930» — одна и та же компания. Код это знает? Нет. Он может лишь сравнивать строки на равенство.

metric — строка «매출» (выручка). «매출», «매출액» и «revenue» — одно и то же или разные вещи? Код не знает. Строки разные — значит, разные.

А если определить схему? Управлять списком компаний через Enum, списком показателей. Конечно. Работает.

Тогда попробуем выразить следующее.

«Ли Сунсин был великим.»

opinion = Opinion("이순신", "was", "위대했다")

Что это? Строка «이순신» (Ли Сунсин) связана со строкой «위대했다» (был великим). Это не выражает «Ли Сунсин был великим». Это сохраняет «이순신» и «위대했다».

Код не знает значения «위대했다» (был великим). Похожи ли «위대했다» (был великим) и «훌륭했다» (был превосходным), противоположно ли «비겁했다» (был трусливым) — код не может определить.

Структурированные факты вроде финансовых данных более или менее поддаются обработке. Оценки, контекст, отношения, абстрактные описания находятся за пределами выразительных возможностей языков программирования.


Код не знает, что он делает

def process(data):
    result = []
    for item in data:
        if item["value"] > threshold:
            result.append(transform(item))
    return result

Этот код исполняется безупречно. Но что он делает?

Фильтрует данные о выручке? Отбирает медицинские записи? Очищает сенсорные данные?

Сам код этого не знает. data, value, threshold, transform — всё абстрактные имена. Принадлежит ли этот код финансовой или медицинской системе — зависит от контекста за пределами кода.

Можно написать комментарии. Но комментарии — это естественный язык. Машины их не понимают. Если комментарий противоречит коду, компилятор этого не заметит. Комментарии для людей, не для машин.

Когда ИИ получает код в качестве контекста, эта проблема проявляется непосредственно. Поскольку код не обладает самоидентификацией, ИИ вынужден каждый раз восстанавливать его идентичность посредством вывода. Поскольку это вывод, он стоит вычислений и может ошибаться.


Фундаментальная причина

То, что языки программирования не могут описать мир, — не дефект проектирования. Цель другая.

Цель языка программирования — инструктировать машину о процедурах. «Когда поступит этот вход, выполни эту операцию.» Семантика языка программирования — это семантика исполнения. Каждая конструкция интерпретируется как «что делает машина».

x = 3 — инструкция «сохрани 3 в ячейке памяти с именем x». if x > 0 — инструкция «если x больше 0, выполни следующий блок». return x — инструкция «верни значение x вызывающей стороне».

Всё глаголы. Всё действия. Всё процедуры.

«Samsung Electronics — корейская компания» — это не глагол. Не действие. Не процедура. Это описание состояния мира.

В языках программирования для этого нет места. Можно сохранить в переменную, но это хранение, а не описание. Смысл сохранённого значения не входит в компетенцию кода.


А как насчёт JSON, YAML, XML?

Если не языки программирования, то форматы данных?

{
  "company": "삼성전자",
  "metric": "매출",
  "period": "2024-Q3",
  "value": 79000000000000,
  "currency": "KRW"
}

Структура есть. Поля явные. Но смысла нет.

Означает ли «company» корпорацию — JSON не знает. Является ли «삼성전자» тем же, что «Samsung Electronics» в другом месте — JSON не знает. Описывают ли этот и тот объект JSON одну сущность — JSON не знает.

JSON предоставляет структуру, но не смысл. Это пары ключ-значение, а не сущность-отношение-атрибут.

Определение схем помогает. JSON Schema, Protocol Buffers, GraphQL. Определяются типы полей, обязательность, ссылки.

Но всё это — структуры, спроектированные для конкретных систем. Это не универсальное представление знаний. Схема финансовых данных не может выразить оценку исторической личности. Схема медицинских данных не может выразить конкурентные отношения между компаниями.

Отдельная схема для каждой области. Отдельный инструмент для каждой схемы. Никакой интероперабельности между схемами.

Это ограничение подробнее рассматривается в статье Почему MD/JSON/XML не подходят.


А как насчёт LISP?

Некоторые читатели наверняка подумали о контрпримере.

LISP.

(is 삼성전자 (company korea))
(revenue 삼성전자 2024-Q3 79000000000000)
(great 이순신)

S-выражения — это древовидные структуры, и код есть данные, а данные есть код. Гомоиконичность (homoiconicity).

На самом деле ранний ИИ целиком базировался на LISP. SHRDLU, CYC, экспертные системы. Знания представлялись на LISP, и работали движки вывода. Кажется, существует историческое опровержение тезиса «языки программирования не могут описать мир».

Но контрпример не работает по трём причинам.

Что знает LISP и что решил программист

В (is 삼성전자 company) LISP не знает, что is означает отношение «является». Это решил программист.

Замените is на zzz — LISP не заметит разницы. (zzz 삼성전자 company) — для LISP совершенно валидное выражение.

LISP предоставляет структуру. Дерево под названием S-expression. Но смысл внутри этой структуры присвоен программистом, а не языком. Это принципиально то же самое, что JSON не знающий значения своих ключей.

Предоставлять структуру и встраивать смысл — разные вещи.

30 лет CYC

Самой амбициозной попыткой был CYC.

Начат в 1984 году. Пытался представить общие знания на LISP. Миллионы правил вводились вручную.

30 лет доказали не осуществимость, а ограничения.

Онтологии приходилось проектировать вручную для каждой предметной области. Междоменная интероперабельность не работала. Не удавалось поспевать за гибкостью естественного языка. По мере роста масштаба поддержание согласованности становилось практически невозможным.

Что представление знаний на LISP «возможно» — верно. Что оно «работает хорошо» — это то, что опровергают результаты 30 лет.

Если не использовать eval, нет смысла использовать LISP

Самая фундаментальная проблема.

Сила LISP — в eval. Поскольку код есть данные, данные можно исполнить. Метапрограммирование, макросы, генерация кода во время выполнения. Это то, что делает LISP LISPом.

Но что происходит, если применить eval к (is 삼성전자 company)?

Это становится вызовом функции, передающим 삼성전자 и company как аргументы функции is. Не описание — исполнение.

Для использования в представлении знаний eval применять нельзя. Если не применяете eval — не используете семантику LISP. Вы лишь заимствуете синтаксис S-выражений.

Это не «описание мира на LISP». Это «хранение данных с помощью скобочной нотации LISP».

Семантика LISP как языка программирования — семантика исполнения — по-прежнему предназначена для описания процедур. Заимствование синтаксиса не меняет семантику.


Что нужно языку для описания мира

Языки программирования описывают процедуры. Форматы данных предоставляют структуру, но не смысл. Даже LISP лишь заимствует синтаксис без семантики описания.

Что нужно языку для описания мира?

Идентичность сущностей. «Samsung Electronics» должен иметь уникальный идентификатор. Машина должна знать, что это то же, что «삼성전자». Не сравнение строк, а тождество идентичности.

Выражение отношений. В «Samsung Electronics — корейская компания» должна быть возможность выразить отношение «корейская компания». Не присваивание переменной, а описание отношений.

Самоописывающиеся описания. О чём описание, кто его сделал, на какой момент времени, насколько оно достоверно — всё это должно содержаться в самом описании. Внутри кода, а не снаружи.

Независимость от предметной области. Финансовые данные, исторические факты, субъективные оценки, абстрактные отношения — всё должно быть выразимо в одном формате. Не отдельная схема для каждой области, а одна универсальная структура.

Языки программирования не обладают ни одним из этих четырёх свойств. Потому что языки программирования не для этого создавались. Они создавались для описания процедур.

Естественный язык может делать все четыре вещи. Двусмысленно. Нужно сочетание выразительного диапазона естественного языка и точности языков программирования.


Итог

Языки программирования однозначны, верифицируемы и Тьюринг-полны. Но они не могут описать мир.

Языки программирования описывают процедуры. «Когда поступит этот вход, сделай это.» Всё глаголы, всё действия. «Samsung Electronics — корейская компания» — это не действие. В языках программирования для этого нет места.

Код не знает собственной идентичности. К какой области он относится, какую цель выполняет — ничего из этого не записано в коде.

Форматы данных вроде JSON и YAML предоставляют структуру, но не смысл. LISP может заимствовать синтаксис, но у него нет семантики описания. CYC потратил 30 лет на попытку представления знаний на базе LISP, и доказал ограниченность подхода.

Описание мира требует идентичности сущностей, выражения отношений, самоописывающихся описаний и независимости от предметной области. Языки программирования не для этого создавались. Естественный язык это может, но двусмысленно. То, что нужно, находится где-то между ними.