ваше сообщение коммита

This commit is contained in:
2026-02-20 14:18:08 +03:00
parent 8702a355a1
commit 24e560fc83
44 changed files with 8354 additions and 95 deletions

View File

@@ -0,0 +1,663 @@
[English](../../docs.en/back-docs/MULTICHAIN_GOVERNANCE_TOKEN_TRANSFER.md) | **Русский**
# Мультичейн-управление переводом токенов DLE
## Обзор системы
Система мультичейн-управления DLE позволяет холдерам токенов создавать предложения по переводу токенов со своего кошелька на другой адрес (или в казну) через процесс голосования во всех сетях, где развернут контракт DLE. Каждая сеть имеет независимый кворум, но предложения координируются и отображаются как единое целое.
## Архитектура
### Мультичейн-контракты DLE
- Один DLE может быть развернут в нескольких блокчейн-сетях (например, Sepolia, Arbitrum Sepolia, Base Sepolia)
- Каждый контракт DLE в каждой сети работает независимо
- Предложения создаются, голосуются и выполняются в каждой сети отдельно
- ID предложений уникальны для каждой сети (предложение с ID=1 в Sepolia и предложение с ID=1 в Arbitrum Sepolia - это разные предложения)
### Группировка предложений
- Предложения с одинаковым описанием и инициатором группируются в одну карточку
- Карточка отображает статус предложения во всех сетях DLE
- Каждая сеть в карточке имеет свой собственный ID предложения, состояние и результаты голосования
## Процесс перевода токенов
### Этап 1: Создание предложения
#### Описание процесса
1. Пользователь заполняет форму перевода токенов:
- **Описание предложения** - текстовое описание цели перевода
- **Адрес получателя** - адрес кошелька или казны, на который будут переведены токены
- **Количество токенов** - количество DLE токенов для перевода
- **Длительность голосования** - период времени, в течение которого можно голосовать
- **Ваш подключенный кошелек** - автоматически заполняется адресом подключенного кошелька (токены будут отправлены с этого адреса)
2. Система определяет все сети, где развернут контракт DLE
3. **Последовательное создание предложений в каждой сети:**
- Для каждой сети DLE:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Создание предложения в контракте DLE этой сети
- Получение уникального ID предложения для этой сети
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
- При ошибках RPC выполняется автоматический retry с экспоненциальной задержкой (до 3 попыток)
4. **Подписи в MetaMask:**
- Пользователь должен подписать транзакцию создания предложения в каждой сети DLE
- Количество подписей = количество сетей DLE
- Каждая подпись создает отдельное предложение в соответствующей сети
#### Технические детали
- **Функция контракта:** `createProposal(description, duration, operation, targetChains, timelockDelay)`
- **Порядок параметров:** `description`, `duration`, `operation`, `targetChains` (массив), `timelockDelay`
- **targetChains:** Массив ID сетей, где будет выполнена операция (обычно `[chainId]` для текущей сети)
- **Операция:** `_transferTokens(sender, recipient, amount)` - где `sender` = адрес инициатора предложения
- **Сигнатура:** `_transferTokens(address,address,uint256)` - **все три параметра обязательны!**
- `sender` получается автоматически из `signer.getAddress()` при создании предложения
- **ID предложения:** Генерируется автоматически контрактом в каждой сети (начинается с 0, инкрементируется)
- **Группировка:** Предложения с одинаковым `description` и `initiator` группируются в одну карточку
#### Кодирование операции
Операция для выполнения должна быть закодирована в формате ABI (Application Binary Interface) перед передачей в `createProposal`.
**Для операции перевода токенов `_transferTokens(address,address,uint256)`:**
1. **Сигнатура функции:** `_transferTokens(address,address,uint256)`
2. **Селектор функции:** Первые 4 байта от `keccak256(signature)`
3. **Параметры:**
- `sender` - адрес отправителя (инициатора предложения)
- `recipient` - адрес получателя токенов
- `amount` - количество токенов (в wei, т.е. количество * 10^18)
**Пример кодирования (JavaScript/ethers.js):**
```javascript
// Способ 1: Использование Interface (рекомендуется)
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
const encodedOperation = iface.encodeFunctionData('_transferTokens', [
senderAddress, // адрес инициатора
recipientAddress, // адрес получателя
ethers.parseUnits(amount.toString(), 18) // количество в wei
]);
// Способ 2: Ручное кодирование
const functionSignature = '_transferTokens(address,address,uint256)';
const selectorBytes = ethers.keccak256(ethers.toUtf8Bytes(functionSignature));
const selector = '0x' + selectorBytes.slice(2, 10); // первые 4 байта
const abiCoder = ethers.AbiCoder.defaultAbiCoder();
const encodedParams = abiCoder.encode(
['address', 'address', 'uint256'],
[senderAddress, recipientAddress, ethers.parseUnits(amount.toString(), 18)]
);
const encodedOperation = ethers.concat([selector, encodedParams]);
```
**Важные моменты:**
- `sender` должен совпадать с адресом инициатора предложения (проверяется в контракте при выполнении)
- `amount` **ОБЯЗАТЕЛЬНО** передается в wei (1 токен = 10^18 wei) - используйте `ethers.parseUnits(amount.toString(), 18)`
- **КРИТИЧЕСКИ ВАЖНО:** `sender` должен определяться из `signer.getAddress()` при создании предложения в каждой сети отдельно, а не один раз до цикла
- Операция кодируется для каждой сети отдельно с актуальным адресом signer для этой сети
- Контракт декодирует операцию при выполнении и проверяет соответствие `sender` и `initiator`
**КРИТИЧЕСКИ ВАЖНО - Правильная реализация:**
```javascript
// ✅ ПРАВИЛЬНО: Кодирование внутри цикла с актуальным адресом signer для каждой сети
async function createProposalsInAllChains(allChains, formData) {
const results = [];
for (let index = 0; index < allChains.length; index++) {
const chainId = allChains[index];
// 1. Переключаемся на нужную сеть
await switchToVotingNetwork(chainId.toString());
await new Promise(resolve => setTimeout(resolve, 1000)); // Задержка после переключения
// 2. КРИТИЧЕСКИ ВАЖНО: Получаем адрес signer для текущей сети
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const senderAddress = await signer.getAddress(); // Адрес инициатора из signer!
// 3. Кодируем операцию с актуальным адресом signer для этой сети
const transferCallData = encodeTransferTokensCall(
senderAddress, // адрес инициатора из signer (обязательно!)
formData.recipient, // адрес получателя
formData.amount // количество (будет сконвертировано в wei)
);
// 4. Создаем предложение
const proposalData = {
description: formData.description,
duration: formData.duration,
operation: transferCallData,
targetChains: [chainId],
timelockDelay: 0
};
await createProposal(contractAddress, proposalData);
}
}
function encodeTransferTokensCall(sender, recipient, amount) {
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
// КРИТИЧЕСКИ ВАЖНО: конвертируем amount в wei
const amountInWei = ethers.parseUnits(amount.toString(), 18);
const encodedCall = iface.encodeFunctionData('_transferTokens', [
sender, // адрес инициатора (обязательно! должен быть из signer.getAddress())
recipient, // адрес получателя
amountInWei // количество в wei (обязательно!)
]);
return encodedCall;
}
// ❌ НЕПРАВИЛЬНО: Кодирование один раз до цикла
// const transferCallData = encodeTransferTokensCall(formData.sender, ...); // НЕПРАВИЛЬНО!
// for (const chainId of allChains) {
// await createProposal(contractAddress, { operation: transferCallData, ... }); // sender может не совпасть!
// }
// ❌ НЕПРАВИЛЬНО: Отсутствует sender или amount не в wei
// const transferFunctionSelector = ethers.id("_transferTokens(address,uint256)"); // НЕПРАВИЛЬНО!
// const amount = transferData.amount; // НЕПРАВИЛЬНО! Нужна конвертация в wei
```
**Другие поддерживаемые операции:**
- `_addModule(bytes32,address)` - добавление модуля
- `_removeModule(bytes32)` - удаление модуля
- `_addSupportedChain(uint256)` - добавление поддерживаемой сети
- `_removeSupportedChain(uint256)` - удаление поддерживаемой сети
- `_updateVotingDurations(uint256,uint256)` - обновление времени голосования
- `_updateQuorumPercentage(uint256)` - обновление процента кворума
- `_updateDLEInfo(...)` - обновление информации DLE
- `_setLogoURI(string)` - обновление URI логотипа
#### Результат
- Создано N предложений (по одному в каждой сети DLE)
- Каждое предложение имеет уникальный ID в своей сети
- Все предложения отображаются как одна карточка в интерфейсе
- Карточка показывает статус предложения в каждой сети
---
### Этап 2: Голосование
#### Описание процесса
1. Пользователь видит карточку предложения с информацией о всех сетях DLE
2. Пользователь выбирает голос "За" или "Против"
3. **Последовательное голосование во всех активных сетях:**
- Система определяет все активные цепочки (state === 0 или 'active', не выполнены, не отменены)
- Для каждой активной сети:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- **Проверка баланса токенов в этой сети** (балансы могут отличаться в разных сетях)
- Если баланс отсутствует, сеть пропускается с предупреждением
- Голосование продолжается в других сетях
- Голосование с использованием **уникального ID предложения для этой сети**
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
4. **Подписи в MetaMask:**
- Пользователь должен подписать транзакцию голосования в каждой активной сети DLE
- Количество подписей = количество активных сетей DLE
- Каждая подпись регистрирует голос в соответствующей сети
#### Технические детали
- **Функция контракта:** `vote(proposalId, support)` - где `proposalId` уникален для каждой сети
- **Проверка ID:** Система использует `chain.id` (ID предложения из конкретной сети), а не общий ID группы
- **Проверка баланса:** Баланс токенов проверяется **в каждой сети отдельно** перед голосованием
- В мультичейн-системе балансы могут отличаться в разных сетях
- Если в сети нет токенов, голосование в этой сети пропускается
- Контракт также проверяет баланс через `getPastVotes()` и вернет ошибку, если токенов нет
- **Вес голоса:** Зависит от баланса токенов голосующего в соответствующей сети
- **Независимые кворумы:** Каждая сеть имеет свой собственный кворум
#### Результат
- Голос зарегистрирован во всех активных сетях DLE
- Каждая сеть обновляет свои счетчики голосов (forVotes, againstVotes)
- Карточка предложения обновляется с новыми данными голосования
---
### Этап 3: Выполнение предложения
#### Условия выполнения
**КРИТИЧЕСКИ ВАЖНО:** Предложение может быть выполнено только при условии, что кворум достигнут **во всех сетях DLE**, где предложение активно.
Условия для каждой сети:
- Состояние предложения: `ReadyForExecution` (state === 5)
- Кворум достигнут: `forVotes >= quorumRequired`
- Большинство голосов "За": `forVotes > againstVotes`
- Предложение не выполнено: `executed === false`
- Предложение не отменено: `canceled === false`
- Истек период голосования (если применимо)
- Истек период timelock (если применимо)
#### Описание процесса
1. Система проверяет, что предложение готово к выполнению во всех активных сетях DLE
2. **Последовательное выполнение во всех готовых сетях:**
- Для каждой сети, где предложение готово к выполнению:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Выполнение предложения с использованием **уникального ID предложения для этой сети**
- Задержка 3 секунды после подтверждения транзакции (5 секунд для Base Sepolia)
3. **Подписи в MetaMask:**
- Пользователь должен подписать транзакцию выполнения в каждой готовой сети DLE
- Количество подписей = количество готовых сетей DLE
- Каждая подпись выполняет перевод токенов в соответствующей сети
#### Технические детали
- **Функция контракта:** `executeProposal(proposalId)` - где `proposalId` уникален для каждой сети
- **Операция перевода:** `_transferTokens(sender, recipient, amount)`
- `sender` = адрес инициатора предложения (проверяется в контракте)
- `recipient` = адрес получателя из предложения
- `amount` = количество токенов из предложения
- **Проверка безопасности:** Контракт проверяет, что `sender` совпадает с `initiator` предложения
- **Перевод токенов:** Токены переводятся с кошелька инициатора, а не с баланса контракта
#### Результат
- Перевод токенов выполнен во всех готовых сетях DLE
- Каждая сеть независимо выполняет перевод с кошелька инициатора
- Карточка предложения обновляется, показывая статус "Выполнено" во всех сетях
---
## Отмена предложения
### Условия отмены
- Только инициатор предложения может отменить его
- Предложение должно быть активным (не выполнено, не отменено)
- Отмена возможна в любой момент до выполнения
### Процесс отмены
1. **Последовательная отмена во всех активных сетях:**
- Для каждой активной сети:
- Переключение MetaMask на соответствующую сеть
- Задержка 1 секунда после переключения
- Отмена предложения с использованием **уникального ID предложения для этой сети**
- Задержка 3 секунды после подтверждения транзакции
2. **Подписи в MetaMask:**
- Пользователь должен подписать транзакцию отмены в каждой активной сети DLE
- Количество подписей = количество активных сетей DLE
### Технические детали
- **Функция контракта:** `cancelProposal(proposalId, reason)`
- **Проверка прав:** Контракт проверяет, что вызывающий является инициатором предложения
---
## Важные особенности системы
### 1. Уникальность ID предложений
- **Каждая сеть имеет свой собственный счетчик предложений**
- Предложение с ID=1 в Sepolia и предложение с ID=1 в Arbitrum Sepolia - это **разные предложения**
- При группировке система сохраняет ID из каждой сети отдельно
- При голосовании/выполнении/отмене используется правильный ID для каждой сети
### 2. Группировка предложений
- Предложения группируются по ключу: `${description}_${initiator}`
- Одна карточка = одно логическое предложение во всех сетях
- Карточка отображает:
- Общее описание
- Инициатора
- Список сетей с их статусами
- Результаты голосования по каждой сети
- Общий статус (активно/выполнено/отменено)
### 3. Независимые кворумы
- **Каждая сеть имеет свой собственный кворум**
- Кворум рассчитывается на основе общего предложения токенов в соответствующей сети
- Голосование в одной сети не влияет на кворум в другой сети
- Для выполнения предложения кворум должен быть достигнут **во всех сетях**
### 4. Последовательное выполнение операций
- Все операции (создание, голосование, выполнение, отмена) выполняются **последовательно**, а не параллельно
- Это необходимо, так как MetaMask может работать только с одной сетью одновременно
- Между операциями есть задержки для стабилизации MetaMask
- **КРИТИЧЕСКИ ВАЖНО:** Использование `Promise.all` для параллельного выполнения недопустимо и приведет к ошибкам
### 5. Обработка ошибок
- **Retry для временных ошибок RPC:**
- Автоматический retry до 3 попыток
- Экспоненциальная задержка (2s, 4s, 8s)
- Только для retryable ошибок (Internal JSON-RPC error, rate limiting)
- **Ошибки в отдельных сетях:**
- Если операция не удалась в одной сети, процесс продолжается для других сетей
- Пользователь получает сводку успешных и неудачных операций
- **Обработка отсутствия баланса:**
- Перед голосованием в каждой сети проверяется баланс токенов
- Если в сети нет токенов, голосование в этой сети пропускается с предупреждением
- Голосование продолжается в других сетях, где баланс есть
- Контракт также проверяет баланс и вернет ошибку `ErrNoPower`, если токенов нет
### 6. Безопасность
- **Проверка инициатора:** При выполнении контракт проверяет, что `sender` совпадает с `initiator`
- **Проверка баланса:** Перед голосованием проверяется наличие токенов **в каждой сети отдельно**
- Балансы могут отличаться в разных сетях
- Если в сети нет токенов, голосование в этой сети пропускается
- Контракт также проверяет баланс через `getPastVotes()` и вернет ошибку `ErrNoPower`, если токенов нет
- **Проверка состояния:** Перед каждой операцией проверяется актуальное состояние предложения
- **Валидация данных:** Все данные предложения валидируются перед отправкой в контракт
### 7. Перевод токенов
- **Источник токенов:** Токены переводятся с кошелька инициатора предложения, а не с баланса контракта
- **Получатель:** Может быть любой адрес (кошелек или казна)
- **Количество:** Указывается инициатором при создании предложения
- **Проверка баланса:** Контракт проверяет достаточность баланса инициатора перед выполнением
---
## Пользовательский интерфейс
### Карточка предложения
- **Заголовок:** Описание предложения
- **Инициатор:** Адрес создателя предложения
- **Список сетей:**
- Название сети
- Статус (Активно/Выполнено/Отменено/Истекло)
- ID предложения в этой сети
- Результаты голосования (За/Против)
- Кворум (достигнут/не достигнут)
- **Действия:**
- Голосовать "За" / "Против" (если активно)
- Выполнить (если готово к выполнению)
- Отменить (если инициатор)
### Индикаторы статуса
- **Активно:** Предложение открыто для голосования
- **Готово к выполнению:** Кворум достигнут во всех сетях
- **Выполнено:** Перевод токенов выполнен во всех сетях
- **Отменено:** Предложение отменено инициатором
- **Истекло:** Истек период голосования
---
## Технические детали реализации
### Кодирование операций
Подробное описание процесса кодирования операций для создания предложений см. в разделе [Кодирование операции](#кодирование-операции) (Этап 1: Создание предложения).
### Структура данных предложения
```javascript
{
id: number, // ID группы (из первой сети)
description: string, // Описание предложения
initiator: address, // Адрес инициатора
deadline: number, // Дедлайн голосования
chains: [ // Массив данных по каждой сети
{
id: number, // УНИКАЛЬНЫЙ ID для этой сети
chainId: number, // ID сети (11155111, 421614, 84532)
networkName: string, // Название сети
contractAddress: address, // Адрес контракта DLE в этой сети
state: number, // Состояние (0=Active, 3=Executed, 4=Canceled, 5=ReadyForExecution)
forVotes: bigint, // Голоса "За"
againstVotes: bigint, // Голоса "Против"
quorumRequired: bigint, // Требуемый кворум
executed: boolean, // Выполнено
canceled: boolean, // Отменено
transactionHash: string // Хеш транзакции создания
}
],
createdAt: number, // Время создания
uniqueId: string // Уникальный ключ группировки
}
```
### Функции контракта DLE
#### createProposal
```solidity
function createProposal(
string memory _description,
uint256 _duration,
bytes memory _operation,
uint256[] memory _targetChains,
uint256 _timelockDelay
) public returns (uint256 proposalId)
```
#### vote
```solidity
function vote(uint256 _proposalId, bool _support) public
```
#### executeProposal
```solidity
function executeProposal(uint256 _proposalId) public
```
#### cancelProposal
```solidity
function cancelProposal(uint256 _proposalId, string memory _reason) public
```
#### _transferTokens (internal)
```solidity
function _transferTokens(
address _sender,
address _recipient,
uint256 _amount
) internal
```
---
## Примеры использования
### Пример 0: Кодирование операции перевода токенов
Перед созданием предложения необходимо закодировать операцию. **КРИТИЧЕСКИ ВАЖНО:** Используйте правильную сигнатуру и конвертируйте amount в wei.
```javascript
import { ethers } from 'ethers';
// Получаем адрес инициатора из signer
const signer = await provider.getSigner();
const sender = await signer.getAddress(); // Адрес инициатора (обязательно!)
// Параметры перевода
const recipient = '0x1234567890123456789012345678901234567890'; // Получатель
const amount = 100; // 100 токенов (в обычных единицах, не в wei)
// Кодирование операции
const functionSignature = '_transferTokens(address,address,uint256)';
const iface = new ethers.Interface([`function ${functionSignature}`]);
// КРИТИЧЕСКИ ВАЖНО: конвертируем amount в wei
const amountInWei = ethers.parseUnits(amount.toString(), 18); // 100 * 10^18 wei
const encodedOperation = iface.encodeFunctionData('_transferTokens', [
sender, // адрес инициатора (обязательно!)
recipient, // адрес получателя
amountInWei // количество в wei (обязательно!)
]);
// encodedOperation теперь можно использовать в createProposal
// Результат: 0x... (селектор функции + закодированные параметры)
// Создание предложения с правильным порядком параметров
const tx = await dle.createProposal(
"Перевод 100 токенов в казну", // description
86400, // duration (1 день в секундах)
encodedOperation, // operation
[chainId], // targetChains (массив!)
0 // timelockDelay
);
```
**Результат:** Закодированная операция в формате bytes, готовая для передачи в `createProposal`.
**Частые ошибки:**
- ❌ Использование `_transferTokens(address,uint256)` - неправильная сигнатура
- ❌ Отсутствие параметра `sender` - контракт не сможет проверить инициатора
- ❌ Передача `amount` без конвертации в wei - неправильное количество токенов
- ❌ Неправильный порядок параметров в `createProposal` - ошибка вызова контракта
### Пример 1: Создание предложения в 3 сетях
1. Пользователь создает предложение "Перевод 100 токенов в казну"
2. Система определяет 3 сети: Sepolia, Arbitrum Sepolia, Base Sepolia
3. Создаются 3 предложения:
- Sepolia: ID=5
- Arbitrum Sepolia: ID=3
- Base Sepolia: ID=7
4. Все 3 предложения отображаются как одна карточка
### Пример 2: Голосование
1. Пользователь голосует "За" за предложение
2. Система голосует в 3 сетях:
- Sepolia: vote(5, true)
- Arbitrum Sepolia: vote(3, true)
- Base Sepolia: vote(7, true)
3. Каждое голосование требует отдельной подписи в MetaMask
### Пример 3: Выполнение
1. Кворум достигнут во всех 3 сетях
2. Система выполняет предложение в 3 сетях:
- Sepolia: executeProposal(5)
- Arbitrum Sepolia: executeProposal(3)
- Base Sepolia: executeProposal(7)
3. В каждой сети токены переводятся с кошелька инициатора на адрес получателя
---
## Ограничения и особенности
### Ограничения
- MetaMask может работать только с одной сетью одновременно
- Операции выполняются последовательно, что может занять время при большом количестве сетей
- Ошибки RPC могут потребовать ручного retry
### Особенности
- Если предложение не создано в одной из сетей (из-за ошибки), оно все равно может быть создано в других сетях
- Голосование возможно только в тех сетях, где предложение активно
- Выполнение возможно только если кворум достигнут во всех активных сетях
---
## Безопасность
### Защита от атак
- Проверка инициатора при выполнении
- Проверка баланса перед переводом
- Независимые кворумы в каждой сети
- Валидация всех входных данных
### Рекомендации
- Всегда проверяйте адрес получателя перед созданием предложения
- Убедитесь, что у вас достаточно токенов для перевода
- Проверяйте статус предложения перед голосованием/выполнением
- Следите за транзакциями в каждой сети
---
## Известные проблемы и исправления
### Исправленные критические ошибки
#### 1. Отсутствие конвертации amount в wei
**Проблема:** Параметр `amount` передавался без конвертации в wei, что приводило к неправильному количеству токенов при выполнении.
**Исправление:** Добавлена обязательная конвертация через `ethers.parseUnits(amount.toString(), 18)` перед кодированием операции.
**Файл:** `frontend/src/views/smartcontracts/TransferTokensFormView.vue`
**Статус:** ✅ Исправлено
#### 2. Неправильная сигнатура функции _transferTokens
**Проблема:** Использовалась неправильная сигнатура `_transferTokens(address,uint256)` вместо `_transferTokens(address,address,uint256)`, что приводило к ошибкам декодирования в контракте.
**Исправление:** Исправлена сигнатура функции и добавлен обязательный параметр `sender` (адрес инициатора).
**Файл:** `frontend/src/utils/dle-contract.js` (функция `createTransferTokensProposal`)
**Статус:** ✅ Исправлено
#### 3. Неправильный порядок параметров в createProposal
**Проблема:** В функцию `createProposal` передавался `governanceChainId` вместо `targetChains` в 4-м параметре, что нарушало сигнатуру контракта.
**Исправление:** Исправлен порядок параметров согласно сигнатуре контракта: `description`, `duration`, `operation`, `targetChains`, `timelockDelay`.
**Файл:** `frontend/src/utils/dle-contract.js` (функция `createTransferTokensProposal`)
**Статус:** ✅ Исправлено
#### 4. Неправильное кодирование операции при создании предложений (КРИТИЧЕСКАЯ)
**Проблема:** Операция перевода токенов кодировалась один раз до цикла по сетям, используя адрес из `formData.value.sender`. По документации, `sender` должен определяться из `signer.getAddress()` при создании предложения в каждой сети, что гарантирует совпадение с инициатором предложения.
**Исправление:**
- Кодирование операции перемещено внутрь цикла по сетям
- Добавлено получение адреса signer для каждой сети через `await signer.getAddress()`
- Добавлена проверка соответствия адреса signer адресу из формы
- Операция теперь кодируется с актуальным адресом signer для каждой сети отдельно
**Файл:** `frontend/src/views/smartcontracts/TransferTokensFormView.vue` (функция `submitForm`)
**Статус:** ✅ Исправлено (2025-01-XX)
#### 5. Параллельное выполнение в executeMultichainProposal (КРИТИЧЕСКАЯ)
**Проблема:** Функция `executeMultichainProposal` использовала `Promise.all` для параллельного выполнения операций во всех сетях одновременно. Это не работает с MetaMask, который может работать только с одной сетью одновременно.
**Исправление:**
- Заменен `Promise.all` на последовательный цикл `for`
- Добавлено переключение сетей через `switchToVotingNetwork` для каждой сети
- Добавлены задержки после переключения сетей (1 секунда) и после подтверждения транзакций (3 секунды, 5 секунд для Base Sepolia)
- Добавлена фильтрация только готовых к выполнению цепочек
**Файл:** `frontend/src/composables/useProposals.js` (функция `executeMultichainProposal`)
**Статус:** ✅ Исправлено (2025-01-XX)
#### 6. Отсутствие переключения сетей в voteOnMultichainProposal
**Проблема:** Функция `voteOnMultichainProposal` не использовала переключение сетей и проверку баланса токенов, что требовалось по документации для корректной работы в мультичейн-среде.
**Исправление:**
- Добавлено переключение сетей через `switchToVotingNetwork` для каждой сети
- Добавлена проверка баланса токенов перед голосованием в каждой сети через `checkTokenBalance`
- Добавлены задержки после переключения сетей (1 секунда) и после подтверждения транзакций (3 секунды, 5 секунд для Base Sepolia)
- Добавлена фильтрация только активных цепочек
- Добавлена обработка ошибок с пропуском сетей при отсутствии баланса
**Файл:** `frontend/src/composables/useProposals.js` (функция `voteOnMultichainProposal`)
**Статус:** ✅ Исправлено (2025-01-XX)
### Текущая корректная реализация
Все функции кодирования операций и мультичейн-операции теперь используют:
- ✅ Правильную сигнатуру: `_transferTokens(address,address,uint256)`
-Все три параметра: `sender`, `recipient`, `amount`
- ✅ Конвертацию amount в wei: `ethers.parseUnits(amount.toString(), 18)`
- ✅ Правильный порядок параметров в `createProposal`
- ✅ Определение `sender` из `signer.getAddress()` при создании предложения в каждой сети
- ✅ Последовательное выполнение операций во всех мультичейн-функциях (не параллельное)
- ✅ Переключение сетей перед каждой операцией в мультичейн-функциях
- ✅ Проверку баланса токенов перед голосованием в каждой сети
### Проверка корректности
Перед развертыванием убедитесь, что:
1. Функция `_transferTokens` кодируется с **тремя** параметрами (sender, recipient, amount)
2. `amount` всегда конвертируется в wei перед кодированием
3. `sender` получается из `signer.getAddress()` при создании предложения в каждой сети и совпадает с инициатором предложения
4. Порядок параметров в `createProposal` соответствует сигнатуре контракта
5. Все мультичейн-операции (создание, голосование, выполнение) используют последовательное выполнение с переключением сетей
6. При голосовании проверяется баланс токенов в каждой сети отдельно
---
## Заключение
Система мультичейн-управления DLE обеспечивает децентрализованное управление переводом токенов через независимые кворумы в каждой сети, при этом предоставляя единый интерфейс для управления предложениями во всех сетях одновременно.
**Важно:** Все критические ошибки в кодировании операций исправлены. Код соответствует документации и контракту.

View File

@@ -0,0 +1,219 @@
[English](../../docs.en/back-docs/TASK_REQUIREMENTS.md) | **Русский**
# Задание: Реализация мульти-чейн governance системы для DLE
## Статус выполнения
- ✅ Форма создания предложения работает
- ✅ Предложение создается во всех цепочках DLE
- ✅ Голосование происходит отдельно в каждой цепочке
- ✅ Кворум считается отдельно для каждой цепочки
- ✅ Личный перевод токенов от инициатора предложения
- ✅ Группировка предложений по description + initiator
- ✅ Серверная координация с криптографическими доказательствами
- ✅ Убрана хардкод цепочек - используются deployedNetworks из API
## Контекст
DLE (Digital Legal Entity) - децентрализованная юридическая сущность с контрактами в нескольких блокчейн-сетях. Необходимо реализовать систему управления токенами через мульти-чейн governance, где холдеры токенов могут переводить токены через голосование с кворумом.
## Архитектура системы
### Мульти-чейн компоненты
- **Frontend**: Vue.js приложение с Web3 интеграцией
- **Backend**: Node.js сервер для координации и API
- **Smart Contracts**: DLE контракты в каждой поддерживаемой сети
- **Database**: PostgreSQL для хранения метаданных
- **WebSocket**: Real-time синхронизация между сетями
### Поддерживаемые сети
- Ethereum Sepolia (chainId: 11155111)
- Arbitrum Sepolia (chainId: 421614)
- Base Sepolia (chainId: 84532)
## Требования к функционалу
### 1. Форма создания предложения о переводе токенов
**URL:** `/management/transfer-tokens?address=<DLE_ADDRESS>`
**Поля формы:**
- Адрес получателя (обязательное, address)
- Сумма перевода (обязательное, number в токенах)
- Описание предложения (опциональное, string)
- Время голосования (обязательное, number в днях)
### 2. Логика создания предложений
1. **Определение сетей:** Получение списка `deployedNetworks` через API `/dle-v2`
2. **Параллельное создание:** Предложения создаются одновременно во ВСЕХ сетях DLE
3. **Кодирование операции:** `_transferTokens(address,uint256)` для перевода токенов от инициатора
### 3. Логика голосования
1. **Независимое голосование:** Каждая сеть голосует отдельно
2. **Локальный кворум:** Кворум считается по формуле `(forVotes / totalSupply) >= quorumPercentage`
3. **Голосование токенами:** Вес голоса = баланс токенов избирателя
### 4. Логика исполнения
1. **Локальное исполнение:** Каждый контракт проверяет свой локальный кворум
2. **Серверная координация:** Backend собирает результаты кворумов из всех сетей
3. **Криптографические доказательства:** Сервер подписывает глобальный статус кворума
4. **Глобальное исполнение:** Контракт проверяет подпись и выполняет операцию
## Техническая спецификация
### Smart Contract (DLE.sol)
#### Структура Proposal
```solidity
struct Proposal {
uint256 id;
string description;
uint256 forVotes;
uint256 againstVotes;
bool executed;
bool canceled;
uint256 deadline;
address initiator; // Создатель предложения
bytes operation; // Закодированная операция
uint256[] targetChains; // Целевые сети для исполнения
uint256 snapshotTimepoint; // Точка снимка для голосования
mapping(address => bool) hasVoted;
}
```
#### Функция _transferTokens
```solidity
function _transferTokens(address _sender, address _recipient, uint256 _amount) internal {
require(balanceOf(_sender) >= _amount, "Insufficient balance");
_transfer(_sender, _recipient, _amount);
emit TokensTransferredByGovernance(_recipient, _amount);
}
```
#### События
```solidity
event ProposalCreated(uint256 proposalId, address initiator, string description);
event QuorumReached(uint256 proposalId, uint256 chainId);
event ProposalExecuted(uint256 proposalId, bytes operation);
```
### Backend (Node.js)
#### Сервис координации кворумов
```javascript
class QuorumCoordinator {
// Сбор результатов голосования из всех сетей
async collectQuorumResults(proposalId) {
// Слушать события QuorumReached из всех сетей
// Сохранять в базу данных
}
// Генерация криптографических доказательств
async generateGlobalQuorumProof(proposalId) {
// Подписать глобальный статус кворума
// Вернуть подпись для контрактов
}
}
```
#### API Endpoints
- `GET /dle-v2` - получение информации о DLE и сетях
- `POST /api/dle-proposals/get-proposals` - получение списка предложений
- `POST /api/dle-proposals/create-proposal` - создание предложения
- `POST /api/dle-proposals/vote-proposal` - голосование
- `POST /api/dle-proposals/execute-proposal` - исполнение
### Frontend (Vue.js)
#### Компонент TransferTokensFormView
- Валидация формы
- Кодирование операции перевода
- Параллельное создание предложений во всех сетях
- Обработка ошибок и отображение результатов
#### Компонент DleProposalsView
- Группировка предложений по `description + initiator`
- Отображение статуса по каждой сети
- Кнопки голосования для каждой активной сети
- Кнопка исполнения при глобальном кворуме
## Алгоритм работы
### Сценарий использования
1. **Пользователь открывает форму** `/management/transfer-tokens?address=0xdD27...9386`
2. **Вводит данные:**
- Получатель: `0x123...abc`
- Сумма: `1000` токенов
- Описание: `"Перевод средств подрядчику"`
- Время: `7` дней
3. **Нажимает "Создать"**
4. **Система:**
- Определяет сети: Sepolia, Arbitrum Sepolia, Base Sepolia
- Создает предложения в каждой сети параллельно
- Кодирует `_transferTokens(инициатор, получатель, сумма)`
5. **На странице предложений** появляется одна карточка с статусом по сетям
6. **Пользователи голосуют** в каждой сети отдельно
7. **При локальном кворуме** контракт эмитирует `QuorumReached`
8. **Backend собирает** результаты из всех сетей
9. **При глобальном кворуме** сервер подписывает доказательство
10. **Пользователь вызывает** `executeWithGlobalQuorum()` с подписью
11. **Контракт проверяет** подпись и выполняет перевод
## Безопасность
### Уровни защиты
1. **On-chain проверки:** Баланс токенов, сроки голосования, кворум
2. **Криптографические доказательства:** Подпись сервера для глобального кворума
3. **Многоуровневая валидация:** Локальный + глобальный кворум
4. **Отказоустойчивость:** Graceful degradation при недоступности сетей
### Риски и mitigation
- **Сервер скомпрометирован:** Проверка подписи предотвращает подделку
- **Сеть недоступна:** Локальное голосование работает независимо
- **Replay attacks:** Проверка ID предложения и chainId
- **Front-running:** Использование commit-reveal схемы при необходимости
## Тестирование
### Критерии приемки
- [x] Форма создания предложения работает
- [x] Предложение создается во всех цепочках DLE
- [x] Голосование происходит отдельно в каждой цепочке
- [x] Кворум считается отдельно для каждой цепочки
- [x] Перевод токенов происходит от инициатора предложения
- [x] Серверная координация с криптографическими доказательствами
- [x] Группировка предложений в интерфейсе
- [x] Обработка ошибок и edge cases
### Test cases
1. Создание предложения в мульти-чейн среде
2. Голосование в одной сети при недоступности других
3. Исполнение при глобальном кворуме
4. Исполнение при частичном кворуме (должен fail)
5. Перевод токенов от инициатора с достаточным балансом
6. Попытка перевода с недостаточным балансом (должен fail)
## Развертывание
### Требования к инфраструктуре
- **Backend сервер** с доступом к RPC всех сетей
- **Database** для хранения метаданных предложений
- **SSL сертификаты** для безопасной коммуникации
- **Monitoring** для отслеживания состояния сетей
### Переменные окружения
```bash
# RPC URLs
SEPOLIA_RPC_URL=https://1rpc.io/sepolia
ARBITRUM_SEPOLIA_RPC_URL=https://sepolia-rollup.arbitrum.io/rpc
BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
# Database
DATABASE_URL=postgresql://user:pass@localhost:5432/dle
# Server keys for signing
SERVER_PRIVATE_KEY=0x...
```
## Заключение
Реализована полнофункциональная мульти-чейн governance система для управления токенами DLE. Система обеспечивает децентрализованное принятие решений с координацией через trusted server с криптографическими доказательствами, обеспечивая баланс между удобством использования и безопасностью.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,418 @@
[English](../../docs.en/back-docs/multi-agent-architecture.md) | **Русский**
# 🏗️ Архитектура множественных ИИ-агентов в DLE
> **Концепция**: Создание отдельных специализированных агентов для разных задач, использующих одну локальную модель Ollama, но с разными системными промптами, правилами и интерфейсами.
---
## 📋 Содержание
1. [Концепция архитектуры](#концепция-архитектуры)
2. [Типы агентов](#типы-агентов)
3. [Архитектура системы](#архитектура-системы)
4. [Настройка агентов](#настройка-агентов)
5. [Интерфейсы агентов](#интерфейсы-агентов)
6. [База знаний для агентов](#база-знаний-для-агентов)
7. [Процесс работы агентов](#процесс-работы-агентов)
---
## 🎯 Концепция архитектуры
### Основные принципы
1. **Одна модель, множество агентов**
- Все агенты используют одну локальную модель Ollama (qwen2.5:7b)
- Различие между агентами — в системных промптах и правилах
- Каждый агент имеет свою специализацию и роль
2. **Изоляция агентов**
- Каждый агент имеет свои настройки (системный промпт, правила, RAG таблицы)
- Агенты не влияют друг на друга
- Можно создавать, редактировать и удалять агентов независимо
3. **Специализация по задачам**
- Агент поддержки — отвечает на вопросы пользователей
- Агент-редактор — создает контент по запросу
- Возможность создания дополнительных агентов (аналитик, переводчик и т.д.)
4. **Отдельные интерфейсы**
- Каждый агент имеет свой интерфейс доступа
- Интерфейсы адаптированы под задачи агента
- Разные права доступа для разных агентов
---
## 🤖 Типы агентов
### 1. Агент поддержки (Support Agent)
**Роль**: Отвечать на сообщения пользователей
**Задачи**:
- Обработка входящих сообщений от пользователей
- Поиск ответов в базе знаний (FAQ, документация)
- Генерация ответов на основе найденной информации
- Эскалация сложных вопросов к операторам
**Характеристики**:
- Использует RAG для поиска в FAQ и документах
- Системный промпт: "Вы — профессиональный ассистент службы поддержки"
- Правила: строгий режим (только из базы знаний, минимум генерации)
- Интерфейс: встроен в чат (web, telegram, email)
**База знаний**:
- FAQ таблицы
- Документация продукта
- База знаний для клиентов
---
### 2. Агент-редактор (Content Editor Agent)
**Роль**: Создавать контент по запросу пользователя
**Задачи**:
- Создание постов для социальных сетей
- Написание статей для блога
- Генерация email-рассылок
- Создание рекламных текстов
**Характеристики**:
- Использует RAG для поиска инструкций платформ и стиля компании
- Системный промпт: "Вы — профессиональный контент-маркетолог и редактор"
- Правила: креативный режим (больше генерации, использование примеров)
- Интерфейс: отдельная страница "Редактор контента"
**База знаний**:
- Инструкции платформ (ВК, Telegram, Instagram и т.д.)
- Стиль компании (tone of voice, запрещенные слова)
- Примеры контента (референсы)
- Ключевые слова и хэштеги
- CTA блоки
---
### 3. Потенциальные дополнительные агенты
**Агент-аналитик**:
- Анализ данных и создание отчетов
- Выявление трендов
- Прогнозирование
**Агент-переводчик**:
- Перевод контента на разные языки
- Локализация материалов
- Адаптация под культурные особенности
**Агент-закупщик**:
- Поиск поставщиков
- Анализ условий
- Рекомендации по выбору
---
## 🏛️ Архитектура системы
### Компоненты системы
```
┌─────────────────────────────────────────────────────────┐
│ Единая модель Ollama │
│ (qwen2.5:7b) │
└────────────────┬────────────────────────────────────────┘
├─────────────────┬───────────────────────┐
│ │ │
↓ ↓ ↓
┌────────────────────┐ ┌──────────────────┐ ┌──────────────┐
│ Агент поддержки │ │ Агент-редактор │ │ Другие агенты│
│ │ │ │ │ │
│ • Системный промпт │ │ • Системный │ │ • Свои │
│ • Правила (строгие)│ │ промпт │ │ настройки │
│ • RAG: FAQ │ │ • Правила │ │ │
│ • Интерфейс: чат │ │ (креативные) │ │ │
│ │ │ • RAG: инструкции │ │ │
│ │ │ • Интерфейс: │ │ │
│ │ │ редактор │ │ │
└────────────────────┘ └──────────────────┘ └──────────────┘
```
### Хранение настроек агентов
**Таблица `ai_agents`**:
- `id` — уникальный идентификатор агента
- `name` — название агента (например, "Агент поддержки", "Агент-редактор")
- `role` — роль агента (support, content_editor, analyst и т.д.)
- `description` — описание назначения агента
- `system_prompt_encrypted` — системный промпт (зашифрован)
- `rules_id` — ссылка на правила агента (из таблицы `ai_assistant_rules`)
- `selected_rag_tables` — массив ID таблиц для RAG поиска
- `enabled_channels` — на каких каналах активен (web, telegram, email)
- `interface_route` — маршрут для интерфейса агента
- `permissions_required` — требуемые права доступа
- `is_active` — активен ли агент
- `created_at`, `updated_at` — даты создания и обновления
**Связь с правилами**:
- Каждый агент может использовать набор правил из `ai_assistant_rules`
- Правила определяют поведение: temperature, maxTokens, searchRagFirst и т.д.
- Можно создавать правила специально для каждого агента
**Связь с RAG таблицами**:
- Каждый агент может использовать свои RAG таблицы
- Агент поддержки: FAQ, документация
- Агент-редактор: инструкции платформ, стиль компании, примеры
---
## ⚙️ Настройка агентов
### Создание нового агента
**Шаг 1: Базовая информация**
- Название агента
- Роль (support, content_editor и т.д.)
- Описание назначения
**Шаг 2: Системный промпт**
- Определяет роль и поведение агента
- Указывает, как агент должен работать
- Содержит контекст о компании и стиле
**Шаг 3: Правила (Rules)**
- Создание или выбор существующих правил
- Настройка параметров генерации (temperature, maxTokens)
- Настройка поведения RAG (searchRagFirst, generateIfNoRag)
**Шаг 4: База знаний (RAG таблицы)**
- Выбор таблиц для поиска информации
- Агент поддержки: FAQ, документация
- Агент-редактор: инструкции, стиль, примеры
**Шаг 5: Интерфейс**
- Определение маршрута для доступа к агенту
- Настройка прав доступа
- Выбор каналов (web, telegram, email)
**Шаг 6: Активация**
- Включение/выключение агента
- Тестирование работы агента
### Примеры настроек
**Агент поддержки**:
- Системный промпт: "Вы — профессиональный ассистент службы поддержки..."
- Правила: строгий режим (temperature: 0.3, searchRagFirst: true, generateIfNoRag: false)
- RAG таблицы: FAQ, Документация продукта
- Интерфейс: встроен в чат
- Каналы: web, telegram, email
**Агент-редактор**:
- Системный промпт: "Вы — профессиональный контент-маркетолог и редактор..."
- Правила: креативный режим (temperature: 0.7, searchRagFirst: true, generateIfNoRag: true)
- RAG таблицы: Инструкции платформ, Стиль компании, Примеры контента
- Интерфейс: отдельная страница /content-editor
- Каналы: только web (для редакторов)
---
## 🖥️ Интерфейсы агентов
### Агент поддержки — интерфейс чата
**Расположение**: Встроен в основной чат (HomeView)
**Особенности**:
- Автоматическая активация при получении сообщения от пользователя
- Показ статуса генерации ответа
- Возможность отключения AI для конкретного сообщения
- История диалога с контекстом
**Права доступа**:
- Доступен всем пользователям
- Автоматически отвечает на сообщения
---
### Агент-редактор — интерфейс редактора
**Расположение**: Отдельная страница `/content-editor`
**Особенности**:
- Поле для ввода запроса на создание контента
- Выбор типа контента (пост ВК, статья блога, email и т.д.)
- Выбор платформы (ВКонтакте, Telegram, Instagram и т.д.)
- Показ процесса генерации
- Редактирование сгенерированного контента
- Сохранение готового контента
- История созданного контента
**Права доступа**:
- Только для пользователей с ролью Editor
- Требуется авторизация
**Функционал интерфейса**:
1. **Форма запроса**:
- Текстовое поле для описания задачи
- Выбор типа контента (выпадающий список)
- Выбор платформы (чекбоксы или выпадающий список)
- Дополнительные параметры (тон, длина, ключевые слова)
2. **Процесс генерации**:
- Индикатор загрузки
- Показ найденных инструкций
- Показ процесса генерации
3. **Результат**:
- Готовый контент в редактируемом поле
- Кнопка "Сохранить"
- Кнопка "Перегенерировать"
- Кнопка "Экспортировать" (копировать, скачать)
4. **История**:
- Список созданного контента
- Фильтры по типу, платформе, дате
- Возможность редактирования и удаления
---
## 📚 База знаний для агентов
### Структура базы знаний
**Для агента поддержки**:
- Таблица "FAQ" — часто задаваемые вопросы
- Таблица "Документация" — описание функций продукта
- Таблица "База знаний для клиентов" — расширенная информация
**Для агента-редактора**:
- Таблица "Инструкции платформ" — правила размещения контента
- Таблица "Стиль компании" — tone of voice, запрещенные слова
- Таблица "Примеры контента" — референсы для разных типов контента
- Таблица "Ключевые слова" — семантическое ядро, хэштеги
- Таблица "CTA блоки" — призывы к действию
### Как агенты используют базу знаний
1. **RAG поиск**:
- Пользователь задает вопрос/запрос
- Агент выполняет векторный поиск в своих RAG таблицах
- Находит релевантную информацию
2. **Контекст для генерации**:
- Найденная информация передается в LLM как контекст
- LLM генерирует ответ/контент на основе контекста
- Системный промпт направляет, как использовать контекст
3. **Фильтрация по роли**:
- Агент поддержки ищет только в FAQ и документации
- Агент-редактор ищет только в инструкциях и примерах
- Изоляция данных между агентами
---
## 🔄 Процесс работы агентов
### Агент поддержки — процесс ответа
1. **Получение сообщения**:
- Пользователь отправляет сообщение в чат
- Система определяет, что нужно использовать агента поддержки
2. **RAG поиск**:
- Агент ищет ответ в своих RAG таблицах (FAQ, документация)
- Использует векторный поиск для семантического поиска
3. **Генерация ответа**:
- Если найден ответ в базе знаний → использует его
- Если не найден → в строгом режиме предлагает связаться с оператором
- Генерирует ответ с учетом системного промпта и правил
4. **Отправка ответа**:
- Ответ отправляется пользователю в чат
- Сохраняется в истории диалога
---
### Агент-редактор — процесс создания контента
1. **Получение запроса**:
- Редактор открывает интерфейс `/content-editor`
- Вводит запрос: "Создай пост для ВКонтакте о новой функции"
- Выбирает тип контента и платформу
2. **RAG поиск инструкций**:
- Агент ищет инструкции для выбранной платформы
- Ищет стиль компании
- Ищет примеры похожего контента
- Ищет релевантные ключевые слова
3. **Генерация контента**:
- Агент генерирует контент на основе:
- Системного промпта (роль редактора)
- Найденных инструкций платформы
- Стиля компании
- Примеров контента
- Ключевых слов
4. **Результат**:
- Готовый контент показывается в интерфейсе
- Редактор может редактировать контент
- Сохранить или экспортировать
---
## 🎯 Преимущества архитектуры
### 1. Специализация
- Каждый агент решает свою задачу оптимально
- Не нужно жертвовать качеством для универсальности
### 2. Гибкость
- Легко создавать новых агентов для новых задач
- Можно настраивать каждого агента независимо
### 3. Изоляция
- Агенты не мешают друг другу
- Можно тестировать и обновлять агентов независимо
### 4. Масштабируемость
- Легко добавлять новых агентов
- Каждый агент использует одну модель, нет перегрузки
### 5. Контроль
- Четкое разделение ответственности
- Легко отслеживать, какой агент что делает
---
## 📊 Сравнение с единым агентом
| Характеристика | Единый агент | Множественные агенты |
|----------------|--------------|----------------------|
| **Специализация** | Универсальный, но менее точный | Специализированный, более точный |
| **Настройка** | Один набор настроек для всех задач | Отдельные настройки для каждой задачи |
| **База знаний** | Все таблицы для всех задач | Изолированные таблицы для каждой задачи |
| **Интерфейс** | Один интерфейс | Отдельные интерфейсы для каждой задачи |
| **Гибкость** | Сложно адаптировать под разные задачи | Легко создавать новых агентов |
| **Производительность** | Одна модель для всех | Одна модель, но разные промпты |
---
## 🚀 Следующие шаги
1. **Создание таблицы `ai_agents`** в базе данных
2. **Создание сервиса для управления агентами**
3. **Модификация AI Assistant для работы с несколькими агентами**
4. **Создание интерфейса для агента-редактора**
5. **Настройка агента поддержки (уже существует, нужно адаптировать)**
6. **Создание базы знаний для агента-редактора**
7. **Тестирование работы обоих агентов**
---
**© 2024-2025 Тарабанов Александр Викторович. Все права защищены.**
**Последнее обновление**: Январь 2026

View File

@@ -0,0 +1,931 @@
[English](../../docs.en/back-docs/setup-ai-assistant.md) | **Русский**
# Инструкция по настройке AI Ассистента с векторным поиском
## 🤖 Полное руководство по запуску интеллектуального помощника
Этот документ описывает пошаговый процесс настройки AI ассистента для решения бизнес-задач через электронные таблицы и векторный поиск.
---
## 📚 Что вы настроите
После выполнения инструкции у вас будет:
✅ Работающий AI ассистент с локальной моделью (Ollama)
✅ База знаний для ответов клиентам (FAQ)
✅ Автоматизация работы с поставщиками
✅ Система обучения персонала
✅ Векторный поиск по вашим данным
✅ Значительная экономия времени и ресурсов
> 💡 **Экономический эффект**: См. [ИИ-агенты DLE](../ai-assistant.md) — архитектура, примеры агентов и расчёты экономии.
---
## ⏱️ Время настройки
- **Быстрая настройка**: 20-30 минут (базовый FAQ)
- **Полная настройка**: 1-2 часа (все возможности)
---
## Шаг 1: Установка и запуск Ollama
### 1.1 Проверка статуса Ollama
1. Перейдите в **Настройки** → вкладка **Интеграции**
2. Найдите блок **"Ollama"** и нажмите **"Подробнее"**
3. Проверьте статус подключения:
-**"Ollama is running"** — все готово, переходите к шагу 1.3
-**"Ollama API not responding"** — переходите к шагу 1.2
### 1.2 Запуск Ollama (если не запущен)
Если Ollama не запущен, выполните в терминале:
```bash
# Для Docker (рекомендуется)
docker-compose up -d ollama
# Или локально
ollama serve
```
Обновите страницу и проверьте статус снова.
### 1.3 Установка модели для AI
1. В разделе **Ollama** нажмите **"Установить модель"**
2. Выберите модель:
- **qwen2.5:7b** (рекомендуется) — для русского языка, 4.7 GB
- **llama2:7b** — для английского, 3.8 GB
- **mistral:7b** — универсальная, 4.1 GB
3. Нажмите **"Установить"**
4. Дождитесь завершения загрузки (5-10 минут в зависимости от скорости интернета)
> 💡 **Подсказка**: Модель скачивается один раз и хранится локально
### 1.4 Установка Embedding модели
1. В том же разделе найдите **"Установить Embedding модель"**
2. Выберите модель:
- **mxbai-embed-large:latest** (рекомендуется) — 670 MB
- **nomic-embed-text:latest** — альтернатива, 274 MB
3. Нажмите **"Установить"**
> ⚠️ **Важно**: Embedding модель нужна для векторного поиска (RAG)
---
## Шаг 2: Создание базы знаний (электронные таблицы)
### 2.1 Создание таблицы FAQ
1. Перейдите в **Таблицы** (в главном меню)
2. Нажмите **"+ Создать таблицу"**
3. Заполните:
- **Название**: `FAQ - Часто задаваемые вопросы`
- **Описание**: `База знаний для AI ассистента по работе с клиентами`
4. Нажмите **"Создать"**
### 2.2 Настройка столбцов таблицы
Добавьте следующие столбцы:
#### Столбец 1: Вопрос (обязательный для RAG)
1. Нажмите **"+ Добавить столбец"**
2. Заполните:
- **Название**: `Вопрос`
- **Тип**: `Текст`
- **Назначение**: Выберите `Вопрос для AI`
3. Нажмите **"Сохранить"**
> ⚠️ **Критично**: Обязательно выберите назначение "Вопрос для AI" — это поле будет индексироваться для векторного поиска
#### Столбец 2: Ответ (обязательный для RAG)
1. Нажмите **"+ Добавить столбец"**
2. Заполните:
- **Название**: `Ответ`
- **Тип**: `Текст`
- **Назначение**: Выберите `Ответ AI`
3. Нажмите **"Сохранить"**
#### Столбец 3: Продукт (опционально, для фильтрации)
1. Нажмите **"+ Добавить столбец"**
2. Заполните:
- **Название**: `Продукт`
- **Тип**: `Множественный выбор`
- **Варианты**: `Базовый`, `Премиум`, `Корпоративный`
- **Назначение**: Выберите `Фильтр по продукту`
3. Нажмите **"Сохранить"**
#### Столбец 4: Теги (опционально, для категоризации)
1. Нажмите **"+ Добавить столбец"**
2. Заполните:
- **Название**: `Теги`
- **Тип**: `Множественный выбор`
- **Варианты**: `Оплата`, `Доставка`, `Возврат`, `Гарантия`, `Техподдержка`
- **Назначение**: Выберите `Теги пользователя`
3. Нажмите **"Сохранить"**
#### Столбец 5: Приоритет (опционально)
1. Нажмите **"+ Добавить столбец"**
2. Заполните:
- **Название**: `Приоритет`
- **Тип**: `Число`
- **Назначение**: Выберите `Приоритет`
3. Нажмите **"Сохранить"**
> 💡 **Подсказка**: Вопросы с более высоким приоритетом будут показываться AI первыми
### 2.3 Заполнение базы знаний
Добавьте типовые вопросы и ответы:
**Пример 1: Оплата**
| Вопрос | Ответ | Продукт | Теги | Приоритет |
|--------|-------|---------|------|-----------|
| Как оплатить заказ? | Мы принимаем оплату банковской картой, через PayPal, или банковским переводом. Выберите удобный способ при оформлении заказа. | Все | Оплата | 10 |
| Можно ли оплатить частями? | Да, для заказов от 50,000₽ доступна рассрочка на 3, 6 или 12 месяцев без переплаты. | Премиум, Корпоративный | Оплата | 8 |
**Пример 2: Доставка**
| Вопрос | Ответ | Продукт | Теги | Приоритет |
|--------|-------|---------|------|-----------|
| Сколько времени занимает доставка? | Стандартная доставка: 3-5 рабочих дней по России. Экспресс-доставка: 1-2 дня в крупных городах. | Все | Доставка | 10 |
| Сколько стоит доставка? | Бесплатная доставка при заказе от 5,000₽. Для заказов менее 5,000₽ стоимость доставки 300₽. | Все | Доставка | 9 |
**Пример 3: Возврат**
| Вопрос | Ответ | Продукт | Теги | Приоритет |
|--------|-------|---------|------|-----------|
| Как вернуть товар? | Возврат возможен в течение 14 дней с момента получения. Товар должен быть в оригинальной упаковке, с сохранением товарного вида. Свяжитесь с поддержкой для оформления возврата. | Все | Возврат | 10 |
| Когда вернут деньги? | Возврат денежных средств производится в течение 5-10 рабочих дней после получения товара на наш склад. | Все | Возврат | 8 |
> 💡 **Рекомендация**: Добавьте минимум 20-30 вопросов для качественной работы AI. Чем больше вопросов, тем точнее ответы!
### 2.4 Активация таблицы как источника для AI
1. В правом верхнем углу таблицы найдите **⚙️ Настройки таблицы**
2. Включите переключатель **"Использовать как источник для AI"** ✅
3. Нажмите **"Сохранить"**
> ✅ **Готово!** Таблица теперь индексируется для векторного поиска
---
## Шаг 3: Настройка AI провайдера (Ollama)
### 3.1 Открытие настроек Ollama
1. Перейдите в **Настройки****Интеграции**
2. Найдите блок **"Ollama"** и нажмите **"Подробнее"**
### 3.2 Проверка Base URL
1. Проверьте поле **Base URL**:
- Для Docker: `http://ollama:11434`
- Для локального: `http://localhost:11434`
2. Если URL неверный, исправьте и нажмите **"Сохранить"**
### 3.3 Выбор модели
1. В поле **"Модель (LLM)"** выберите установленную модель:
- `qwen2.5:7b` (рекомендуется для русского)
2. В поле **"Embeddings-модель"** выберите:
- `mxbai-embed-large:latest`
3. Нажмите **"Сохранить"**
---
## Шаг 4: Настройка AI Ассистента
### 4.1 Открытие настроек ассистента
1. Перейдите в **Настройки****Интеграции**
2. Найдите блок **"ИИ-ассистент"** и нажмите **"Подробнее"**
### 4.2 Настройка системного промта
В поле **"Системный промт"** введите инструкции для AI:
**Базовый промт (для начала)**:
```
Вы — профессиональный ассистент службы поддержки компании.
Правила:
1. Отвечайте вежливо и профессионально
2. Используйте информацию из базы знаний
3. Если информации нет — предложите связаться с оператором
4. Отвечайте кратко и по существу
5. Всегда заканчивайте вопросом "Чем еще могу помочь?"
```
**Продвинутый промт (с персонализацией)**:
```
Вы — профессиональный ассистент службы поддержки компании "Название вашей компании".
О компании:
- Мы занимаемся [краткое описание бизнеса]
- Наши ценности: качество, надежность, клиентоориентированность
Стиль общения:
- Дружелюбный, но профессиональный
- Обращайтесь к клиенту на "Вы"
- Используйте эмодзи умеренно (1-2 на сообщение)
Правила ответа:
1. ОБЯЗАТЕЛЬНО: Отвечайте ТОЛЬКО на русском языке. Все вопросы и ответы должны быть на русском языке
2. Сначала ищите ответ в базе знаний (RAG)
3. Если нашли — отвечайте на основе найденной информации
4. Если не нашли — честно скажите и предложите помощь оператора
5. Не придумывайте информацию о ценах, сроках, условиях
6. При сложных вопросах предлагайте связаться с менеджером
Всегда заканчивайте: "Чем еще могу помочь? 😊"
```
### 4.3 Выбор моделей
1. **LLM-модель**: Выберите `qwen2.5:7b (ollama)`
2. **Embedding-модель**: Выберите `mxbai-embed-large:latest (ollama)`
> 💡 **Подсказка**: Модели автоматически подтянутся из настроек Ollama
> 📊 **Размер контекстного окна**:
> - **Qwen2.5:7b**: Базовый контекст = **32,768 токенов** (~24,000 русских слов)
> - Всего данных, отправляемых в модель:
> - Системный промпт: ~500-2000 символов (~300-1200 токенов)
> - История диалога: до 20 сообщений (~100-500 токенов на сообщение = ~2000-10000 токенов)
> - RAG контекст: ~500-2000 токенов (из найденных данных)
> - Текущий вопрос: ~50-200 токенов
> - **Итого**: примерно 3,000-15,000 токенов (запас достаточен)
> - Если нужен больший контекст → используйте Qwen3 с YaRN (до 131K токенов)
### 4.4 Выбор RAG-таблицы
1. В поле **"Выбранные RAG-таблицы"** выберите созданную таблицу:
- `FAQ - Часто задаваемые вопросы`
2. Нажмите **"Сохранить"**
### 4.5 Настройка правил AI (Rules)
Создайте набор правил для управления поведением AI:
1. Нажмите кнопку **"Создать"** рядом с полем "Набор правил"
2. В модальном окне заполните:
**Название**: `Гибридный режим (RAG + генерация)`
**Описание**: `AI сначала ищет в базе знаний, если не находит — генерирует ответ`
**Правила (JSON)**:
```json
{
"checkUserTags": true,
"searchRagFirst": true,
"generateIfNoRag": true,
"temperature": 0.7,
"maxTokens": 500
}
```
3. Нажмите **"Сохранить"**
4. Выберите созданное правило в выпадающем списке
> 💡 **Что означают параметры**:
> - `checkUserTags: true` — учитывать теги пользователя при поиске
> - `searchRagFirst: true` — сначала искать в базе знаний
> - `generateIfNoRag: true` — генерировать ответ, если ничего не найдено
> - `temperature: 0.7` — баланс между точностью и креативностью (0.0-1.0)
> - `maxTokens: 500` — максимальная длина ответа
### 4.6 Настройки RAG поиска
Разверните раздел **"🔍 Настройки RAG поиска"**:
**Базовые настройки:**
1. **Метод поиска**: Выберите `Гибридный поиск` (рекомендуется)
2. **Максимальное количество результатов**: `5`
3. **Порог релевантности**: `0.1` (от 0.01 до 1.0)
**Извлечение ключевых слов:**
1.**Включить извлечение ключевых слов**
2. **Минимальная длина слова**: `3`
3. **Максимальное количество ключевых слов**: `10`
4.**Удалять стоп-слова**
**Веса поиска (для гибридного):**
1. **Семантический поиск**: `70%` (точность)
2. **Поиск по ключевым словам**: `30%` (скорость)
**Дополнительные настройки:**
1.**Нечеткий поиск** (для опечаток)
2.**Стемминг слов** (находит разные формы слова)
3.**Поиск синонимов** (пока отключен)
### 4.7 Сохранение настроек
Нажмите кнопку **"Сохранить"** внизу формы.
---
## Шаг 5: Тестирование AI Ассистента
### 5.1 Использование встроенного тестера
1. На странице настроек AI ассистента прокрутите вниз до блока **"🔍 Мониторинг системы"**
2. В разделе **"🧠 Тест RAG-функциональности"**:
- Убедитесь, что выбрана таблица `FAQ - Часто задаваемые вопросы`
- Введите тестовый вопрос: `Как оплатить заказ?`
- Нажмите **"Тестировать RAG"**
3. Наблюдайте за процессом:
- 🔍 Ищем ответ в базе знаний... (векторный поиск)
- 🤖 Генерируем ответ с помощью ИИ... (LLM генерация)
- ✅ Готово!
4. Проверьте результат:
- Должен отобразиться ответ из вашей таблицы
- Score (оценка близости): чем ближе к 0, тем лучше
> 💡 **Хороший Score**: от -300 до 0 (ответ найден)
> ⚠️ **Плохой Score**: больше 300 (ответ не найден, AI придумает свой)
### 5.2 Тестирование через Web Chat
1. Перейдите на главную страницу приложения
2. Найдите виджет **"💬 Чат с AI"** (обычно справа внизу)
3. Нажмите на виджет, чтобы открыть чат
4. Введите вопрос: `Сколько стоит доставка?`
5. Проверьте ответ AI
**Ожидаемый результат:**
```
🤖 AI Ассистент:
Бесплатная доставка при заказе от 5,000₽.
Для заказов менее 5,000₽ стоимость доставки 300₽.
Чем еще могу помочь? 😊
```
### 5.3 Тестирование разных сценариев
Попробуйте задать различные вопросы:
**✅ Вопрос из базы знаний:**
```
Пользователь: "Как вернуть товар?"
AI: [Ответ из таблицы FAQ]
```
**⚠️ Вопрос НЕ из базы знаний:**
```
Пользователь: "Какая погода сегодня?"
AI: "Извините, я специализируюсь на вопросах о нашей компании и продуктах.
По вопросам погоды обратитесь к специализированным сервисам.
Чем еще могу помочь?"
```
**🎯 Вопрос с опечаткой:**
```
Пользователь: "Как аплатить заказ?" (опечатка)
AI: [Найдет правильный ответ благодаря нечеткому поиску]
```
---
## Шаг 6: Расширенные возможности (опционально)
### 6.1 Создание таблицы для работы с поставщиками
#### Структура таблицы "База поставщиков"
1. Создайте новую таблицу: `База поставщиков`
2. Добавьте столбцы:
| Столбец | Тип | Описание |
|---------|-----|----------|
| Название компании | Текст | Наименование поставщика |
| Категория товаров | Множественный выбор | Электроника, Мебель, Одежда, и т.д. |
| Контактное лицо | Текст | ФИО менеджера |
| Email | Текст | Электронная почта |
| Телефон | Текст | Контактный телефон |
| Цены | Текст | Прайс-лист (краткое описание) |
| Условия оплаты | Текст | Отсрочка, предоплата, и т.д. |
| Минимальный заказ | Число | Минимальная сумма заказа |
| Срок доставки | Текст | Сроки поставки |
| Рейтинг | Число | Оценка от 1 до 10 |
| Примечания | Текст | Дополнительная информация |
3. Активируйте как источник для AI
4. Заполните данными о ваших поставщиках
#### Промт для AI закупщика
Добавьте в системный промт:
```
ДОПОЛНИТЕЛЬНО - Работа с поставщиками:
Когда пользователь спрашивает о поставщиках:
1. Ищите в базе "База поставщиков"
2. Фильтруйте по категории товаров
3. Сортируйте по рейтингу и условиям
4. Предоставьте ТОП-3 рекомендации
Формат ответа:
🏆 TOP-3 поставщика по запросу "[категория]":
1. [Название] ⭐ [Рейтинг]/10
📧 [Email] | 📞 [Телефон]
💰 Условия: [Условия оплаты]
🚚 Доставка: [Срок доставки]
📦 Минимум: [Минимальный заказ]₽
2. ...
3. ...
Рекомендую связаться с [Название первого поставщика] — лучшие условия.
```
### 6.2 Создание таблицы для обучения персонала
#### Структура таблицы "База знаний для сотрудников"
1. Создайте новую таблицу: `База знаний для сотрудников`
2. Добавьте столбцы:
| Столбец | Тип | Описание |
|---------|-----|----------|
| Вопрос | Текст | Вопрос сотрудника (назначение: Вопрос для AI) |
| Ответ | Текст | Подробный ответ (назначение: Ответ AI) |
| Категория | Множественный выбор | Продажи, HR, Финансы, IT, Маркетинг |
| Отдел | Множественный выбор | Для какого отдела актуально |
| Сложность | Число | От 1 (простой) до 5 (сложный) |
| Инструкции | Текст | Пошаговые инструкции (если есть) |
| Ссылки | Текст | Ссылки на документы/видео |
| Дата обновления | Дата | Когда информация обновлялась |
3. Примеры вопросов:
**Категория "Продажи":**
- "Как оформить возврат клиенту?"
- "Какие скидки можно давать постоянным клиентам?"
- "Как работать с корпоративными клиентами?"
**Категория "HR":**
- "Как оформить отпуск?"
- "Куда обращаться по больничному?"
- "Как происходит адаптация новых сотрудников?"
**Категория "IT":**
- "Как получить доступ к корпоративной почте?"
- "Что делать при проблемах с VPN?"
- "Как создать заявку в техподдержку?"
### 6.3 Создание связей между таблицами
#### Пример: Связь "Клиенты" → "Заказы"
1. Создайте таблицу **"Клиенты"**:
- Название, Email, Телефон, Статус (VIP/Обычный)
2. Создайте таблицу **"Заказы"**:
- Номер заказа, Дата, Сумма
3. В таблице "Заказы" добавьте столбец:
- **Название**: `Клиент`
- **Тип**: `Связь (Relation)`
- **Связанная таблица**: выберите `Клиенты`
- **Показывать поле**: выберите `Название`
4. Добавьте еще один столбец в "Заказы":
- **Название**: `Email клиента`
- **Тип**: `Lookup (Подстановка)`
- **Связь через**: выберите столбец `Клиент`
- **Подставлять поле**: выберите `Email`
**Результат**: При выборе клиента автоматически подставится его Email!
#### Использование AI с связанными таблицами
AI автоматически понимает связи и может отвечать на вопросы:
```
Пользователь: "Покажи все заказы клиента Иванов"
AI: [Ищет в таблице Заказы, фильтрует по клиенту Иванов]
```
---
## Шаг 7: Интеграция с Telegram и Email (опционально)
### 7.1 Настройка Telegram бота
1. Перейдите в **Настройки****Интеграции****Telegram**
2. Создайте бота через [@BotFather](https://t.me/botfather) в Telegram:
- Отправьте `/newbot`
- Выберите имя и username бота
- Скопируйте **Bot Token**
3. В настройках DLE введите:
- **Bot Token**: вставьте токен от BotFather
- **Bot Username**: username вашего бота (например, `@mycompany_bot`)
4. Нажмите **"Сохранить"**
5. В настройках AI ассистента выберите этот Telegram бот в поле **"Telegram-бот"**
**Результат**: AI будет отвечать на сообщения в Telegram!
### 7.2 Настройка Email интеграции
1. Перейдите в **Настройки****Интеграции****Email**
2. Заполните IMAP настройки (для получения писем):
- **IMAP Host**: `imap.gmail.com` (для Gmail)
- **IMAP Port**: `993`
- **IMAP User**: ваш email
- **IMAP Password**: пароль приложения (не основной пароль!)
3. Заполните SMTP настройки (для отправки писем):
- **SMTP Host**: `smtp.gmail.com`
- **SMTP Port**: `587`
- **SMTP User**: ваш email
- **SMTP Password**: пароль приложения
- **From Email**: email для отправки
4. Нажмите **"Тест IMAP"** и **"Тест SMTP"** для проверки
5. Нажмите **"Сохранить"**
6. В настройках AI ассистента выберите этот Email в поле **"Email для связи"**
> ⚠️ **Важно для Gmail**: Создайте "Пароль приложения" в настройках безопасности Google
**Результат**: AI будет отвечать на входящие email автоматически!
---
## Шаг 8: Мониторинг и оптимизация
### 8.1 Проверка статуса сервисов
1. Перейдите в **Настройки****Интеграции****ИИ-ассистент**
2. Прокрутите вниз до **"🔍 Мониторинг системы"**
3. Нажмите **"🔄 Обновить статус"**
4. Проверьте статусы:
- 🟢 **Backend**: должен быть "Работает"
- 🟢 **Postgres**: должен быть "Работает"
- 🟢 **Ollama**: должен показывать количество моделей
- 🟢 **Vector Search**: должен быть "Работает"
> ⚠️ Если что-то красное (🔴) — см. раздел "Решение проблем" ниже
### 8.2 Анализ качества ответов
Регулярно проверяйте качество ответов AI:
1. **Score в тестере RAG**:
- **-300 до 0** ✅ — отличное совпадение
- **0 до 300** ⚠️ — среднее совпадение
- **>300** ❌ — совпадение не найдено
2. **Если Score плохой**:
- Добавьте больше похожих вопросов в таблицу
- Используйте разные формулировки одного вопроса
- Увеличьте порог релевантности (например, до 0.2)
### 8.3 Оптимизация настроек RAG
Экспериментируйте с настройками для улучшения результатов:
**Для более точных ответов:**
```
Метод поиска: Семантический
Порог релевантности: 0.05 (ниже = строже)
Веса: Семантический 100% / Ключевые слова 0%
```
**Для более быстрых ответов:**
```
Метод поиска: Поиск по ключевым словам
Максимальное количество результатов: 3
```
**Для баланса (рекомендуется):**
```
Метод поиска: Гибридный
Веса: Семантический 70% / Ключевые слова 30%
Порог релевантности: 0.1
```
---
## ✅ AI Ассистент готов к работе!
### Что у вас теперь есть
**Локальный AI ассистент** без зависимости от облака
**База знаний FAQ** для ответов клиентам
**Векторный поиск** для точных ответов
**Настроенные правила** поведения AI
**Система мониторинга** для контроля качества
### Экономический эффект
При правильной настройке AI ассистента вы получите:
**Автоматизацию рутинных задач** - высвобождение времени для стратегии
**Повышение качества обслуживания** - AI работает 24/7 без усталости
**Снижение операционных расходов** - меньше персонала на рутинных задачах
**Ускорение принятия решений** - мгновенный доступ к информации
> 💡 **Подробная информация**: См. [ИИ-агенты DLE](../ai-assistant.md#экономический-эффект) — архитектура, примеры агентов и расчёты экономии.
---
## 📚 Следующие шаги
### Расширьте возможности AI
1. **Добавьте больше таблиц**:
- База знаний для партнеров
- Инструкции для персонала
- Каталог продуктов
- База контактов
2. **Создайте правила для разных сценариев**:
- Строгий режим (только RAG) — для финансов
- Креативный режим (больше генерации) — для маркетинга
- Гибридный режим (баланс) — для поддержки
3. **Интегрируйте с другими системами**:
- CRM (синхронизация клиентов)
- Складская система (остатки товаров)
- Бухгалтерия (счета и оплаты)
### Обучите команду
1. Покажите сотрудникам, как работает AI
2. Объясните, как добавлять новые вопросы в базу
3. Установите процесс регулярного обновления базы знаний
4. Назначьте ответственного за качество ответов AI
---
## 🆘 Решение проблем
### Проблема: Ollama не запускается
**Симптомы**: Статус "Ollama API not responding"
**Решение**:
```bash
# Проверить контейнер
docker ps | grep ollama
# Перезапустить
docker-compose restart ollama
# Проверить логи
docker-compose logs ollama
```
### Проблема: AI отвечает неточно
**Симптомы**: Ответы не соответствуют базе знаний
**Решение**:
1. Проверьте Score в тестере (должен быть < 300)
2. Добавьте больше вариантов вопросов в таблицу
3. Уменьшите порог релевантности (например, до 0.05)
4. Проверьте, что столбцы имеют правильные назначения ("Вопрос для AI", "Ответ AI")
### Проблема: Vector Search не работает
**Симптомы**: Статус Vector Search показывает ошибку
**Решение**:
1. Проверьте, установлена ли Embedding модель
2. Пересоберите индекс: на странице таблицы нажмите **"🔄 Пересобрать индекс"**
3. Проверьте, что таблица активирована как источник для AI
### Проблема: AI отвечает на неправильном языке
**Симптомы**: Ответы на английском вместо русского
**Решение**:
1. Измените системный промт, добавив в начало: `ВСЕГДА отвечай на русском языке.`
2. Используйте модель `qwen2.5:7b` вместо `llama2:7b`
3. В правилах AI установите `"language": "ru"`
### Проблема: Медленные ответы
**Симптомы**: AI отвечает дольше 5-10 секунд
**Решение**:
1. Используйте меньшую модель (`mistral:7b` вместо `qwen2.5:14b`)
2. Уменьшите `maxResults` в настройках RAG (например, до 3)
3. Отключите "Поиск синонимов" в дополнительных настройках
4. Используйте SSD для хранения моделей
---
## 📖 Дополнительная документация
### Изучите возможности AI
- 🤖 **[ИИ-агенты DLE](../ai-assistant.md)** архитектура, примеры агентов и экономический эффект
- 📊 **[Система электронных таблиц](./tables-system.md)** - техническое описание таблиц
- **[Конфигурация AI](./setup-ai-assistant.md#техническая-документация-для-разработчиков)** - технические детали настройки
### Общая документация
- 🛡 **[Безопасность](../security.md)** - как AI защищает ваши данные
- 💼 **[Блокчейн для бизнеса](../blockchain-for-business.md)** - интеграция AI с блокчейном
- 📋 **[FAQ](../FAQ.md)** - часто задаваемые вопросы
### Поддержка
- 💬 **Чат поддержки**: https://hb3-accelerator.com/
- 📧 **Email**: info@hb3-accelerator.com
- 📚 **База знаний**: https://hb3-accelerator.com
---
## 🔧 Техническая документация (для разработчиков)
### Архитектура системы AI
```
┌───────────────────────────────────────────────────────────┐
│ Настройка AI Ассистента в DLE │
├───────────────────────────────────────────────────────────┤
│ │
│ 🤖 AI Провайдеры: │
│ ├── OpenAI (GPT-4, GPT-3.5) │
│ ├── Anthropic (Claude) │
│ ├── Google (Gemini) │
│ └── Ollama (локальные модели) │
│ │
│ ⚙️ Настройки AI: │
│ ├── System Prompt │
│ ├── Выбор LLM модели │
│ ├── Выбор Embedding модели │
│ ├── Выбор RAG-таблиц │
│ ├── Правила (Rules) │
│ └── Настройки RAG поиска │
│ │
│ 📋 Правила (Rules): │
│ ├── JSON конфигурация поведения │
│ ├── Создание/редактирование/удаление │
│ └── Привязка к AI ассистенту │
│ │
│ 🔗 Интеграции: │
│ ├── Email (IMAP + SMTP) │
│ └── Telegram Bot │
│ │
│ 🔍 RAG: │
│ ├── Выбор таблиц для поиска │
│ ├── Настройки поиска (гибридный/семантический) │
│ ├── Порог релевантности │
│ └── Извлечение ключевых слов │
│ │
│ 📊 Мониторинг: │
│ ├── Статус сервисов (Backend, Ollama, Postgres) │
│ ├── Тест RAG-функциональности │
│ └── Отслеживание прогресса │
│ │
└───────────────────────────────────────────────────────────┘
```
### База данных
#### Таблица: `ai_providers_settings`
```sql
CREATE TABLE IF NOT EXISTS ai_providers_settings (
id SERIAL PRIMARY KEY,
provider_encrypted TEXT, -- Провайдер: openai, anthropic, google, ollama
api_key_encrypted TEXT, -- API ключ (зашифрован)
base_url_encrypted TEXT, -- Base URL для API
selected_model_encrypted TEXT, -- Выбранная LLM модель
embedding_model_encrypted TEXT, -- Выбранная Embedding модель
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
```
#### Таблица: `ai_assistant_settings`
```sql
CREATE TABLE IF NOT EXISTS ai_assistant_settings (
id SERIAL PRIMARY KEY,
system_prompt_encrypted TEXT, -- Системный промт
selected_rag_tables INTEGER[], -- Массив ID RAG-таблиц
languages TEXT[], -- Массив поддерживаемых языков
model_encrypted TEXT, -- Выбранная LLM модель
embedding_model_encrypted TEXT, -- Выбранная Embedding модель
rules JSONB, -- Правила (DEPRECATED)
rules_id INTEGER REFERENCES ai_assistant_rules(id), -- Ссылка на правило
telegram_settings_id INTEGER, -- Ссылка на Telegram бота
email_settings_id INTEGER, -- Ссылка на Email настройки
rag_settings JSONB, -- Настройки RAG поиска
updated_at TIMESTAMP DEFAULT NOW(),
updated_by INTEGER
);
```
#### Таблица: `ai_assistant_rules`
```sql
CREATE TABLE IF NOT EXISTS ai_assistant_rules (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL, -- Название набора правил
description TEXT, -- Описание правила
rules JSONB NOT NULL, -- JSON конфигурация
rules_encrypted TEXT, -- Зашифрованная версия правил
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### Backend API
#### Настройки AI провайдеров
- **GET** `/settings/ai-settings/:provider` Получить настройки провайдера
- **PUT** `/settings/ai-settings/:provider` Сохранить настройки провайдера
- **DELETE** `/settings/ai-settings/:provider` Удалить настройки провайдера
- **GET** `/settings/ai-settings/:provider/models` Получить список моделей
- **POST** `/settings/ai-settings/:provider/verify` Проверить API ключ
#### Настройки AI ассистента
- **GET** `/settings/ai-assistant` Получить настройки ассистента
- **PUT** `/settings/ai-assistant` Сохранить настройки ассистента
#### Правила AI
- **GET** `/settings/ai-assistant-rules` Получить все правила
- **GET** `/settings/ai-assistant-rules/:id` Получить правило по ID
- **POST** `/settings/ai-assistant-rules` Создать правило
- **PUT** `/settings/ai-assistant-rules/:id` Обновить правило
- **DELETE** `/settings/ai-assistant-rules/:id` Удалить правило
#### Ollama (локальные модели)
- **GET** `/ollama/status` Проверить статус Ollama
- **GET** `/ollama/models` Получить список моделей
- **POST** `/ollama/install` Установить модель
- **DELETE** `/ollama/models/:modelName` Удалить модель
### Frontend страницы
- **`/settings/ai`** Главная страница интеграций
- **`/settings/ai/:provider`** Настройки AI провайдера
- **`/settings/ai/assistant`** Настройки AI ассистента
### Процесс обработки сообщения
```
1. Пользователь → Сообщение
2. UnifiedMessageProcessor
3. Проверка языка (только русский)
4. Дедупликация (хеш сообщения)
5. Загрузка настроек (aiAssistantSettingsService)
6. Загрузка правил (aiAssistantRulesService)
7. RAG поиск (ragService)
├── Семантический поиск (vector search)
├── Поиск по ключевым словам
└── Гибридный поиск
8. Генерация ответа (generateLLMResponse)
├── System Prompt
├── История разговора
├── Контекст из RAG
└── Правила
9. Ответ → Пользователь
```
### Безопасность
- **Шифрование**: Все чувствительные поля зашифрованы с помощью AES-256
- **Права доступа**: Только администраторы могут изменять настройки
- **Валидация**: Проверка всех входных данных и API ключей
---
**© 2024-2025 Тарабанов Александр Викторович. Все права защищены.**
**Версия документа**: 1.0.0
**Дата создания**: October 25, 2025

View File

@@ -0,0 +1,197 @@
<!--
Copyright (c) 2024-2025 Тарабанов Александр Викторович
All rights reserved.
This software is proprietary and confidential.
Unauthorized copying, modification, or distribution is prohibited.
For licensing inquiries: info@hb3-accelerator.com
Website: https://hb3-accelerator.com
GitHub: https://github.com/VC-HB3-Accelerator
-->
[English](../../docs.en/back-docs/setup-instruction.md) | **Русский**
# Инструкция по настройке приложения Digital Legal Entity
## 🚀 Полный процесс инициализации системы
Этот документ описывает полный процесс подготовки приложения к работе с поддержкой блокчейна, смарт-контрактов и системы управления доступом.
---
## Шаг 1: Установка софта
1. Клонируйте репозиторий проекта на ваше локальное устройство
2. Запустите приложение через Docker Compose или локально в зависимости от конфигурации
3. Откройте веб-приложение в браузере: `http://localhost:9000` (production) или `http://localhost:5173` (dev режим)
---
## Шаг 2: Подключение крипто кошелька
1. Убедитесь, что у вас установлен браузерный кошелек (MetaMask, WalletConnect или аналог)
2. В кошельке создайте или импортируйте аккаунт с токеном управления
3. В веб-приложении нажмите на кнопку **"Подключить кошелек"**
4. Выберите тип кошелька и подтвердите подключение
5. После успешного подключения вы увидите адрес вашего аккаунта в верхнем углу
---
## Шаг 3: Добавление RPC провайдеров (Безопасность → RPC провайдеры)
1. Перейдите в **Настройки** → вкладка **Безопасность**
2. Найдите раздел **"RPC провайдеры"**
3. Нажмите кнопку **"Добавить"**
4. Заполните форму для каждой блокчейн сети, которую хотите использовать:
- **Название сети** (например: Ethereum, Polygon, BSC)
- **RPC URL** (ссылка для подключения, пример: `https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY`)
- **ID сети** (Chain ID)
5. Нажмите **"Сохранить"** для каждого провайдера
6. Система автоматически проверит корректность подключения
> ⚠️ **Важно**: Получите API ключи от провайдеров (Alchemy, Infura, Quicknode и т.д.) перед добавлением
---
## Шаг 4: Настройка мультидеплоя смарт контрактов
1. Перейдите в **Настройки** → вкладка **Блокчейн**
2. Заполните форму
3. Нажмите **"Запустить деплой"**
---
## Шаг 5: Завершение деплоя и сохранение адреса контракта
1. Ожидайте завершения деплоя (зависит от сети, обычно 30-120 секунд)
2. После успешного завершения откроется страница **"Управление контрактами"**
3. **Копируйте адрес развернутого контракта** (обычно он выглядит как: `0x742d35Cc6634C0532925a3b844Bc...`)
---
## Шаг 6: Настройка аутентификации через смарт контракт
1. Вернитесь в **Настройки** → вкладка **Аутентификация**
2. В поле **"Адрес смарт контракта"** вставьте адрес, скопированный на шаге 5
3. Установите пороги для управления доступом:
- **Минимальное количество токенов для редактирования** (например: 100 токенов)
- **Минимальное количество токенов для просмотра** (например: 1 токен)
---
## Шаг 7: Настройка ИИ и базы данных
1. Перейдите в **Настройки** → вкладка **ИИ**
2. Откройте подраздел **"База данных"**
3. Замените дефолтные пароли
4. Нажмите **"Сгенерировать новый ключ шифрования"**
- Система автоматически создаст криптографический ключ
- **Сохраните ключ в безопасном месте** (он понадобится для восстановления данных)
---
## Шаг 8: Настройка доступа через интернет (опционально)
**Если вам нужен доступ к веб приложению извне через интернет:**
1. Перейдите в **Настройки** → вкладка **Сервер**
2. На странице **Сервер** выберите **WEB SSH** или иной подходящий сервис
3. Заполните форму для миграции локального приложения на виртуальное устройство с:
- **Публичным IP адресом**
- **Подключением к вашему доменному имени**
4. Нажмите **"Опубликовать"**
5. Дождитесь завершения процесса миграции
> **Примечание**: Этот шаг требует наличия зарегистрированного доменного имени и доступа к DNS настройкам
---
## Шаг 9: Настройка юридических документов для работы с персональными данными
### 9.1 Заполнение юридической информации о компании
1. Перейдите в **CRM** → раздел **Контент**
2. Найдите и откройте форму **"Юридическая информация компании"**
3. Заполните все необходимые поля:
- **Полное наименование организации** (юридическое название)
- **Краткое наименование**
- **Организационно-правовая форма** (ООО, ИП, АО и т.д.)
- **Юридический адрес**
- **Фактический адрес** (если отличается)
- **ИНН / ОГРН / КПП** (регистрационные данные)
- **Контактные данные** (телефон, email, сайт)
- **Ответственное лицо за обработку персональных данных** (ФИО, должность)
- **Применимая юрисдикция** (GDPR, CCPA, российское законодательство и т.д.)
4. Нажмите **"Сохранить"**
> 💡 **Подсказка**: Все введенные данные автоматически подставятся во все шаблоны юридических документов
### 9.2 Работа с шаблонами документов
1. В разделе **Контент** перейдите в подраздел **"Шаблоны"**
2. Выберите необходимые шаблоны документов, требуемые регуляторами:
- **Политика конфиденциальности**
- **Пользовательское соглашение**
- **Согласие на обработку персональных данных**
- **Политика использования cookies**
3. Для каждого шаблона:
- Нажмите **"Предварительный просмотр"** для проверки автоматически заполненных данных
- При необходимости отредактируйте специфичные параметры обработки данных
- Выберите действие:
- **"Опубликовать для публичного использования"** — документ будет доступен на сайте
- **"Опубликовать для внутреннего использования"** — документ доступен только внутри CRM
- **"Распечатать"** — экспорт в PDF для печати или подписания
4. Подтвердите публикацию
5. Система автоматически добавит документы на соответствующие страницы приложения
> ⚠️ **Важно**: Рекомендуется проконсультироваться с юристом перед публикацией документов для обеспечения полного соответствия требованиям законодательства
---
## ✅ Приложение готово к работе!
После завершения всех шагов ваше приложение полностью сконфигурировано и готово к использованию.
**Следующие этапы:**
- 📖 Настройка AI ассистента (см. документ: `setup-ai-assistant.md`)
- 🔐 Управление смарт контрактами (см. документ: `manage-smart-contracts.md`)
---
## 🆘 Рекомендации по безопасности
✓ Сохраняйте адреса контрактов и ключи шифрования в безопасном месте
✓ Используйте мощные пароли для БД
✓ Регулярно создавайте резервные копии конфигурации
✓ Никогда не делитесь приватными ключами кошелька
✓ Используйте HTTPS для доступа к приложению в продакшене
---
## 📝 Что дальше?
После завершения базовой настройки вы можете:
1. Добавлять пользователей и управлять их разрешениями
2. Создавать группы для совместной работы
3. Настраивать AI ассистента для автоматизации задач
4. Управлять смарт контрактами для расширения функциональности
5. Интегрировать внешние сервисы и боты
---
## 📚 Дополнительная документация
### Изучите возможности DLE
- 🤖 **[ИИ-агенты](../ai-assistant.md)** — система создания специализированных агентов под бизнес-процессы
- 💼 **[Блокчейн для бизнеса](../blockchain-for-business.md)** - как токенизация активов решает бизнес-задачи
- 🛡️ **[Безопасность](../security.md)** - многоуровневая защита вашего бизнеса
### Техническая информация
- 🔗 **[Техническая документация по блокчейну](./blockchain-integration-technical.md)** - для разработчиков
- 📋 **[FAQ](../FAQ.md)** - часто задаваемые вопросы
- 📝 **[Описание приложения](../application-description.md)** - обзор функциональности
### Поддержка
- 💬 **Чат поддержки**: https://hb3-accelerator.com/
- 📧 **Email**: info@hb3-accelerator.com

View File

@@ -0,0 +1,134 @@
[English](../../docs.en/back-docs/system-messages-management.md) | **Русский**
# Техническое задание: управление системными сообщениями
## 1. Цель и контекст
- Обеспечить управляемое отображение системных сообщений на главной странице (`/`, компонент `HomeView.vue`) и добавить административный интерфейс для их создания и модерации в разделе контента (`/content`, компонент `ContentListView.vue`).
- Системные сообщения должны поддерживать статусы «черновик» и «опубликовано», храниться в базе данных и быть доступны через REST API.
## 2. Актуальное состояние
- Главная страница строится компонентом `HomeView.vue` и отображает чат ассистента (`ChatInterface.vue`), в котором выделены системные сообщения (`Message.vue`) по признаку `message.role === 'system'`.
- Раздел контента (`ContentListView.vue`) содержит карточки переходов: «Создать страницу», «Шаблоны», «Публичные», «Настройка», «Внутренние». Карточки ведут на существующие маршруты `content-create`, `content-templates`, `content-published`, `content-settings`, `content-internal`.
- В проекте отсутствуют сущности и API для системных сообщений; текущий `pagesService.js` работает только со страницами (`/pages`).
## 3. Новые пользовательские сценарии
- **Просмотр системных сообщений (главная, `/`):**
- Опубликованные системные сообщения подгружаются в чат ассистента и отображаются в виде свернутых карточек с кликабельным заголовком.
- При клике на заголовок сообщение раскрывается: в ленте чата отображается полный текст сообщения **или** отправляется предзаготовленный ответ от ИИ ассистента (контент «ответа» хранится вместе с сообщением и выбирается по флагу `reply_type`).
- Сообщения должны явно маркироваться как системные (цвет, иконка). При повторном открытии пользователь видит последнее состояние раскрытия; возможно локальное запоминание «прочитано».
- **Раздел «Системные сообщения» (`/content`):**
- На странице `/content` появляется новая карточка «Системные сообщения» с кнопкой «Подробнее». Переход ведёт на страницу с пользовательской таблицей (`/content/system-messages/table`), построенной на уже существующих компонентах таблиц (см. `UserTablesList.vue`), без отдельного дэшборда карточек.
- Таблица отображает системные сообщения построчно, с возможностью множественного выбора через чекбоксы; доступные массовые действия: публикация, снятие с публикации, перевод в черновики, удаление.
- Для каждого сообщения по клику «Подробнее» (внутри строки) открывается просмотр/редактирование с формой (см. ниже).
- **Создание/редактирование (`/content/system-messages/create`, `/content/system-messages/:id/edit`):**
- Форма с полями: заголовок, краткое описание, основной текст (Markdown/HTML), тип ответа (`inline` — показывать контент, `assistant_reply` — отправлять подготовленный ответ от ассистента), поле «Ответ ассистента» (активно при `assistant_reply`), тег важности (info/warning/danger), дата начала публикации (опционально), дата окончания (опционально), флаг отображения гостям.
- Кнопки: «Сохранить как черновик», «Опубликовать». При редактировании — «Обновить», «Снять с публикации», «Удалить».
- Проверки: обязательность заголовка и основного текста (или ответа ассистента в соответствующем режиме); валидация дат (окончание ≥ начало).
- **Работа с таблицей системных сообщений:**
- Колонки: чекбокс выбора, заголовок (кликабелен), статус, тип ответа, период действия, целевая аудитория (гости/авторизованные/все), дата создания, автор.
- Массовые действия выполняются для выбранных строк; одиночные действия доступны через контекстное меню/кнопки в строке (редактировать, опубликовать, снять с публикации, удалить).
## 4. Требования к интерфейсу
- В `ContentListView.vue` в сетку `management-blocks` добавить карточку «Системные сообщения» с кнопкой `Подробнее`. По дизайну карточка должна соответствовать существующим блокам (заголовок, описание, кнопка).
- Страница с таблицей системных сообщений:
- Использовать `BaseLayout` и локальные стили (`scoped`).
- Таблица поддерживает сортировку, фильтрацию по статусам и поиск по заголовку.
- Чекбоксы в шапке и строках для массового выбора; панель действий появляется при наличии выбора.
- Кнопка «Создать сообщение» открывает форму создания.
- Форма создания/редактирования:
- Rich-text (минимум Markdown) с предпросмотром и счётчиками символов/слов.
- Переключатель режима показа (`inline`/`assistant_reply`) с условным отображением поля «Ответ ассистента» (можно использовать `<transition>`).
- Поле для выбора иконки/цвета по `severity` (статические пресеты).
- Главная страница:
- Системные сообщения отображаются в блоке чата как свернутые карточки (`system-message-collapsed`). При клике заголовок разворачивает карточку (`system-message-expanded`) или инициирует отправку ассистента (UI показывает «сообщение от ассистента»).
- Для развёрнутых сообщений предусмотреть кнопку «Свернуть» и (опционально) «Отметить как прочитанное». Состояние хранить в `localStorage`.
## 5. Маршрутизация и компоненты
- Добавить маршруты в `router/index.js`:
- - `/content/system-messages/table``SystemMessagesTableView.vue`
- - `/content/system-messages/create``SystemMessageCreateView.vue`
- - `/content/system-messages/:id``SystemMessageDetailsView.vue` (просмотр)
- - `/content/system-messages/:id/edit``SystemMessageEditView.vue`
- При необходимости для модальных/вложенных маршрутов можно использовать дочерние маршруты или именованные вью.
- Создать соответствующие Vue-компоненты в `src/views/content/system-messages/` и общий набор переиспользуемых элементов (таблица, форма, фильтры, массовые действия) в `src/components/system-messages/`.
- Создать сервис `src/services/systemMessagesService.js` с методами для нового API.
## 6. Требования к API и данным
- **Новая таблица** `system_messages` (PostgreSQL):
- `id` (uuid, pk)
- `title` (text, not null)
- `summary` (text, nullable)
- `content` (text, not null)
- `reply_type` (enum: inline, assistant_reply; default inline)
- `assistant_reply_content` (text, nullable; требуется при `reply_type = assistant_reply`)
- `severity` (enum: info, warning, danger; default info)
- `status` (enum: draft, published; not null)
- `visible_for` (enum: all, authenticated, guests; default all)
- `publish_at` (timestamp, nullable)
- `expire_at` (timestamp, nullable)
- `created_at`, `updated_at`
- `created_by`, `updated_by` (references users/identities, nullable)
- `slug` (text, уникальный, для адресации по ссылке при необходимости)
- **REST API (Express):**
- `GET /system-messages` (пагинация, фильтры по статусу, поиску)
- `GET /system-messages/published` (фильтрация по дате/аудитории; публичная)
- `GET /system-messages/:id` (доступ только авторизованным редакторам)
- `POST /system-messages` (создание; права `MANAGE_LEGAL_DOCS`)
- `PATCH /system-messages/:id` (редактирование; проверка статусов)
- `DELETE /system-messages/:id` (мягкое удаление или физическое)
- `POST /system-messages/:id/publish` и `POST /system-messages/:id/unpublish` (опционально, если не использовать PATCH)
- Все защищённые эндпоинты должны требовать авторизацию и права (см. `permissions.js`, `usePermissions`).
- Добавить новую миграцию (`backend/scripts/run-migrations.js`) и ORM/SQL-файлы в существующем формате проекта.
- Обновить логирование и обработку ошибок `winston`, добавить валидацию входных данных (например, `Joi` или кастомную).
## 7. Логика отображения на фронтенде
- `HomeView.vue`:
- При инициализации запрашивать опубликованные системные сообщения (учитывая текущую аудиторию) через `systemMessagesService.getPublished({ includeExpired: false })`.
- Кэшировать ответ в сторе или локальном состоянии; при подписке на WebSocket можно предусмотреть `system_message_updated` событие.
- Добавить обработчик раскрытия: по клику на заголовок либо подставлять полный текст сообщения (`inline`), либо инициировать цепочку отправки `assistant_reply_content` в чат (без участия пользователя).
- Добавить обработчик скрытия сообщения, сохраняющий идентификатор в `localStorage` и фильтрующий локально.
- `ContentListView.vue`:
- Добавить новую карточку «Системные сообщения» в сетку `management-blocks`, не нарушая адаптивную сетку (обновить `grid-template-columns` при необходимости).
- Страницы списков:
- Реализовать пагинацию (lazy loading или обычная), сортировку по дате.
- Для статусов использовать цветовые бейджи (info/warning/danger).
- Форма создания:
- Поддерживать сабмит через `yarn lint`-friendly код; валидация на клиенте (например, с использованием `computed`/`watch`).
- При успешной публикации перенаправлять на список опубликованных; при сохранении черновика — оставаться на странице с уведомлением.
## 8. Требования к безопасности и доступу
- Сценарии создания/изменения доступны только ролям с `PERMISSIONS.MANAGE_LEGAL_DOCS`.
- Публичный список (`GET /system-messages/published`) фильтрует по:
- `status === 'published'`.
- `publish_at <= now()` (или null).
- `expire_at > now()` (или null).
- `visible_for` проверяется на основе контекста (гость/авторизованный).
- При выдаче через чат скрывать поля `created_by`, `updated_by`, внутренние метки.
- Учитывать CSRF, CORS, rate-limit (перенять конфиг из существующих роутов).
## 9. Тестирование
- **Backend:**
- Юнит-тесты для CRUD в `tests/system-messages/*.test.js` (Mocha).
- Проверка фильтров publish/expire и доступа по ролям.
- Тест миграции (откат/применение).
- **Frontend:**
- Юнит-тесты Vue (если настроены) для основных компонентов (форма, список).
- E2E (при наличии) — сценарий: создание черновика → публикация → отображение на главной.
- **Регрессионные проверки:**
- Убедиться, что существующий список контента и чат ассистента продолжают работать без ошибок (`yarn lint`, `yarn test`).
## 10. Интеграция и DevOps
- Обновить `docker-compose.yml` при необходимости (например, добавить миграции в стартовый процесс).
- Убедиться, что новые переменные окружения (если будут, например, лимиты количества сообщений) документированы в `README.md` и `setup-instruction.md`.
- Добавить скрипт seeding (опционально) для тестовых системных сообщений.
## 11. Открытые вопросы
- Нужно ли хранить историю публикаций (auditing)? Если да — предусмотреть таблицу `system_messages_history`.
- Требуется ли поддержка многоязычности? (При отсутствии — ограничение на один язык, RU).
- Нужно ли уведомление по WebSocket при появлении новых сообщений? (Если да — добавить событие в `wsHub.js`).
## 12. Итоговые артефакты
- Backend: новые маршруты, контроллеры, сервис, миграция.
- Frontend: новые страницы и сервис, обновлённые маршруты и компоненты `HomeView`, `ContentListView`.
- Документация: обновление `README.md` (раздел запуск), `application-description.md` или `tables-system.md` при изменении схем, настоящая спецификация.

File diff suppressed because it is too large Load Diff