feat(bot): every free-text message gets the agent's answer; every demo entry is a private copy
CI / ruff · pytest (pull_request) Successful in 2m41s
CI / ruff · pytest (pull_request) Successful in 2m41s
- Idle chat: all messages go to the agent (greetings, side-effect and substitute questions included); check-in signals are still recorded silently, quit intent keeps its rule reaction. Seen live: «какие будут побочки» was logged as a complaint («Спасибо, отметил»), «привет» got a canned phrase. - Agent prompt: warm short replies to small talk; substitutes → only the doctor changes the drug. - ЕМИАС demo persona button creates a private copy too (starting from consent + questionnaires, subject to the screening switch); the web app drops a stored session of the old shared demo account. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+13
-3
@@ -148,6 +148,10 @@ async def auth_emias_demo(body: EmiasDemoIn) -> AuthOut:
|
||||
persona = next((p for p in fixtures.DEMO_PERSONAS if p.token == body.persona_token), None)
|
||||
if persona is None:
|
||||
raise HTTPException(404, "persona not found")
|
||||
if persona.token == DEMO_SOURCE:
|
||||
# Every visitor gets a private copy (never the shared account); this entry starts from consent and the
|
||||
# initial questionnaires (subject to the screening switch), unlike the one-click chat demo.
|
||||
return await _fresh_demo_copy(start_in_chat=False)
|
||||
await _seed_demo_persona(persona.token)
|
||||
log.info("web auth: emias demo tok=%s", persona.token[:6])
|
||||
return AuthOut(token=persona.token, mode="emias", display_name=persona.display_name)
|
||||
@@ -158,6 +162,10 @@ DEMO_SOURCE = "tok-andrey"
|
||||
|
||||
@router.post("/auth/demo/fresh", response_model=AuthOut)
|
||||
async def auth_demo_fresh() -> AuthOut:
|
||||
return await _fresh_demo_copy(start_in_chat=True)
|
||||
|
||||
|
||||
async def _fresh_demo_copy(*, start_in_chat: bool) -> AuthOut:
|
||||
"""Presentation entry: a fresh private copy of veteran Andrey for every sign-in — consent given, prescription
|
||||
and ЕМИАС record in place, dialog resting in the chat. Nobody's crisis or half-done questionnaire leaks into
|
||||
another viewer's demo. Synthetic data only."""
|
||||
@@ -177,12 +185,14 @@ async def auth_demo_fresh() -> AuthOut:
|
||||
birth_year=now.year - 34,
|
||||
sex="m",
|
||||
)
|
||||
await identity_repo.record_consent(s, token=token, version="demo", now=now)
|
||||
if start_in_chat:
|
||||
await identity_repo.record_consent(s, token=token, version="demo", now=now)
|
||||
await _seed_demo_persona(token, source=DEMO_SOURCE)
|
||||
async with get_clinical_session() as s:
|
||||
await repo.upsert_profile(s, token=token, sex="m", birth_year=now.year - 34)
|
||||
consent = {"consent": True, "consent_ver": "demo", "consent_at": now.isoformat(), "revoked_at": None}
|
||||
await repo.set_dialog_state(s, token=token, state="idle", data={**consent, "adult": True}, now=now)
|
||||
if start_in_chat:
|
||||
consent = {"consent": True, "consent_ver": "demo", "consent_at": now.isoformat(), "revoked_at": None}
|
||||
await repo.set_dialog_state(s, token=token, state="idle", data={**consent, "adult": True}, now=now)
|
||||
log.info("web auth: demo fresh tok=%s", token[:14])
|
||||
return AuthOut(token=token, mode="emias", display_name=persona.display_name)
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ from app.dialog.flows.meds import (
|
||||
)
|
||||
from app.dialog.fsm import Ctx, Step, abandonable, on_enter, state, trigger
|
||||
from app.dialog.scale_session import register_scale_intro
|
||||
from app.llm import agent
|
||||
|
||||
PREFIX = "daily"
|
||||
STEPS = ("mood", "med", "sleep", "nightmares", "alcohol", "text")
|
||||
@@ -429,6 +430,9 @@ async def _idle_text(ctx: Ctx, event: Event) -> Step:
|
||||
assert ctx.text is not None
|
||||
facts = await ctx.gateway.extract_checkin(ctx.text)
|
||||
if facts is None:
|
||||
smart = await agent.answer_chat(ctx.token, ctx.text)
|
||||
if smart is not None:
|
||||
return Step(replies=[menu_reply(ctx, agent.render(smart))])
|
||||
text = await knowledge_answer(ctx, explicit=False) if looks_like_question(ctx.text) else None
|
||||
return Step(replies=[menu_reply(ctx, text or await ctx.gateway.phrase("support"))])
|
||||
fields: dict[str, Any] = {}
|
||||
@@ -446,6 +450,14 @@ async def _idle_text(ctx: Ctx, event: Event) -> Step:
|
||||
text = (await side_effect_reply(ctx, codes)).text
|
||||
if fields:
|
||||
await save_checkin(ctx, **fields)
|
||||
if not facts.quit_intent:
|
||||
# Chat first: whatever the patient writes in idle gets the agent's answer; check-in signals above are
|
||||
# recorded silently for the doctor. Quit intent keeps its rule reaction (explanation + doctor notified).
|
||||
smart = await agent.answer_chat(ctx.token, ctx.text)
|
||||
if smart is not None:
|
||||
return Step(
|
||||
replies=[menu_reply(ctx, agent.render(smart))], notify_monitoring=bool(fields or facts.side_effects)
|
||||
)
|
||||
if text is None and facts.med_question and not facts.quit_intent:
|
||||
text = await answer_question(ctx, await current_prescription(ctx))
|
||||
# A question is answered even if it also mentioned alcohol/side effects (those are recorded above).
|
||||
|
||||
@@ -32,6 +32,10 @@ _SYSTEM_PROMPT = """Ты — помощник пациента в сервисе
|
||||
- проводить психотерапию; обещать конфиденциальность сверх реальной;
|
||||
- выдумывать факты, которых нет в результатах инструментов.
|
||||
|
||||
На приветствия и болтовню («привет», «как дела?») отвечай коротко и тепло и предложи, с чем можешь помочь:
|
||||
вопросы о лечении, лекарствах, побочных эффектах, самочувствии. Про замену препарата или аналоги: объясни,
|
||||
что подбор и замену делает только врач, и при желании назови группу препарата по инструкции.
|
||||
|
||||
Стиль: по-русски, на «вы», спокойно и по-человечески, 3–6 коротких предложений, без markdown-заголовков.
|
||||
Если в вопросе есть признаки угрозы жизни — коротко скажи, что важно сразу позвонить 112 или на телефон
|
||||
доверия {phone:helpline}, и что врач будет уведомлён."""
|
||||
|
||||
Reference in New Issue
Block a user