-
Notifications
You must be signed in to change notification settings - Fork 1.4k
fix(agent): use actual value in fillForm instead of hallucinated placeholder #1863
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
base: main
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import type { V3 } from "../../v3.js"; | |
| import type { Action } from "../../types/public/methods.js"; | ||
| import type { AgentModelConfig, Variables } from "../../types/public/agent.js"; | ||
| import { TimeoutError } from "../../types/public/sdkErrors.js"; | ||
| import { substituteVariables } from "../utils/variables.js"; | ||
|
|
||
| export const fillFormTool = ( | ||
| v3: V3, | ||
|
|
@@ -12,18 +13,22 @@ export const fillFormTool = ( | |
| toolTimeout?: number, | ||
| ) => { | ||
| const hasVariables = variables && Object.keys(variables).length > 0; | ||
| const valueDescription = hasVariables | ||
| ? `The exact text to type into the field. Use %variableName% to substitute a variable value. Available: ${Object.keys(variables).join(", ")}` | ||
| : "The exact text to type into the field"; | ||
| const actionDescription = hasVariables | ||
| ? `Must follow the pattern: "type <exact value> into the <field name> <fieldType>". Use %variableName% to substitute a variable value. Available: ${Object.keys(variables).join(", ")}. Examples: "type %email% into the email input", "type %password% into the password input"` | ||
| : 'Must follow the pattern: "type <exact value> into the <field name> <fieldType>". Examples: "type john@example.com into the email input", "type John into the first name input"'; | ||
| ? `Describe which field to target, e.g. "type into the email input", "type into the password field". Use %variableName% to substitute a variable value. Available: ${Object.keys(variables).join(", ")}. Example: "type %email% into the email input"` | ||
| : 'Describe which field to target, e.g. "type into the email input", "type into the first name input"'; | ||
|
|
||
| return tool({ | ||
| description: | ||
| 'FORM FILL - MULTI-FIELD INPUT TOOL\nFill 2+ form inputs/textareas at once. Each action MUST include the exact text to type and the target field, e.g. "type john@example.com into the email field".', | ||
| 'FORM FILL - MULTI-FIELD INPUT TOOL\nFill 2+ form inputs/textareas at once. Each field requires an action describing the target element and a value with the text to type.', | ||
| inputSchema: z.object({ | ||
| fields: z | ||
| .array( | ||
| z.object({ | ||
| action: z.string().describe(actionDescription), | ||
| value: z.string().describe(valueDescription), | ||
| }), | ||
| ) | ||
| .min(1, "Provide at least one field to fill"), | ||
|
|
@@ -50,6 +55,17 @@ export const fillFormTool = ( | |
| : { timeout: toolTimeout }; | ||
| const observeResults = await v3.observe(instruction, observeOptions); | ||
|
|
||
| // Override observe results with the actual values provided by the agent. | ||
| // The LLM used by observe() may hallucinate placeholder values instead of | ||
| // using the intended text, so we inject the real values before calling act(). | ||
| for (let i = 0; i < observeResults.length && i < fields.length; i++) { | ||
| const res = observeResults[i]; | ||
| if (res.method === "fill" && res.arguments && res.arguments.length > 0) { | ||
| const actualValue = substituteVariables(fields[i].value, variables); | ||
| res.arguments[0] = actualValue; | ||
| } | ||
| } | ||
|
Comment on lines
+61
to
+67
Contributor
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.
The loop assumes A concrete example: given
In the old code the value was embedded in the action string so each observe result already carried its own value regardless of ordering. The new separation of value from action makes the ordering dependency a correctness issue. A safer approach would be to make one |
||
|
|
||
| const completed = [] as unknown[]; | ||
| const replayableActions: Action[] = []; | ||
| for (const res of observeResults) { | ||
|
|
||
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.
actiondescription is misleadingThe
actionDescriptionfor thehasVariablescase still mentions%variableName%syntax and includes the example"type %email% into the email input". This suggests putting variable tokens in theactionfield, but theactionstring is used only to build theobserve()instruction and nosubstituteVariables()call is ever applied to it—so any%variableName%token placed there would be passed verbatim to the observe LLM and never resolved.Since the intent of this PR is to separate element targeting (action) from value typing (value), the
actiondescription should be cleaned up to remove the variable-substitution guidance entirely: