84 lines
2.9 KiB
Python
84 lines
2.9 KiB
Python
|
|
"""独立候选回放:多轮 user 链式调用 LLM,不走路由 WS / ChatOrchestrator。"""
|
|||
|
|
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import time
|
|||
|
|
from typing import Any
|
|||
|
|
|
|||
|
|
from app.core.logging import get_logger
|
|||
|
|
|
|||
|
|
logger = get_logger(__name__)
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _system_prompt_for_eval(version_config: dict | None) -> str:
|
|||
|
|
cfg = version_config or {}
|
|||
|
|
extra = (cfg.get("system_prompt_suffix") or "").strip()
|
|||
|
|
base = """你是「岁月留书」老年友好访谈员。语气温暖、耐心,先承接情绪再 gently 追问事实与感受;回答简洁分段,避免术语。"""
|
|||
|
|
if extra:
|
|||
|
|
return f"{base}\n\n{extra}"
|
|||
|
|
return base
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _model_override(version_config: dict | None) -> str | None:
|
|||
|
|
if not version_config:
|
|||
|
|
return None
|
|||
|
|
m = version_config.get("model")
|
|||
|
|
return str(m).strip() if m else None
|
|||
|
|
|
|||
|
|
|
|||
|
|
class EvalCandidateRunner:
|
|||
|
|
"""使用 LangChain Chat 模型回放用户轮次。"""
|
|||
|
|
|
|||
|
|
def __init__(self, llm: Any) -> None:
|
|||
|
|
self._llm = llm
|
|||
|
|
|
|||
|
|
async def replay_utterances(
|
|||
|
|
self,
|
|||
|
|
utterances: list[str],
|
|||
|
|
*,
|
|||
|
|
version_config: dict | None = None,
|
|||
|
|
temperature: float = 0.7,
|
|||
|
|
) -> tuple[list[str], list[int]]:
|
|||
|
|
"""返回每轮 assistant 回复与耗时 ms。"""
|
|||
|
|
if not self._llm:
|
|||
|
|
raise RuntimeError(" replay: llm 未配置")
|
|||
|
|
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
|||
|
|
|
|||
|
|
sys_prompt = _system_prompt_for_eval(version_config)
|
|||
|
|
model = _model_override(version_config)
|
|||
|
|
lc_messages: list = [SystemMessage(content=sys_prompt)]
|
|||
|
|
replies: list[str] = []
|
|||
|
|
latencies: list[int] = []
|
|||
|
|
bound = (
|
|||
|
|
self._llm.bind(model=model, temperature=temperature)
|
|||
|
|
if model
|
|||
|
|
else self._llm.bind(temperature=temperature)
|
|||
|
|
)
|
|||
|
|
for u in utterances:
|
|||
|
|
text = (u or "").strip()
|
|||
|
|
if not text:
|
|||
|
|
continue
|
|||
|
|
lc_messages.append(HumanMessage(content=text))
|
|||
|
|
t0 = time.perf_counter()
|
|||
|
|
result = await bound.ainvoke(lc_messages)
|
|||
|
|
elapsed_ms = int((time.perf_counter() - t0) * 1000)
|
|||
|
|
reply = str(getattr(result, "content", "") or "").strip()
|
|||
|
|
replies.append(reply)
|
|||
|
|
latencies.append(elapsed_ms)
|
|||
|
|
lc_messages.append(AIMessage(content=reply))
|
|||
|
|
return replies, latencies
|
|||
|
|
|
|||
|
|
|
|||
|
|
def simple_memoir_from_transcript(utterances: list[str], replies: list[str]) -> str:
|
|||
|
|
"""轻量成稿:供评审用占位(非生产叙事管线)。"""
|
|||
|
|
lines = ["# 访谈摘录整理(评测占位稿)", ""]
|
|||
|
|
for i, u in enumerate(utterances):
|
|||
|
|
lines.append(f"## 片段 {i + 1}")
|
|||
|
|
lines.append("")
|
|||
|
|
lines.append(f"**用户:** {u.strip()}")
|
|||
|
|
if i < len(replies):
|
|||
|
|
lines.append("")
|
|||
|
|
lines.append(f"**访谈者:** {replies[i].strip()}")
|
|||
|
|
lines.append("")
|
|||
|
|
return "\n".join(lines)
|