Skip to content

feat(assistant): LangGraph agent with rule-based recommendation (PR 3b) - #10

Merged
jcmoro merged 6 commits into
mainfrom
feature/assistant-agent
Oct 28, 2026
Merged

jcmoro merged 6 commits into
mainfrom
feature/assistant-agent

Conversation

@jcmoro

@jcmoro jcmoro commented Oct 28, 2026

Copy link
Copy Markdown
Owner

What

PR 3b of docs/plan-assistant-genai.md: a LangGraph agent for POST /assistant/ask, behind ASSISTANT_MODE=agent. rag stays the default. It also adds the code-review fixes RB1–RB5 and an opt-in evals harness.

Why

The agent can look products up with tools. When asked to choose, it calls recommend_candidate, which reuses the 3a selector: a Claude rubric plus the pure select(). The recommendation therefore comes from rules, not from the model's text. Decisions and acceptance criteria (B1–B15, RB1–RB6) are in docs/step-18-assistant-agent.md.

Commits

Commit What
16386f1 Records the CI evidence for 3a (docs)
b4d4339 LangGraph agent with semantic_search, get_product_by_sku and recommend_candidate. After AGENT_MAX_TOOL_CALLS tool calls it answers in RAG mode. The contract gains an additive field, recommendation; rag behaviour is unchanged.
87611b1 Refactor: one messages.create call per turn, with omit when there are no tools
cd69a01 Code-review fixes RB1–RB5 (below)
0a0843f Evals harness E1–E4 behind the eval marker, plus make eval
3c452e1 README: agent mode, the recommendation field, settings, make eval, roadmap

RB1–RB5:

  • RB1: the agent is only built in agent mode.
  • RB2: a transient failure mid-run keeps the recommendation, sources and usage gathered so far.
  • RB3: find_by_sku returns the real cosine score, and the question is embedded once per run.
  • RB4: a reply cut off by max_tokens or refused now gets the deterministic fallback in both modes. The agent's run stops with model_incomplete, which is recorded in the DTO and the log, not the API.
  • RB5: usage.model is the model the API returned.

Contract changes

  • AnswerResponse.recommendation: always present. It is null in rag mode, and in agent mode when the selector did not run. When it ran, it is {product_id, sku, score, reason}; product_id, sku and score are non-null only when reason == "chosen" (a validator enforces this).
  • In rag mode the only behaviour change is RB4. The OpenAPI description of fallback_used and the README say so.

Proof

  • Red → green for RB1–RB5: all 17 RB tests failed on the unmodified code, then passed after the fix (details in step-18).
  • make lint: ruff clean, pylint 10.00/10, mypy clean.
  • make test: 272 passed, 4 deselected (the eval tests).
  • Evals harness, checked with zero API calls: the eval database is created and migrated, 20 products are indexed, and the run stops at the SDK's authentication check.
  • Evals not run yet. About 1,230 calls on claude-haiku-5-5, still waiting for the go-ahead. Retrieval caps E1–E3 at 8/10: the acceptable SKU isn't in the top 10 for q06 or q10.

Deferred

🤖 Generated with Claude Code

@jcmoro
jcmoro merged commit 8341c5f into main Oct 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants