feat(llm): двухфазный жизненный цикл сессий, двусторонний Diff, Topic Drift Guard и актуализация документации
This commit is contained in:
+59
-10
@@ -51,12 +51,12 @@ def print_stats():
|
||||
print("=" * 60)
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
tables = ['scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history', 'ai_knowledge_base', 'chat_messages', 'session_states']
|
||||
tables = ['scud_logs', 'zup_staff', 'zup_absences', 'anomalies_history', 'ai_knowledge_base', 'chat_messages', 'session_states', 'system_prompt_nodes', 'tasks']
|
||||
for t in tables:
|
||||
try:
|
||||
cursor.execute(f"SELECT COUNT(*) FROM {t}")
|
||||
cnt = cursor.fetchone()[0]
|
||||
print(f" • Таблица [{t:<18}]: {cnt:>6} записей")
|
||||
print(f" • Таблица [{t:<20}]: {cnt:>6} записей")
|
||||
except Exception:
|
||||
pass
|
||||
print("=" * 60 + "\n")
|
||||
@@ -151,7 +151,7 @@ def inspect_scud(snapshot_id=None, date_str=None, export_xlsx=None):
|
||||
df = load_scud_from_db_by_snapshot(target_date, snapshot_param=snapshot_id)
|
||||
|
||||
if df.empty:
|
||||
print(f"Записи СКУД не найдены.")
|
||||
print("Записи СКУД не найдены.")
|
||||
print("=" * 90 + "\n")
|
||||
return
|
||||
|
||||
@@ -318,7 +318,6 @@ def print_chat_messages(session_id=None, limit=50):
|
||||
if not rows:
|
||||
print("Таблица chat_messages пуста.")
|
||||
else:
|
||||
# Если выводили все без конкретной сессии в порядке DESC, развернем для читаемости
|
||||
if not session_id:
|
||||
rows = list(reversed(rows))
|
||||
|
||||
@@ -327,13 +326,53 @@ def print_chat_messages(session_id=None, limit=50):
|
||||
eph_marker = " [ЭФЕМЕРНОЕ]" if ephemeral else ""
|
||||
print(f"[{msg_id}] {created} | Сессия: {sess} | Роль: {role.upper()}{eph_marker}")
|
||||
print("-" * 105)
|
||||
# Выводим контент, ограничивая длинные сообщения при необходимости
|
||||
content_preview = content if content else ""
|
||||
print(f"{content_preview}")
|
||||
print("=" * 105)
|
||||
print("\n")
|
||||
|
||||
|
||||
def purge_chat_context(session_id=None, purge_all=False):
|
||||
"""
|
||||
Очистка контекста сообщений:
|
||||
- По умолчанию: удаляет эфемерные сообщения, осиротевшие превью и сбрасывает стейты сессий.
|
||||
- purge_all=True (--all): полностью очищает всю таблицу chat_messages и сбрасывает сессии.
|
||||
"""
|
||||
with get_connection() as conn:
|
||||
cursor = conn.cursor()
|
||||
|
||||
if purge_all:
|
||||
if session_id:
|
||||
cursor.execute("DELETE FROM chat_messages WHERE session_id = ?", (session_id,))
|
||||
cursor.execute("DELETE FROM session_states WHERE session_id = ?", (session_id,))
|
||||
else:
|
||||
cursor.execute("DELETE FROM chat_messages")
|
||||
cursor.execute("DELETE FROM session_states")
|
||||
deleted_msgs = cursor.rowcount
|
||||
conn.commit()
|
||||
print(f"\n[✓] Полная очистка истории выполнена! Удалено сообщений: {deleted_msgs}\n")
|
||||
return
|
||||
|
||||
query = """
|
||||
DELETE FROM chat_messages
|
||||
WHERE is_ephemeral = 1
|
||||
OR content LIKE '%Предпросмотр изменений%'
|
||||
OR content LIKE '%Удален пункт:%'
|
||||
OR content LIKE '%добавлен пункт:%'
|
||||
"""
|
||||
if session_id:
|
||||
cursor.execute(query + " AND session_id = ?", (session_id,))
|
||||
cursor.execute("DELETE FROM session_states WHERE session_id = ?", (session_id,))
|
||||
else:
|
||||
cursor.execute(query)
|
||||
cursor.execute("DELETE FROM session_states")
|
||||
|
||||
deleted_msgs = cursor.rowcount
|
||||
conn.commit()
|
||||
|
||||
print(f"\n[✓] Умная зачистка контекста выполнена! Удалено сообщений: {deleted_msgs}\n")
|
||||
|
||||
|
||||
HELP_TEXT = """
|
||||
CLI-утилита инспекции и управления SQLite базой данных СКУД (scud_orion_ai.db)
|
||||
|
||||
@@ -348,6 +387,7 @@ CLI-утилита инспекции и управления SQLite базой
|
||||
tools -- Посмотреть реестр действий инструментов и кнопок (tool_action_registry)
|
||||
sessions -- Посмотреть активные сессии и превью (session_states)
|
||||
context [session_id] [--limit N] -- Посмотреть таблицу контекста сообщений чата (chat_messages)
|
||||
context purge [session_id] [--all] -- Очистить контекст (умная зачистка или полная с флагом --all)
|
||||
dump [output.xlsx] -- Полный дамп всех таблиц БД в многостраничный Excel
|
||||
snapshot del [ID] или [--day ДД.ММ.ГГГГ] -- Удаление снапшота по ID или всех за выбранный день
|
||||
|
||||
@@ -360,6 +400,9 @@ CLI-утилита инспекции и управления SQLite базой
|
||||
python scripts/db_cli.py tools
|
||||
python scripts/db_cli.py sessions
|
||||
python scripts/db_cli.py context web_session_main --limit 20
|
||||
python scripts/db_cli.py context purge
|
||||
python scripts/db_cli.py context purge --all
|
||||
python scripts/db_cli.py context purge web_session_main --all
|
||||
python scripts/db_cli.py snapshot del Y20260805-007
|
||||
python scripts/db_cli.py dump my_dump.xlsx
|
||||
"""
|
||||
@@ -376,12 +419,13 @@ def main():
|
||||
add_help=False
|
||||
)
|
||||
parser.add_argument('command', nargs='?', default=None, choices=['stats', 'snapshots', 'scud', 'absences', 'anomalies', 'rules', 'prompts', 'sessions', 'dump', 'snapshot', 'tools', 'context'], help="Основная команда")
|
||||
parser.add_argument('action', nargs='?', default=None, help="Дополнительное действие (например, 'del') или session_id для контекста")
|
||||
parser.add_argument('param', nargs='?', default=None, help="Параметр (дата, ID снапшота или имя файла)")
|
||||
parser.add_argument('action', nargs='?', default=None, help="Дополнительное действие (например, 'del', 'purge') или session_id для контекста")
|
||||
parser.add_argument('param', nargs='?', default=None, help="Параметр (дата, ID снапшота, session_id, '--all' или имя файла)")
|
||||
parser.add_argument('--snapshot', type=str, default=None, help="ID конкретного снапшота для инспекции")
|
||||
parser.add_argument('--export-xlsx', type=str, default=None, help="Экспорт среза СКУД в Excel-файл")
|
||||
parser.add_argument('--day', type=str, default=None, help="Удалить снапшоты за конкретный день (ДД.ММ.ГГГГ)")
|
||||
parser.add_argument('--limit', type=int, default=50, help="Лимит выводимых сообщений чата (для команды context)")
|
||||
parser.add_argument('--all', action='store_true', help="Полная очистка всех сообщений (для команды context purge)")
|
||||
|
||||
if len(sys.argv) == 1:
|
||||
print_stats()
|
||||
@@ -410,8 +454,13 @@ def main():
|
||||
elif args.command == 'tools':
|
||||
print_tool_actions()
|
||||
elif args.command == 'context':
|
||||
sess_id = args.action if args.action else None
|
||||
print_chat_messages(session_id=sess_id, limit=args.limit)
|
||||
if args.action in ['purge', 'clear']:
|
||||
is_all = args.all or (args.param == '--all')
|
||||
sess_id = None if (args.param == '--all' or not args.param) else args.param
|
||||
purge_chat_context(session_id=sess_id, purge_all=is_all)
|
||||
else:
|
||||
sess_id = args.action if args.action else None
|
||||
print_chat_messages(session_id=sess_id, limit=args.limit)
|
||||
elif args.command == 'dump':
|
||||
filename = args.param if args.param else "db_dump_full.xlsx"
|
||||
dump_all_to_excel(filename)
|
||||
@@ -428,7 +477,7 @@ def main():
|
||||
print(f"\n[❌] Ошибка: Неизвестное действие '{args.action}' для команды snapshot.")
|
||||
print("Используйте: python scripts/db_cli.py snapshot del [ID или --day 'ДД.ММ.ГГГГ']\n")
|
||||
else:
|
||||
print(f"\n[❌] Ошибка: Неизвестная команда.")
|
||||
print("\n[❌] Ошибка: Неизвестная команда.")
|
||||
print(HELP_TEXT)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -2,15 +2,29 @@ import os
|
||||
|
||||
OUTPUT_SNAPSHOT = "project_code_snapshot.md"
|
||||
|
||||
# Расширения файлов для включения в снимок
|
||||
ALLOWED_EXTENSIONS = {'.py', '.json', '.md', '.sh', '.ini', '.js', '.html', '.css', '.sql'}
|
||||
EXCLUDE_DIRS = {'.git', '__pycache__', 'venv', '.venv', 'output', 'logs', 'extracted_project'}
|
||||
EXCLUDE_FILES = {OUTPUT_SNAPSHOT, 'scud_orion_ai_v2.tar.gz', 'context_memory.db'}
|
||||
# Расширения файлов для включения в снимок (только исходный код)
|
||||
ALLOWED_EXTENSIONS = {'.py', '.json', '.sh', '.ini', '.js', '.html', '.css', '.sql'}
|
||||
|
||||
print(f"🔄 Сборка полного контекстного слепка проекта в {OUTPUT_SNAPSHOT}...")
|
||||
# Исключаемые каталоги (убираем docs, кэши, архивы и окружения)
|
||||
EXCLUDE_DIRS = {
|
||||
'.git', '__pycache__', 'venv', '.venv', 'output', 'logs',
|
||||
'extracted_project', 'docs', 'data'
|
||||
}
|
||||
|
||||
# Исключаемые файлы
|
||||
EXCLUDE_FILES = {
|
||||
OUTPUT_SNAPSHOT,
|
||||
'api_code_snapshot.md',
|
||||
'project_code_snapshot.md',
|
||||
'scud_orion_ai_v2.tar.gz',
|
||||
'scud_context_api.tar.gz',
|
||||
'context_memory.db'
|
||||
}
|
||||
|
||||
print(f"🔄 Сборка оптимизированного слепка кода в {OUTPUT_SNAPSHOT}...")
|
||||
|
||||
with open(OUTPUT_SNAPSHOT, 'w', encoding='utf-8') as out:
|
||||
out.write("# 📦 ПОЛНЫЙ ИСХОДНЫЙ КОД И КОНФИГУРАЦИЯ ПРОЕКТА scud_orion_ai_v2\n\n")
|
||||
out.write("# 📦 ОПТИМИЗИРОВАННЫЙ СЛЕПОК ИСХОДНОГО КОДА scud_orion_ai\n\n")
|
||||
|
||||
for root, dirs, files in os.walk('.'):
|
||||
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
|
||||
@@ -20,7 +34,7 @@ with open(OUTPUT_SNAPSHOT, 'w', encoding='utf-8') as out:
|
||||
if ext in ALLOWED_EXTENSIONS and file not in EXCLUDE_FILES:
|
||||
filepath = os.path.join(root, file)
|
||||
out.write(f"## File: `{filepath}`\n")
|
||||
out.write("```" + (ext.replace('.', '') if ext != '.md' else '') + "\n")
|
||||
out.write("```" + ext.replace('.', '') + "\n")
|
||||
try:
|
||||
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
|
||||
out.write(f.read())
|
||||
@@ -28,4 +42,5 @@ with open(OUTPUT_SNAPSHOT, 'w', encoding='utf-8') as out:
|
||||
out.write(f"// Ошибка чтения файла: {e}\n")
|
||||
out.write("\n```\n\n")
|
||||
|
||||
print(f"✓ Успешно создан слепок проекта: {OUTPUT_SNAPSHOT} ({os.path.getsize(OUTPUT_SNAPSHOT):,} bytes)")
|
||||
size_kb = os.path.getsize(OUTPUT_SNAPSHOT) / 1024
|
||||
print(f"✓ Слепок успешно создан: {OUTPUT_SNAPSHOT} ({size_kb:.1f} KB)")
|
||||
Reference in New Issue
Block a user