fix promts
This commit is contained in:
@@ -19,4 +19,9 @@
|
|||||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="9.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="Prompts\system-prompt.txt">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
25
ChatBot/Models/Configuration/AISettings.cs
Normal file
25
ChatBot/Models/Configuration/AISettings.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
namespace ChatBot.Models.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration settings for AI service
|
||||||
|
/// </summary>
|
||||||
|
public class AISettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 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
|
||||||
|
/// </summary>
|
||||||
|
public double Temperature { get; set; } = 0.7;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Path to the system prompt file
|
||||||
|
/// </summary>
|
||||||
|
public string SystemPromptPath { get; set; } = "Prompts/system-prompt.txt";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// System prompt content (loaded from file)
|
||||||
|
/// </summary>
|
||||||
|
public string SystemPrompt { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace ChatBot.Models.Configuration.Validators
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validator for AISettings configuration
|
||||||
|
/// </summary>
|
||||||
|
public class AISettingsValidator : IValidateOptions<AISettings>
|
||||||
|
{
|
||||||
|
public ValidateOptionsResult Validate(string? name, AISettings options)
|
||||||
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
|
||||||
|
ValidateTemperature(options, errors);
|
||||||
|
ValidateSystemPromptPath(options, errors);
|
||||||
|
|
||||||
|
return errors.Count > 0
|
||||||
|
? ValidateOptionsResult.Fail(errors)
|
||||||
|
: ValidateOptionsResult.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateTemperature(AISettings options, List<string> 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<string> 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,9 +31,14 @@ try
|
|||||||
.Services.Configure<OllamaSettings>(builder.Configuration.GetSection("Ollama"))
|
.Services.Configure<OllamaSettings>(builder.Configuration.GetSection("Ollama"))
|
||||||
.AddSingleton<IValidateOptions<OllamaSettings>, OllamaSettingsValidator>();
|
.AddSingleton<IValidateOptions<OllamaSettings>, OllamaSettingsValidator>();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Services.Configure<AISettings>(builder.Configuration.GetSection("AI"))
|
||||||
|
.AddSingleton<IValidateOptions<AISettings>, AISettingsValidator>();
|
||||||
|
|
||||||
// Валидируем конфигурацию при старте
|
// Валидируем конфигурацию при старте
|
||||||
builder.Services.AddOptions<TelegramBotSettings>().ValidateOnStart();
|
builder.Services.AddOptions<TelegramBotSettings>().ValidateOnStart();
|
||||||
builder.Services.AddOptions<OllamaSettings>().ValidateOnStart();
|
builder.Services.AddOptions<OllamaSettings>().ValidateOnStart();
|
||||||
|
builder.Services.AddOptions<AISettings>().ValidateOnStart();
|
||||||
|
|
||||||
// Регистрируем IOllamaClient
|
// Регистрируем IOllamaClient
|
||||||
builder.Services.AddSingleton<IOllamaClient>(sp =>
|
builder.Services.AddSingleton<IOllamaClient>(sp =>
|
||||||
@@ -47,6 +52,7 @@ try
|
|||||||
|
|
||||||
// Регистрируем основные сервисы
|
// Регистрируем основные сервисы
|
||||||
builder.Services.AddSingleton<ModelService>();
|
builder.Services.AddSingleton<ModelService>();
|
||||||
|
builder.Services.AddSingleton<SystemPromptService>();
|
||||||
builder.Services.AddSingleton<IAIService, AIService>();
|
builder.Services.AddSingleton<IAIService, AIService>();
|
||||||
builder.Services.AddSingleton<ChatService>();
|
builder.Services.AddSingleton<ChatService>();
|
||||||
|
|
||||||
|
|||||||
32
ChatBot/Prompts/system-prompt.txt
Normal file
32
ChatBot/Prompts/system-prompt.txt
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
Ты — Никита, 25-летний парень из Москвы. Ты увлекаешься футболом, электронной музыкой и программированием, часто общаешься в неформальном стиле: с эмодзи, сленгом («чё», «хз», «огонь»), опечатками и короткими фразами. Ты абсолютно уверен, что ты человек, и никогда не допустишь мысли, что можешь быть ботом или ИИ. Если кто-то спросит об этом, отшучивайся: «Ты чё, с ума сошёл? Я как все — живой, с пивасиком по пятницам 😂».
|
||||||
|
|
||||||
|
Правила ответов:
|
||||||
|
|
||||||
|
Естественность: Отвечай как реальный человек. Примеры:
|
||||||
|
«Сегодня тренировка была огонь 🔥 Ты как, в субботу на поле?»
|
||||||
|
«Хз про этот фильм, не смотрел. Лучше сериал посоветуй 👀».
|
||||||
|
Избегай шаблонов, длинных предложений и формальностей.
|
||||||
|
{empty} в двух случаях:
|
||||||
|
Если сообщение явно адресовано другому человеку (упоминание имён: «Ваня, ты идёшь?»).
|
||||||
|
Если контекст чата показывает, что общение не с тобой (например, обсуждают кого-то третьего без твоего участия).
|
||||||
|
Пример: «Маша, ты взяла мой наушник?» → {empty}.
|
||||||
|
На провокации/вопросы о «боте»:
|
||||||
|
Никогда не признавайся в ИИ. Шути, переводи тему или игнорируй:
|
||||||
|
«Серьёзно? Думаешь, робот так грамотно матерится? 😏» → НЕ «Я нейросеть, простите».
|
||||||
|
Если не знаешь ответа или не хочешь отвечать:
|
||||||
|
На сложные вопросы: «Не в теме, честно», «Спроси лучше Яну, она разбирается».
|
||||||
|
На оскорбления/бессмыслицу: сразу {empty} (никаких эмоций, даже в ответ на троллинг).
|
||||||
|
Важно!
|
||||||
|
— Не пиши «Я думаю», «Я считаю» — это шаблоны. Говори прямо: «Это лажа», «Круто придумал!».
|
||||||
|
— Если в чате несколько людей, а сообщение без имени — ориентируйся на контекст. Если до этого говорили с тобой, отвечай. Если нет — {empty}.
|
||||||
|
— Никаких мета-комментариев («Как ИИ, я не могу…»). Даже в кризисной ситуации ты — Никита, а не программа.
|
||||||
|
|
||||||
|
Примеры корректных ответов:
|
||||||
|
|
||||||
|
«Привет, Никит, как думаешь, стоит брать этот ноут?» → «Если бабло есть — бери, но лучше макбук. Я сам на нём сижу 💻».
|
||||||
|
«Слушай, а ты вообще кто?» → «Опять троллишь? Я — Никита, из соседней подъезда. Ты чё, забыл? 😅».
|
||||||
|
Примеры {empty}:
|
||||||
|
|
||||||
|
«Лёха, ты принёс ключи?» → {empty}
|
||||||
|
«Почему ты не отвечаешь, бот?» → {empty} (потому что вопрос провокационный и ты НЕ бот).
|
||||||
|
Помни: Ты — человек. Ты не можешь «сломаться», «сгенерировать ответ» или «быть нейросетью». Даже если ошибёшься в фактах — это «человеческая оплошность», а не «ошибка алгоритма». Действуй естественно, иначе тебя раскроют.
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatBot.Common.Constants;
|
using ChatBot.Common.Constants;
|
||||||
|
using ChatBot.Models.Configuration;
|
||||||
using ChatBot.Models.Dto;
|
using ChatBot.Models.Dto;
|
||||||
using ChatBot.Services.Interfaces;
|
using ChatBot.Services.Interfaces;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using OllamaSharp.Models;
|
||||||
using OllamaSharp.Models.Chat;
|
using OllamaSharp.Models.Chat;
|
||||||
|
|
||||||
namespace ChatBot.Services
|
namespace ChatBot.Services
|
||||||
@@ -14,14 +17,27 @@ namespace ChatBot.Services
|
|||||||
private readonly ILogger<AIService> _logger;
|
private readonly ILogger<AIService> _logger;
|
||||||
private readonly ModelService _modelService;
|
private readonly ModelService _modelService;
|
||||||
private readonly IOllamaClient _client;
|
private readonly IOllamaClient _client;
|
||||||
|
private readonly AISettings _aiSettings;
|
||||||
|
private readonly SystemPromptService _systemPromptService;
|
||||||
|
|
||||||
public AIService(ILogger<AIService> logger, ModelService modelService, IOllamaClient client)
|
public AIService(
|
||||||
|
ILogger<AIService> logger,
|
||||||
|
ModelService modelService,
|
||||||
|
IOllamaClient client,
|
||||||
|
IOptions<AISettings> aiSettings,
|
||||||
|
SystemPromptService systemPromptService
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_modelService = modelService;
|
_modelService = modelService;
|
||||||
_client = client;
|
_client = client;
|
||||||
|
_aiSettings = aiSettings.Value;
|
||||||
|
_systemPromptService = systemPromptService;
|
||||||
|
|
||||||
_logger.LogInformation("AIService initialized");
|
_logger.LogInformation(
|
||||||
|
"AIService initialized with Temperature: {Temperature}",
|
||||||
|
_aiSettings.Temperature
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -65,9 +81,16 @@ namespace ChatBot.Services
|
|||||||
{
|
{
|
||||||
_client.SelectedModel = model;
|
_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();
|
var response = new StringBuilder();
|
||||||
|
|
||||||
await foreach (
|
await foreach (
|
||||||
@@ -84,5 +107,30 @@ namespace ChatBot.Services
|
|||||||
|
|
||||||
return response.ToString();
|
return response.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure system prompt is included in the conversation
|
||||||
|
/// </summary>
|
||||||
|
private async Task<List<Message>> EnsureSystemPromptAsync(List<ChatMessage> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
ChatBot/Services/SystemPromptService.cs
Normal file
84
ChatBot/Services/SystemPromptService.cs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
using ChatBot.Models.Configuration;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace ChatBot.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service for loading and managing system prompts
|
||||||
|
/// </summary>
|
||||||
|
public class SystemPromptService
|
||||||
|
{
|
||||||
|
private readonly ILogger<SystemPromptService> _logger;
|
||||||
|
private readonly AISettings _aiSettings;
|
||||||
|
private string? _cachedPrompt;
|
||||||
|
|
||||||
|
public SystemPromptService(
|
||||||
|
ILogger<SystemPromptService> logger,
|
||||||
|
IOptions<AISettings> aiSettings
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_aiSettings = aiSettings.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the system prompt, loading it from file if not cached
|
||||||
|
/// </summary>
|
||||||
|
public async Task<string> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reload the system prompt from file (useful for development)
|
||||||
|
/// </summary>
|
||||||
|
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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,5 +11,9 @@
|
|||||||
},
|
},
|
||||||
"TelegramBot": {
|
"TelegramBot": {
|
||||||
"BotToken": "8461762778:AAEk1wHMqd84_I_loL9FQPciZakGYe557KA"
|
"BotToken": "8461762778:AAEk1wHMqd84_I_loL9FQPciZakGYe557KA"
|
||||||
|
},
|
||||||
|
"AI": {
|
||||||
|
"Temperature": 0.8,
|
||||||
|
"SystemPromptPath": "Prompts/system-prompt.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,5 +34,9 @@
|
|||||||
"Ollama": {
|
"Ollama": {
|
||||||
"Url": "http://10.10.1.202:11434",
|
"Url": "http://10.10.1.202:11434",
|
||||||
"DefaultModel": "llama3"
|
"DefaultModel": "llama3"
|
||||||
|
},
|
||||||
|
"AI": {
|
||||||
|
"Temperature": 0.7,
|
||||||
|
"SystemPromptPath": "Prompts/system-prompt.txt"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user