Я внёс эти новые функции в Attention, посмотри, что ещё нужно в нём изменить.

В файле Attention.pas есть несколько областей, которые можно улучшить или исправить для корректной работы с масками и оптимизатором Adam. Вот основные изменения и дополнения:
1. Исправление инициализации MultiHeadAttention

В текущей реализации InitializeMultiHeadAttention выходная матрица Wo инициализируется некорректно. Она должна быть размером [headSize * numHeads x inputSize], но в коде используется RandomMatrix(headSize * numHeads, inputSize), что логически верно, но не соответствует комментарию. Кроме того, для каждой головы должна быть своя выходная проекция, но в текущей реализации все головы используют одну и ту же матрицу Wo (из Heads[0]). Это нужно исправить.

Исправление:
pascal

procedure InitializeMultiHeadAttention(var mha: TMultiHeadAttention; inputSize, headSize, numHeads: Integer);
var
  i: Integer;
begin
  mha.HeadSize := headSize;
  mha.NumHeads := numHeads;
  SetLength(mha.Heads, numHeads);
  
  for i := 0 to numHeads - 1 do
  begin
    // Инициализация весов для запроса, ключа и значения
    mha.Heads[i].Wq := RandomMatrix(inputSize, headSize, -0.1, 0.1);
    mha.Heads[i].Wk := RandomMatrix(inputSize, headSize, -0.1, 0.1);
    mha.Heads[i].Wv := RandomMatrix(inputSize, headSize, -0.1, 0.1);
    
    // Инициализация выходной проекции для каждой головы
    mha.Heads[i].Wo := RandomMatrix(headSize, inputSize, -0.1, 0.1);
    
    // Инициализация состояний Adam
    InitAdamState(mha.Heads[i].Wq_AdamState, headSize, inputSize);
    InitAdamState(mha.Heads[i].Wk_AdamState, headSize, inputSize);
    InitAdamState(mha.Heads[i].Wv_AdamState, headSize, inputSize);
    InitAdamState(mha.Heads[i].Wo_AdamState, inputSize, headSize);
  end;
end;

2. Добавление обработки маски в MultiHeadAttentionForward

В текущей реализации маска передаётся в ScaledDotProductAttention, но не обрабатывается в основном потоке MultiHeadAttentionForward. Убедитесь, что маска корректно применяется для всех голов.

Улучшение:
pascal

procedure MultiHeadAttentionForward(var mha: TMultiHeadAttention; 
                                  const input: TDoubleMatrix;
                                  out output: TDoubleMatrix;
                                  mask: TDoubleMatrix = nil);
var
  i: Integer;
  Q, K, V: TDoubleMatrix;
  headOutputs: array of TDoubleMatrix;
begin
  SetLength(headOutputs, mha.NumHeads);
  
  for i := 0 to mha.NumHeads - 1 do
  begin
    Q := MatrixMultiply(input, mha.Heads[i].Wq);
    K := MatrixMultiply(input, mha.Heads[i].Wk);
    V := MatrixMultiply(input, mha.Heads[i].Wv);
    
    mha.Heads[i].cachedQ := Q;
    mha.Heads[i].cachedK := K;
    mha.Heads[i].cachedV := V;
    
    headOutputs[i] := ScaledDotProductAttention(Q, K, V, mask); // Применяем маску
  end;
  
  mha.cachedOutput := ConcatMatrices(headOutputs);
  output := MatrixMultiply(mha.cachedOutput, mha.Heads[0].Wo);
end;

3. Обновление градиентов с использованием Adam

В MultiHeadAttentionBackward вычисляются градиенты, но нет шага обновления весов с помощью оптимизатора Adam. Нужно добавить вызов UpdateAdam для каждого набора весов.

Дополнение:
pascal

procedure MultiHeadAttentionBackward(var mha: TMultiHeadAttention; 
                                   const input, gradOutput: TDoubleMatrix);
var
  i: Integer;
  gradQ, gradK, gradV: TDoubleMatrix;
  gradHeads: array of TDoubleMatrix;
