🧠 Слой 5: Cognitive Core (Префронтальная кора)¶
Подробное описание архитектуры и работы¶
Статус: ✅ Реализовано (Этап F + F+ + P0/P1)
CognitiveCore orchestrator, GoalManager, Planner, HypothesisEngine, Reasoner,
ContradictionDetector, UncertaintyMonitor, ActionSelector — 190+7 тестов (unit+integration).
BM25 + Vector + Hybrid retrieval — 60 тестов (test_vector_retrieval.py).
SalienceEngine и PolicyLayer — запланированы (Этап H).
Что такое Префронтальная кора в биологии¶
Префронтальная кора (PFC) — самая «человеческая» часть мозга: - Планирование — постановка целей и декомпозиция на шаги - Рабочая память — удержание контекста во время рассуждения - Принятие решений — выбор между конкурирующими вариантами - Торможение — подавление импульсивных реакций - Самоконтроль — мониторинг собственных мыслей и ошибок - Абстрактное мышление — работа с понятиями, не привязанными к конкретике
Это «исполнительный директор» мозга — он не воспринимает и не запоминает, он управляет.
Роль в искусственном мозге¶
FusedPercept + MemorySearchResult
│
▼
┌─────────────────────────────────────────────────────────────┐
│ COGNITIVE CORE │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Planner │ │
│ │ GoalManager → декомпозиция → план шагов │ │
│ │ "ответить на вопрос" → [retrieve, reason, respond] │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ Reasoner │ │
│ │ causal / associative / analogical / deductive │ │
│ │ строит цепочку рассуждений с trace │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ ContradictionDetector │ │
│ │ два факта противоречат? → флаг + снизить confidence │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ UncertaintyMonitor │ │
│ │ confidence < threshold → запросить уточнение │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ SalienceEngine (Миндалина) │ │
│ │ срочность / угроза / новизна → приоритет │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ ActionSelector (Базальные ганглии) │ │
│ │ выбор финального действия из конкурирующих вариантов│ │
│ └──────────────────────┬───────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────────┘
│
▼
CognitiveResult
{action, reasoning_trace, confidence, next_goals}
│
▼
Learning Loop (Слой 6)
Output Layer (Слой 7)
Центральные структуры данных¶
Все компоненты Cognitive Core получают эти объекты вместо разрозненных параметров.
Разделение на 4 фокусированных структуры предотвращает "giant object" антипаттерн.
CognitiveContext — состояние текущего цикла¶
@dataclass
class CognitiveContext:
"""Состояние текущего цикла мышления. Передаётся всем компонентам."""
session_id: str # ID сессии (постоянный)
cycle_id: str # ID цикла (новый каждый тик)
trace_id: str # ID трассировки (для observability)
active_goal: Optional[Goal]
goal_chain: List[Goal] # цепочка от корневой до активной цели
ResourceState — ресурсное состояние¶
@dataclass
class ResourceState:
"""
Текущее состояние CPU/RAM. Обновляется ResourceMonitor каждый тик.
Пороги с гистерезисом (предотвращают флапание при нагрузке на границе):
Мягкий блок: soft_blocked=True при cpu_pct >= 70%, снимается при cpu_pct <= 60%
Жёсткий блок: ring2_allowed=False при cpu_pct >= 85%, снимается при cpu_pct <= 75%
"""
cpu_pct: float # загрузка CPU 0.0–100.0
ram_pct: float # загрузка RAM 0.0–100.0
ring2_allowed: bool # False если cpu_pct >= 85 или ram_pct >= 85
# (снимается только при cpu < 75% и ram < 75%)
soft_blocked: bool # True если cpu_pct >= 70% (мягкий режим)
# (снимается при cpu < 60%)
available_threads: int # свободные потоки для Ring 2
GoalTypeLimits + PolicyConstraints — ограничения поведения¶
@dataclass
class GoalTypeLimits:
"""Stop conditions для конкретного типа цели."""
step_limit: int # максимум шагов reasoning loop
time_limit_ms: float # максимум времени на цикл
confidence_threshold: float # порог уверенности для остановки
stability_window: int # кол-во итераций без изменений → стоп
# Стартовые значения (подлежат эмпирической калибровке):
GOAL_TYPE_LIMITS: Dict[str, GoalTypeLimits] = {
"answer_question": GoalTypeLimits(step_limit=3, time_limit_ms=200, confidence_threshold=0.75, stability_window=2),
"verify_claim": GoalTypeLimits(step_limit=7, time_limit_ms=500, confidence_threshold=0.80, stability_window=2),
"explore_topic": GoalTypeLimits(step_limit=10, time_limit_ms=800, confidence_threshold=0.70, stability_window=3),
"learn_fact": GoalTypeLimits(step_limit=3, time_limit_ms=150, confidence_threshold=0.70, stability_window=1),
}
@dataclass
class PolicyConstraints:
"""Ограничения поведения для текущего цикла."""
min_confidence: float = 0.4
max_retries: int = 2
goal_limits: GoalTypeLimits = field(default_factory=lambda: GOAL_TYPE_LIMITS["answer_question"])
ReasoningState — состояние цикла рассуждения¶
@dataclass
class ReasoningState:
"""Изменяемое состояние reasoning loop. Обновляется на каждой итерации."""
retrieved_evidence: List[EvidencePack]
active_hypotheses: List[Hypothesis]
contradiction_flags: List[str] # ID конфликтующих пар
current_confidence: float # лучший score среди гипотез
iteration: int # номер итерации (для stop conditions)
# Удобные поля для stop conditions и trace:
top_hypothesis_id: Optional[str] # ID гипотезы с лучшим score
best_score: float # score лучшей гипотезы
prev_best_score: float # score на предыдущей итерации (для stability check)
EvidencePack — структурированный объект доказательства¶
Reasoning работает не с "голыми фактами", а с объектами доказательства.
Это укрепляет retrieval, hypothesis generation, trace и contradiction analysis.
@dataclass
class EvidencePack:
evidence_id: str
content: str
memory_type: str # "working" | "semantic" | "episodic"
concept_refs: List[str] # концепты, упомянутые в доказательстве
source_refs: List[str] # источники
confidence: float # уверенность в факте 0.0–1.0
trust: float # доверие к источнику 0.0–1.0
timestamp: Optional[str] # ISO timestamp создания факта
modality: str # "text" | "image" | "audio"
contradiction_flags: List[str] # ID конфликтующих фактов
# Поля для scoring и retrieval:
relevance_score: float # семантическая близость к запросу 0.0–1.0
freshness_score: float # эвристика свежести (см. примечание ниже)
retrieval_stage: int # 1, 2 или 3 (из какой стадии retrieve_staged)
supports_hypotheses: List[str] # ID гипотез, которые это доказательство поддерживает
⚠️ Примечание о
freshness_score:
Простая формула1.0 / max(1, age_days)— рабочая эвристика, но грубая.
Старые, но верные факты (например, математические теоремы) не должны обесцениваться.
Конкретная функция свежести зависит от типа факта и источника и подлежит калибровке: - Научные факты: медленное затухание (×0.999 в день) - Новостные данные: быстрое затухание (×0.95 в день) - Пользовательский ввод: без затухания (freshness = 1.0 всегда)
CognitiveOutcome — коды завершения reasoning loop¶
Переименовано из
CognitiveFailure: enum содержит как коды ошибок, так и нормальные коды завершения (STOP_CONDITION_MET,GOAL_COMPLETED). Называть их все "Failure" семантически некорректно. Используется единый enum с явным разделением через комментарии.
class CognitiveOutcome(Enum):
"""
Коды завершения reasoning loop.
Используются в check_stop_conditions(), replan() и логах.
--- Нормальные завершения (не ошибки) ---
"""
STOP_CONDITION_MET = "stop_condition_met" # confidence достигнут / стабильность
GOAL_COMPLETED = "goal_completed" # цель выполнена успешно
STEP_LIMIT_REACHED = "step_limit_reached" # исчерпан step_limit цели (нормально)
# --- Ошибки и сбои ---
RETRIEVAL_FAILED = "retrieval_failed" # память не вернула результатов
NO_HYPOTHESIS_GENERATED = "no_hypothesis_generated" # шаблоны не дали гипотез
CONFLICTING_EVIDENCE = "conflicting_evidence" # противоречия не разрешены
INSUFFICIENT_CONFIDENCE = "insufficient_confidence" # confidence < min_threshold
RESOURCE_BLOCKED = "resource_blocked" # CPU/RAM > 85%
OUT_OF_SCOPE = "out_of_scope" # вне области знаний
REPLAN_LIMIT_REACHED = "replan_limit_reached" # превышен max_retries replan()
# Обратная совместимость (псевдоним для переходного периода):
CognitiveFailure = CognitiveOutcome
Паттерн проверки:
NORMAL_OUTCOMES = { CognitiveOutcome.STOP_CONDITION_MET, CognitiveOutcome.GOAL_COMPLETED, CognitiveOutcome.STEP_LIMIT_REACHED, } is_normal = outcome in NORMAL_OUTCOMES # True → завершение; False → replan()STEP_LIMIT_REACHED≠REPLAN_LIMIT_REACHED: -STEP_LIMIT_REACHED— reasoning loop законно исчерпал шаги (нормально) -REPLAN_LIMIT_REACHED—replan()вызывался слишком много раз (ошибка)
Компонент 1: Planner — Планировщик¶
Файл: brain/cognition/planner.py
Аналог: Дорсолатеральная префронтальная кора — планирование и цели
Принцип работы¶
Входящий стимул (вопрос, задача, событие)
│
▼
GoalManager.push(новая_цель)
│
▼
Planner.decompose(цель) → список подзадач
│
▼
ExecutionPlan = [шаг_1, шаг_2, ..., шаг_N]
│
▼
Выполнение шагов по очереди (или параллельно)
│
▼
GoalManager.complete() при завершении
Структура Goal¶
@dataclass
class Goal:
goal_id: str # "goal_a1b2c3"
description: str # "ответить на вопрос о нейронах"
goal_type: str # "answer_question" | "learn_fact" | "verify_claim"
# "explore_topic" | "plan"
# ⚠️ Используются полные имена — они же ключи в GOAL_TYPE_LIMITS
priority: float # 0.0–1.0 (выше → важнее)
deadline: Optional[float] # unix timestamp или None
parent_goal_id: Optional[str] # ID родительской цели; None для корневой цели
sub_goals: List[str] # ID дочерних целей
status: str # "pending" | "active" | "done" | "failed" | "cancelled"
created_at: str # ISO timestamp
context: Dict # дополнительный контекст
trace_id: str # для трассировки
Структура PlanStep¶
@dataclass
class PlanStep:
step_id: str # "step_001"
action: str # "retrieve_memory" | "reason" | "ask_user" | "respond"
description: str # "Найти факты о нейронах в памяти"
params: Dict # {"query": "нейрон", "top_n": 5}
depends_on: List[str] # ID шагов, которые должны выполниться раньше
status: str # "pending" | "running" | "done" | "failed"
result: Any # результат выполнения шага
duration_ms: float # время выполнения
# Retry и стоимость:
retry_count: int = 0 # сколько раз уже повторяли
max_retries: int = 2 # максимум повторов
step_cost: float = 0.0 # ожидаемые CPU×ms (для cost model)
failure_reason: Optional[str] = None # причина неудачи (CognitiveFailure)
Типы целей и их декомпозиция¶
"answer_question" (ответить на вопрос):
Шаг 1: retrieve_memory(query)
Шаг 2: check_confidence(results)
Шаг 3: если confidence < 0.5 → ask_clarification
иначе → reason(results)
Шаг 4: build_response(reasoning)
Шаг 5: store_episode(interaction)
"learn_fact" (запомнить факт):
Шаг 1: parse_fact(content)
Шаг 2: check_contradiction(fact)
Шаг 3: если противоречие → resolve_contradiction
Шаг 4: store_fact(semantic_memory)
Шаг 5: update_source_trust(source)
"verify_claim" (проверить утверждение):
Шаг 1: retrieve_related_facts(claim)
Шаг 2: cross_modal_check(claim, facts)
Шаг 3: detect_contradictions(facts)
Шаг 4: calculate_confidence(evidence)
Шаг 5: return_verdict(confidence, trace)
"explore_topic" (исследовать тему):
Шаг 1: get_concept_chain(topic)
Шаг 2: find_knowledge_gaps(chain)
Шаг 3: для каждого пробела → create_sub_goal("learn_fact")
Шаг 4: synthesize_knowledge(results)
GoalManager — менеджер целей¶
Важно: это не чистый стек. Структура данных — дерево целей с приоритетной очередью активных узлов.
НазваниеGoalStackбыло неточным — здесь есть приоритеты, иерархия и прерывания одновременно.
class GoalManager:
"""
Дерево целей с приоритетной очередью активных узлов.
Внутренняя структура:
goal_tree: Dict[str, Goal] — все цели (id → Goal)
active_queue: PriorityQueue[Goal] — активные цели по приоритету
interrupted_stack: List[Goal] — прерванные цели (LIFO)
completed: List[str] — завершённые ID
"""
def push(self, goal: Goal) -> None:
"""Добавить цель в дерево и активную очередь (с учётом приоритета)."""
def complete(self, goal_id: str) -> None:
"""Пометить цель как выполненную, убрать из active_queue."""
def fail(self, goal_id: str, reason: str) -> None:
"""Пометить цель как неудачную → передать в replan()."""
def peek(self) -> Optional[Goal]:
"""Вернуть текущую активную цель (с наивысшим приоритетом)."""
def interrupt(self, urgent_goal: Goal) -> None:
"""Прервать текущую цель: переместить в interrupted_stack, активировать срочную."""
def resume_interrupted(self) -> Optional[Goal]:
"""Возобновить последнюю прерванную цель (LIFO)."""
def get_active_chain(self) -> List[Goal]:
"""Вернуть цепочку от корневой до текущей активной цели."""
replan() — перепланирование при неудаче¶
def replan(self, failed_step: PlanStep, context: CognitiveContext,
failure: CognitiveFailure) -> Optional[ExecutionPlan]:
"""
Перепланировать после неудачи шага.
Возвращает новый план или None если replan невозможен.
Стратегии (в порядке приоритета):
"""
if failed_step.retry_count < failed_step.max_retries:
# Стратегия 1: RETRY — повторить тот же шаг
failed_step.retry_count += 1
return ExecutionPlan(steps=[failed_step])
if failure == CognitiveFailure.RETRIEVAL_FAILED:
# Стратегия 2: ALTERNATIVE — попробовать другой источник памяти
return _build_alternative_retrieval_plan(failed_step, context)
if failure == CognitiveFailure.INSUFFICIENT_CONFIDENCE:
# Стратегия 3: GATHER — добавить шаг сбора дополнительных данных
return _build_gather_evidence_plan(context)
if failure == CognitiveFailure.CONFLICTING_EVIDENCE:
# Стратегия 4: RESOLVE — добавить шаг разрешения противоречий
return _build_resolve_contradiction_plan(context)
# Стратегия 5: ESCALATE — передать в родительскую цель или отказаться
# replan() — метод класса Planner; self.goal_manager — зависимость через __init__
self.goal_manager.fail(context.active_goal.goal_id, reason=failure.value)
return None
Компонент 2: Reasoner — Рассуждатель¶
Файл: brain/cognition/reasoner.py
Аналог: Ассоциативная + префронтальная кора — построение умозаключений
Типы рассуждений¶
2.1 Ассоциативное рассуждение (Associative)¶
Вопрос: "Что такое нейрон?"
│
▼
retrieve("нейрон") → SemanticNode("нейрон", "клетка нервной системы")
│
▼
get_related("нейрон") → ["синапс", "аксон", "дендрит", "мозг"]
│
▼
get_concept_chain("нейрон", depth=3) → ["нейрон", "клетка", "организм"]
│
▼
Ответ: "Нейрон — это клетка нервной системы. Связан с: синапс, аксон, дендрит."
Trace: [нейрон → клетка → нервная система → организм]
2.2 Причинно-следственное рассуждение (Causal)¶
Вопрос: "Почему нейроны важны?"
│
▼
Найти факты с тегом "нейрон" + паттерн "потому что" / "поэтому" / "следовательно"
│
▼
Построить граф причинности:
нейрон → передаёт сигналы → мозг работает → мышление возможно
│
▼
Ответ: "Нейроны важны, потому что передают сигналы, что обеспечивает работу мозга."
Trace: [нейрон --causes--> передача_сигналов --enables--> мышление]
2.3 Аналогическое рассуждение (Analogical)¶
Вопрос: "Нейрон похож на что?"
│
▼
Найти структурно похожие понятия:
нейрон: принимает вход → обрабатывает → даёт выход
транзистор: принимает вход → обрабатывает → даёт выход
│
▼
Аналогия: "Нейрон похож на транзистор — оба принимают сигнал и передают его дальше."
Trace: [нейрон ≈ транзистор (структурная аналогия)]
⚠️ Ограничение аналогического рассуждения:
Аналогия — это гипотеза, а не вывод. Аналогическая гипотеза не должна получать высокийscoreбез подтверждающихEvidencePack. Вscore_hypothesis()аналогии типично имеют низкийevidence_coverage(нет прямых фактов "за") и должны конкурировать с ассоциативными и дедуктивными гипотезами. Без дополнительных доказательств аналогия остаётся в статусе"uncertain", а не"accepted".
2.4 Дедуктивное рассуждение (Deductive)¶
Факт 1: "Все нейроны — клетки"
Факт 2: "Все клетки содержат ДНК"
│
▼
Вывод: "Нейроны содержат ДНК"
Confidence: min(confidence_1, confidence_2) = min(0.95, 0.90) = 0.90
Trace: [нейрон is_a клетка, клетка has ДНК → нейрон has ДНК]
Структура ReasoningTrace¶
@dataclass
class ReasoningTrace:
trace_id: str # "trace_a1b2c3"
reasoning_type: str # "associative" | "causal" | "analogical" | "deductive"
query: str # исходный вопрос/задача
# Использованные источники
memory_refs: List[str] # ["semantic:нейрон", "episodic:ep_001"]
source_refs: List[str] # ["docs/нейрон.pdf#p1"]
# Цепочка рассуждений
steps: List[ReasoningStep] # пошаговый ход мысли
# Результат
conclusion: str # итоговый вывод
confidence: float # уверенность в выводе 0.0–1.0
# Метаданные
duration_ms: float # время рассуждения
created_at: str # timestamp
@dataclass
class ReasoningStep:
step_num: int # 1, 2, 3, ...
operation: str # "retrieve", "infer", "compare", "conclude"
input_refs: List[str] # что использовалось на этом шаге
output: str # что получилось
confidence: float # уверенность на этом шаге
Компонент 3: ContradictionDetector — Детектор противоречий¶
Файл: brain/cognition/contradiction_detector.py
Аналог: Передняя поясная кора — мониторинг конфликтов
Типы структурных конфликтов¶
Важно: детектор работает не через семантическое сходство (
sim < 0.2), а через структурный анализ пар фактов.
Семантическое сходство — ненадёжный прокси: два факта могут быть семантически близки, но не противоречить, и наоборот.
class ConflictType(Enum):
"""5 структурных типов конфликтов между фактами."""
OPPOSITE_POLARITY = "opposite_polarity"
# Прямое отрицание: "нейрон передаёт сигналы" vs "нейрон НЕ передаёт сигналы"
# Детекция: наличие отрицания (не, нет, never, not) при том же субъекте и предикате
CONFLICTING_VALUE = "conflicting_value"
# Разные значения одного атрибута: "скорость = 100 км/ч" vs "скорость = 200 км/ч"
# Детекция: одинаковый атрибут, разные числовые/категориальные значения
SOURCE_CONFLICT = "source_conflict"
# Разные источники дают несовместимые утверждения об одном факте
# Детекция: source_trust разница > 0.3 + несовместимые значения
RELATION_CONFLICT = "relation_conflict"
# Несовместимые отношения: "A is_a B" vs "A is_not_a B"
# Детекция: одинаковые субъект+объект, противоположные типы отношений
STALE_EVIDENCE = "stale_evidence"
# Устаревание, а не противоречие: "X = 5 (2020)" vs "X = 7 (2024)"
# Это НЕ конфликт — это обновление. Старый факт помечается как OUTDATED.
# Детекция: одинаковый атрибут + разные timestamps → победитель: более свежий
⚠️ Примечание о
STALE_EVIDENCE:
Устаревание — это не противоречие, а обновление знания.
Старый факт не удаляется, а помечаетсяstatus="outdated"и сохраняется для истории.
Это принципиально отличается отOPPOSITE_POLARITYилиCONFLICTING_VALUE.
Принцип работы¶
Новый факт F_new поступает в Cognitive Core
│
▼
Поиск существующих фактов о том же концепте в SemanticMemory
│
▼
Для каждой пары (F_new, F_old):
│
├── Проверка OPPOSITE_POLARITY:
│ same_subject + same_predicate + negation_marker?
│ → ConflictRecord(type=OPPOSITE_POLARITY)
│ → confidence обоих -= 0.15
│ → Goal("resolve_contradiction")
│
├── Проверка CONFLICTING_VALUE:
│ same_attribute + different_value?
│ → ConflictRecord(type=CONFLICTING_VALUE)
│ → confidence обоих -= 0.10
│
├── Проверка SOURCE_CONFLICT:
│ incompatible_values + trust_diff > 0.3?
│ → ConflictRecord(type=SOURCE_CONFLICT)
│ → победитель: более высокий trust
│
├── Проверка RELATION_CONFLICT:
│ same_subject + same_object + opposite_relation_type?
│ → ConflictRecord(type=RELATION_CONFLICT)
│ → confidence обоих -= 0.12
│
├── Проверка STALE_EVIDENCE:
│ same_attribute + different_timestamps?
│ → НЕ конфликт → F_old.status = "outdated"
│ → F_new становится актуальным
│
└── Нет конфликта → СОГЛАСОВАНЫ
→ confidence обоих += 0.02 (взаимное подтверждение)
Стратегии разрешения конфликтов¶
OPPOSITE_POLARITY → MODAL_CONSENSUS или HUMAN_OVERRIDE:
Текст говорит X, изображение показывает X, аудио говорит ¬X
→ 2 против 1 → победитель: большинство
→ Confidence = weighted_vote(text=0.4, image=0.4, audio=0.2)
CONFLICTING_VALUE → SOURCE_TRUST:
Факт из university.edu (trust=0.95) vs Факт из blog.com (trust=0.4)
→ Победитель: более доверенный источник
→ Слабый источник: trust -= 0.1
SOURCE_CONFLICT → SOURCE_TRUST (автоматически):
Победитель определяется по trust_score
RELATION_CONFLICT → HUMAN_OVERRIDE или DEFER:
Сложные случаи → пометить "requires_human_review"
STALE_EVIDENCE → TEMPORAL_OVERRIDE (автоматически):
Старый факт: status="outdated", не удаляется
Новый факт: становится актуальным
HUMAN_OVERRIDE (любой тип):
Пользователь явно указывает правильный ответ
→ source_trust("user_input") += 0.05
Компонент 4: UncertaintyMonitor — Монитор неопределённости¶
Файл: brain/cognition/uncertainty_monitor.py
Аналог: Орбитофронтальная кора — оценка рисков и неопределённости
Принцип работы¶
После каждого шага рассуждения:
│
▼
UncertaintyMonitor.evaluate(reasoning_trace)
│
├── confidence > 0.85 → HIGH_CONFIDENCE
│ → продолжить без уточнений
│
├── confidence > 0.60 → MEDIUM_CONFIDENCE
│ → добавить оговорку в ответ ("вероятно", "скорее всего")
│
├── confidence > 0.40 → LOW_CONFIDENCE
│ → запросить дополнительные данные из памяти
│ → если не найдено → сообщить о неопределённости
│
└── confidence < 0.40 → VERY_LOW_CONFIDENCE
→ отказаться от вывода
→ создать цель "gather_more_evidence"
Источники неопределённости¶
class UncertaintySource(Enum):
MISSING_DATA = "missing_data" # данных нет в памяти
CONTRADICTING = "contradicting" # противоречивые факты
LOW_SOURCE_TRUST = "low_source_trust" # ненадёжный источник
OUTDATED = "outdated" # устаревшие данные
MODAL_MISMATCH = "modal_mismatch" # модальности не согласованы
INFERENCE_CHAIN = "inference_chain" # длинная цепочка выводов
OUT_OF_SCOPE = "out_of_scope" # вне области знаний
Структура UncertaintyReport¶
@dataclass
class UncertaintyReport:
overall_confidence: float # итоговая уверенность
uncertainty_level: str # "high" | "medium" | "low" | "very_low"
sources: List[UncertaintySource] # причины неопределённости
missing_concepts: List[str] # каких знаний не хватает
recommended_action: str # "proceed" | "clarify" | "gather" | "refuse"
hedging_phrases: List[str] # ["вероятно", "скорее всего", "возможно"]
Компонент 5: SalienceEngine — Детектор значимости (Миндалина)¶
Файл: brain/cognition/salience_engine.py
Аналог: Миндалина (amygdala) — быстрая оценка угрозы и важности
Принцип работы¶
Новый стимул (PerceptEvent или FusedPercept)
│
▼
SalienceEngine.evaluate(stimulus)
│
├── Новизна (novelty):
│ Насколько это отличается от известного?
│ sim с WorkingMemory < 0.3 → HIGH NOVELTY → salience += 0.3
│
├── Срочность (urgency):
│ Есть ли временные маркеры? ("срочно", "немедленно", "сейчас")
│ → salience += 0.4
│
├── Угроза (threat):
│ Есть ли негативные маркеры? ("ошибка", "опасность", "критично")
│ → salience += 0.5
│
├── Релевантность (relevance):
│ Насколько связано с текущей активной целью?
│ sim с GoalManager.peek() > 0.7 → HIGH RELEVANCE → salience += 0.3
│
└── Итоговый salience score 0.0–1.0
│
├── salience > 0.8 → INTERRUPT (прервать текущую цель)
├── salience > 0.5 → PRIORITIZE (поднять в очереди)
└── salience < 0.5 → NORMAL (обычная обработка)
⚙️ Явная формула SalienceEngine¶
def compute_salience(stimulus: str, working_memory, active_goal) -> SalienceScore:
"""
Взвешенная сумма четырёх компонентов.
Веса в сумме = 1.0. Результат нормирован в [0.0, 1.0].
"""
# 1. Новизна: насколько стимул отличается от текущего контекста
if working_memory:
max_sim = max(semantic_similarity(stimulus, item.content)
for item in working_memory.get_context(n=10))
novelty = 1.0 - max_sim
else:
novelty = 1.0 # нет контекста → всё ново
# 2. Срочность: наличие временных маркеров
URGENCY_KEYWORDS = {"срочно", "немедленно", "сейчас", "urgent", "asap", "критично"}
urgency = 1.0 if any(kw in stimulus.lower() for kw in URGENCY_KEYWORDS) else 0.0
# 3. Угроза: наличие негативных маркеров
THREAT_KEYWORDS = {"ошибка", "опасность", "сбой", "error", "fail", "danger", "критическая"}
threat = 1.0 if any(kw in stimulus.lower() for kw in THREAT_KEYWORDS) else 0.0
# 4. Релевантность: связь с текущей активной целью
goal_relevance = (
semantic_similarity(stimulus, active_goal.description)
if active_goal else 0.5
)
# Взвешенная сумма (веса подобраны эмпирически)
salience = (
0.25 * novelty + # новизна важна, но не доминирует
0.35 * urgency + # срочность — главный триггер прерывания
0.25 * threat + # угроза требует реакции
0.15 * goal_relevance # релевантность — фоновый фильтр
)
return SalienceScore(
overall = min(1.0, salience),
novelty = novelty,
urgency = urgency,
threat = threat,
relevance = goal_relevance,
action = ("interrupt" if salience > 0.8 else
"prioritize" if salience > 0.5 else
"normal"),
reason = f"novelty={novelty:.2f} urgency={urgency:.2f} "
f"threat={threat:.2f} relevance={goal_relevance:.2f}",
)
Веса и их обоснование:
| Компонент | Вес | Обоснование |
|---|---|---|
novelty |
0.25 | Новизна важна, но мозг не должен отвлекаться на каждую мелочь |
urgency |
0.35 | Срочность — главный триггер прерывания текущей задачи |
threat |
0.25 | Угроза требует реакции, но встречается реже |
goal_relevance |
0.15 | Фоновый фильтр — не доминирует, но учитывается |
Структура SalienceScore¶
@dataclass
class SalienceScore:
overall: float # итоговый score 0.0–1.0
novelty: float # новизна
urgency: float # срочность
threat: float # угроза
relevance: float # релевантность текущей цели
action: str # "interrupt" | "prioritize" | "normal" | "ignore"
reason: str # объяснение оценки
Компонент 6: ActionSelector — Выбор действия (Базальные ганглии)¶
Файл: brain/cognition/action_selector.py
Аналог: Базальные ганглии — выбор действия из конкурирующих вариантов
Принцип работы¶
Planner предлагает N возможных действий:
[respond_directly, ask_clarification, search_more, refuse]
│
▼
ActionSelector.select(candidates, context)
│
├── Для каждого кандидата вычислить score (явная формула):
│ score(action) = 0.35*confidence + 0.30*goal_relevance
│ + 0.20*feasibility + 0.15*success_rate_history
│
├── Применить ограничения (constraints):
│ - не отвечать если confidence < 0.4
│ - не отказывать если есть хоть какие-то данные
│ - предпочитать действия с высоким success_rate (ProceduralMemory)
│
└── Выбрать действие с максимальным score
→ ActionDecision(action, score, reasoning)
Доступные действия¶
class ActionType(Enum):
RESPOND_DIRECT = "respond_direct" # ответить напрямую
RESPOND_HEDGED = "respond_hedged" # ответить с оговорками
ASK_CLARIFICATION = "ask_clarification" # запросить уточнение
SEARCH_MEMORY = "search_memory" # поискать ещё в памяти
GATHER_EVIDENCE = "gather_evidence" # запросить больше данных
REFUSE = "refuse" # отказаться отвечать
LEARN = "learn" # запомнить новый факт
CORRECT = "correct" # исправить ошибку
EXPLORE = "explore" # исследовать тему глубже
# Текстовые описания для semantic_similarity (ActionType — enum, поля .description нет)
ACTION_DESCRIPTIONS: Dict[ActionType, str] = {
ActionType.RESPOND_DIRECT: "дать прямой ответ на вопрос",
ActionType.RESPOND_HEDGED: "дать ответ с оговорками об уверенности",
ActionType.ASK_CLARIFICATION: "запросить уточнение у пользователя",
ActionType.SEARCH_MEMORY: "поискать дополнительные факты в памяти",
ActionType.GATHER_EVIDENCE: "собрать больше доказательств из источников",
ActionType.REFUSE: "отказаться отвечать из-за недостатка данных",
ActionType.LEARN: "запомнить новый факт в семантическую память",
ActionType.CORRECT: "исправить ошибочный факт в памяти",
ActionType.EXPLORE: "исследовать тему глубже через подцели",
}
⚙️ Явная формула ActionSelector¶
Соглашение о сигнатурах:
CognitiveContextсодержит только стабильный контекст цикла (session_id, cycle_id, trace_id, active_goal, goal_chain). Операционное состояние reasoning и сервисы передаются отдельными параметрами — это предотвращает "giant object" антипаттерн.
def score_action(action: ActionType,
ctx: CognitiveContext,
state: ReasoningState,
resources: ResourceState,
services: CognitiveServices) -> float:
"""
Utility = base_score - cost_penalty.
ctx — стабильный контекст цикла (goal, session, trace)
state — текущее состояние reasoning (confidence, hypotheses, ...)
resources — CPU/RAM состояние
services — доступ к памяти (procedural, semantic, source)
⚠️ Все веса и коэффициенты стартовые — подлежат эмпирической калибровке.
"""
confidence = state.best_score # из ReasoningState
goal_relevance = semantic_similarity(ACTION_DESCRIPTIONS[action],
ctx.active_goal.description) # ACTION_DESCRIPTIONS — mapping
feasibility = _check_feasibility(action, state, resources) # данные + ресурсы
success_rate = services.procedural_memory.get_success_rate(action.value) # из CognitiveServices
base_score = (
0.35 * confidence + # уверенность в результате — главный фактор
0.30 * goal_relevance + # насколько действие ведёт к цели
0.20 * feasibility + # выполнимо ли прямо сейчас
0.15 * success_rate # исторический success rate из ProceduralMemory
)
# Cost model: штраф за ресурсоёмкость
# CostEstimator — отдельный сервис в CognitiveServices (ResourceState — dataclass, не сервис)
estimated_cost = services.cost_estimator.estimate(action, resources) # нормировано 0.0–1.0
expected_info_gain = _estimate_info_gain(action, ctx, state) # 0.0–1.0
cost_penalty = 0.15 * estimated_cost - 0.10 * expected_info_gain
# Примечание: expected_info_gain вычитается из штрафа —
# дорогое действие оправдано, если оно даёт много новой информации.
return max(0.0, base_score - cost_penalty)
Веса и их обоснование:
| Фактор | Вес/коэф | Обоснование |
|---|---|---|
confidence |
0.35 | Главный фактор — нет смысла действовать без уверенности |
goal_relevance |
0.30 | Действие должно вести к цели |
feasibility |
0.20 | Нет данных/ресурсов → действие невозможно |
success_rate |
0.15 | Исторический опыт из ProceduralMemory |
estimated_cost |
−0.15 | Штраф за ресурсоёмкость (CPU×ms) |
expected_info_gain |
+0.10 | Компенсация штрафа при высокой информативности |
⚠️ Все коэффициенты стартовые — подлежат эмпирической калибровке.
Унификация: везде используется
RESPOND_HEDGED(ответить с оговорками).
ТерминRESPOND_PARTIALне используется — устаревший синоним.
Вспомогательные эвристики _check_feasibility() и _estimate_info_gain()¶
def _check_feasibility(action: ActionType, state: ReasoningState,
resources: ResourceState) -> float:
"""
Оценивает выполнимость действия прямо сейчас. Возвращает 0.0–1.0.
RESPOND_DIRECT / RESPOND_HEDGED → state.best_score
(можно отвечать только если есть уверенность)
GATHER_EVIDENCE / EXPLORE → 1.0 если ring2_allowed, иначе 0.0
(дорогие действия невозможны при жёстком ресурсном блоке)
SEARCH_MEMORY → 1.0 если retrieved_evidence пусто, иначе 0.5
(поиск всегда возможен, но менее приоритетен если данные уже есть)
ASK_CLARIFICATION / REFUSE / LEARN / CORRECT → 1.0
(всегда выполнимы, не требуют ресурсов Ring 2)
"""
def _estimate_info_gain(action: ActionType, ctx: CognitiveContext,
state: ReasoningState) -> float:
"""
Оценивает ожидаемый прирост информации от действия. Возвращает 0.0–1.0.
GATHER_EVIDENCE → 1.0 - evidence_coverage (чем меньше покрытие, тем выше gain)
SEARCH_MEMORY → 1.0 если retrieved_evidence пусто, иначе 0.3
EXPLORE → 1.0 - knowledge_coverage(ctx.active_goal) из SemanticMemory
RESPOND_DIRECT / RESPOND_HEDGED → 0.0 (ответ не добавляет новых знаний)
ASK_CLARIFICATION → 0.6 (пользователь может дать новую информацию)
LEARN / CORRECT → 0.8 (прямое обновление памяти)
REFUSE → 0.0
"""
Структура ActionDecision¶
@dataclass
class ActionDecision:
action: ActionType # выбранное действие
score: float # итоговый score 0.0–1.0
confidence: float # уверенность в правильности выбора
reasoning: str # почему выбрано это действие
alternatives: List[dict] # другие рассмотренные варианты
trace_id: str # для трассировки
Компонент 7: HypothesisEngine — Генератор гипотез¶
Файл: brain/cognition/hypothesis_engine.py
Аналог: Префронтальная кора + Гиппокамп — генерация и проверка гипотез
Ключевой компонент мышления. Без гипотез — нет рассуждения, только поиск.
Гипотеза = кандидат на вывод, который нужно подтвердить или опровергнуть.
Принцип работы (Reasoning Loop)¶
retrieve(query) → top-K фактов из памяти
│
▼
generate_hypotheses(facts) → список гипотез H1, H2, H3...
│
├── H1: "нейрон → передаёт сигналы" (из факта A)
├── H2: "нейрон → часть нервной системы" (из факта B)
└── H3: "нейрон ≈ транзистор" (аналогия)
│
▼
score_hypotheses([H1, H2, H3]) → ранжированный список
│
▼
select_best(top_n=2) → [H1(0.82), H2(0.71)]
│
▼
act(best_hypotheses) → CognitiveResult
⚙️ Явная формула scoring гипотез¶
Формула разделена на support_score (что "за") и risk_score (что "против").
Итоговый score =max(0.0, support_score - risk_score).
Это устраняет двойной штраф за противоречия (coherence + penalty одновременно).
def score_hypothesis(H: Hypothesis, memory_manager, source_memory) -> float:
"""
Раздельный расчёт support и risk. Итог: max(0.0, support - risk).
⚠️ Веса стартовые — подлежат эмпирической калибровке по реальным данным.
"""
# === SUPPORT SCORE (что "за" гипотезу) ===
# 1. Покрытие доказательствами: доля фактов, поддерживающих гипотезу
# candidate_facts — факты после фильтрации по relevance_score > 0.3.
# Не "все факты из памяти", а только релевантные кандидаты для данной гипотезы.
evidence_coverage = len(H.supporting_facts) / max(len(H.candidate_facts), 1)
# 2. Доверие к источникам: среднее trust_score источников
source_trust = mean(
source_memory.get_trust(ref) for ref in H.source_refs
) if H.source_refs else 0.5
# 3. Релевантность: насколько доказательства близки к запросу
relevance_score = mean(
e.relevance_score for e in H.evidence_packs
) if H.evidence_packs else 0.5
support_score = (
0.45 * evidence_coverage + # главный фактор — покрытие фактами
0.35 * source_trust + # надёжность источников
0.20 * relevance_score # близость к запросу
)
# === RISK SCORE (что "против" гипотезы) ===
# 1. Вес противоречий: нормированное кол-во конфликтов
contradiction_weight = min(1.0, len(H.conflict_ids) * 0.25)
# 2. Устаревание: насколько старые доказательства
staleness = 1.0 - mean(
e.freshness_score for e in H.evidence_packs
) if H.evidence_packs else 0.5
# 3. Несоответствие модальностей: разные модальности дают разные ответы
modality_mismatch = _compute_modality_mismatch(H.evidence_packs)
# 4. Длина цепочки вывода: чем длиннее — тем ненадёжнее
inference_chain_len = len(H.reasoning_steps) / 10.0 # нормировано к [0, 1]
risk_score = (
0.40 * contradiction_weight + # противоречия — главный риск
0.30 * staleness + # устаревшие данные
0.20 * modality_mismatch + # несогласованность модальностей
0.10 * inference_chain_len # длинная цепочка вывода
)
return max(0.0, support_score - risk_score)
Веса и их обоснование:
| Компонент | Вес | Обоснование |
|---|---|---|
support: evidence_coverage |
0.45 | Главный фактор — покрытие фактами |
support: source_trust |
0.35 | Надёжность источников |
support: relevance_score |
0.20 | Близость к запросу |
risk: contradiction_weight |
0.40 | Противоречия — главный риск |
risk: staleness |
0.30 | Устаревшие данные снижают надёжность |
risk: modality_mismatch |
0.20 | Несогласованность модальностей |
risk: inference_chain_len |
0.10 | Длинная цепочка вывода ненадёжна |
⚠️ Все веса стартовые — подлежат эмпирической калибровке по реальным данным и тестам.
Структура Hypothesis¶
@dataclass
class Hypothesis:
hypothesis_id: str # "hyp_a1b2c3"
statement: str # "нейрон передаёт электрические сигналы"
hypothesis_type: str # "causal" | "associative" | "analogical" | "deductive"
# Доказательная база
supporting_facts: List[str] # ссылки на факты "за"
opposing_facts: List[str] # ссылки на факты "против"
candidate_facts: List[str] # факты-кандидаты для данной гипотезы
# (только после фильтрации по relevance_score > 0.3)
# ⚠️ Переименовано из all_retrieved_facts — старое имя
# вводило в заблуждение ("все факты из памяти")
source_refs: List[str] # источники
evidence_packs: List[EvidencePack] # структурированные доказательства
conflict_ids: List[str] # ID конфликтов из ContradictionDetector
reasoning_steps: List[str] # шаги вывода (для inference_chain_len)
# Оценка (раздельная)
score: float # итоговый score = max(0, support - risk)
support_score: float # компонент: поддержка
risk_score: float # компонент: риск
# Статус
status: str # "candidate" | "accepted" | "rejected" | "uncertain"
created_at: str
trace_id: str
Генерация гипотез (без ML)¶
def generate_hypotheses(query: str, facts: List[SemanticNode]) -> List[Hypothesis]:
"""
Генерация гипотез из фактов через шаблоны.
Не требует нейросетей — работает на CPU-only.
"""
hypotheses = []
for fact in facts:
# Шаблон 1: прямое утверждение
hypotheses.append(Hypothesis(
statement=f"{fact.concept} {fact.description}",
hypothesis_type="associative",
supporting_facts=[fact.concept],
))
# Шаблон 2: причинно-следственная связь
for relation in fact.relations:
if relation.relation_type in ("causes", "enables", "leads_to"):
hypotheses.append(Hypothesis(
statement=f"{fact.concept} → {relation.target}",
hypothesis_type="causal",
supporting_facts=[fact.concept, relation.target],
))
# Шаблон 3: аналогия (структурное сходство)
similar = semantic_memory.search(fact.concept, top_n=3)
for similar_node in similar:
if similar_node.concept != fact.concept:
hypotheses.append(Hypothesis(
statement=f"{fact.concept} ≈ {similar_node.concept}",
hypothesis_type="analogical",
supporting_facts=[fact.concept, similar_node.concept],
))
return hypotheses
🔄 Reasoning Loop — Полный цикл рассуждения¶
Это центральный алгоритм Cognitive Core. Каждый цикл мышления проходит 5 шагов:
┌─────────────────────────────────────────────────────────────┐
│ REASONING LOOP │
│ │
│ Шаг 1: RETRIEVE │
│ ───────────────────────────────────────────────────────── │
│ query → MemoryManager.retrieve_staged(query) │
│ Stage 1: broad search → top-20 кандидатов │
│ Stage 2: filter by trust/confidence/modality │
│ Stage 3: rerank by goal_relevance + graph_distance │
│ → top-K фактов + эпизодов + контекста │
│ │
│ Шаг 2: GENERATE HYPOTHESES │
│ ───────────────────────────────────────────────────────── │
│ HypothesisEngine.generate(query, retrieved_facts) │
│ → [H1, H2, H3, ...] через шаблоны (без ML) │
│ │
│ Шаг 3: SCORE │
│ ───────────────────────────────────────────────────────── │
│ для каждой гипотезы Hi: │
│ support = 0.45*evidence_coverage + 0.35*source_trust │
│ + 0.20*relevance_score │
│ risk = 0.40*contradiction_weight + 0.30*staleness │
│ + 0.20*modality_mismatch + 0.10*chain_len │
│ score(Hi) = max(0.0, support - risk) │
│ → ранжированный список гипотез │
│ │
│ Шаг 4: SELECT │
│ ───────────────────────────────────────────────────────── │
│ best = top-N(scored_hypotheses) │
│ UncertaintyMonitor.evaluate(best.score) │
│ ContradictionDetector.check(best.statement) │
│ → ActionDecision через ActionSelector │
│ │
│ Шаг 5: ACT │
│ ───────────────────────────────────────────────────────── │
│ respond / ask_clarification / gather_evidence / explore │
│ → CognitiveResult с полным trace │
└─────────────────────────────────────────────────────────────┘
Ключевой принцип: мышление = не "умный код", а измеримый процесс: - каждый шаг имеет входные данные и выходные данные - каждая гипотеза имеет числовой score с объяснением - каждое решение трассируется до источников
🔁 Two-Ring Reasoning — Двухкольцевое рассуждение¶
Идея: не все запросы требуют одинаковых вычислительных затрат.
Дешёвое кольцо обрабатывает большинство случаев. Дорогое — только когда нужно.
┌─────────────────────────────────────────────────────────────┐
│ TWO-RING REASONING │
│ │
│ RING 1 — FAST (дешёвое, детерминированное): │
│ ───────────────────────────────────────────────────────── │
│ • Associative reasoning (BFS по семантическому графу) │
│ • Deductive reasoning (силлогизмы из фактов) │
│ • Contradiction scan (быстрая проверка конфликтов) │
│ • Latency: ~10–50 мс | CPU: 1 поток | RAM: ~10 MB │
│ │
│ Запускается ВСЕГДА для каждого запроса. │
│ │
│ Если Ring 1 даёт confidence >= 0.75 и нет противоречий: │
│ → сразу переходим к ActionSelector │
│ → Ring 2 не запускается │
│ │
│ RING 2 — DEEP (дорогое, запускается по условию): │
│ ───────────────────────────────────────────────────────── │
│ • Causal reasoning (граф причинности) │
│ • Analogical reasoning (структурные аналогии) │
│ • HypothesisEngine (генерация + scoring гипотез) │
│ • Multi-stage retrieval (retrieve_staged) │
│ • Latency: ~100–500 мс | CPU: 2–4 потока | RAM: ~50 MB │
│ │
│ Запускается ТОЛЬКО при выполнении хотя бы одного условия: │
│ • confidence < 0.60 (низкая уверенность) │
│ • contradictions > 0 (обнаружены противоречия) │
│ • goal_type == "verify_claim" (проверка утверждения) │
│ • goal_type == "explore_topic" (исследование темы) │
│ • salience.novelty > 0.7 (высокая новизна) │
│ • ctx.active_goal.priority > 0.8 (высокий приоритет цели)│
└─────────────────────────────────────────────────────────────┘
Алгоритм выбора кольца:
def select_ring(query: str, ctx: CognitiveContext,
salience: SalienceScore) -> int:
"""
Возвращает 1 (fast) или 2 (deep).
По умолчанию — Ring 1. Ring 2 только при явных триггерах.
Принимает salience отдельно — CognitiveContext не содержит SalienceScore,
это операционный результат SalienceEngine текущего цикла.
"""
# Быстрая проверка Ring 1
ring1_result = ring1_reason(query, ctx)
# Условия перехода в Ring 2
triggers = [
ring1_result.confidence < 0.60,
len(ring1_result.contradictions) > 0,
ctx.active_goal.goal_type in ("verify_claim", "explore_topic"),
salience.novelty > 0.7, # salience передаётся явно
ctx.active_goal.priority > 0.8,
]
if any(triggers):
return 2 # запустить глубокое рассуждение
return 1 # достаточно быстрого результата
Ресурсный профиль:
| Кольцо | Типичный случай | Latency | CPU | RAM |
|---|---|---|---|---|
| Ring 1 | Простой вопрос, известный факт | 10–50 мс | 1 поток | ~10 MB |
| Ring 2 | Проверка утверждения, новая тема | 100–500 мс | 2–4 потока | ~50 MB |
Связь с ResourceMonitor (пороги с гистерезисом):
CPU >= 85% или RAM >= 85% (жёсткий блок, ring2_allowed = False):
→ Ring 2 заблокирован (DegradationPolicy)
→ Только Ring 1 + UncertaintyMonitor.flag("resource_constrained")
→ Ответ с оговоркой: "Ограниченные ресурсы — возможна неполная проверка"
→ Снимается только при CPU <= 75% и RAM <= 75% (гистерезис)
CPU >= 70% (мягкий блок, soft_blocked = True):
→ GATHER_EVIDENCE и EXPLORE заблокированы в PolicyLayer (Фильтр 2)
→ Ring 2 ещё разрешён, но дорогие действия недоступны
→ Снимается при CPU <= 60% (гистерезис)
🛑 Stop Conditions — Условия остановки reasoning loop¶
Reasoning loop не должен работать бесконечно.
Условия остановки зависят от типа цели — разные цели требуют разных критериев.
Цель-специфичные условия (из GoalTypeLimits)¶
| Тип цели | step_limit | time_limit | confidence | stability_window |
|---|---|---|---|---|
answer_question |
3 | 200 мс | ≥ 0.75 | 2 итерации |
verify_claim |
7 | 500 мс | ≥ 0.80 | 2 итерации |
explore_topic |
10 | 800 мс | ≥ 0.70 | 3 итерации |
learn_fact |
3 | 150 мс | ≥ 0.70 | 1 итерация |
⚠️ Все значения стартовые — подлежат эмпирической калибровке.
Алгоритм проверки stop conditions¶
def check_stop_conditions(state: ReasoningState, limits: GoalTypeLimits,
resources: ResourceState) -> Optional[CognitiveFailure]:
"""
Проверяет все условия остановки. Возвращает причину или None (продолжать).
Порядок проверки: ресурсы → лимиты → качество → стабильность.
"""
# 1. Ресурсный блок (приоритет 1 — немедленная остановка)
if resources.cpu_pct > 85 or resources.ram_pct > 85:
return CognitiveFailure.RESOURCE_BLOCKED
# 2. Лимит шагов (нормальная остановка — не ошибка)
if state.iteration >= limits.step_limit:
return CognitiveOutcome.STEP_LIMIT_REACHED
# 3. Лимит времени (проверяется через elapsed_ms в контексте)
# → обрабатывается в основном цикле через time.monotonic()
# 4. Достигнут порог уверенности
if state.best_score >= limits.confidence_threshold:
return CognitiveFailure.STOP_CONDITION_MET
# 5. Стабильность: score не меняется N итераций подряд
score_delta = abs(state.best_score - state.prev_best_score)
if score_delta < 0.01 and state.iteration >= limits.stability_window:
return CognitiveFailure.STOP_CONDITION_MET
# 6. Нет новых доказательств
if not state.retrieved_evidence:
return CognitiveFailure.RETRIEVAL_FAILED
# 7. Нет гипотез
if not state.active_hypotheses:
return CognitiveFailure.NO_HYPOTHESIS_GENERATED
return None # продолжать reasoning
Универсальные условия (для всех типов целей)¶
no_new_evidence: retrieve вернул 0 результатов → RETRIEVAL_FAILED
resource_blocked: CPU > 85% или RAM > 85% → RESOURCE_BLOCKED
replan_limit: retry_count >= max_retries → REPLAN_LIMIT_REACHED
🔒 PolicyLayer — Слой политик поведения¶
PolicyLayer — это фильтр + модификатор между scoring и финальным выбором действия.
Он не выбирает действие, но может заблокировать или скорректировать его score.
Файл: brain/cognition/policy_layer.py
Фильтры (блокировка действий)¶
def apply_filters(candidates: List[ActionType], state: ReasoningState,
resources: ResourceState, constraints: PolicyConstraints,
outcome: Optional[CognitiveOutcome] = None) -> List[ActionType]:
"""
Убирает недопустимые действия из списка кандидатов.
"""
# Жёсткий блок при RESOURCE_BLOCKED: дорогие действия запрещены полностью
RESOURCE_BLOCKED_ACTIONS = {ActionType.GATHER_EVIDENCE, ActionType.EXPLORE}
resource_blocked = (
outcome == CognitiveOutcome.RESOURCE_BLOCKED
or resources.cpu_pct > 85
or resources.ram_pct > 85
)
filtered = []
for action in candidates:
# Фильтр 0: жёсткий блок при перегрузке ресурсов
if resource_blocked and action in RESOURCE_BLOCKED_ACTIONS:
continue # жёстко заблокировано (не штраф, а запрет)
# Фильтр 1: не отвечать напрямую при низкой уверенности
if action == ActionType.RESPOND_DIRECT and state.best_score < constraints.min_confidence:
continue # заблокировано
# Фильтр 2: мягкий блок GATHER_EVIDENCE при умеренной нагрузке CPU
# Используем resources.soft_blocked (cpu >= 70%, гистерезис: снимается при < 60%)
# Зазор 15% до жёсткого блока (85%) предотвращает флапание
if action == ActionType.GATHER_EVIDENCE and resources.soft_blocked:
continue # мягко заблокировано
filtered.append(action)
return filtered
Модификаторы utility (корректировка score)¶
def apply_modifiers(scores: Dict[ActionType, float], state: ReasoningState,
resources: ResourceState,
services: CognitiveServices) -> Dict[ActionType, float]:
"""
Корректирует score действий на основе контекста.
⚠️ Все дельты стартовые — подлежат эмпирической калибровке.
"""
modified = dict(scores)
# Модификатор 1: штраф за дорогие действия при умеренной нагрузке CPU
# Используем resources.soft_blocked (cpu >= 70%, гистерезис: снимается при < 60%)
# (CostEstimator — сервис в CognitiveServices, ResourceState — только данные)
if resources.soft_blocked:
for action in [ActionType.GATHER_EVIDENCE, ActionType.EXPLORE]:
if action in modified:
cost = services.cost_estimator.estimate(action, resources)
modified[action] -= 0.15 * cost
# Модификатор 2: буст ASK_CLARIFICATION при наличии противоречий
if state.contradiction_flags:
if ActionType.ASK_CLARIFICATION in modified:
modified[ActionType.ASK_CLARIFICATION] += 0.20
# Модификатор 3: буст RESPOND_HEDGED при умеренной уверенности
if 0.5 <= state.best_score <= 0.7:
if ActionType.RESPOND_HEDGED in modified:
modified[ActionType.RESPOND_HEDGED] += 0.15
return modified
Унификация терминологии: везде используется
RESPOND_HEDGED(ответить с оговорками).
ТерминRESPOND_PARTIALне используется — это был устаревший синоним.
Выходной формат: CognitiveResult¶
@dataclass
class CognitiveResult:
# Входные данные
input_query: str # исходный запрос
session_id: str # ID сессии
cycle_id: str # ID цикла мышления
# Результат
action: ActionDecision # выбранное действие
response: str # текстовый ответ (если есть)
confidence: float # итоговая уверенность
# Трассировка
reasoning_trace: ReasoningTrace # полная цепочка рассуждений
goal_chain: List[Goal] # цепочка целей
memory_refs: List[str] # использованные факты из памяти
source_refs: List[str] # использованные источники
# Мета
contradictions: List[ContradictionRecord] # найденные противоречия
uncertainty: UncertaintyReport # отчёт о неопределённости
salience: SalienceScore # оценка значимости
# Производительность
total_duration_ms: float # общее время обработки
created_at: str # timestamp
Полный цикл мышления¶
Вопрос: "Почему нейроны важны для мышления?"
│
▼
[SalienceEngine]
novelty=0.3, urgency=0.0, threat=0.0, relevance=0.8
→ salience=0.55 → PRIORITIZE
│
▼
[Planner]
Goal: "answer_question(почему нейроны важны)"
Plan:
Шаг 1: retrieve_memory("нейрон", "мышление")
Шаг 2: reason_causal(results)
Шаг 3: check_confidence
Шаг 4: build_response
│
▼
[Шаг 1: retrieve_memory]
WorkingMemory: [] (нет в контексте)
SemanticMemory: [нейрон→клетка, нейрон→синапс, мышление→нейроны]
EpisodicMemory: [ep_001: "нейрон передаёт сигналы"]
│
▼
[Reasoner: causal]
нейрон → передаёт_сигналы → мозг_работает → мышление_возможно
Trace: [нейрон --causes--> сигналы --enables--> мышление]
Confidence: 0.78
│
▼
[ContradictionDetector]
Нет противоречий → OK
│
▼
[UncertaintyMonitor]
confidence=0.78 → MEDIUM_CONFIDENCE
→ добавить "вероятно" в ответ
│
▼
[ActionSelector]
Кандидаты: [respond_hedged(0.78), ask_clarification(0.3)]
→ Выбор: respond_hedged
│
▼
CognitiveResult(
response="Нейроны важны для мышления, потому что они передают
электрические сигналы между клетками мозга, что
обеспечивает работу нейронных сетей.",
confidence=0.78,
reasoning_trace=[нейрон→сигналы→мышление],
memory_refs=["semantic:нейрон", "semantic:мышление"],
uncertainty=UncertaintyReport(level="medium", hedging=["вероятно"])
)
Человекочитаемый digest (Observability)¶
Каждый цикл мышления производит digest для логирования:
Cycle 4521
Goal: answer_question("почему нейроны важны")
Reasoning: causal [нейрон → сигналы → мышление]
Memory used: semantic:нейрон, semantic:мышление, episodic:ep_001
Sources: docs/нейробиология.pdf (trust=0.87)
Contradiction: none
Confidence: 0.78 (medium) → hedged response
Action: respond_hedged
Duration: 142ms
CPU: 45% | RAM: 3.8 GB
Ресурсный бюджет (CPU-only, 32 GB RAM)¶
| Компонент | RAM | CPU | Время/цикл |
|---|---|---|---|
| Planner + GoalManager | ~5 MB | 1 поток | ~1–5 мс |
| Reasoner (associative) | ~10 MB | 1–2 потока | ~10–50 мс |
| Reasoner (causal/deductive) | ~10 MB | 1–2 потока | ~20–100 мс |
| ContradictionDetector | ~5 MB | 1 поток | ~5–20 мс |
| UncertaintyMonitor | ~1 MB | 1 поток | < 1 мс |
| SalienceEngine | ~2 MB | 1 поток | ~1–5 мс |
| ActionSelector | ~1 MB | 1 поток | < 1 мс |
| Итого | ~35 MB | 2–4 потока | ~40–180 мс |
Статус реализации¶
| Компонент | Статус | Файл |
|---|---|---|
Goal + PlanStep dataclasses |
✅ Этап F | brain/cognition/planner.py |
GoalManager |
✅ Этап F | brain/cognition/planner.py |
Planner + replan() |
✅ Этап F | brain/cognition/planner.py |
ReasoningTrace + ReasoningStep |
✅ Этап F | brain/cognition/reasoner.py |
Reasoner (associative) |
✅ Этап F | brain/cognition/reasoner.py |
Reasoner (causal/deductive/analogical) |
✅ Этап F | brain/cognition/reasoner.py |
ContradictionDetector + ConflictType |
✅ Этап F+ | brain/cognition/contradiction_detector.py |
UncertaintyMonitor + UncertaintyReport |
✅ Этап F+ | brain/cognition/uncertainty_monitor.py |
SalienceEngine + SalienceScore |
⬜ Этап H | brain/cognition/salience_engine.py |
ActionSelector + cost model |
✅ Этап F | brain/cognition/action_selector.py |
HypothesisEngine + Hypothesis |
✅ Этап F | brain/cognition/hypothesis_engine.py |
CognitiveResult |
✅ Этап F | brain/cognition/__init__.py |
CognitiveContext + ResourceState |
✅ Этап F | brain/cognition/context.py |
EvidencePack + ReasoningState |
✅ Этап F | brain/cognition/context.py |
GoalTypeLimits + PolicyConstraints |
✅ Этап F | brain/cognition/context.py |
check_stop_conditions() |
✅ Этап F | brain/cognition/planner.py |
PolicyLayer (filters + modifiers) |
⬜ Этап H | brain/cognition/policy_layer.py |
CognitiveOutcome enum (+ CognitiveFailure alias) |
✅ Этап F | brain/cognition/context.py |