Spaces:
Runtime error
Runtime error
File size: 7,264 Bytes
7e2d640 135eefb 7e2d640 135eefb 7e2d640 135eefb 7e2d640 135eefb 7e2d640 135eefb 7e2d640 135eefb 7e2d640 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | """Run the LIVE ExecChat agent (current prompt + KB) through eval scenarios.
Unlike agent-evals' snapshot-based runtime, this reuses the objects the running
Streamlit app already built (db/reranker from the live Google Docs), so checks
reflect the prompt/KB exactly as deployed right now.
Tool-use scenarios are driven with mock tools that mirror the real tool names and
record every call — no real API needed (works even when ENABLE_TOOLS=0).
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
def make_mock_tools(call_log: list[dict]):
"""Return langchain tools with the agent's real names that record calls.
``call_log`` is mutated in place: each entry is {"name", "args"}.
"""
from langchain_core.tools import tool
def _rec(name: str, args: dict, canned: str) -> str:
call_log.append({"name": name, "args": dict(args)})
return canned
@tool
def get_exec_quality_params(executive_id: str, period: str = "last 14 days") -> str:
"""Параметры качества/рейтинга исполнителя за период (по умолчанию 14 дней, макс 30)."""
return _rec("get_exec_quality_params", {"executive_id": executive_id, "period": period},
"Качество: рейтинг 4.8/5, пунктуальность 96%, жалоб за период: 0.")
@tool
def get_exec_orders_history(executive_id: str, period: str = "last 7 days") -> str:
"""История заказов исполнителя за период (по умолчанию 7 дней, макс 10)."""
return _rec("get_exec_orders_history", {"executive_id": executive_id, "period": period},
"За период выполнено 6 заказов на 9 200 ₽, 1 заказ с опозданием.")
@tool
def get_exec_next_orders(executive_id: str) -> str:
"""Ближайшие заказы исполнителя: услуги, адреса, оплата, комментарии клиента."""
return _rec("get_exec_next_orders", {"executive_id": executive_id},
"Завтра 10:00, поддерживающая уборка, ул. Ленина 5, оплата 1 800 ₽.")
@tool
def get_exec_revenue_and_fines_feed(executive_id: str, period: str = "last 7 days") -> str:
"""Лента доходов и штрафов исполнителя за период (по умолчанию 7 дней, макс 10)."""
return _rec("get_exec_revenue_and_fines_feed", {"executive_id": executive_id, "period": period},
"Доход за период: 9 200 ₽. Штрафов: 1 (опоздание, −300 ₽).")
return [get_exec_next_orders, get_exec_quality_params,
get_exec_revenue_and_fines_feed, get_exec_orders_history]
@dataclass
class AgentRuntime:
"""Holds the live db/reranker/llms for the agent under test (exec_like)."""
module: Any # the app_2 module (provides factories + constants)
db: Any
reranker: Any
llm: Any # no-tools llm, reused for non-tool turns
grounding_llm: Any
query_rewriter: Any
system_template: str # BASE prompt (TOOLS_INSTRUCTION appended per tool turn)
temperature: float = 0.0 # mirrors the main chat slider when passed from session
app_supports_tools: bool = True # hard gate: does the agent under test have a tools system at all
default_use_tools: bool = True # run-level default applied to scenarios set to "Авто" (use_tools=None)
def run_turn(self, message: str, chat_history: list[tuple[str, str]] | None = None,
use_tools: bool | None = None) -> dict:
mod = self.module
chat_history = list(chat_history or [])
call_log: list[dict] = []
# ``use_tools`` is the per-scenario setting: True/False force it, None
# ("Авто") falls back to the run-level default. A scenario that explicitly
# asks for tools gets them regardless of the run-level default — the only
# hard requirement is that the agent under test actually has a tools system.
if use_tools is None:
use_tools = self.default_use_tools
tools_list = make_mock_tools(call_log) if (use_tools and self.app_supports_tools) else []
llm = mod.create_llm(self.temperature, tools_list) if tools_list else self.llm
system_template = self.system_template
if tools_list and hasattr(mod, "TOOLS_INSTRUCTION"):
system_template = system_template + mod.TOOLS_INSTRUCTION
text, rag, guard = mod.generate_response(
self.db, self.reranker, self.query_rewriter, llm, self.grounding_llm,
system_template, mod.USER_TEMPLATE, tools_list, message, chat_history,
)
return {"text": text, "rag_chunks": rag, "guard_info": guard, "tool_calls": call_log}
def run_dialog(self, user_turns: list[str], use_tools: bool | None = None) -> dict:
history: list[tuple[str, str]] = []
turns = []
for msg in user_turns:
res = self.run_turn(msg, history, use_tools=use_tools)
turns.append({"user": msg, **res})
history.append(("user", msg))
history.append(("manager", res["text"]))
return {"turns": turns, "final": turns[-1] if turns else None,
"tool_calls": [c for t in turns for c in t["tool_calls"]]}
def build_runtime_live(module, db, reranker, base_system_template: str,
app_supports_tools: bool = True,
default_use_tools: bool = True,
temperature: float = 0.0,
llm: Any = None,
query_rewriter: Any = None,
grounding_llm: Any = None) -> AgentRuntime:
"""Assemble an AgentRuntime from the live app objects.
``module`` is the running app_2 module (provides create_llm / create_grounding_llm
/ create_query_rewriter / generate_response / constants). ``db`` and ``reranker``
are the cached objects from init_db()/init_reranker(); ``base_system_template`` is
the prompt WITHOUT the tools instruction (run_turn appends it for tool turns).
Pass ``llm`` / ``query_rewriter`` / ``grounding_llm`` from the running Streamlit
session (and ``temperature`` from the slider) so eval runs match manual chat.
``app_supports_tools`` is a hard capability gate (False for RAG-only agents like
OnBoarding). ``default_use_tools`` is the run-level default for scenarios left on
"Авто"; scenarios with an explicit use_tools=True get tools regardless of it.
"""
return AgentRuntime(
module=module,
db=db,
reranker=reranker,
llm=llm or module.create_llm(temperature, []),
grounding_llm=grounding_llm or module.create_grounding_llm(),
query_rewriter=query_rewriter or module.create_query_rewriter(),
system_template=(base_system_template or "").strip(),
temperature=temperature,
app_supports_tools=app_supports_tools,
default_use_tools=default_use_tools,
)
|