AdvancedChatAI.pas
PAS 2.79KB
PostProcessor.pas
PAS 5.07KB
ucs4opunit.pas
PAS 1.79KB
ucs4unit.pas
PAS 4.79KB
UniversalFileReader.pas
PAS 5.56KB
Привет! В прошлом чате мы совершенствовали наш чат ИИ на FreePascal'е под Debian Sid GNU/Linux на x86-64. И написали новый расширенный чат для него, который должен уметь либо просто получить от пользователя однострочное сообщение в консоли, либо прочитать многострочное из файла post.txt, если пользователь в консоли ввёл пустую строку. Также была добавлена поддержка загрузки файлов. Мы добились его компиляции без ошибок. Я его запустил и увидел, что он не ожидает пользовательского ввода и в цикле выдаёт: === Данные успешно загружены ===


=== Получены данные ===
Время: 2025-07-18 06:26:37
Язык: Неизвестен
Тип ввода: однострочный

Сообщение отсутствует

Вложения отсутствуют

=== Данные успешно загружены ===


=== Получены данные ===
Время: 2025-07-18 06:26:37
Язык: Неизвестен
Тип ввода: однострочный

Сообщение отсутствует

Вложения отсутствуют

=== Данные успешно загружены ===


=== Получены данные ===
Время: 2025-07-18 06:26:37
Язык: Неизвестен
^CТип ввода: однострочный

Сообщение отсутствует

Вложения отсутствуют

=== Данные успешно загружены ===
  Давай продолжим его совершенствование. Во-первых, чтобы он ждал от пользователя ввода не зацикливаясь. Во-вторых, удалял использованные файлы post.txt и postfiles.txt

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

    В AdvancedChatAI.pas нужно добавить ожидание пользовательского ввода и улучшить обработку файлов:

pascal

program AdvancedChatAI;
{$MODE OBJFPC}{$H+}{$RANGECHECKS ON}

uses
  SysUtils, Classes, PostProcessor, Unix, BaseUnix;

const
  TEMP_DIR = 'temp_attachments/';
  POST_FILE = 'post.txt';
  POST_FILES_LIST = 'postfiles.txt';

var
  UserInput: TUserInput;
  ShouldExit: Boolean = False;

procedure HandleSigInt(sig: cint); cdecl;
begin
  ShouldExit := True;
end;

procedure Initialize;
begin
  // Создаем временную директорию для вложений
  if not DirectoryExists(TEMP_DIR) then
    ForceDirectories(TEMP_DIR);
end;

function WaitForUserInput: Boolean;
var
  input: string;
begin
  Write('Введите сообщение (пустая строка для чтения из файла): ');
  ReadLn(input);
  
  if input = '' then
  begin
    // Пользователь хочет ввести многострочное сообщение через файл
    WriteLn('Пожалуйста, введите сообщение в файл ', POST_FILE, 
            ' и список вложений в ', POST_FILES_LIST);
    WriteLn('После завершения нажмите Enter...');
    ReadLn; // Ждем подтверждения
    Result := True;
  end
  else
  begin
    // Сохраняем однострочное сообщение во временный файл
    with TStringList.Create do
    try
      Text := input;
      SaveToFile(POST_FILE);
    finally
      Free;
    end;
    Result := True;
  end;
end;

procedure ProcessUserInput;
var
  i: Integer;
  filesList: TStringList;
