"""Whether the employee found an assistant answer useful. Revision ID: 512 Revises: 511 Create Date: 2026-08-18 The assistant could file a ticket, propose a flow and answer from the knowledge base, but nothing ever asked the one question that says whether any of it landed: was this useful? Deflection counted tickets NOT filed, which measures avoidance rather than success — an employee who gives up is indistinguishable from one who got their answer. One row per verdict, keyed to the message so a bad answer can be read back with the question that produced it and the tools it used. That pairing is what turns 「没用」 into a fixable knowledge gap instead of a number on a dashboard. """ from __future__ import annotations import sqlalchemy as sa from alembic import op from app.core.migration_helpers import id_type_for, table_kw_for revision = "512" down_revision = "511" branch_labels = None depends_on = None def upgrade() -> None: op.create_table( "assistant_message_feedback", sa.Column("id", id_type_for(), primary_key=True), sa.Column( "message_id", id_type_for("assistant_messages"), sa.ForeignKey("assistant_messages.id", ondelete="CASCADE"), nullable=False, ), sa.Column( "person_id", id_type_for("identity_persons"), sa.ForeignKey("identity_persons.id"), nullable=False, ), # 'helpful' | 'unhelpful' — a two-value verdict on purpose: a five-star # scale collects shrugs, and the only actionable signal is "this did # not work for me". sa.Column("verdict", sa.String(16), nullable=False), sa.Column("note", sa.Text(), nullable=True), sa.Column("trace_id", sa.String(64), nullable=True), sa.Column( "created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), ), sa.Column( "updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.func.now(), onupdate=sa.func.now(), ), # One verdict per person per message: tapping again changes the mind # already recorded rather than stuffing the ballot. sa.UniqueConstraint( "message_id", "person_id", name="uq_assistant_feedback_message_person", ), **table_kw_for("assistant_message_feedback"), ) op.create_index( "ix_assistant_feedback_verdict_created", "assistant_message_feedback", ["verdict", "created_at"], ) def downgrade() -> None: op.drop_index( "ix_assistant_feedback_verdict_created", table_name="assistant_message_feedback", ) op.drop_table("assistant_message_feedback")