- LLMProvider interface: AnthropicLLM + ScriptedLLM test fake - System prompt composition from super-admin template + structured tenant data only (§6.3); auto-generated greetings with recording disclosure, out-of-hours variant, chat variant - Agent tools (§6.5): get_salon_info, check_availability (≤8 slots to model, agent offers ≤3), submit_booking_request (caller-ID fallback, hallucinated service-id guard), take_message - Conversation loop with tool dispatch, round-limit guard, transcript capture, outcome classification (request_created/message_taken/info_only/abandoned) - Terminal playground (python -m gogo.agent.cli) + demo seed (python -m gogo.seed) - Deterministic loop tests (ScriptedLLM) + Appendix A golden-scenario acceptance tests against a real LLM (skipped without an API key) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
"""Terminal playground: text conversation with a tenant's agent (M2).
|
|
|
|
Usage: python -m gogo.agent.cli salon-merima [--voice]
|
|
Requires GOGO_ANTHROPIC_API_KEY (or ANTHROPIC_API_KEY via the SDK default).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import sys
|
|
|
|
from sqlalchemy import select
|
|
|
|
from gogo.agent.loop import AgentConversation
|
|
from gogo.db import get_sessionmaker
|
|
from gogo.models import Tenant
|
|
|
|
|
|
async def main() -> None:
|
|
args = [a for a in sys.argv[1:] if not a.startswith("--")]
|
|
slug = args[0] if args else "salon-merima"
|
|
channel = "voice" if "--voice" in sys.argv else "chat"
|
|
|
|
async with get_sessionmaker()() as session:
|
|
tenant = (
|
|
await session.execute(select(Tenant).where(Tenant.slug == slug))
|
|
).scalar_one_or_none()
|
|
if tenant is None:
|
|
print(f"tenant '{slug}' not found — run: python -m gogo.seed")
|
|
sys.exit(1)
|
|
|
|
convo = AgentConversation(
|
|
session,
|
|
tenant,
|
|
channel=channel,
|
|
caller_phone="+38765123456" if channel == "voice" else "",
|
|
)
|
|
print(f"[{tenant.name} — {channel} mode, ctrl-d to exit]\n")
|
|
print(f"Gogo: {await convo.greeting()}\n")
|
|
|
|
while True:
|
|
try:
|
|
text = input("Vi: ").strip()
|
|
except (EOFError, KeyboardInterrupt):
|
|
break
|
|
if not text:
|
|
continue
|
|
reply = await convo.user_turn(text)
|
|
await session.commit()
|
|
print(f"\nGogo: {reply}\n")
|
|
|
|
print(f"\n[outcome: {convo.outcome}]")
|
|
for name, tool_input in convo.executor.tool_calls:
|
|
print(f"[tool] {name}({tool_input})")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|