Add LLM-backed planning agent (A4/A11/A13) — step 5a
- planner.py: LLMPlanningAgent offers exactly two tools, submit_plan and ask_human, so gating stays structural at the tool layer; no proceed tool exists. System prompt requires explicit, low-inference plans because a weaker model executes them (A13) - Model output is appended to the canonical log as it arrives, before being interpreted, so a crash mid-planning leaves a truthful transcript - A bad turn (prose instead of a tool call, malformed arguments, or a failed call) retries once, then raises PlanningError; the orchestrator gates to AWAIT_INPUT rather than inventing a plan - Orchestrator: appends every operator message, plan_submitted, and gate_opened to the transcript, so re-planning sees rejection feedback as conversation rather than out-of-band parameters; pins the A11 role mapping at task start - llm.py: non-JSON or non-object tool arguments now raise LLMError instead of escaping as JSONDecodeError — observed from llama3.2, which emits truncated argument strings - Live planning test is opt-in on NISSE_LIVE_PLANNER_MODEL: planning escalates unconditionally (A12) and the local model does not hold the tool-call contract, so it awaits a frontier route Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ac9ae27bdc
commit
cf85648719
7 files changed
+522
-5
No files matched your search
@@ -131,11 +131,26 @@ class LiteLLMClient:
|
||||
# gets a locally-minted canonical ID; the provider's is dropped, since
|
||||
# every future request is rendered wholly from the canonical log.
|
||||
for call in message.get("tool_calls") or []:
|
||||
raw = call["function"]["arguments"] or "{}"
|
||||
# Weak models emit truncated or non-JSON argument strings; that is
|
||||
# a bad model turn, not a client crash (A15).
|
||||
try:
|
||||
arguments = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise LLMError(
|
||||
f"tool call {call['function']['name']!r} had non-JSON "
|
||||
f"arguments: {raw[:200]!r}"
|
||||
) from exc
|
||||
if not isinstance(arguments, dict):
|
||||
raise LLMError(
|
||||
f"tool call {call['function']['name']!r} arguments were "
|
||||
f"{type(arguments).__name__}, expected object"
|
||||
)
|
||||
out.append(
|
||||
ToolCall(
|
||||
call_id=mint_call_id(),
|
||||
tool=call["function"]["name"],
|
||||
arguments=json.loads(call["function"]["arguments"] or "{}"),
|
||||
arguments=arguments,
|
||||
)
|
||||
)
|
||||
return out
|
||||
|
||||
Reference in new issue
Block a user