-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Update close tool + add output to agent result #1505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7a4ffc7
060dd41
1c10b11
2db6f84
a0cdf3b
1212046
050b846
8555ada
d0082c6
dfb703a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@browserbasehq/stagehand": patch | ||
| --- | ||
|
|
||
| Add structured output to agent result + ensure close tool is always called |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import { generateText, ModelMessage, LanguageModel, ToolSet } from "ai"; | ||
| import { z } from "zod"; | ||
| import { tool } from "ai"; | ||
| import { LogLine } from "../../types/public/logs"; | ||
| import { StagehandZodObject } from "../../zodCompat"; | ||
| interface CloseResult { | ||
| reasoning: string; | ||
| taskComplete: boolean; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
| messages: ModelMessage[]; | ||
| output?: Record<string, unknown>; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend making output required, LLMs are really good at inferring what the ideal output should be for a task. e.g. if user is researching something often it nails it and puts the exact data they were looking for in output. |
||
| } | ||
|
|
||
| const baseCloseSchema = z.object({ | ||
| reasoning: z | ||
| .string() | ||
| .describe("Brief summary of what actions were taken and the outcome"), | ||
| taskComplete: z | ||
| .boolean() | ||
| .describe("true if the task was fully completed, false otherwise"), | ||
| }); | ||
|
|
||
| /** | ||
| * Force a close tool call at the end of an agent run. | ||
| * This ensures we always get a structured final response, | ||
| * even if the main loop ended without calling close. | ||
| */ | ||
| export async function handleCloseToolCall(options: { | ||
| model: LanguageModel; | ||
| inputMessages: ModelMessage[]; | ||
| instruction: string; | ||
| outputSchema?: StagehandZodObject; | ||
| logger: (message: LogLine) => void; | ||
| }): Promise<CloseResult> { | ||
| const { model, inputMessages, instruction, outputSchema, logger } = options; | ||
|
|
||
| logger({ | ||
| category: "agent", | ||
| message: "Agent calling tool: close", | ||
| level: 1, | ||
| }); | ||
| // Merge base close schema with user-provided output schema if present | ||
| const closeToolSchema = outputSchema | ||
| ? baseCloseSchema.extend({ | ||
| output: outputSchema.describe( | ||
| "The specific data the user requested from this task", | ||
| ), | ||
| }) | ||
| : baseCloseSchema; | ||
|
|
||
| const outputInstructions = outputSchema | ||
| ? `\n\nThe user also requested the following information from this task. Provide it in the "output" field:\n${JSON.stringify( | ||
| Object.fromEntries( | ||
| Object.entries(outputSchema.shape).map(([key, value]) => [ | ||
| key, | ||
| value.description || "no description", | ||
| ]), | ||
| ), | ||
| null, | ||
| 2, | ||
| )}` | ||
| : ""; | ||
|
|
||
| const systemPrompt = `You are a web automation assistant that was tasked with completing a task. | ||
| The task was: | ||
| "${instruction}" | ||
| Review what was accomplished and provide your final assessment in whether the task was completed successfully. you have been provided with the history of the actions taken so far, use this to determine if the task was completed successfully.${outputInstructions} | ||
| Call the "close" tool with: | ||
| 1. A brief summary of what was done | ||
| 2. Whether the task was completed successfully${outputSchema ? "\n3. The requested output data based on what you found" : ""}`; | ||
|
|
||
| const closeTool = tool({ | ||
| description: outputSchema | ||
| ? "Complete the task with your assessment and the requested output data." | ||
| : "Complete the task with your final assessment.", | ||
| inputSchema: closeToolSchema, | ||
| execute: async (params) => { | ||
| return { success: true, ...params }; | ||
| }, | ||
| }); | ||
|
|
||
| const userPrompt: ModelMessage = { | ||
| role: "user", | ||
| content: outputSchema | ||
| ? "Provide your final assessment and the requested output data." | ||
| : "Provide your final assessment.", | ||
| }; | ||
|
|
||
| const result = await generateText({ | ||
| model, | ||
| system: systemPrompt, | ||
| messages: [...inputMessages, userPrompt], | ||
| tools: { close: closeTool } as ToolSet, | ||
| toolChoice: { type: "tool", toolName: "close" }, | ||
| }); | ||
|
|
||
| const closeToolCall = result.toolCalls.find((tc) => tc.toolName === "close"); | ||
| const outputMessages: ModelMessage[] = [ | ||
| userPrompt, | ||
| ...(result.response?.messages || []), | ||
| ]; | ||
|
|
||
| if (!closeToolCall) { | ||
| return { | ||
| reasoning: result.text || "Task execution completed", | ||
| taskComplete: false, | ||
| messages: outputMessages, | ||
| }; | ||
| } | ||
|
|
||
| const input = closeToolCall.input as z.infer<typeof baseCloseSchema> & { | ||
| output?: Record<string, unknown>; | ||
| }; | ||
| logger({ | ||
| category: "agent", | ||
| message: `Task completed`, | ||
| level: 1, | ||
| }); | ||
|
|
||
| return { | ||
| reasoning: input.reasoning, | ||
| taskComplete: input.taskComplete, | ||
| messages: outputMessages, | ||
| output: input.output, | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.