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)
|