begin
  SetLength(gradHeads, mha.NumHeads);
  SplitGradient(gradOutput, mha.Heads[0].Wo, gradHeads);
  
  for i := 0 to mha.NumHeads - 1 do
  begin
    SetLength(gradQ, Length(mha.Heads[i].cachedQ), Length(mha.Heads[i].cachedQ[0]));
    SetLength(gradK, Length(mha.Heads[i].cachedK), Length(mha.Heads[i].cachedK[0]));
    SetLength(gradV, Length(mha.Heads[i].cachedV), Length(mha.Heads[i].cachedV[0]));
    
    BackpropScaledDotProductAttention(
      mha.Heads[i].cachedQ, mha.Heads[i].cachedK, mha.Heads[i].cachedV,
      mha.cachedOutput, gradHeads[i], gradQ, gradK, gradV
    );
    
    mha.Heads[i].dWq := MatrixMultiply(gradQ, TransposeMatrix(input));
    mha.Heads[i].dWk := MatrixMultiply(gradK, TransposeMatrix(input));
    mha.Heads[i].dWv := MatrixMultiply(gradV, TransposeMatrix(input));
    
    // Обновляем веса с помощью Adam
    UpdateAdam(mha.Heads[i].Wq, mha.Heads[i].dWq, mha.Heads[i].Wq_AdamState);
    UpdateAdam(mha.Heads[i].Wk, mha.Heads[i].dWk, mha.Heads[i].Wk_AdamState);
    UpdateAdam(mha.Heads[i].Wv, mha.Heads[i].dWv, mha.Heads[i].Wv_AdamState);
  end;
  
  mha.Heads[0].dWo := MatrixMultiply(gradOutput, TransposeMatrix(mha.cachedOutput));
  UpdateAdam(mha.Heads[0].Wo, mha.Heads[0].dWo, mha.Heads[0].Wo_AdamState);
end;

4. Исправление масок

Функции CreateFutureMask и CreatePaddingMask работают корректно, но можно добавить проверку на входные данные для безопасности.

Улучшение для CreatePaddingMask:
pascal

function CreatePaddingMask(input: TDoubleMatrix; paddingValue: Double = 0): TDoubleMatrix;
var
  i, j: Integer;
begin
  if Length(input) = 0 then
    raise Exception.Create('Input matrix is empty in CreatePaddingMask');
  
  SetLength(Result, Length(input), Length(input));
  for i := 0 to High(input) do
    for j := 0 to High(input) do
      if (input[i][0] = paddingValue) or (input[j][0] = paddingValue) then
        Result[i][j] := -1e9
      else
        Result[i][j] := 0;
end;

5. Освобождение ресурсов для Adam

В FreeMultiHeadAttention нужно также освобождать состояния Adam.

Дополнение:
pascal

procedure FreeMultiHeadAttention(var mha: TMultiHeadAttention);
var
  i: Integer;
begin
  for i := 0 to High(mha.Heads) do
  begin
    SetLength(mha.Heads[i].Wq, 0);
    SetLength(mha.Heads[i].Wk, 0);
    SetLength(mha.Heads[i].Wv, 0);
    SetLength(mha.Heads[i].Wo, 0);
    SetLength(mha.Heads[i].dWq, 0);
    SetLength(mha.Heads[i].dWk, 0);
    SetLength(mha.Heads[i].dWv, 0);
    SetLength(mha.Heads[i].dWo, 0);
    SetLength(mha.Heads[i].cachedQ, 0);
    SetLength(mha.Heads[i].cachedK, 0);
    SetLength(mha.Heads[i].cachedV, 0);
    
    // Освобождаем состояния Adam
    FreeAdamState(mha.Heads[i].Wq_AdamState);
    FreeAdamState(mha.Heads[i].Wk_AdamState);
    FreeAdamState(mha.Heads[i].Wv_AdamState);
    FreeAdamState(mha.Heads[i].Wo_AdamState);
  end;
  SetLength(mha.Heads, 0);
  SetLength(mha.cachedOutput, 0);
