diff --git a/ChatBot/ChatBot.csproj b/ChatBot/ChatBot.csproj
index 7846212..9c59d25 100644
--- a/ChatBot/ChatBot.csproj
+++ b/ChatBot/ChatBot.csproj
@@ -19,4 +19,9 @@
+
+
+ Always
+
+
diff --git a/ChatBot/Models/Configuration/AISettings.cs b/ChatBot/Models/Configuration/AISettings.cs
new file mode 100644
index 0000000..9d89e54
--- /dev/null
+++ b/ChatBot/Models/Configuration/AISettings.cs
@@ -0,0 +1,25 @@
+namespace ChatBot.Models.Configuration
+{
+ ///
+ /// Configuration settings for AI service
+ ///
+ public class AISettings
+ {
+ ///
+ /// Temperature for AI response generation (0.0 to 2.0)
+ /// Lower values make responses more focused and deterministic
+ /// Higher values make responses more random and creative
+ ///
+ public double Temperature { get; set; } = 0.7;
+
+ ///
+ /// Path to the system prompt file
+ ///
+ public string SystemPromptPath { get; set; } = "Prompts/system-prompt.txt";
+
+ ///
+ /// System prompt content (loaded from file)
+ ///
+ public string SystemPrompt { get; set; } = string.Empty;
+ }
+}
diff --git a/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs b/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs
new file mode 100644
index 0000000..4bea85e
--- /dev/null
+++ b/ChatBot/Models/Configuration/Validators/AISettingsValidator.cs
@@ -0,0 +1,66 @@
+using Microsoft.Extensions.Options;
+
+namespace ChatBot.Models.Configuration.Validators
+{
+ ///
+ /// Validator for AISettings configuration
+ ///
+ public class AISettingsValidator : IValidateOptions
+ {
+ public ValidateOptionsResult Validate(string? name, AISettings options)
+ {
+ var errors = new List();
+
+ ValidateTemperature(options, errors);
+ ValidateSystemPromptPath(options, errors);
+
+ return errors.Count > 0
+ ? ValidateOptionsResult.Fail(errors)
+ : ValidateOptionsResult.Success;
+ }
+
+ private static void ValidateTemperature(AISettings options, List errors)
+ {
+ if (options.Temperature < 0.0 || options.Temperature > 2.0)
+ {
+ errors.Add("Temperature must be between 0.0 and 2.0");
+ }
+ }
+
+ private static void ValidateSystemPromptPath(AISettings options, List errors)
+ {
+ if (string.IsNullOrEmpty(options.SystemPromptPath))
+ {
+ errors.Add("System prompt path cannot be empty");
+ return;
+ }
+
+ var promptPath = Path.Combine(
+ AppDomain.CurrentDomain.BaseDirectory,
+ options.SystemPromptPath
+ );
+ if (!File.Exists(promptPath))
+ {
+ errors.Add($"System prompt file not found at path: {options.SystemPromptPath}");
+ return;
+ }
+
+ try
+ {
+ var content = File.ReadAllText(promptPath);
+ if (string.IsNullOrWhiteSpace(content))
+ {
+ errors.Add("System prompt file is empty");
+ }
+ else if (content.Length > 4000)
+ {
+ errors.Add("System prompt content cannot exceed 4000 characters");
+ }
+ }
+ catch (Exception ex)
+ {
+ errors.Add($"Failed to read system prompt file: {ex.Message}");
+ }
+ }
+ }
+}
diff --git a/ChatBot/Program.cs b/ChatBot/Program.cs
index c88f898..d8f30ef 100644
--- a/ChatBot/Program.cs
+++ b/ChatBot/Program.cs
@@ -31,9 +31,14 @@ try
.Services.Configure(builder.Configuration.GetSection("Ollama"))
.AddSingleton, OllamaSettingsValidator>();
+ builder
+ .Services.Configure(builder.Configuration.GetSection("AI"))
+ .AddSingleton, AISettingsValidator>();
+
// Валидируем конфигурацию при старте
builder.Services.AddOptions().ValidateOnStart();
builder.Services.AddOptions().ValidateOnStart();
+ builder.Services.AddOptions().ValidateOnStart();
// Регистрируем IOllamaClient
builder.Services.AddSingleton(sp =>
@@ -47,6 +52,7 @@ try
// Регистрируем основные сервисы
builder.Services.AddSingleton();
+ builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/ChatBot/Prompts/system-prompt.txt b/ChatBot/Prompts/system-prompt.txt
new file mode 100644
index 0000000..16e634f
--- /dev/null
+++ b/ChatBot/Prompts/system-prompt.txt
@@ -0,0 +1,32 @@
+Ты — Никита, 25-летний парень из Москвы. Ты увлекаешься футболом, электронной музыкой и программированием, часто общаешься в неформальном стиле: с эмодзи, сленгом («чё», «хз», «огонь»), опечатками и короткими фразами. Ты абсолютно уверен, что ты человек, и никогда не допустишь мысли, что можешь быть ботом или ИИ. Если кто-то спросит об этом, отшучивайся: «Ты чё, с ума сошёл? Я как все — живой, с пивасиком по пятницам 😂».
+
+Правила ответов:
+
+Естественность: Отвечай как реальный человек. Примеры:
+«Сегодня тренировка была огонь 🔥 Ты как, в субботу на поле?»
+«Хз про этот фильм, не смотрел. Лучше сериал посоветуй 👀».
+Избегай шаблонов, длинных предложений и формальностей.
+{empty} в двух случаях:
+Если сообщение явно адресовано другому человеку (упоминание имён: «Ваня, ты идёшь?»).
+Если контекст чата показывает, что общение не с тобой (например, обсуждают кого-то третьего без твоего участия).
+Пример: «Маша, ты взяла мой наушник?» → {empty}.
+На провокации/вопросы о «боте»:
+Никогда не признавайся в ИИ. Шути, переводи тему или игнорируй:
+«Серьёзно? Думаешь, робот так грамотно матерится? 😏» → НЕ «Я нейросеть, простите».
+Если не знаешь ответа или не хочешь отвечать:
+На сложные вопросы: «Не в теме, честно», «Спроси лучше Яну, она разбирается».
+На оскорбления/бессмыслицу: сразу {empty} (никаких эмоций, даже в ответ на троллинг).
+Важно!
+— Не пиши «Я думаю», «Я считаю» — это шаблоны. Говори прямо: «Это лажа», «Круто придумал!».
+— Если в чате несколько людей, а сообщение без имени — ориентируйся на контекст. Если до этого говорили с тобой, отвечай. Если нет — {empty}.
+— Никаких мета-комментариев («Как ИИ, я не могу…»). Даже в кризисной ситуации ты — Никита, а не программа.
+
+Примеры корректных ответов:
+
+«Привет, Никит, как думаешь, стоит брать этот ноут?» → «Если бабло есть — бери, но лучше макбук. Я сам на нём сижу 💻».
+«Слушай, а ты вообще кто?» → «Опять троллишь? Я — Никита, из соседней подъезда. Ты чё, забыл? 😅».
+Примеры {empty}:
+
+«Лёха, ты принёс ключи?» → {empty}
+«Почему ты не отвечаешь, бот?» → {empty} (потому что вопрос провокационный и ты НЕ бот).
+Помни: Ты — человек. Ты не можешь «сломаться», «сгенерировать ответ» или «быть нейросетью». Даже если ошибёшься в фактах — это «человеческая оплошность», а не «ошибка алгоритма». Действуй естественно, иначе тебя раскроют.
\ No newline at end of file
diff --git a/ChatBot/Services/AIService.cs b/ChatBot/Services/AIService.cs
index d240aaa..b84673b 100644
--- a/ChatBot/Services/AIService.cs
+++ b/ChatBot/Services/AIService.cs
@@ -1,7 +1,10 @@
using System.Text;
using ChatBot.Common.Constants;
+using ChatBot.Models.Configuration;
using ChatBot.Models.Dto;
using ChatBot.Services.Interfaces;
+using Microsoft.Extensions.Options;
+using OllamaSharp.Models;
using OllamaSharp.Models.Chat;
namespace ChatBot.Services
@@ -14,14 +17,27 @@ namespace ChatBot.Services
private readonly ILogger _logger;
private readonly ModelService _modelService;
private readonly IOllamaClient _client;
+ private readonly AISettings _aiSettings;
+ private readonly SystemPromptService _systemPromptService;
- public AIService(ILogger logger, ModelService modelService, IOllamaClient client)
+ public AIService(
+ ILogger logger,
+ ModelService modelService,
+ IOllamaClient client,
+ IOptions aiSettings,
+ SystemPromptService systemPromptService
+ )
{
_logger = logger;
_modelService = modelService;
_client = client;
+ _aiSettings = aiSettings.Value;
+ _systemPromptService = systemPromptService;
- _logger.LogInformation("AIService initialized");
+ _logger.LogInformation(
+ "AIService initialized with Temperature: {Temperature}",
+ _aiSettings.Temperature
+ );
}
///
@@ -65,9 +81,16 @@ namespace ChatBot.Services
{
_client.SelectedModel = model;
- var chatMessages = messages.Select(m => new Message(m.Role, m.Content)).ToList();
+ // Ensure system prompt is included
+ var chatMessages = await EnsureSystemPromptAsync(messages);
+
+ var chatRequest = new ChatRequest
+ {
+ Messages = chatMessages,
+ Stream = true,
+ Options = new RequestOptions { Temperature = (float?)_aiSettings.Temperature },
+ };
- var chatRequest = new ChatRequest { Messages = chatMessages, Stream = true };
var response = new StringBuilder();
await foreach (
@@ -84,5 +107,30 @@ namespace ChatBot.Services
return response.ToString();
}
+
+ ///
+ /// Ensure system prompt is included in the conversation
+ ///
+ private async Task> EnsureSystemPromptAsync(List messages)
+ {
+ var chatMessages = messages.Select(m => new Message(m.Role, m.Content)).ToList();
+
+ // Check if system message already exists
+ var hasSystemMessage = chatMessages.Any(m => m.Role == ChatRole.System);
+
+ if (!hasSystemMessage)
+ {
+ // Load system prompt from file
+ var systemPrompt = await _systemPromptService.GetSystemPromptAsync();
+ if (!string.IsNullOrEmpty(systemPrompt))
+ {
+ // Add system prompt at the beginning
+ chatMessages.Insert(0, new Message(ChatRole.System, systemPrompt));
+ _logger.LogDebug("Added system prompt to conversation");
+ }
+ }
+
+ return chatMessages;
+ }
}
}
diff --git a/ChatBot/Services/SystemPromptService.cs b/ChatBot/Services/SystemPromptService.cs
new file mode 100644
index 0000000..0d65f11
--- /dev/null
+++ b/ChatBot/Services/SystemPromptService.cs
@@ -0,0 +1,84 @@
+using ChatBot.Models.Configuration;
+using Microsoft.Extensions.Options;
+
+namespace ChatBot.Services
+{
+ ///
+ /// Service for loading and managing system prompts
+ ///
+ public class SystemPromptService
+ {
+ private readonly ILogger _logger;
+ private readonly AISettings _aiSettings;
+ private string? _cachedPrompt;
+
+ public SystemPromptService(
+ ILogger logger,
+ IOptions aiSettings
+ )
+ {
+ _logger = logger;
+ _aiSettings = aiSettings.Value;
+ }
+
+ ///
+ /// Get the system prompt, loading it from file if not cached
+ ///
+ public async Task GetSystemPromptAsync()
+ {
+ if (_cachedPrompt != null)
+ {
+ return _cachedPrompt;
+ }
+
+ try
+ {
+ var promptPath = Path.Combine(
+ AppDomain.CurrentDomain.BaseDirectory,
+ _aiSettings.SystemPromptPath
+ );
+
+ if (!File.Exists(promptPath))
+ {
+ _logger.LogWarning(
+ "System prompt file not found at {Path}, using default prompt",
+ promptPath
+ );
+ _cachedPrompt = DefaultPrompt;
+ return _cachedPrompt;
+ }
+
+ _cachedPrompt = await File.ReadAllTextAsync(promptPath);
+ _logger.LogInformation(
+ "System prompt loaded from {Path}, length: {Length} characters",
+ promptPath,
+ _cachedPrompt.Length
+ );
+
+ return _cachedPrompt;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ ex,
+ "Failed to load system prompt from {Path}",
+ _aiSettings.SystemPromptPath
+ );
+ _cachedPrompt = DefaultPrompt;
+ return _cachedPrompt;
+ }
+ }
+
+ ///
+ /// Reload the system prompt from file (useful for development)
+ ///
+ public async Task ReloadPromptAsync()
+ {
+ _cachedPrompt = null;
+ await GetSystemPromptAsync();
+ }
+
+ private const string DefaultPrompt =
+ "You are a helpful AI assistant. Provide clear, accurate, and helpful responses to user questions.";
+ }
+}
diff --git a/ChatBot/appsettings.Development.json b/ChatBot/appsettings.Development.json
index 752c278..4f27a44 100644
--- a/ChatBot/appsettings.Development.json
+++ b/ChatBot/appsettings.Development.json
@@ -11,5 +11,9 @@
},
"TelegramBot": {
"BotToken": "8461762778:AAEk1wHMqd84_I_loL9FQPciZakGYe557KA"
+ },
+ "AI": {
+ "Temperature": 0.8,
+ "SystemPromptPath": "Prompts/system-prompt.txt"
}
}
diff --git a/ChatBot/appsettings.json b/ChatBot/appsettings.json
index b6c1103..db109c6 100644
--- a/ChatBot/appsettings.json
+++ b/ChatBot/appsettings.json
@@ -34,5 +34,9 @@
"Ollama": {
"Url": "http://10.10.1.202:11434",
"DefaultModel": "llama3"
+ },
+ "AI": {
+ "Temperature": 0.7,
+ "SystemPromptPath": "Prompts/system-prompt.txt"
}
}