Add prompt improvement button in drawer

This commit is contained in:
Xin Wang
2026-08-26 12:18:18 +08:00
parent 239b9027ca
commit ab409ba9c0
13 changed files with 2067 additions and 39 deletions
+48 -35
View File
@@ -15,7 +15,6 @@ from services.test_runs.text_runner import RawToolCall, RawTurnResult, TextCaseR
from test_schemas import (
BatchEvaluationResult,
BatchTurnResult,
FixedInputTurn,
OverallCriterion,
ReplyExpectedBehavior,
TestCaseDefinition,
@@ -32,9 +31,7 @@ class EvaluatedCase:
@property
def passed(self) -> bool:
evaluations = [
item
for turn in self.turns
for item in turn.evaluations
item for turn in self.turns for item in turn.evaluations
] + self.overall_criteria
return all(item.status == "pass" for item in evaluations)
@@ -93,29 +90,47 @@ class LLMJudge:
)
try:
async with httpx.AsyncClient(timeout=self._timeout_seconds) as client:
response = await client.post(
f"{base_url}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=payload,
)
response.raise_for_status()
body = response.json()
content = body["choices"][0]["message"]["content"]
if isinstance(content, list):
content = "".join(
str(part.get("text") or "")
for part in content
if isinstance(part, dict)
)
parsed = self._parse_json(str(content or ""))
return (
bool(parsed.get("passed")),
str(parsed.get("reason") or "LLM 未提供判断理由"),
str(parsed.get("actual") or ""),
)
for attempt in range(2):
request_payload = dict(payload)
if attempt:
request_payload["messages"] = [
{
"role": "user",
"content": (
f"{prompt}\n\n格式修正:上一次响应无法解析。"
"请只返回一个完整 JSON 对象,不要重复输出、"
"不要添加解释或 Markdown。"
),
}
]
response = await client.post(
f"{base_url}/chat/completions",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json=request_payload,
)
response.raise_for_status()
try:
body = response.json()
content = body["choices"][0]["message"]["content"]
if isinstance(content, list):
content = "".join(
str(part.get("text") or "")
for part in content
if isinstance(part, dict)
)
parsed = self._parse_json(str(content or ""))
except (KeyError, IndexError, TypeError, ValueError):
if attempt == 0:
continue
raise
return (
bool(parsed.get("passed")),
str(parsed.get("reason") or "LLM 未提供判断理由"),
str(parsed.get("actual") or ""),
)
except TestExecutionError:
raise
except (httpx.HTTPError, KeyError, IndexError, TypeError, ValueError) as exc:
@@ -133,14 +148,10 @@ class LLMJudge:
text = re.sub(r"^```(?:json)?\s*", "", text)
text = re.sub(r"\s*```$", "", text)
start = text.find("{")
end = text.rfind("}")
if start < 0 or end < start:
if start < 0:
raise ValueError("评估模型没有返回 JSON")
parsed = json.loads(text[start : end + 1])
if (
not isinstance(parsed, dict)
or not isinstance(parsed.get("passed"), bool)
):
parsed, _end = json.JSONDecoder().raw_decode(text[start:])
if not isinstance(parsed, dict) or not isinstance(parsed.get("passed"), bool):
raise ValueError("评估模型返回格式不正确")
return parsed
@@ -221,7 +232,9 @@ class EvaluationEngine:
)
normalized_reply = reply.casefold()
matches = [keyword.casefold() in normalized_reply for keyword in behavior.keywords]
matches = [
keyword.casefold() in normalized_reply for keyword in behavior.keywords
]
matched = all(matches) if behavior.keyword_match_mode == "all" else any(matches)
passed = not matched if behavior.negate_keywords else matched
relation = (