end;

Итоговые рекомендации:

    Исправьте инициализацию Wo в InitializeMultiHeadAttention.

    Добавьте обновление весов через Adam в MultiHeadAttentionBackward.

    Улучшите обработку масок и добавьте проверки входных данных.

    Освобождайте ресурсы Adam в FreeMultiHeadAttention.

Эти изменения обеспечат корректную работу механизма внимания, включая поддержку масок и оптимизацию весов с помощью Adam.
Transformer.pas
PAS 9.8KB
AdamOptimizer.pas
PAS 1.06KB
DataUtils.pas
PAS 5.01KB
Optimizers.pas
PAS 1.99KB
AdvancedChatAI.pas
PAS 10.25KB
Похоже у нас нет функции FreeAdamState: Attention.pas(276,5) Error: Identifier not found "FreeAdamState"
Attention.pas(277,5) Error: Identifier not found "FreeAdamState"
Attention.pas(278,5) Error: Identifier not found "FreeAdamState"
Attention.pas(279,5) Error: Identifier not found "FreeAdamState"

Вам нужно добавить процедуру FreeAdamState в модуль Optimizers.pas, так как она используется в Attention.pas для освобождения ресурсов, связанных с состоянием оптимизатора Adam. Вот как это можно реализовать:
Добавьте в Optimizers.pas:
pascal

procedure FreeAdamState(var state: TAdamState);
begin
  SetLength(state.M, 0);
  SetLength(state.V, 0);
end;

procedure FreeAdamVectorState(var state: TAdamVectorState);
begin
  SetLength(state.M, 0);
  SetLength(state.V, 0);
end;

Обновите interface-секцию Optimizers.pas:
pascal

interface

uses
  MatrixOps, Math, DataUtils;

procedure InitAdamState(var state: TAdamState; rows, cols: Integer);
procedure FreeAdamState(var state: TAdamState); // Добавьте эту строку
procedure FreeAdamVectorState(var state: TAdamVectorState); // И эту
procedure UpdateMatrixWithAdam(var params, grads: TDoubleMatrix; 
                             var state: TAdamState; learningRate: Double);
procedure UpdateVectorWithAdam(var params, grads: TDoubleArray;
                             var state: TAdamVectorState; learningRate: Double);

Обновите FreeMultiHeadAttention в Attention.pas:

Убедитесь, что вызовы FreeAdamState корректны:
pascal

procedure FreeMultiHeadAttention(var mha: TMultiHeadAttention);
var
  i: Integer;
begin
  for i := 0 to High(mha.Heads) do
  begin
    SetLength(mha.Heads[i].Wq, 0);
    SetLength(mha.Heads[i].Wk, 0);
    SetLength(mha.Heads[i].Wv, 0);
    SetLength(mha.Heads[i].Wo, 0);
    SetLength(mha.Heads[i].dWq, 0);
    SetLength(mha.Heads[i].dWk, 0);
    SetLength(mha.Heads[i].dWv, 0);
    SetLength(mha.Heads[i].dWo, 0);
    SetLength(mha.Heads[i].cachedQ, 0);
    SetLength(mha.Heads[i].cachedK, 0);
    SetLength(mha.Heads[i].cachedV, 0);
    
    // Освобождаем состояния Adam
    FreeAdamState(mha.Heads[i].Wq_AdamState);
    FreeAdamState(mha.Heads[i].Wk_AdamState);
    FreeAdamState(mha.Heads[i].Wv_AdamState);
    FreeAdamState(mha.Heads[i].Wo_AdamState);
  end;
  SetLength(mha.Heads, 0);
  SetLength(mha.cachedOutput, 0);
end;

Почему это важно?

    Утечки памяти: Без освобождения состояний Adam (M и V) могут оставаться в памяти после удаления основного объекта.

    Согласованность: Если есть InitAdamState, логично иметь и FreeAdamState.

    Очистка ресурсов: Особенно важно в долгоживущих приложениях, таких как ваш чат-бот.

