feat: 回忆录证据血缘与内部评测可追溯,顺带对齐本地评测台与 CI

数据库与模型:新增多版迁移(章节证据快照、对话血缘、记忆事实/时间线 lineage 等),把「成稿 ↔ 对话/记忆」的溯源信息落到表结构里。
业务链路:会话与 WS、回忆录/故事流水线、记忆写入与 enrichment 等跟着接上线索与快照;新增章节证据快照与评测侧 EvalTraceService 等模块,方便组评审用的证据包。
内部评测:自动化 run 与手工 memoir 评审共用可追溯证据;rubric/ judge 相关脚本与文档有配套调整。
app-eval-web:Memoir/实验详情里能展开看证据摘要与 evidence_trace(含对话轮次 id);Vite 代理与 development.sh 注入的 API 端口与当前默认内部评测端口一致,避免改端口后页面连错服务。
工程杂项:GitHub Actions / 仓库说明有更新;各适配器与支付/配额/plan 等多处为小改动或跟随主改动的收尾;新增/扩充了?
This commit is contained in:
Kevin
2026-04-08 15:37:09 +08:00
parent 6772e1269c
commit 309a051038
109 changed files with 4125 additions and 858 deletions

View File

@@ -11,8 +11,14 @@ from app.core.logging import get_logger
logger = get_logger(__name__)
@shared_task(name="evaluation.run_experiment")
def run_eval_experiment_task(experiment_id: str) -> None:
@shared_task(
bind=True,
name="evaluation.run_experiment",
max_retries=1,
soft_time_limit=1800,
time_limit=2400,
)
def run_eval_experiment_task(self, experiment_id: str) -> None:
from app.features.evaluation.execution_service import execute_experiment_full
logger.info("evaluation task start experiment_id={}", experiment_id)

View File

@@ -18,7 +18,6 @@ from app.agents.chat.background_voice import infer_background_voice
from app.agents.chat.prompts_profile import format_user_profile_context
from app.agents.memoir import MemoirOrchestrator
from app.agents.stage_constants import normalize_chapter_category
from app.agents.state_schema import MemoirStateSchema, default_state
from app.core.chapter_pipeline_lock import (
acquire_chapter_pipeline_lock as _acquire_chapter_lock,
)
@@ -34,8 +33,6 @@ from app.core.memoir_pipeline_trace import (
new_memoir_correlation_id,
)
from app.features.conversation.models import Conversation, Segment
from app.tasks.celery_app import celery_app
from app.features.memoir.cover_eligibility import (
chapter_needs_cover_enqueue,
)
@@ -64,6 +61,7 @@ from app.features.memoir.story_pipeline_sync import (
run_story_pipeline_for_category_batch,
)
from app.features.user.models import User
from app.tasks.celery_app import celery_app
logger = get_logger(__name__)
_REDIS_CLIENTS: dict[bool, redis.Redis] = {}
@@ -597,27 +595,37 @@ def process_memoir_phase1(self, user_id: str, segment_ids: List[str]):
)
return {"status": "no_segments"}
conv_id = getattr(segments[0], "conversation_id", None) or ""
transcript = "\n\n".join(seg.user_input_text or "" for seg in segments)
if transcript.strip():
for seg in segments:
conv_id = getattr(seg, "conversation_id", None) or ""
text = (seg.user_input_text or "").strip()
if not text:
continue
try:
from app.features.memory.service import ingest_transcript_sync
source_id = ingest_transcript_sync(db, user_id, conv_id, transcript)
ln = getattr(seg, "lineage_json", None)
lineage_payload = ln if isinstance(ln, dict) else None
source_id = ingest_transcript_sync(
db,
user_id,
conv_id,
text,
lineage_json=lineage_payload,
)
logger.info(
"event=memory_transcript_ingested user_id={} task_id={} "
"source_id={} conversation_id={} transcript_chars={} "
"segment_count={}",
"source_id={} conversation_id={} segment_id={} transcript_chars={}",
user_id,
task_id,
source_id,
conv_id,
len(transcript),
len(segments),
seg.id,
len(text),
)
except Exception as e:
logger.warning(
"Memory ingest 跳过: {} exc_type={}",
"Memory ingest 跳过 segment_id={}: {} exc_type={}",
getattr(seg, "id", ""),
e,
type(e).__name__,
)