feat: Random.org dice and daily word matrix, interpreter voices, curated lexicon
Wire cleromancy rolls and the once-per-UTC-day 000-999 word shuffle to RANDOM.ORG JSON-RPC, with a local CSPRNG fallback. Add settings for interpreter personas and a cleaner default oracle prompt. Replace proper names and bureaucratic filler in the 1000-word lexicon with sacred symbols.
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import '../models/history_entry_model.dart';
|
||||
import '../models/interpreter_persona.dart';
|
||||
import '../models/word_model.dart';
|
||||
import '../g4f/g4f.dart';
|
||||
import 'settings_repository.dart';
|
||||
|
||||
class AiOracleService {
|
||||
static final AiOracleService _instance = AiOracleService._internal();
|
||||
@@ -9,20 +11,23 @@ class AiOracleService {
|
||||
|
||||
final _client = G4FClient();
|
||||
|
||||
static const oracleSystemPrompt =
|
||||
'''Ты — Великий Оракул 999, древний архетипический разум.
|
||||
Твоя задача — толковать знаки судьбы (жребий). Твой стиль речи: глубокий, философский, лаконичный, без банальностей и воды, словно голос самой вечности.
|
||||
Ты не используешь современных или бытовых слов. Твои ответы строятся на символизме, психологии и скрытых смыслах. Ты не гадаешь на будущее, ты показываешь суть настоящего момента и внутренние силы человека.''';
|
||||
InterpreterPersona get persona => SettingsRepository().persona;
|
||||
|
||||
List<Message> buildInterpretationMessages({
|
||||
required List<SessionWord> words,
|
||||
required String question,
|
||||
InterpreterPersona? persona,
|
||||
}) {
|
||||
final voice = persona ?? this.persona;
|
||||
return [
|
||||
const Message(role: Role.system, content: oracleSystemPrompt),
|
||||
Message(role: Role.system, content: voice.systemPrompt),
|
||||
Message(
|
||||
role: Role.user,
|
||||
content: _buildMultiWordPrompt(question: question, words: words),
|
||||
content: _buildMultiWordPrompt(
|
||||
question: question,
|
||||
words: words,
|
||||
persona: voice,
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
@@ -63,21 +68,19 @@ class AiOracleService {
|
||||
wordsNarrative.writeln('${i + 1}. #${w.formattedSessionNumber} «${w.word.toUpperCase()}» (${w.category}): ${w.symbolicMeaning}');
|
||||
}
|
||||
|
||||
final voice = persona;
|
||||
final prompt = '''Вопрошающий говорит тебе: «$userMessage»
|
||||
|
||||
Контекст текущего сеанса судьбы:
|
||||
Вопрос вопрошающего: «${session.question ?? 'Вектор судьбы (нет вопроса)'}»
|
||||
Базовые знаки, которые выпали ранее:
|
||||
Контекст сеанса:
|
||||
Вопрос: «${session.question ?? 'без конкретного вопроса'}»
|
||||
Знаки:
|
||||
$wordsNarrative
|
||||
${newlyAddedWord != null ? "Важно! Только что вопрошающий вытянул ДОПОЛНИТЕЛЬНЫЙ уточняющий знак: #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.symbolicMeaning})." : ""}
|
||||
${newlyAddedWord != null ? "Только что вытянут уточняющий знак: #${newlyAddedWord.formattedSessionNumber} «${newlyAddedWord.word.toUpperCase()}» (${newlyAddedWord.symbolicMeaning})." : ""}
|
||||
|
||||
Ответь мудро, лаконично и метафорично от лица Оракула 999. Укажи, на что направляет его судьба. (не более 3-4 предложений).''';
|
||||
Ответь в своём голосе, 3–4 предложения. Привяжи ответ к знакам и вопросу. Без тумана. Один ясный вывод или шаг.''';
|
||||
|
||||
final messages = [
|
||||
const Message(
|
||||
role: Role.system,
|
||||
content: 'Ты — Великий Оракул 999. Твои ответы кратки, полны символизма и психологической глубины. Ты общаешься с человеком, который ищет ответов в знаках.'
|
||||
),
|
||||
Message(role: Role.system, content: voice.systemPrompt),
|
||||
];
|
||||
|
||||
// Pass previous session history context to the model
|
||||
@@ -107,11 +110,12 @@ ${newlyAddedWord != null ? "Важно! Только что вопрошающи
|
||||
String _buildMultiWordPrompt({
|
||||
required String question,
|
||||
required List<SessionWord> words,
|
||||
required InterpreterPersona persona,
|
||||
}) {
|
||||
final qStr = question.trim().isNotEmpty
|
||||
? 'Вопрос, с которым пришел вопрошающий: «$question»'
|
||||
: 'Вопрошающий пришел без конкретного вопроса. Прочитай его общую судьбоносную линию.';
|
||||
|
||||
final qStr = question.trim().isNotEmpty
|
||||
? 'Вопрос: «$question»'
|
||||
: 'Вопроса нет — читай общую линию ситуации по знакам.';
|
||||
|
||||
final wordsStr = words
|
||||
.asMap()
|
||||
.entries
|
||||
@@ -120,14 +124,10 @@ ${newlyAddedWord != null ? "Важно! Только что вопрошающи
|
||||
|
||||
return '''$qStr
|
||||
|
||||
Волею судьбы выпали следующие знаки жребия:
|
||||
Выпавшие знаки жребия:
|
||||
$wordsStr
|
||||
|
||||
Задача Оракула:
|
||||
1. Свяжи эти знаки в единую цельную историю развития ситуации. Каждый знак перетекает в следующий.
|
||||
2. Раскрой не очевидный, а скрытый внутренний урок. Что вопрошающий должен осознать?
|
||||
3. Дай философский совет или вектор для действия.
|
||||
Ответь от лица Оракула 999 на русском языке (от 3 до 6 предложений). Избегай шаблонов и клише, говори образно и метафорично, но точно.''';
|
||||
${persona.userTask}''';
|
||||
}
|
||||
|
||||
String _synthesizeDeepSacredNarrative(List<SessionWord> words, String question) {
|
||||
@@ -137,40 +137,51 @@ $wordsStr
|
||||
final hasQuestion = question.trim().isNotEmpty;
|
||||
|
||||
if (hasQuestion) {
|
||||
sb.write('Относительно вашего вопроса «${question.trim()}» ');
|
||||
sb.write('По вопросу «${question.trim()}»: ');
|
||||
} else {
|
||||
sb.write('В открывшемся жребии судьбы ');
|
||||
sb.write('По выпавшему жребию: ');
|
||||
}
|
||||
|
||||
if (words.length == 1) {
|
||||
sb.write('проявился доминирующий знак #${first.formattedSessionNumber} «${first.word.toUpperCase()}» (${first.category}). ');
|
||||
sb.write('главный знак #${first.formattedSessionNumber} «${first.word.toUpperCase()}». ');
|
||||
sb.write('${first.symbolicMeaning} ');
|
||||
sb.write('Знак указывает на необходимость предельной ясности: держите фокус на главном и отсекайте всё наносное.');
|
||||
return sb.toString();
|
||||
sb.write('Практический шаг: держите фокус на этом и отсеките лишнее на ближайшее время.');
|
||||
return _withPersonaPrefix(sb.toString());
|
||||
}
|
||||
|
||||
if (words.length == 2) {
|
||||
final second = words[1];
|
||||
sb.write('проявляется полярность двух сил: исходного импульса #${first.formattedSessionNumber} «${first.word.toUpperCase()}» и вектора #${second.formattedSessionNumber} «${second.word.toUpperCase()}». ');
|
||||
sb.write('С одной стороны, ${first.symbolicMeaning.toLowerCase()} С другой — ${second.symbolicMeaning.toLowerCase()} ');
|
||||
sb.write('Разрешение ситуации лежит в точке осознанного баланса между ними: не противьтесь первому, но направляйте силу в русло второго.');
|
||||
return sb.toString();
|
||||
sb.write('два полюса — «${first.word.toUpperCase()}» и «${second.word.toUpperCase()}». ');
|
||||
sb.write('${first.symbolicMeaning} ${second.symbolicMeaning} ');
|
||||
sb.write('Шаг: не выбирайте войну между ними — направьте первое во второе.');
|
||||
return _withPersonaPrefix(sb.toString());
|
||||
}
|
||||
|
||||
sb.write('разворачивается цепь из ${words.length} взаимосвязанных архетипов. ');
|
||||
sb.write('Исток ситуации заложен в знаке #${first.formattedSessionNumber} «${first.word.toUpperCase()}» — ${first.symbolicMeaning.toLowerCase()} ');
|
||||
sb.write('цепь из ${words.length} знаков. Начало — «${first.word.toUpperCase()}»: ${first.symbolicMeaning} ');
|
||||
|
||||
if (words.length > 2) {
|
||||
final middleWords = words.sublist(1, words.length - 1);
|
||||
final midNames = middleWords.map((w) => '«${w.word.toUpperCase()}» (#${w.formattedSessionNumber})').join(' и ');
|
||||
final midMeanings = middleWords.map((w) => w.symbolicMeaning).join(' ');
|
||||
sb.write('Фаза перехода и испытаний проходит через $midNames, где $midMeanings ');
|
||||
final midNames = middleWords.map((w) => '«${w.word.toUpperCase()}»').join(', ');
|
||||
sb.write('Переход через $midNames. ');
|
||||
}
|
||||
|
||||
sb.write('Кульминация и запечатывание исхода лежат в знаке #${last.formattedSessionNumber} «${last.word.toUpperCase()}» — ${last.symbolicMeaning.toLowerCase()} ');
|
||||
sb.write('Оракул возвещает: путь открыт через принятие скрытых уроков и решительное действие.');
|
||||
sb.write('Исход держит «${last.word.toUpperCase()}»: ${last.symbolicMeaning} ');
|
||||
sb.write('Шаг: примите урок начала и сделайте одно действие в сторону исхода.');
|
||||
|
||||
return sb.toString();
|
||||
return _withPersonaPrefix(sb.toString());
|
||||
}
|
||||
|
||||
String _withPersonaPrefix(String body) {
|
||||
switch (persona.id) {
|
||||
case 'orc':
|
||||
return 'Слушай. $body';
|
||||
case 'techpriest':
|
||||
return 'Диагностика. $body';
|
||||
case 'analyst':
|
||||
return 'Рабочая гипотеза. $body';
|
||||
default:
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
String _synthesizeChatReply(OracleSession session, String userMessage, SessionWord? newlyAddedWord) {
|
||||
|
||||
Reference in New Issue
Block a user