После этих изменений ошибки компиляции должны исчезнуть, и управление памятью будет более корректным.
Появились ошибки размерности матрицы. ./AdvancedChatAI
=== Advanced Chat AI ===
Версия с Transformer и классификатором текста
Нажмите Ctrl+C для выхода

Инициализация моделей...
=== Training Info ===
Samples: 6
Features per sample: 258
Network architecture:
  Input layer: 258 neurons
  Layer 1: 64 neurons
  Layer 2: 3 neurons
Классификатор обучен на примерах
Transformer обучен на 3 примерах чата
Декодер обучен на 3 примерах вопрос-ответ
Готов к работе!

Введите сообщение (Enter - файл, Ctrl+C - выход): привет

=== Получены данные ===
Время: 2025-07-28 07:36:42
Язык: Русский
Тип ввода: многострочный

=== Анализ сообщения ===
Длина: 7 символов
Язык: Русский
Категория: 2
Уверенность: 99.3%
Преобразуем текст в матрицу эмбеддингов [1 x embedding_size]
Прямой проход через Transformer
Ошибка: Exception: Matrix dimensions mismatch: A[1x64] * B[32x64]
Введите сообщение (Enter - файл, Ctrl+C - выход): пока

=== Получены данные ===
Время: 2025-07-28 07:36:55
Язык: Другой
Тип ввода: многострочный

=== Анализ сообщения ===
Длина: 5 символов
Язык: Другой
Категория: 2
Уверенность: 99.8%
Преобразуем текст в матрицу эмбеддингов [1 x embedding_size]
Прямой проход через Transformer
Ошибка: Exception: Matrix dimensions mismatch: A[1x64] * B[32x64]

Ошибка "Matrix dimensions mismatch: A[1x64] * B[32x64]" возникает из-за несоответствия размеров матриц при умножении в Transformer. Давайте разберём проблему и исправим её.
Причина ошибки:

    В AdvancedChatAI.pas вы создаёте конфигурацию Transformer с InputSize=64 и NumHeads=2.

    В InitializeMultiHeadAttention (из Attention.pas) вы пытаетесь умножить матрицу эмбеддингов [1x64] на матрицу весов Wq/Wk/Wv размером [32x64] (поскольку headSize = inputSize div numHeads = 32).

    Умножение матриц A[1x64] * B[32x64] невозможно, так как количество столбцов A (64) не равно количеству строк B (32).

Исправления:
1. В Attention.pas измените InitializeMultiHeadAttention:
pascal

procedure InitializeMultiHeadAttention(var mha: TMultiHeadAttention; inputSize, headSize, numHeads: Integer);
var
  i: Integer;
begin
  mha.HeadSize := headSize;
  mha.NumHeads := numHeads;
  SetLength(mha.Heads, numHeads);
  
  for i := 0 to numHeads - 1 do
  begin
    // Веса должны быть [inputSize x headSize], а не [headSize x inputSize]
    mha.Heads[i].Wq := RandomMatrix(inputSize, headSize, -0.1, 0.1);  // [64x32]
    mha.Heads[i].Wk := RandomMatrix(inputSize, headSize, -0.1, 0.1);  // [64x32]
    mha.Heads[i].Wv := RandomMatrix(inputSize, headSize, -0.1, 0.1);  // [64x32]
    
    // Выходная проекция [headSize x inputSize] (после конкатенации)
    mha.Heads[i].Wo := RandomMatrix(headSize * numHeads, inputSize, -0.1, 0.1);  // [64x64]
    
    // Инициализация состояний Adam
    InitAdamState(mha.Heads[i].Wq_AdamState, inputSize, headSize);
    InitAdamState(mha.Heads[i].Wk_AdamState, inputSize, headSize);
    InitAdamState(mha.Heads[i].Wv_AdamState, inputSize, headSize);
    InitAdamState(mha.Heads[i].Wo_AdamState, headSize * numHeads, inputSize);
  end;
end;