begin
  // Создаем временный список файлов
  filesList := TStringList.Create;
  try
    if FileExists(POST_FILES_LIST) then
      filesList.LoadFromFile(POST_FILES_LIST);
    
    // Получаем пользовательский ввод
    UserInput := GetUserInput(POST_FILE, filesList);
  
    // Выводим информацию о полученных данных
    Writeln(#10'=== Получены данные ===');
    Writeln('Время: ', FormatDateTime('yyyy-mm-dd hh:nn:ss', UserInput.Timestamp));
    Writeln('Язык: ', LanguageToStr(UserInput.Language));
    Writeln('Тип ввода: ', IfThen(UserInput.IsMultiline, 'многострочный', 'однострочный'));
    
    if UserInput.Message <> '' then
    begin
      Writeln(#10'Сообщение:');
      Writeln(UserInput.Message);
    end
    else
      Writeln(#10'Сообщение отсутствует');

    // Обработка вложений
    if Length(UserInput.Attachments) > 0 then
    begin
      Writeln(#10'Вложения:');
      for i := 0 to High(UserInput.Attachments) do
      begin
        Writeln(Format('%d. %s (%s, %d bytes)', [
          i+1, 
          UserInput.Attachments[i].FileName, 
          UserInput.Attachments[i].ContentType, 
          Length(UserInput.Attachments[i].Content)
        ]));
        
        // Сохраняем временную копию файла
        SaveTempAttachment(UserInput.Attachments[i], TEMP_DIR);
      end;
    end
    else
      Writeln(#10'Вложения отсутствуют');

    Writeln(#10'=== Данные успешно загружены ==='#10);
  finally
    filesList.Free;
  end;
end;

procedure Cleanup;
begin
  // Очистка временных файлов
  if FileExists(POST_FILE) then
    DeleteFile(POST_FILE);
  if FileExists(POST_FILES_LIST) then
    DeleteFile(POST_FILES_LIST);
end;

begin
  FpSignal(SIGINT, @HandleSigInt);
  
  Initialize;
  try
    while not ShouldExit do
    begin
      try
        if WaitForUserInput then
          ProcessUserInput;
        Cleanup; // Очищаем файлы после каждой итерации
      except
        on E: Exception do
          Writeln('Ошибка: ', E.ClassName, ': ', E.Message);
      end;
    end;
  finally
    Cleanup;
  end;
end.

    В PostProcessor.pas изменений не требуется, но убедитесь, что функция GetUserInput корректно обрабатывает отсутствие файлов.

Основные изменения:

    Добавлена функция WaitForUserInput, которая ожидает ввод от пользователя

    Улучшена логика обработки однострочного и многострочного ввода

    Добавлен вызов Cleanup после каждой итерации цикла

    Улучшены сообщения для пользователя

Теперь программа будет:

    Ожидать ввод от пользователя

    Принимать либо однострочное сообщение, либо многострочное через файл

    Корректно обрабатывать вложения

    Удалять временные файлы после каждой итерации

    Корректно завершаться по Ctrl+C

Для использования:

    Для однострочного сообщения - просто введите текст и нажмите Enter

    Для многострочного сообщения - нажмите Enter, затем создайте файл post.txt с сообщением и postfiles.txt со списком вложений (если нужно), затем снова нажмите Enter в программе

Программа теперь не будет зацикливаться и будет корректно удалять временные файлы.
DataUtils.pas
PAS 3.44KB
NeuralNetwork.pas
PAS 10.09KB
Спасибо! Теперь надо понять, как подготавливать, передавать полученные данные дальше, чтобы их могла обработать нейросеть.

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

    Сначала создадим новый модуль NeuralNetworkIntegration.pas:

pascal

unit NeuralNetworkIntegration;
{$MODE OBJFPC}{$H+}{$RANGECHECKS ON}

interface

uses
  SysUtils, Classes, PostProcessor, DataUtils, NeuralNetwork;

type
  TTextProcessingResult = record
    Category: Integer;
    Confidence: Double;
    Features: TDoubleArray;
  end;

procedure InitializeTextClassifier(var network: TNeuralNetwork);
function ProcessTextToFeatures(const text: string): TDoubleArray;
function ClassifyText(var network: TNeuralNetwork; const text: string): TTextProcessingResult;
procedure TrainTextClassifier(var network: TNeuralNetwork; const samples: array of string; const labels: array of Integer);

implementation

procedure InitializeTextClassifier(var network: TNeuralNetwork);
const
  // Архитектура сети: 100 входных нейронов (признаков), 64 скрытых, 3 выходных (категории)
  LAYERS: array of Integer = [100, 64, 3];
  LEARNING_RATE = 0.01;
  LAMBDA = 0.0001; // Параметр регуляризации
begin
  SafeInitializeNetwork(network, LAYERS, LEARNING_RATE, LAMBDA);
end;

function ProcessTextToFeatures(const text: string): TDoubleArray;
var
  i, charCode: Integer;
  u8Char: string;
  charCounts: array[0..255] of Integer;
  totalChars: Integer;
begin
  // Инициализация массива подсчёта символов
  FillChar(charCounts, SizeOf(charCounts), 0);
  totalChars := 0;

  // Подсчёт частоты символов (упрощённая версия)
  for i := 1 to UTF8Length(text) do
  begin
    u8Char := UTF8Copy(text, i, 1);
    charCode := Ord(u8Char[1]) mod 256; // Упрощённое преобразование символа
    Inc(charCounts[charCode]);
    Inc(totalChars);
  end;

  // Нормализация и создание вектора признаков
  SetLength(Result, 256);
  if totalChars > 0 then
  begin
    for i := 0 to 255 do
      Result[i] := charCounts[i] / totalChars;
  end
  else
  begin
    for i := 0 to 255 do
      Result[i] := 0;
  end;
end;

function ClassifyText(var network: TNeuralNetwork; const text: string): TTextProcessingResult;
var
  features: TDoubleArray;
  output: TDoubleArray;
  i, bestCategory: Integer;
  maxConfidence: Double;
begin
  // Преобразование текста в числовые признаки
  features := ProcessTextToFeatures(text);
  
  // Классификация
  output := PredictNetwork(network, features);
  
  // Находим категорию с наибольшей уверенностью
  bestCategory := 0;
  maxConfidence := output[0];
  for i := 1 to High(output) do
  begin
    if output[i] > maxConfidence then
    begin
      maxConfidence := output[i];
      bestCategory := i;
    end;
  end;
  
  // Возвращаем результат
  Result.Category := bestCategory;
  Result.Confidence := maxConfidence;
  Result.Features := features;
end;

procedure TrainTextClassifier(var network: TNeuralNetwork; const samples: array of string; const labels: array of Integer);
var
  i: Integer;
  x: TDoubleMatrix;
  y: TDoubleArray;
begin
  if Length(samples) <> Length(labels) then
    raise Exception.Create('Samples and labels count mismatch');
  
  // Подготовка данных
  SetLength(x, Length(samples));
  SetLength(y, Length(samples));
  
  for i := 0 to High(samples) do
  begin
    x[i] := ProcessTextToFeatures(samples[i]);
    y[i] := labels[i];
  end;
  
  // Нормализация данных
  NormalizeData(x);
  
  // Обучение сети
  TrainNetwork(network, x, y, 100); // 100 эпох обучения
end;

end.

    Затем модифицируем AdvancedChatAI.pas для использования нейросети:

pascal

// Добавляем в раздел uses:
uses ..., NeuralNetworkIntegration;

// Добавляем переменные:
var
  TextClassifier: TNeuralNetwork;
  IsClassifierInitialized: Boolean = False;

// В процедуре Initialize добавляем инициализацию нейросети:
procedure Initialize;
begin
  // Создаем временную директорию для вложений
  if not DirectoryExists(TEMP_DIR) then
    ForceDirectories(TEMP_DIR);
  
  // Инициализация нейросети
  InitializeTextClassifier(TextClassifier);
  IsClassifierInitialized := True;
end;

// Модифицируем ProcessUserInput для классификации текста:
procedure ProcessUserInput;
var
  classification: TTextProcessingResult;
begin
  // ... существующий код ...
  
  if UserInput.Message <> '' then
  begin
    Writeln(#10'Сообщение:');
    Writeln(UserInput.Message);
    
    // Классификация текста
    if IsClassifierInitialized then
    begin
      classification := ClassifyText(TextClassifier, UserInput.Message);
      Writeln('Категория: ', classification.Category);
      Writeln('Уверенность: ', classification.Confidence:0:2);
    end;
  end
  else
    Writeln(#10'Сообщение отсутствует');
  
  // ... остальной код ...
end;

    Пример обучения нейросети (можно добавить отдельную процедуру):

pascal

procedure TrainClassifierExample;
var
  samples: array of string;
  labels: array of Integer;
begin
  // Пример данных для обучения
  SetLength(samples, 6);
  SetLength(labels, 6);
  
  // Русские сообщения (категория 0)
  samples[0] := 'Привет, как дела?';
  labels[0] := 0;
  samples[1] := 'Спасибо за помощь';
  labels[1] := 0;
  
  // Английские сообщения (категория 1)
  samples[2] := 'Hello, how are you?';
  labels[2] := 1;
  samples[3] := 'Thanks for your help';
  labels[3] := 1;
  
  // Другие (категория 2)
  samples[4] := '12345 test 67890';
  labels[4] := 2;
  samples[5] := '@#$%^&*()';
  labels[5] := 2;
  
  // Обучение классификатора
  TrainTextClassifier(TextClassifier, samples, labels);
  Writeln('Классификатор обучен на примерах');
end;

Этот подход позволяет:

    Преобразовывать текст в числовые признаки (частоты символов)

    Использовать нейросеть для классификации текста

    Интегрировать классификатор в вашу систему обработки сообщений

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

    Добавить более сложные методы извлечения признаков (TF-IDF, word embeddings)

    Увеличить размерность входных данных

    Добавить предобработку текста (стемминг, удаление стоп-слов)

    Реализовать более сложную архитектуру сети (LSTM, CNN для текста)

Текущая реализация использует простой частотный анализ символов, что подходит для демонстрации работы системы. Для реального применения стоит рассмотреть более сложные методы обработки естественного языка.