fix(Structured Output Parser Node, Auto-fixing Output Parser Node, Tools Agent Node): Structured output improvements (#13908)

Co-authored-by: Oleg Ivaniv <me@olegivaniv.com>
This commit is contained in:
Eugene
2025-03-13 18:20:24 +03:00
committed by GitHub
parent 31037484a5
commit 5b6b78709e
7 changed files with 138 additions and 13 deletions

View File

@@ -211,7 +211,7 @@ export function handleParsedStepOutput(
/**
* Parses agent steps using the provided output parser.
* If the agent used the 'format_final_response' tool, the output is parsed accordingly.
* If the agent used the 'format_final_json_response' tool, the output is parsed accordingly.
*
* @param steps - The agent finish or action steps
* @param outputParser - The output parser (if defined)
@@ -221,9 +221,9 @@ export function handleParsedStepOutput(
export const getAgentStepsParser =
(outputParser?: N8nOutputParser, memory?: BaseChatMemory) =>
async (steps: AgentFinish | AgentAction[]): Promise<AgentFinish | AgentAction[]> => {
// Check if the steps contain the 'format_final_response' tool invocation.
// Check if the steps contain the 'format_final_json_response' tool invocation.
if (Array.isArray(steps)) {
const responseParserTool = steps.find((step) => step.tool === 'format_final_response');
const responseParserTool = steps.find((step) => step.tool === 'format_final_json_response');
if (responseParserTool && outputParser) {
const toolInput = responseParserTool.toolInput;
// Ensure the tool input is a string
@@ -318,9 +318,9 @@ export async function getTools(
const schema = getOutputParserSchema(outputParser);
const structuredOutputParserTool = new DynamicStructuredTool({
schema,
name: 'format_final_response',
name: 'format_final_json_response',
description:
'Always use this tool for the final output to the user. It validates the output so only use it when you are sure the output is final.',
'Use this tool to format your final response to the user in a structured JSON format. This tool validates your output against a schema to ensure it meets the required format. ONLY use this tool when you have completed all necessary reasoning and are ready to provide your final answer. Do not use this tool for intermediate steps or for asking questions. The output from this tool will be directly returned to the user.',
// We do not use a function here because we intercept the output with the parser.
func: async () => '',
});
@@ -454,7 +454,7 @@ export async function toolsAgentExecute(this: IExecuteFunctions): Promise<INodeE
input,
system_message: options.systemMessage ?? SYSTEM_MESSAGE,
formatting_instructions:
'IMPORTANT: Always call `format_final_response` to format your final response!',
'IMPORTANT: For your response to user, you MUST use the `format_final_json_response` tool with your complete answer formatted according to the required schema. Do not attempt to format the JSON manually - always use this tool. Your response will be rejected if it is not properly formatted through this tool. Only use this tool once you are ready to provide your final answer.',
},
{ signal: this.getExecutionCancelSignal() },
);

View File

@@ -217,7 +217,7 @@ describe('getTools', () => {
const tools = await getTools(ctx, fakeOutputParser);
// Our fake getConnectedTools returns one tool; with outputParser, one extra is appended.
expect(tools.length).toEqual(2);
const dynamicTool = tools.find((t) => t.name === 'format_final_response');
const dynamicTool = tools.find((t) => t.name === 'format_final_json_response');
expect(dynamicTool).toBeDefined();
});
});