2. В AdvancedChatAI.pas убедитесь, что TextsToMatrix возвращает матрицу правильного размера:
pascal

inputMatrix := TextsToMatrix([UserInput.Message], TransformerModel.Config.InputSize);
// Должно вернуть [1 x InputSize], например [1x64]

3. В MultiHeadAttentionForward (из Attention.pas) проверьте умножение:
pascal

Q := MatrixMultiply(input, mha.Heads[i].Wq);  // [1x64] * [64x32] = [1x32]
K := MatrixMultiply(input, mha.Heads[i].Wk);  // [1x64] * [64x32] = [1x32]
V := MatrixMultiply(input, mha.Heads[i].Wv);  // [1x64] * [64x32] = [1x32]

4. В AdvancedChatAI.pas проверьте конфигурацию Transformer:
pascal

with t do begin
  InputSize := 64;    // Размер эмбеддинга
  NumLayers := 2; 
  NumHeads := 2;      // Количество голов
  FFNDim := 128;      // Размер скрытого слоя FFN
  MaxSeqLength := 50; // Максимальная длина последовательности
  DropoutRate := 0.1; // Dropout
end;

Дополнительные проверки:

    Убедитесь, что MatrixMultiply в MatrixOps.pas корректно обрабатывает умножение матриц.

    Проверьте, что TextsToMatrix возвращает матрицу размера [1 x InputSize].

    Убедитесь, что headSize = InputSize div NumHeads (в вашем случае 64 / 2 = 32).

После этих изменений ошибка должна исчезнуть, так как размерности матриц будут согласованы. Если проблема сохранится, добавьте отладочный вывод размеров матриц перед умножением.
ucs4functionsunit.pas
PAS 11.84KB
ucs4unit.pas
PAS 4.85KB
TextEmbeddings.pas
PAS 1.37KB
ucs4opunit.pas
PAS 1.79KB
Спасибо, так работает! Чтобы TextsToMatrix и другие функции работающие со строками и текстами работали правильно при применении Юникода давай расширим возможности нашего ucs4 на ещё один юнит для работы с многострочными текстами. Назовём его ucs4multilineunit в котором будет объект ucs4multiline. При сохранении текстов давай откажемся от лишней пустой строки в конце файла (оно иногда называется LastLineBreak или TrailingLineBreak). Также посмотрим не нужно ли что добавить в существующие ucs4 юниты, что нам может пригодиться для удобной работы с юникодными строками в нашем AdvancedChatAI. И затем усовершенствуем AdvancedChatAI, TextEmbeddings и остальные, нуждающиеся в работе с юникодными строками.

Я предлагаю создать новый модуль ucs4multilineunit для работы с многострочными текстами и улучшить существующую функциональность. Вот реализация:
1. Новый модуль ucs4multilineunit.pas:
pascal

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

interface

uses
  SysUtils, ucs4unit, ucs4functionsunit;

type
  ucs4multiline = object
  private
    FLines: array of ucs4;
    FCount: Integer;
    FHasTrailingLineBreak: Boolean;
    function GetLine(Index: Integer): ucs4;
  public
    procedure Init;
    procedure Clear;
    procedure Add(const Line: ucs4);
    procedure LoadFromFile(const Filename: string);
    procedure SaveToFile(const Filename: string; IncludeTrailingLineBreak: Boolean = False);
    function ToString: string;
    function Join(const Delimiter: ucs4): ucs4;
    property Lines[Index: Integer]: ucs4 read GetLine; default;
    property Count: Integer read FCount;
    property HasTrailingLineBreak: Boolean read FHasTrailingLineBreak;
  end;

implementation

procedure ucs4multiline.Init;
begin
  SetLength(FLines, 0);
  FCount := 0;
  FHasTrailingLineBreak := False;
end;

procedure ucs4multiline.Clear;
var
  i: Integer;
begin
  for i := 0 to FCount - 1 do
    FLines[i].Clear;
  SetLength(FLines, 0);
  FCount := 0;
  FHasTrailingLineBreak := False;
