fix(agent): tolerant intent JSON parse + log raw output on failure
The classifier was silently falling back to a clarification prompt every
time the LLM wrapped its JSON in markdown fences, prefixed it with
'json', or added surrounding prose. The bot then asked 'Could you
clarify what you need?' to every message regardless of clarity.
Now: strip code fences, slice to the first {...} block, and on parse
failure log the raw content (truncated) and treat the message as 'no
specialist agent' so the direct-answer fallback responds instead of
looping on clarification.
This commit is contained in:
@@ -147,23 +147,37 @@ class MasterAgent:
|
||||
msgs = [{'role': 'system', 'content': system}, *history,
|
||||
{'role': 'user', 'content': message}]
|
||||
resp = await self._llm.submit(msgs, caller='master')
|
||||
raw = (resp.content or '').strip()
|
||||
# Strip markdown fences like ```json ... ```
|
||||
if raw.startswith('```'):
|
||||
raw = raw.strip('`')
|
||||
if raw.lower().startswith('json'):
|
||||
raw = raw[4:]
|
||||
raw = raw.strip()
|
||||
# Pull out the first {...} block if there's surrounding prose.
|
||||
first = raw.find('{')
|
||||
last = raw.rfind('}')
|
||||
if first != -1 and last != -1 and last > first:
|
||||
raw = raw[first:last + 1]
|
||||
try:
|
||||
raw = resp.content.strip()
|
||||
if raw.startswith(chr(96)*3):
|
||||
raw = raw.split(chr(10), 1)[1].rsplit(chr(10), 1)[0]
|
||||
data = json.loads(raw)
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f'expected JSON object, got {type(data).__name__}')
|
||||
return IntentResult(
|
||||
needs_clarification=data.get('needs_clarification', False),
|
||||
needs_clarification=bool(data.get('needs_clarification', False)),
|
||||
clarification_question=data.get('clarification_question'),
|
||||
is_continuation=data.get('is_continuation', False),
|
||||
agents=data.get('agents', []),
|
||||
intent_summary=data.get('intent_summary', ''),
|
||||
params=data.get('params', {}),
|
||||
context_hints=data.get('context_hints', []))
|
||||
except (json.JSONDecodeError, KeyError) as exc:
|
||||
logger.warning('Intent classification parse failed: %s', exc)
|
||||
return IntentResult(needs_clarification=True,
|
||||
clarification_question='Could you clarify what you need?')
|
||||
is_continuation=bool(data.get('is_continuation', False)),
|
||||
agents=data.get('agents') or [],
|
||||
intent_summary=data.get('intent_summary', '') or '',
|
||||
params=data.get('params') or {},
|
||||
context_hints=data.get('context_hints') or [])
|
||||
except Exception as exc:
|
||||
logger.warning('Intent classification parse failed (%s); raw=%r',
|
||||
exc, (resp.content or '')[:300])
|
||||
# Treat unparseable LLM output as 'no specialist agent applies' so
|
||||
# the direct-answer fallback can handle it instead of looping on
|
||||
# clarification.
|
||||
return IntentResult(needs_clarification=False, agents=[])
|
||||
|
||||
async def _check_access(self, user_id, agents) -> AccessResult:
|
||||
denied = []
|
||||
|
||||
Reference in New Issue
Block a user