new feature: External guardrails/judges
This commit is contained in:
41
app/extensions/tim_judges.py
Normal file
41
app/extensions/tim_judges.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
import json, re
|
||||
from typing import Any
|
||||
from agent_framework.judges.judge import JudgeResult
|
||||
from .tim_prompts.aluc import build_aluc_prompt
|
||||
from .tim_prompts.rqlt import build_rqlt_prompt
|
||||
|
||||
|
||||
def _parse(raw: Any) -> dict[str, Any]:
|
||||
text=str(getattr(raw,'content',raw) or '').strip(); m=re.search(r'\{[\s\S]*\}',text)
|
||||
if m: text=m.group(0)
|
||||
try: return json.loads(text)
|
||||
except Exception: return {'allowed':False,'score':0,'reason':f'Resposta inválida do judge TIM: {text[:300]}'}
|
||||
|
||||
class _TimJudge:
|
||||
name='tim_judge'
|
||||
def __init__(self, llm=None, threshold=0.6, profile_name='judge', fail_closed=True, settings=None, **kwargs):
|
||||
self.llm=llm; self.threshold=float(threshold or 0); self.profile_name=profile_name or 'judge'; self.fail_closed=bool(fail_closed)
|
||||
async def _invoke(self,prompt):
|
||||
if not self.llm:
|
||||
if self.fail_closed: return {'allowed':False,'score':0,'reason':'LLM do framework indisponível para judge TIM'}
|
||||
return {'allowed':True,'score':10,'reason':'Judge TIM sem LLM; fail-open explicitamente configurado'}
|
||||
raw=await self.llm.ainvoke([{'role':'system','content':'Responda apenas JSON válido, sem markdown.'},{'role':'user','content':prompt}], profile_name=self.profile_name, component_name=f'judge.external.{self.name}', generation_name=f'llm.judge.external.{self.name}')
|
||||
return _parse(raw)
|
||||
def _result(self,out):
|
||||
raw=out.get('score', 10 if out.get('allowed',True) else 0)
|
||||
try: score=float(raw); score=score/10 if score>1 else score
|
||||
except Exception: score=0.0
|
||||
passed=bool(out.get('allowed',True)) and score>=self.threshold
|
||||
return JudgeResult(name=self.name,score=max(0,min(1,score)),passed=passed,reason=str(out.get('reason') or ''),metadata={'external':True,'domain':'TIM_CONTAS','raw_llm_answer':out,'threshold':self.threshold})
|
||||
|
||||
class TimGroundednessJudge(_TimJudge):
|
||||
name='tim_groundedness'
|
||||
async def evaluate(self, question, answer, context):
|
||||
evidence=(context or {}).get('evidence') or (context or {}).get('tool_result') or (context or {}).get('sources') or context or {}
|
||||
return self._result(await self._invoke(build_aluc_prompt(str(answer or ''), evidence)))
|
||||
|
||||
class TimResponseQualityJudge(_TimJudge):
|
||||
name='tim_response_quality'
|
||||
async def evaluate(self, question, answer, context):
|
||||
return self._result(await self._invoke(build_rqlt_prompt(str(question or ''), str(answer or ''))))
|
||||
Reference in New Issue
Block a user