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
+54 -2
View File
@@ -1,11 +1,14 @@
"""rp_report / rp_input 的数据访问。**只有本模块可以触这两张表**(rp_share 归后续任务)。"""
"""rp_report / rp_input / rp_feedback / rp_reservation 的数据访问。
**只有本模块可以触这四张表**(rp_share 归后续任务)。
"""
from __future__ import annotations
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from packages.shared.models import RpInput, RpReport
from packages.shared.models import RpFeedback, RpInput, RpReport, RpReservation
STATUS_SUCCEEDED = "succeeded"
TIER_BASIC = "basic"
@@ -45,3 +48,52 @@ async def list_reports(session: AsyncSession, *, user_id: int, limit: int = 20)
async def get_input(session: AsyncSession, report_id: int) -> RpInput | None:
result = await session.execute(select(RpInput).where(RpInput.report_id == report_id))
return result.scalar_one_or_none()
# -- rp_feedback(T-P3-21:同维度覆盖更新,upsert 语义) -----------------------
async def get_feedback(
session: AsyncSession, *, report_id: int, dimension_key: str
) -> RpFeedback | None:
result = await session.execute(
select(RpFeedback).where(
RpFeedback.report_id == report_id,
RpFeedback.dimension_key == dimension_key,
)
)
return result.scalar_one_or_none()
async def list_feedback(session: AsyncSession, *, report_id: int) -> list[RpFeedback]:
result = await session.execute(
select(RpFeedback)
.where(RpFeedback.report_id == report_id)
.order_by(RpFeedback.created_at.asc(), RpFeedback.feedback_id.asc())
)
return list(result.scalars().all())
# -- rp_reservation(T-P3-22:重复预约幂等) ------------------------------------
async def get_reservation(
session: AsyncSession, *, user_id: int, topic: str
) -> RpReservation | None:
result = await session.execute(
select(RpReservation).where(
RpReservation.user_id == user_id,
RpReservation.topic == topic,
)
)
return result.scalar_one_or_none()
async def list_reservation_topics(
session: AsyncSession, *, user_id: int, topic: str | None = None
) -> list[str]:
stmt = select(RpReservation.topic).where(RpReservation.user_id == user_id)
if topic is not None:
stmt = stmt.where(RpReservation.topic == topic)
result = await session.execute(stmt.order_by(RpReservation.topic.asc()))
return [str(row) for row in result.scalars().all()]