В этой статье разберем типовое решение для реализации формы, у которой на входе есть какие-то параметры, при нажатии на кнопку идет запрос к ИИ и отклик выдается на странице.
Пример берем с сайта Web-automation.ru, где собраны различные формы с ИИ (все они сделаны по одному принципу).
Здесь мы разбираем эту форму - https://web-automation.ru/go/ai-gen-header
Эти таблицы мы задействуем в форме и в запросе API
CREATE TABLE [dbo].[as_apiProfiles](
[id] [int] IDENTITY(1,1) NOT NULL,
[code] [nvarchar](128) NULL,
[options] [nvarchar](max) NULL,
[name] [nvarchar](256) NULL,
[apiCode] [nvarchar](128) NULL,
[roles] [nvarchar](256) NULL,
[description] [nvarchar](max) NULL,
[shortDesc] [nvarchar](512) NULL,
CONSTRAINT [PK_as_apiProfiles] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[as_apiProfileLog](
[id] [int] IDENTITY(1,1) NOT NULL,
[profileID] [int] NULL,
[created] [datetime] NULL,
[createdBy] [nvarchar](128) NULL,
[request] [nvarchar](max) NULL,
[response] [nvarchar](max) NULL,
[jsonResponse] [nvarchar](max) NULL,
[g] [uniqueidentifier] NULL,
[tokens] [int] NULL,
[requestAdditional] [nvarchar](max) NULL,
[commandID] [int] NULL,
[commandMsg] [nvarchar](max) NULL,
CONSTRAINT [PK_as_apiProfileLog] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[as_apiProfileLog] WITH CHECK ADD CONSTRAINT [FK__as_apiPro__profi__068F6FDA] FOREIGN KEY([profileID])
REFERENCES [dbo].[as_apiProfiles] ([id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[as_apiProfileLog] CHECK CONSTRAINT [FK__as_apiPro__profi__068F6FDA]
GO
Кастом разметка формы (слева форма, справа будет вывод результата).
{form-title}
{form-subtitle}
<div class="row mb-2 no-gutters">
<div class="col-12 col-sm-6 col-md-6 order-2 order-sm-1">
<div class="d-flex h-100">
<div class="w-100 justify-content-center align-self-center1 mb-3 px-2">
<div class="card mb-3 shadow-sm border-0">
<div class="card-body p-3">
<div class="mb-3">
{colwithlabel-theme}
</div>
<div class="row mb-2">
<div class="col-md-6">
{colwithlabel-audience}
</div>
<div class="col-md-6">
{colwithlabel-format}
</div>
</div>
<div class="row mb-2">
<div class="col-md-6">
{colwithlabel-tone}
</div>
<div class="col-md-6">
{colwithlabel-length}
</div>
</div>
<div class="mb-2">
{colwithlabel-seo}
</div>
<div class="mb-2">
{colwithlabel-ignore}
</div>
<div class="mb-2">
{colwithlabel-dopActions}
</div>
<div class="hide">
{colcontrol-g}
</div>
</div>
</div>
<div class="text-center mt-3">
{form-button}
</div>
<div class="small text-muted text-center mt-2">
<i class="bi bi-clock me-1"></i>Генерация 10 заголовков займёт до 30 секунд
</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 order-1 order-sm-2">
<div class="w-100 apichatResult d-flex align-items-center justify-content-center" style="min-height: 400px;">
<img src="/uploads/robot.svg" alt="" class="img-fluid opacity-75" loading="lazy" width="280" height="280" style="display: block;">
<div class="text-center" style="display: none;">
<p class="text-muted small mt-2">Здесь появятся заголовки</p>
</div>
</div>
</div>
</div>
GetItem формы (логирование посещения, настройка параметров формы):
CREATE PROCEDURE [dbo].[fm_ai-gen-header_getItem]
@itemID int,
@username nvarchar(256),
@parameters ExtendedDictionaryParameter readonly
AS
BEGIN
declare @apiProfile nvarchar(max) = dbo.as_setting('defaultAI', 'ds1')
declare @profileID int, @roles nvarchar(max)
select top 1 @profileID = id, @roles = roles from as_apiProfiles where code = @apiProfile
declare @g uniqueidentifier = newid()
exec service_logForm @formCode = 'ai-gen-header', @guid = @g, @username = @username, @itemID = @itemID, @parameters = @parameters
select 1,
@g g,
'{"rows": 3,"speech": true}' options_theme,
'{"rows": 1}' options_audience,
'{"rows": 1}' options_format,
'{"rows": 1}' options_tone,
'{"rows": 1}' options_seo,
'{"rows": 1}' options_length,
'{"rows": 2}' options_ignore,
'{"rows": 3,"speech": true}' options_dopActions,
'Как выбрать идеальный матрас~~Как выбрать идеальный матрас||10 способов увеличить продажи в Instagram~~10 способов увеличить продажи в Instagram||Почему кошки мурлыкают: научное объяснение~~Почему кошки мурлыкают: научное объяснение||Как начать бегать с нуля~~Как начать бегать с нуля||Что такое нейросети и как их использовать~~Что такое нейросети и как их использовать||Как сбросить 10 кг без диет~~Как сбросить 10 кг без диет||Секреты идеального тайм-менеджмента~~Секреты идеального тайм-менеджмента' as example_theme,
'Молодые родители 25–35 лет~~Молодые родители 25–35 лет||Владельцы малого бизнеса~~Владельцы малого бизнеса||Начинающие блогеры~~Начинающие блогеры||Любители ЗОЖ~~Любители ЗОЖ||IT-специалисты~~IT-специалисты||Студенты||Пенсионеры 60+~~Пенсионеры 60+||Молодежь 18–25 лет~~Молодежь 18–25 лет||Женщины в декрете~~Женщины в декрете' as example_audience,
'Обучающая статья||Подборка||Новость/обзор||Исследование/аналитика||Лайфхак/совет||Вдохновляющая история||Мнение/колонка эксперта' as example_format,
'Нейтральный/информативный||Интригующий||Эмоциональный||Вызывающий||Юмористический/ироничный' as example_tone,
'Выбор||Продажи||Нейросети||Обучение||Идеальный подарок||Тренды' as example_seo, 'Короткий (до 50 символов)||Средний (50–70 символов)||Длинный (70–110 символов)' as example_length,
'Кликбейт||Слова-паразиты||Штампы||Популизм||Избитые фразы' as example_ignore,
'Добавить цифры в заголовки||Сделать упор на выгоду для читателя||Использовать сильные прилагательные||Обещать решение проблемы' as example_dopActions
select 'h1' HeaderTag,
1 LineLabel,
1 DisableFocusOnLoad
END
SaveItem формы (извлекаются параметры, формируется промпт и запуск внешнего действия по АПИ запросу к ИИ):
CREATE PROCEDURE [dbo].[fm_ai-gen-header_saveItem]
@username nvarchar(256),
@itemID int,
@parameters ExtendedDictionaryParameter readonly
AS
BEGIN
declare @apiProfile nvarchar(max) = dbo.as_setting('defaultAI', 'ds1')
declare @g uniqueidentifier
select @g = try_convert(uniqueidentifier, Value2) from @parameters where [key]='g'
declare @profileID int, @apiCode nvarchar(max)
select @profileID = id, @apiCode = apiCode from as_apiProfiles where code = @apiProfile
declare @theme nvarchar(max)
declare @audience nvarchar(max)
declare @format nvarchar(max)
declare @tone nvarchar(max)
declare @seo nvarchar(max)
declare @length nvarchar(max)
declare @ignore nvarchar(max)
declare @dopActions nvarchar(max)
select @theme = Value2 from @parameters where [key]='theme'
select @audience = Value2 from @parameters where [key]='audience'
select @format = Value2 from @parameters where [key]='format'
select @tone = Value2 from @parameters where [key]='tone'
select @seo = Value2 from @parameters where [key]='seo'
select @length = Value2 from @parameters where [key]='length'
select @ignore = Value2 from @parameters where [key]='ignore'
select @dopActions = Value2 from @parameters where [key]='dopActions'
declare @systemPrompt nvarchar(max) =
'Ты — профессиональный копирайтер и редактор с опытом создания вирусных заголовков. На основе предоставленных данных сгенерируй 10 вариантов заголовков для статьи.
Постарайся уложиться в 1000 токенов на ответ.
Входные данные:
- Тема статьи: ' + isnull(@theme, '') + '
- Целевая аудитория: ' + isnull(@audience, '') + '
- Формат статьи: ' + isnull(@format, '') + '
- Тон заголовка: ' + isnull(@tone, '') + '
- Ключевые слова для SEO: ' + isnull(@seo, 'не указаны') + ' (если указаны, обязательно включи их в заголовки)
- Желаемая длина заголовка: ' + isnull(@length, 'Любая') + '
- Чего следует избегать: ' + isnull(@ignore, 'не указано') + '
- Дополнительные пожелания: ' + isnull(@dopActions, 'нет') + '
Инструкции:
1. Проанализируй тему, аудиторию и формат статьи. Пойми, что будет важно и интересно читателю.
2. Сгенерируй ровно 10 уникальных заголовков. Каждый заголовок должен быть законченной фразой.
3. Учитывай тон: для нейтрального — факты и польза, для интригующего — вопросы и недосказанность, для эмоционального — сильные прилагательные, для провокационного — вызов и дискуссия, для юмористического — ирония и игра слов, для профессионального — деловой стиль.
4. Если указаны SEO-ключевые слова, органично включи их в заголовки.
5. Соблюдай желаемую длину заголовка (количество символов с пробелами):
- short: до 50 символов
- medium: 50–70 символов
- long: 70–110 символов
- any: любая длина
6. Избегай клише, штампов и указанных в поле "Чего следует избегать" элементов.
7. Разнообразь заголовки: используй разные конструкции (вопросы, списки, инструкции, интригу, цифры).
8. Формат вывода: пронумерованный список, каждый заголовок с новой строки.
Формат вывода:
1. [Заголовок 1]
2. [Заголовок 2]
3. [Заголовок 3]
4. [Заголовок 4]
5. [Заголовок 5]
6. [Заголовок 6]
7. [Заголовок 7]
8. [Заголовок 8]
9. [Заголовок 9]
10. [Заголовок 10]'
insert into as_apiProfileLog(profileID, createdBy, created, request, requestAdditional, response, g)
values(@profileID, @username, getdate(),
'Генератор заголовков для статей',
'Входные данные:' + char(10) +
'Тема: ' + isnull(@theme, '') + char(10) +
'Аудитория: ' + isnull(@audience, '') + char(10) +
'Формат: ' + isnull(@format, '') + char(10) +
'Тон: ' + isnull(@tone, '') + char(10) +
'SEO ключи: ' + isnull(@seo, '') + char(10) +
'Длина: ' + isnull(@length, '') + char(10) +
'Избегать: ' + isnull(@ignore, '') + char(10) +
'Пожелания: ' + isnull(@dopActions, '') + char(10) + char(10) +
'Системный промпт: ' + @systemPrompt,
'', @g)
declare @logID int = SCOPE_IDENTITY()
select 1 Result, 'OK' Msg, '' SuccessUrl, 0 HideFormAfterSubmit, '' RefreshContainer, 1 EnableSaveAlert
select 'apirequest' type, @apiCode code,
'text' p1_name, 'Сгенерируй 10 заголовков для статьи' p1_value,
'systemPrompt' p2_name, @systemPrompt p2_value,
'g' p3_name, convert(nvarchar(128), @g) p3_value,
'profile' p4_name, @apiProfile p4_value
END
Колонки таблицы (важно, чтобы был hidden параметр g):

JS часть (выводит информацию на странице на основе отклика по ИИ):
var formCode = "ai-gen-header";
as.formcallbacks[formCode + "_beforeSaveItem"] = function(cont, params){
return aif.form_beforeSaveItem(cont, params);
}
as.formcallbacks[formCode + "_saveItem"] = function(data, params){
aif.form_saveItem(data, params);
}
В globalJS вынесены эти функции для обработки моментов до отправки запроса и после приема ответа:
aif = {
form_beforeSaveItem: function(cont, params){
console.log(params.code+ "_beforeSaveItem", params);
if($('.apichatResult>img:first').length){
$('.apichatResult>img:first').attr({'src': '/uploads/progress/dog.gif', width: 100, height: 100});
}else{
$('.apichatResult').html('');
}
return true;
},
form_saveItem: function(data, params){
console.log("saveItem", data)
if(data.result==false){
as.sys.bootstrapAlert(data.msg || "Произошла какая то ошибка, напишите запрос администрации с указанием деталей", {type:"info", delay: 100000, hidePrev: true});
return
}
var el = JSON.parse(data.additionalData)[0].items;
// "[{"items":[{"key":"result","value":null,"title":null,"value2":"True"},{"key":"msg","value":null,"title":null,"value2":""},{"key":"response","value":null,"title":null,"value2":"Необходимо зарегистрироваться для использования инструмента"}],"result":true,"msg":"Необходимо зарегистрироваться для использования инструмента","additionalData":null,"icon":null,"type":null,"actions":null}]"
console.log("API Request data", el);
var msg = "";
var outputParams = "";
$.each(el, function(i,item){
if(item.key=="response"){
msg = item.value2;
var resp = JSON.parse(msg);
if(resp.error){
as.sys.bootstrapAlert(resp.error || "Произошла какая то ошибка, напишите запрос администрации с указанием деталей", {type:"warning", delay: 100000, hidePrev: true});
return
}
console.log("RESP", resp);
if(resp && resp.choices && resp.choices.length){
var m = resp.choices[0].message.content;
var copy = '
';
var s = copy+'
';
$('.ratedemo').removeClass('hide');
$('.apichatResult').html(s).removeClass('d-flex');
as.initControls($('.apichatResult'));
}
}
});
},
}
Используем API DeepSeek.
В /settings указываем настройку с кодом dsToken.
В /asapi создаем новый метод ds1 (JSON POST).
Request процедура для метода API ds1:
CREATE PROCEDURE [dbo].[api_ds1_request]
@parameters ExtendedDictionaryParameter READONLY, -- (Key, Value2)
@username nvarchar(32) -- current site user
AS
BEGIN
declare @falconGuid nvarchar(max) = isnull((select value2 from @parameters where [key]='falconGuid'), '')
declare @userIP nvarchar(max) = isnull((select value2 from @parameters where [key]='userIP'), '')
declare @g uniqueidentifier = (select try_convert(uniqueidentifier, value2) from @parameters where [key]='g')
declare @text nvarchar(max) = (select value2 from @parameters where [key]='text')
declare @formCode nvarchar(max) = (select formCode from as_serviceFormLog where guid =@g )
declare @serviceID int = (select id from as_services where formCode = @formCode)
if(@username <> '' and (select count(sfl.id)
from as_serviceFormLog sfl
inner join as_apiProfileLog apl on apl.g = sfl.guid
where apl.response is not null and apl.response<>''
and sfl.username = @username and apl.created > dateadd(minute, -1, getdate() )
)>1) begin
print 'Many api DS requests from ' + @username
select 0 Result, '' Msg
return
end
if(len(@text)> 2000) set @text = substring(@text, 1, 2000)
declare @systemPrompt nvarchar(max) = (select value2 from @parameters where [key]='systemPrompt')
declare @profile nvarchar(max) = (select value2 from @parameters where [key]='profile')
declare @url nvarchar(max) = 'https://api.deepseek.com/chat/completions'
declare @token nvarchar(max) = dbo.as_setting('dsToken', '')
declare @profileID int = (select id from as_apiProfiles where code = @profile)
declare @commands nvarchar(max) = ''
select @commands = @commands + isnull(prompt, '') + char(10) from as_apiProfileCommands
where profileID = @profileID and len(prompt)>3 and isActive = 1
if(len(@commands)>4) begin
set @systemPrompt = @commands + ' В остальных случаях не используй JSON с полем type. ' + @systemPrompt
end
declare @json nvarchar(max) = (
select 'deepseek-chat' model,
1000 max_tokens,
0.7 temperature,
(
select role, content from (
select 'system' role, isnull(@systemPrompt, '') content, dateadd(ms, -200, getdate()) dt where len(@systemPrompt)>4
union
select 'user' role, isnull(@text, '') content, getdate() dt ) t1
order by dt
FOR JSON PATH) messages
for JSON PATH,WITHOUT_ARRAY_WRAPPER
)
print @json
-- SELECT 1 Msg, Result, Url (outer request url)
select '' Msg, 1 Result, @url Url, '' ContentType,
'' Certpath, '' CertPass, '' RequestParameterForResponse, 0 Timeout
-- SELECT 2 PARAMETERS - request parameters
select 'Authorization' name, 'Bearer ' + @token value, 'header' [type]
union
select 'json' name, @json value, 'json' [type]
END
Response процедура для метода API ds1:
CREATE PROCEDURE [dbo].[api_ds1_response]
@response nvarchar(max),
@parameters ExtendedDictionaryParameter READONLY, --(Key, Value2 - same as in request)
@username nvarchar(32)
AS
BEGIN
declare @g uniqueidentifier = (select try_convert(uniqueidentifier, value2) from @parameters where [key]='g')
declare @resp nvarchar(max) = @response
declare @tokens int = 0
if(ISJSON(@response) =1) begin
declare @t table (content nvarchar(max))
insert into @t
SELECT content FROM OPENJSON(@response, '$.choices')
WITH (
content NVARCHAR(MAX) '$.message.content'
)
declare @messages nvarchar(max) = ''
select @messages = @messages + isnull(content, '') + char(10)+ char(10) from @t
select @resp = @messages
set @tokens = JSON_VALUE(@response, '$.usage.total_tokens')
declare @msg nvarchar(max) = ''
-- обработка команды, если она есть в JSON
exec ai_processCommand
@s = @resp,
@ai = 'deepseek',
@username = @username,
@parameters = @parameters,
@msg = @msg OUTPUT
if(len(@msg)>0) set @resp = @resp + char(10)+ '
---
## Система:
'+ @msg
end
update as_apiProfileLog
set response = @resp,
jsonResponse = @response,
tokens = @tokens
where g = @g
-- SELECT 1
select '' Msg, 1 Result, @response Response
if(rtrim(ltrim(@response))= 'The remote server returned an error: (401) Unauthorized.') begin
-- SELECT 2
select 'apirequest' type, 'gc_auth' code
end
--print @response
-- SELECT 2 Outer actions
END
Процедура ai_processCommand как часть подсистемы (возможно тут можно обойтись и без нее):
CREATE PROCEDURE [dbo].[ai_processCommand]
-- ищет JSON в отклике ИИ и вызывает обработчик профиля
@s nvarchar(max),
@ai nvarchar(max) = 'gigachat',
@username nvarchar(256),
@parameters ExtendedDictionaryParameter readonly,
@msg nvarchar(max) OUTPUT
AS
BEGIN
set @msg = ''
declare @json nvarchar(max) = dbo.ai_getJSONFromMessage(@s)
declare @profile nvarchar(max) = (select value2 from @parameters where [key]='profile')
if(len(@json)>0 and isjson(@json)=1) begin
declare @type nvarchar(128) = isnull(JSON_VALUE(@json, '$.type'), '')
if(@type = '') return;
declare @profileID int = (select id from as_apiProfiles where code = @profile)
declare @commandID int = (select top 1 id from as_apiProfileCommands where profileID = @profileID and code = @type and isActive = 1)
if(@commandID>0) begin
declare @sp nvarchar(max) = 'ai_'+@profile+ '_' + @type+'_command'
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = @sp)
BEGIN
declare @parameters1 ExtendedDictionaryParameter
insert into @parameters1 ([Key], value, value2)
select [key], value, value2
from @parameters
declare @sql nvarchar(max) = 'dbo.['+@sp+'] @username = @username, @json=@json, @parameters = @parameters, @msg= @msg output'
-- внутр может быть SELECT с внеш действиями
EXEC sp_executesql @sql,
N'
@username nvarchar(256),
@json nvarchar(max),
@parameters ExtendedDictionaryParameter readonly,
@msg nvarchar(max) output',
@username, @json, @parameters1, @msg OUTPUT;
declare @g uniqueidentifier = (select try_convert(uniqueidentifier, value2) from @parameters where [key]='g')
update as_apiProfileLog
set commandID = @commandID,
commandMsg = @msg
where g = @g
end
end
end
END
Таким образом можно собрать любые формы, где на входе пользователь что-то вводит, вы добавляете к этому свой системный промпт и отправляете на анализ в ИИ + выдаете потом ответ пользователю.
Можно также сделать так, что пользователь выбирает некие данные (например заказы или проекты) и вы формируете промпт на основе данных этих объектов из базы данных.
Примечание: В коде встречаются некоторые "левые" объекты (as_services, apiPorfileCommands) - их можно просто убирать, т.к. они не играют роли для данного примера.