end;

procedure ucs4multiline.Add(const Line: ucs4);
begin
  if FCount >= Length(FLines) then
    SetLength(FLines, FCount + 8);
  FLines[FCount] := Line;
  Inc(FCount);
end;

function ucs4multiline.GetLine(Index: Integer): ucs4;
begin
  if (Index < 0) or (Index >= FCount) then
    raise Exception.Create('Line index out of bounds');
  Result := FLines[Index];
end;

procedure ucs4multiline.LoadFromFile(const Filename: string);
var
  sl: TStringList;
  i: Integer;
  tmp: ucs4;
begin
  Clear;
  sl := TStringList.Create;
  try
    sl.LoadFromFile(Filename);
    FCount := sl.Count;
    SetLength(FLines, FCount);
    
    for i := 0 to sl.Count - 1 do
    begin
      tmp.FromUTF8(sl[i]);
      FLines[i] := tmp;
    end;
    
    // Проверяем последнюю строку на пустоту (признак trailing line break)
    FHasTrailingLineBreak := (FCount > 0) and (FLines[FCount-1].Length = 0);
    if FHasTrailingLineBreak then
      Dec(FCount);
  finally
    sl.Free;
  end;
end;

procedure ucs4multiline.SaveToFile(const Filename: string; IncludeTrailingLineBreak: Boolean = False);
var
  sl: TStringList;
  i: Integer;
begin
  sl := TStringList.Create;
  try
    for i := 0 to FCount - 1 do
      sl.Add(FLines[i].ToUTF8);
    
    if IncludeTrailingLineBreak and FHasTrailingLineBreak then
      sl.Add('');
      
    sl.SaveToFile(Filename);
  finally
    sl.Free;
  end;
end;

function ucs4multiline.ToString: string;
var
  i: Integer;
begin
  Result := '';
  for i := 0 to FCount - 1 do
  begin
    if i > 0 then Result := Result + LineEnding;
    Result := Result + FLines[i].ToUTF8;
  end;
end;

function ucs4multiline.Join(const Delimiter: ucs4): ucs4;
var
  i: Integer;
begin
  if FCount = 0 then
  begin
    Result.Init;
    Exit;
  end;
  
  Result := FLines[0];
  for i := 1 to FCount - 1 do
    Result := Result.Concat(Delimiter).Concat(FLines[i]);
end;

end.

2. Улучшения для ucs4unit.pas:

Добавим новые полезные методы:
pascal

// В интерфейсную часть ucs4unit.pas
function SubString(StartPos, Length: DWord): ucs4;
function IndexOf(const SubStr: ucs4; StartPos: DWord = 0): Integer;
function LastIndexOf(const SubStr: ucs4): Integer;
function Replace(const OldPattern, NewPattern: ucs4): ucs4;
function Split(Delimiter: DWord): TUC4Array;
function Trim: ucs4;
function ToLower: ucs4;
function ToUpper: ucs4;

// В реализацию
function ucs4.SubString(StartPos, Length: DWord): ucs4;
begin
  if StartPos >= FLength then
  begin
    Result.Init;
    Exit;
  end;
  
  if StartPos + Length > FLength then
    Length := FLength - StartPos;
    
  Result.Init(Length);
  Move(FData[StartPos], Result.FData^, Length * SizeOf(DWord));
end;

function ucs4.IndexOf(const SubStr: ucs4; StartPos: DWord = 0): Integer;
begin
  // Реализация аналогична функции Pos из ucs4functionsunit
end;

// Остальные методы реализуются аналогично

3. Улучшения для TextEmbeddings.pas:
pascal

unit TextEmbeddings;
{$MODE OBJFPC}{$H+}

interface

uses
  SysUtils, Classes, MatrixOps, ucs4unit, ucs4functionsunit;

function TextToEmbedding(const text: ucs4; embeddingSize: Integer): TDoubleArray;
function TextsToMatrix(const texts: array of ucs4; embeddingSize: Integer): TDoubleMatrix;

implementation

