WIP: memory system improvements (in progress)
Interview/chat prompt layers, reply planner, style profiles, memory injection, interview meta store, and related tests. Work not finished. Made-with: Cursor
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Dict, List, cast
|
||||
from typing import Dict, List
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
@@ -15,6 +15,7 @@ from app.agents.stage_constants import (
|
||||
normalize_chat_stage,
|
||||
)
|
||||
from app.agents.state_schema import (
|
||||
InterviewControlState,
|
||||
KnownFact,
|
||||
MemoirStateSchema,
|
||||
PersonaThread,
|
||||
@@ -22,10 +23,9 @@ from app.agents.state_schema import (
|
||||
default_state,
|
||||
)
|
||||
from app.core.config import settings
|
||||
from app.features.memoir import _interview_meta_store as interview_meta
|
||||
from app.features.memoir.models import MemoirState as MemoirStateModel
|
||||
|
||||
_INTERVIEW_STATE_META_KEY = "__interview_state__"
|
||||
|
||||
|
||||
def _slots_snapshot_for_merge(raw: Dict[str, Dict] | None) -> Dict[str, Dict]:
|
||||
"""浅拷贝 slots,避免就地改 JSON 列同一 dict 引用导致 ORM 不标记 dirty。"""
|
||||
@@ -34,67 +34,20 @@ def _slots_snapshot_for_merge(raw: Dict[str, Dict] | None) -> Dict[str, Dict]:
|
||||
return {k: dict(v or {}) for k, v in raw.items()}
|
||||
|
||||
|
||||
def _extract_interview_state_meta(
|
||||
raw_slots: Dict[str, Dict] | None,
|
||||
) -> tuple[list[KnownFact], list[PersonaThread], list[str]]:
|
||||
if not raw_slots or not isinstance(raw_slots, dict):
|
||||
return [], [], []
|
||||
meta = raw_slots.get(_INTERVIEW_STATE_META_KEY)
|
||||
if not isinstance(meta, dict):
|
||||
return [], [], []
|
||||
known = meta.get("known_facts") if isinstance(meta.get("known_facts"), list) else []
|
||||
persona = (
|
||||
meta.get("persona_threads")
|
||||
if isinstance(meta.get("persona_threads"), list)
|
||||
else []
|
||||
)
|
||||
recent = (
|
||||
meta.get("recent_questions")
|
||||
if isinstance(meta.get("recent_questions"), list)
|
||||
else []
|
||||
)
|
||||
return (
|
||||
[KnownFact.model_validate(x) for x in known if isinstance(x, dict)],
|
||||
[PersonaThread.model_validate(x) for x in persona if isinstance(x, dict)],
|
||||
[str(x).strip() for x in recent if str(x).strip()],
|
||||
)
|
||||
|
||||
|
||||
def _inject_interview_state_meta(
|
||||
*,
|
||||
slots: Dict[str, Dict],
|
||||
known_facts: list[KnownFact],
|
||||
persona_threads: list[PersonaThread],
|
||||
recent_questions: list[str],
|
||||
) -> Dict[str, Dict]:
|
||||
out = dict(slots)
|
||||
out[_INTERVIEW_STATE_META_KEY] = cast(
|
||||
Dict,
|
||||
{
|
||||
"known_facts": [x.model_dump() for x in known_facts],
|
||||
"persona_threads": [x.model_dump() for x in persona_threads],
|
||||
"recent_questions": list(recent_questions),
|
||||
},
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def coerce_memoir_state(model: MemoirStateModel) -> MemoirStateSchema:
|
||||
"""把 ORM 行投影成 MemoirStateSchema;控制元数据的读法已隔离在 interview_meta 适配层。"""
|
||||
raw_slots = model.slots if isinstance(model.slots, dict) else None
|
||||
known_facts, persona_threads, recent_questions = _extract_interview_state_meta(
|
||||
raw_slots
|
||||
)
|
||||
clean_slots = dict(raw_slots) if raw_slots else dict(default_state().slots)
|
||||
clean_slots.pop(_INTERVIEW_STATE_META_KEY, None)
|
||||
control = interview_meta.read(raw_slots)
|
||||
clean_slots = interview_meta.strip(raw_slots) or dict(default_state().slots)
|
||||
return MemoirStateSchema.model_validate(
|
||||
{
|
||||
"stage_order": model.stage_order or default_state().stage_order,
|
||||
"current_stage": model.current_stage,
|
||||
"covered_stages": model.covered_stages or [],
|
||||
"slots": clean_slots,
|
||||
"known_facts": known_facts,
|
||||
"persona_threads": persona_threads,
|
||||
"recent_questions": recent_questions,
|
||||
"known_facts": control.known_facts,
|
||||
"persona_threads": control.persona_threads,
|
||||
"recent_questions": control.recent_questions,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -270,11 +223,13 @@ async def save_interview_state_meta(
|
||||
slots = _slots_snapshot_for_merge(
|
||||
state.slots if isinstance(state.slots, dict) else None
|
||||
)
|
||||
state.slots = _inject_interview_state_meta(
|
||||
slots=slots,
|
||||
known_facts=known_facts,
|
||||
persona_threads=persona_threads,
|
||||
recent_questions=recent_questions,
|
||||
state.slots = interview_meta.write(
|
||||
slots,
|
||||
control=InterviewControlState(
|
||||
known_facts=known_facts,
|
||||
persona_threads=persona_threads,
|
||||
recent_questions=recent_questions,
|
||||
),
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(state)
|
||||
|
||||
Reference in New Issue
Block a user