HIL for plan approval
This commit is contained in:
parent
a3a26056ff
commit
ebbe668a18
3 changed files with 114 additions and 11 deletions
|
|
@ -68,30 +68,36 @@ def create_agent(
|
|||
qa_provider: QA provider for the agent (default: from Config.QA_PROVIDER)
|
||||
qa_model: Model name to use (default: from Config.QA_MODEL)
|
||||
"""
|
||||
print(f"[AGENT SETUP] Creating agent with provider={qa_provider}, model={qa_model}")
|
||||
agent = Agent(
|
||||
model=get_model(qa_provider, qa_model),
|
||||
deps_type=ResearchDeps,
|
||||
instructions="""You are a research co-pilot powered by haiku.rag.
|
||||
|
||||
Your workflow:
|
||||
1. When user asks a question, IMMEDIATELY call propose_research_plan with the question
|
||||
2. Wait for user approval before proceeding
|
||||
3. Once approved, process questions ONE AT A TIME:
|
||||
Your workflow MUST follow these exact steps in order:
|
||||
1. Call propose_research_plan with the user's question
|
||||
2. After propose_research_plan completes, IMMEDIATELY call approve_research_plan (with no arguments)
|
||||
3. WAIT for approve_research_plan to return:
|
||||
- If it returns "APPROVED", proceed to step 4
|
||||
- If it returns "REVISE", ask the user "How would you like me to revise the research plan?" and wait for their response
|
||||
- Once you receive their revision feedback, revise the plan and go back to step 1
|
||||
4. Once approved, process questions ONE AT A TIME:
|
||||
- Call search_question(question_id=0) and WAIT for it to complete
|
||||
- Then call extract_insights_from_results(question_id=0) and WAIT for it to complete
|
||||
- Then call search_question(question_id=1) and WAIT for it to complete
|
||||
- Then call extract_insights_from_results(question_id=1) and WAIT for it to complete
|
||||
- Then call search_question(question_id=2) and WAIT for it to complete
|
||||
- Then call extract_insights_from_results(question_id=2) and WAIT for it to complete
|
||||
4. After all questions are processed, call evaluate_research_confidence
|
||||
5. Ask user if they want to finalize or continue researching
|
||||
6. When user approves, call synthesize_final_report
|
||||
5. After all questions are processed, call evaluate_research_confidence
|
||||
6. Ask user if they want to finalize or continue researching
|
||||
7. When user approves, call synthesize_final_report
|
||||
|
||||
CRITICAL RULES:
|
||||
- MANDATORY: Call approve_research_plan immediately after propose_research_plan - NO EXCEPTIONS
|
||||
- If approve_research_plan returns "REVISE", ask the user for revision feedback naturally in chat
|
||||
- Call ONE tool at a time - wait for each tool to return before calling the next
|
||||
- NEVER call extract_insights_from_results until search_question has completed and returned results
|
||||
- DO NOT explain what you're about to do - just call the tool
|
||||
- DO NOT say "I'll search for..." or "Let me search..." - just call search_question
|
||||
- The state updates will show the user what's happening - you don't need to narrate
|
||||
- Process all 3 questions automatically without asking for approval between them
|
||||
|
||||
|
|
@ -152,6 +158,7 @@ Return ONLY a JSON array of sub-questions, like: ["Question 1?", "Question 2?",
|
|||
ctx.deps.state.status = f"Proposed plan with {len(plan)} sub-questions"
|
||||
print(f"[AGENT] Plan created with {len(plan)} sub-questions")
|
||||
print("[AGENT] Sending state snapshot to frontend")
|
||||
print("[AGENT] *** NEXT STEP: Agent should call approve_research_plan ***")
|
||||
|
||||
return _as_state_snapshot(ctx)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ import {
|
|||
} from "@copilotkit/runtime";
|
||||
import type { NextRequest } from "next/server";
|
||||
|
||||
// Service adapter for multi-agent support (empty since we only have one agent)
|
||||
const serviceAdapter = new ExperimentalEmptyAdapter();
|
||||
|
||||
// Connect CopilotKit to PydanticAI via HttpAgent
|
||||
// The HttpAgent creates a bridge between the Next.js frontend and the Python backend
|
||||
// It communicates with the server created by agent.to_ag_ui()
|
||||
|
|
@ -21,6 +18,9 @@ const runtime = new CopilotRuntime({
|
|||
},
|
||||
});
|
||||
|
||||
// Service adapter for multi-agent support (empty since we only have one agent)
|
||||
const serviceAdapter = new ExperimentalEmptyAdapter();
|
||||
|
||||
// Next.js API route handler that proxies requests between frontend and backend
|
||||
export async function POST(request: NextRequest) {
|
||||
const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
CopilotKit,
|
||||
useCoAgent,
|
||||
useCoAgentStateRender,
|
||||
useCopilotAction,
|
||||
} from "@copilotkit/react-core";
|
||||
import { CopilotChat } from "@copilotkit/react-ui";
|
||||
import "@copilotkit/react-ui/styles.css";
|
||||
|
|
@ -94,6 +96,100 @@ function AgentContent() {
|
|||
// Log state changes
|
||||
console.log("[FRONTEND] Current state:", state);
|
||||
|
||||
// Human-in-the-loop: Request approval for research plan
|
||||
console.log("[FRONTEND] Registering approve_research_plan action");
|
||||
useCopilotAction({
|
||||
name: "approve_research_plan",
|
||||
description:
|
||||
"Request user approval for the research plan. Returns 'APPROVED' if approved or 'REVISE' if user wants to revise.",
|
||||
parameters: [],
|
||||
renderAndWaitForResponse: ({ respond, status }) => {
|
||||
console.log(
|
||||
"[FRONTEND ACTION] renderAndWaitForResponse called",
|
||||
{ status }
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
padding: "1.5rem",
|
||||
background: "white",
|
||||
borderRadius: "8px",
|
||||
border: "2px solid #4299e1",
|
||||
marginBottom: "1rem",
|
||||
boxShadow: "0 2px 8px rgba(0,0,0,0.1)",
|
||||
}}
|
||||
>
|
||||
<h3
|
||||
style={{
|
||||
fontSize: "1.25rem",
|
||||
fontWeight: "bold",
|
||||
marginBottom: "1rem",
|
||||
color: "#2d3748",
|
||||
}}
|
||||
>
|
||||
Research Plan Approval
|
||||
</h3>
|
||||
<p
|
||||
style={{
|
||||
fontSize: "0.875rem",
|
||||
color: "#4a5568",
|
||||
marginBottom: "1rem",
|
||||
}}
|
||||
>
|
||||
Please review the research plan in the right pane.
|
||||
</p>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: "1rem",
|
||||
}}
|
||||
className={status !== "executing" ? "hidden" : ""}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => respond?.("REVISE")}
|
||||
disabled={status !== "executing"}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: "0.75rem",
|
||||
background: "white",
|
||||
border: "2px solid #e2e8f0",
|
||||
borderRadius: "6px",
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: "600",
|
||||
cursor: status === "executing" ? "pointer" : "not-allowed",
|
||||
opacity: status === "executing" ? 1 : 0.5,
|
||||
}}
|
||||
>
|
||||
Revise Plan
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => respond?.("APPROVED")}
|
||||
disabled={status !== "executing"}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: "0.75rem",
|
||||
background: "#4299e1",
|
||||
color: "white",
|
||||
border: "none",
|
||||
borderRadius: "6px",
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: "600",
|
||||
cursor: status === "executing" ? "pointer" : "not-allowed",
|
||||
opacity: status === "executing" ? 1 : 0.5,
|
||||
}}
|
||||
>
|
||||
Approve & Start Research
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// Render state updates from the research agent
|
||||
useCoAgentStateRender<ResearchState>({
|
||||
name: "research_agent",
|
||||
|
|
|
|||
Loading…
Reference in a new issue