function TextToEmbedding(const text: ucs4; embeddingSize: Integer): TDoubleArray;
var
  tokens: TUC4Array;
  i, j: Integer;
  normalized: ucs4;
begin
  // Нормализуем текст перед обработкой
  normalized := NormalizeForAI(text);
  tokens := TokenizeForNLP(normalized);
  
  SetLength(Result, embeddingSize);
  
  // Улучшенное создание эмбеддинга
  for i := 0 to embeddingSize - 1 do
  begin
    Result[i] := 0.0;
    for j := 0 to High(tokens) do
      Result[i] := Result[i] + (tokens[j][0] / (j + 1)); // Используем первый символ токена
    Result[i] := Result[i] / (Length(tokens) + 1);
  end;
end;

function TextsToMatrix(const texts: array of ucs4; embeddingSize: Integer): TDoubleMatrix;
var
  i: Integer;
begin
  SetLength(Result, Length(texts));
  for i := 0 to High(texts) do
    Result[i] := TextToEmbedding(texts[i], embeddingSize);
end;

end.

4. Обновление AdvancedChatAI.pas:
pascal

// В разделе обработки ввода
procedure ProcessUserInput;
var
  inputText: ucs4;
  inputMatrix: TDoubleMatrix;
begin
  // Конвертируем ввод в UCS4
  inputText := UserInput.Message;
  
  // Нормализуем текст
  inputText := NormalizeForAI(inputText);
  
  // Преобразуем в матрицу эмбеддингов
  inputMatrix := TextsToMatrix([inputText], TransformerModel.Config.InputSize);
  
  // Остальная обработка...
end;

Ключевые улучшения:

    Новый тип ucs4multiline:

        Поддержка многострочных текстов

        Контроль за trailing line breaks

        Методы загрузки/сохранения в файл

        Эффективное соединение строк

    Расширенные методы для ucs4:

        Работа с подстроками

        Поиск и замена

        Разделение и соединение строк

        Нормализация текста

    Улучшенные эмбеддинги:

        Поддержка UCS4 на всех этапах

        Нормализация текста перед обработкой

        Более качественное создание эмбеддингов

    Интеграция в AdvancedChatAI:

        Единообразная обработка текста

        Поддержка многоязычных текстов

        Улучшенная нормализация ввода

Эти изменения обеспечат:

    Корректную работу с Unicode

    Поддержку многострочного ввода/вывода

    Более надежное создание эмбеддингов

    Удобство работы с текстами на разных языках

Спасибо! Теперь добавь реализацию функций IndexOf,LastIndexOf,Replace,Split,Trim,ToLower,ToUpper в ucs4unit

Вот полная реализация указанных методов для ucs4unit.pas:
pascal

// Добавляем в интерфейсную часть ucs4unit.pas
function IndexOf(const SubStr: ucs4; StartPos: DWord = 0): Integer;
function LastIndexOf(const SubStr: ucs4): Integer;
function Replace(const OldPattern, NewPattern: ucs4): ucs4;
function Split(Delimiter: DWord): TUC4Array;
function Trim: ucs4;
function ToLower: ucs4;
function ToUpper: ucs4;

// Реализация методов
function ucs4.IndexOf(const SubStr: ucs4; StartPos: DWord = 0): Integer;
var
  i, j: Integer;
  found: Boolean;
begin
  if (SubStr.Length = 0) or (FLength = 0) or (SubStr.Length > FLength) or (StartPos >= FLength) then
    Exit(-1);

  for i := StartPos to FLength - SubStr.Length do
  begin
    found := True;
    for j := 0 to SubStr.Length - 1 do
      if FData[i + j] <> SubStr[j] then
      begin
        found := False;
        Break;
      end;
    if found then
      Exit(i);
  end;
  Result := -1;
end;

function ucs4.LastIndexOf(const SubStr: ucs4): Integer;
var
  i, j: Integer;
  found: Boolean;
