feat(agent,report): 会话持久化与画像注入、报告反馈与预约留资——PM/开发双Agent迭代1-3
CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s

- agent: ag_conversation/ag_message 表(迁移0007),上下文组装注入最近报告画像摘要
  (report.profile.latest_profile_summary 经 apps 层闭包注入,同层不互调),历史带最近
  AGENT_HISTORY_TURNS=10 轮;POST/GET /agent/conversations 与消息历史接口
- agent 修复:ChatRequest.conversation_id 改 str(雪花ID超2^53,JS Number丢精度);
  _build_context 挪进 reserve commit 后同一 try,异常走 refund 兜底
- report: rp_feedback(维度反馈 upsert,认同率/修正率 SQL 可算,T-P3-21 最小版——
  L1 记忆未建,不回写结论)+ rp_reservation(预约留资幂等,T-P3-22,预约率口径
  行数/full_report.card_view 去重曝光,门槛≥8%),迁移0008
- 测试:全量 606 passed;ruff/mypy/lint-imports 全绿;E2E 真实 deepseek-v4-flash 验证
  画像注入与历史携带
This commit is contained in:
xsl
2026-09-11 21:50:58 +08:00
parent 00ef56b08b
commit 7bcbb8d40b
25 changed files with 1860 additions and 25 deletions
+43
View File
@@ -0,0 +1,43 @@
"""预约留资服务(T-P3-22)。
topic 预留通用性,首期只有 full_report(全面报告上线提醒)。
幂等口径:UniqueConstraint(user_id, topic)——重复预约返回已有记录
(created=false,HTTP 仍 200),绝不堆行。
指标口径:预约率 = rp_reservation(topic='full_report') 行数 ÷ 埋点
full_report.card_view 去重曝光数;决策门槛 ≥8%(产品方案 §2.2.3)。
"""
from __future__ import annotations
from dataclasses import dataclass
from sqlalchemy.ext.asyncio import AsyncSession
from packages.domain.ids import next_id
from packages.report import repo
from packages.shared.models import RpReservation
@dataclass(frozen=True, slots=True)
class ReservationResult:
reservation: RpReservation
created: bool # false=幂等命中已有记录
class ReservationService:
def __init__(self, session: AsyncSession) -> None:
self._session = session
async def reserve(self, *, user_id: int, topic: str) -> ReservationResult:
"""幂等预约:同 user+topic 已存在即返回原记录。"""
existing = await repo.get_reservation(self._session, user_id=user_id, topic=topic)
if existing is not None:
return ReservationResult(reservation=existing, created=False)
row = RpReservation(reservation_id=next_id(), user_id=user_id, topic=topic)
self._session.add(row)
await self._session.flush()
return ReservationResult(reservation=row, created=True)
async def list_topics(self, *, user_id: int, topic: str | None = None) -> list[str]:
return await repo.list_reservation_topics(self._session, user_id=user_id, topic=topic)