feat(D1): 改稿进飞轮 - 最强真实信号 text_edit +5

倩倩姐2026-06-16拍板:改稿=用户真改了字=最在乎这条方向=最强真实信号。
先激进入飞轮跑一周,北哥用后按真实数据校准是否加"过审才入"闸门。

改动:
- enums.py: SignalType.TEXT_EDIT + SIGNAL_WEIGHTS权重5(≥选择类)
- task.py: TextCandidate.edited 布尔字段
- 013迁移: text_candidates加edited列 + preference_events.signal_type
  ENUM补text_edit(端到端实测抓出:不补会Data truncated)
- task_actions.py: POST /{task_id}/text-candidates/{cid}/edit 端点
  回写content+edited=True+record_signal写text_edit,走task_actions不碰review.py

端到端实测:edited False→True,飞轮信号真落库type=text_edit weight=5。
顺手: tsconfig.tsbuildinfo 移出版本控制(构建产物)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-16 12:20:54 +08:00
parent 77f52e7eb0
commit e3fb6f2655
6 changed files with 85 additions and 1 deletions

1
.gitignore vendored
View File

@@ -27,3 +27,4 @@ logs/
celerybeat-schedule celerybeat-schedule
dump.rdb dump.rdb
.env.bak .env.bak
*.tsbuildinfo

View File

@@ -0,0 +1,46 @@
"""013 text_candidates 表加 edited 字段(改稿进飞轮)
Revision ID: 013
Revises: 012
Create Date: 2026-06-16
改稿=用户真改了文案的字=最强真实飞轮信号倩倩姐2026-06-16拍板权重+5
edited 标记该候选是否被用户改过,配合 SignalType.TEXT_EDIT 写入飞轮偏好事件。
历史行默认 false避免 NULL。
"""
from alembic import op
import sqlalchemy as sa
revision = "013"
down_revision = "012"
branch_labels = None
depends_on = None
def upgrade():
op.add_column(
"text_candidates",
sa.Column(
"edited",
sa.Boolean(),
nullable=False,
server_default=sa.false(),
comment="是否被用户改稿过=最强飞轮信号(text_edit)",
),
)
# preference_events.signal_type 是 MySQL ENUM,必须把 text_edit 加进枚举值,
# 否则写 text_edit 信号会 Data truncated 报错(端到端实测抓出)。
op.execute(
"ALTER TABLE preference_events MODIFY COLUMN signal_type "
"ENUM('text_select','image_select','approve','reject_with_reason',"
"'regenerate','text_edit') NOT NULL"
)
def downgrade():
op.execute(
"ALTER TABLE preference_events MODIFY COLUMN signal_type "
"ENUM('text_select','image_select','approve','reject_with_reason',"
"'regenerate') NOT NULL"
)
op.drop_column("text_candidates", "edited")

View File

@@ -8,6 +8,7 @@ import logging
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
from app.core.database import get_db from app.core.database import get_db
@@ -144,3 +145,37 @@ def get_preference_context(
from app.services.flywheel_service import get_preference_context from app.services.flywheel_service import get_preference_context
ctx = get_preference_context(db, current_user.workspace_id, task.product_id) ctx = get_preference_context(db, current_user.workspace_id, task.product_id)
return ok(ctx) return ok(ctx)
class EditTextRequest(BaseModel):
"""改稿请求candidate_id + 用户改后的新文案。"""
candidate_id: int
content: str = Field(min_length=1, max_length=5000)
@router.post("/{task_id}/text-candidates/{candidate_id}/edit")
def edit_text(
task_id: int, candidate_id: int, body: EditTextRequest,
current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None,
db: Session = Depends(get_db),
):
"""改稿(飞轮信号 text_edit +5最强真实信号
用户真改了文案的字=最在乎这条方向。回写 content + edited=True
并写 text_edit 飞轮偏好事件。倩倩姐2026-06-16拍板先激进入飞轮跑一周再校准。
"""
from app.services.flywheel_service import record_signal
task = _check_task_ownership(
db.query(GenerationTask).filter(GenerationTask.id == task_id).first(),
current_user.workspace_id,
)
tc = db.query(TextCandidate).filter(
TextCandidate.id == candidate_id, TextCandidate.task_id == task_id
).first()
if not tc:
raise_not_found("文案候选不存在")
tc.content = body.content
tc.edited = True
db.commit()
record_signal(db, current_user, task, "text_edit", candidate_id=tc.id, angle_label=tc.angle_label)
return ok({"candidate_id": candidate_id, "edited": True})

View File

@@ -60,6 +60,7 @@ class SignalType(str, Enum):
APPROVE = "approve" APPROVE = "approve"
REJECT_WITH_REASON = "reject_with_reason" REJECT_WITH_REASON = "reject_with_reason"
REGENERATE = "regenerate" REGENERATE = "regenerate"
TEXT_EDIT = "text_edit" # 改稿:用户真改了字=最强真实信号(倩倩姐2026-06-16拍板)
# ── 飞轮信号权重默认值(北哥可校准)───────────────────── # ── 飞轮信号权重默认值(北哥可校准)─────────────────────
@@ -69,6 +70,7 @@ SIGNAL_WEIGHTS: dict[str, int] = {
SignalType.APPROVE: 5, SignalType.APPROVE: 5,
SignalType.REJECT_WITH_REASON: -3, SignalType.REJECT_WITH_REASON: -3,
SignalType.REGENERATE: -1, SignalType.REGENERATE: -1,
SignalType.TEXT_EDIT: 5, # 最强信号,≥选择类(倩倩姐2026-06-16拍板;先激进跑一周再校准)
} }

View File

@@ -92,6 +92,7 @@ class TextCandidate(Base):
) )
eval_score: Mapped[float | None] = mapped_column(Float) # 一期留 NULL eval_score: Mapped[float | None] = mapped_column(Float) # 一期留 NULL
is_selected: Mapped[bool] = mapped_column(default=False, nullable=False) is_selected: Mapped[bool] = mapped_column(default=False, nullable=False)
edited: Mapped[bool] = mapped_column(default=False, nullable=False) # 改稿标记:用户改过=最强飞轮信号
created_at: Mapped[datetime] = mapped_column( created_at: Mapped[datetime] = mapped_column(
DateTime, server_default=func.now(), nullable=False DateTime, server_default=func.now(), nullable=False
) )

File diff suppressed because one or more lines are too long