3.2 KiB
#region AgentChat.Research [C:3] [TYPE ADR] [SEMANTICS research,agent-chat,langgraph,gradio] @BRIEF Research — LangGraph agent with interrupt/resume, PostgresSaver, structured metadata. Final architecture choices.
1. Agent Framework: LangGraph create_react_agent()
Decision: create_react_agent(model, tools, checkpointer=PostgresSaver) from langgraph.prebuilt. Uses interrupt() for HITL, Command(resume=...) for resume.
Rationale: Native HITL support, PostgresSaver checkpointer, astream_events() for structured streaming.
Rejected: LangChain AgentExecutor — no native interrupt/checkpointer, more boilerplate.
2. Tools: Native @tool
Decision: @tool-decorated functions with Pydantic args_schema. Each tool calls FastAPI REST via HTTP with user JWT.
Rationale: Single source of metadata. Auto OpenAI function schema. Old @assistant_tool → @DEPRECATED.
Rejected: Dual registration (@assistant_tool + StructuredTool) — redundant.
3. Confirmation: interrupt_before=DANGEROUS_TOOLS
Decision: LangGraph native interrupt_before=DANGEROUS_TOOLS + Command(resume=...). No custom HumanInTheLoopMiddleware — LangGraph provides HITL natively via checkpointing and resume. Second submit() with additional_inputs[1]="confirm"/"deny" triggers resume.
Rationale: Zero REST endpoints. LangGraph checkpoint ensures safe resume.
Rejected: REST confirmation endpoints + polling — more code, more latency. Custom middleware — LangGraph has native interrupt support.
4. History: RunnableWithMessageHistory
Decision: LangChain auto-loads/saves message history. Svelte does NOT pass history — only conversation_id.
Rationale: Eliminates manual history management on frontend and backend.
Rejected: Manual history parameter in submit() — fragile.
5. Checkpoints: PostgresSaver
Decision: PostgreSQL via langgraph-checkpoint-postgres. Same instance as FastAPI. Survives all container restarts. Thread ID = conversation_id.
Rationale: Agent state persists across container restarts.
Rejected: In-memory only — lost on restart.
6. Gradio Native Features
Decision: additional_inputs, gr.Request, concurrency_limit=1, examples. All used.
Rationale: Eliminates custom code for conversation_id passing, JWT extraction, message queuing, welcome chips.
Rejected: Custom headers, manual queues — redundant.
7. Tool Execution: HTTP to FastAPI
Decision: All @tool functions call FastAPI REST with user JWT (not service JWT).
Rationale: RBAC enforced by FastAPI under user identity. Service JWT only for bootstrap.
Rejected: Direct import — Gradio has no DB connection.
8. Streaming: Structured JSON Metadata
Decision: LangGraph events yield ChatMessage objects with metadata.type (stream_token, tool_start, tool_end, tool_error, confirm_required, confirm_resolved, error). Frontend parses structured JSON from event.data, not emoji strings.
Rationale: Structured metadata is deterministic, typed, and localizable. Emoji string parsing is brittle and collides with model output.
Rejected: Plain-text prefix protocol — rejected because emoji prefix parsing is fragile, hard to localize, and error-prone.
#endregion AgentChat.Research