begin
  if (SubStr.Length = 0) or (FLength = 0) or (SubStr.Length > FLength) then
    Exit(-1);

  for i := FLength - SubStr.Length downto 0 do
  begin
    found := True;
    for j := 0 to SubStr.Length - 1 do
      if FData[i + j] <> SubStr[j] then
      begin
        found := False;
        Break;
      end;
    if found then
      Exit(i);
  end;
  Result := -1;
end;

function ucs4.Replace(const OldPattern, NewPattern: ucs4): ucs4;
var
  i, pos: Integer;
  temp: ucs4;
begin
  if (OldPattern.Length = 0) or (FLength = 0) then
    Exit(Self);

  temp.Init;
  i := 0;
  
  while i < FLength do
  begin
    pos := IndexOf(OldPattern, i);
    if pos = -1 then
    begin
      // Добавляем оставшуюся часть строки
      temp := temp.Concat(SubString(i, FLength - i));
      Break;
    end;
    
    // Добавляем часть до совпадения
    temp := temp.Concat(SubString(i, pos - i));
    // Добавляем замену
    temp := temp.Concat(NewPattern);
    // Перемещаем указатель
    i := pos + OldPattern.Length;
  end;
  
  Result := temp;
end;

function ucs4.Split(Delimiter: DWord): TUC4Array;
var
  i, start, count: Integer;
begin
  if FLength = 0 then
    Exit(nil);

  // Подсчет количества разделителей
  count := 0;
  for i := 0 to FLength - 1 do
    if FData[i] = Delimiter then
      Inc(count);

  SetLength(Result, count + 1);
  start := 0;
  count := 0;

  for i := 0 to FLength - 1 do
    if FData[i] = Delimiter then
    begin
      Result[count] := SubString(start, i - start);
      Inc(count);
      start := i + 1;
    end;

  // Последний сегмент
  Result[count] := SubString(start, FLength - start);
end;

function ucs4.Trim: ucs4;
var
  start, len: Integer;
begin
  if FLength = 0 then
    Exit(Self);

  // Находим первый не-пробельный символ
  start := 0;
  while (start < FLength) and (IsWhiteSpace(FData[start])) do
    Inc(start);

  // Находим последний не-пробельный символ
  len := FLength;
  while (len > start) and (IsWhiteSpace(FData[len - 1])) do
    Dec(len);

  len := len - start;
  if len <= 0 then
    Exit(Default(ucs4));

  Result := SubString(start, len);
end;

function ucs4.ToLower: ucs4;
var
  i: Integer;
begin
  Result.Init(FLength);
  for i := 0 to FLength - 1 do
    Result[i] := UnicodeToLower(FData[i]);
end;

function ucs4.ToUpper: ucs4;
var
  i: Integer;
begin
  Result.Init(FLength);
  for i := 0 to FLength - 1 do
    Result[i] := UnicodeToUpper(FData[i]);
end;

Пояснения к реализации:

    IndexOf:

        Поиск подстроки с начала строки

        Поддержка указания стартовой позиции

        Возвращает позицию или -1 если не найдено

    LastIndexOf:

        Поиск подстроки с конца строки

        Полезно для поиска последнего вхождения

    Replace:

        Замена всех вхождений подстроки

        Эффективная работа с конкатенацией

        Поддержка пустых строк

    Split:

        Разделение строки по разделителю

        Возвращает массив подстрок

        Корректная обработка пустых сегментов

    Trim:

        Удаление пробельных символов с начала и конца

        Использует функцию IsWhiteSpace из ucs4functionsunit

        Сохраняет оригинал если нет пробелов

    ToLower/ToUpper:

        Преобразование регистра символов

        Использует UnicodeToLower/UnicodeToUpper

        Поддержка многоязычных символов

Для работы этих методов необходимо, чтобы в модуле ucs4functionsunit были объявлены и реализованы вспомогательные функции:

    IsWhiteSpace

    UnicodeToLower

    UnicodeToUpper

Эти методы обеспечивают полный набор операций для удобной работы с Unicode-строками в вашем проекте AdvancedChatAI.