Wenn Sie "Interrupt" verwendet, gefolgt von 'neu
Posted: 27 Feb 2025, 06:09
Wenn ich ein Diagramm aufreiche, das Interrupt s in einem seiner Knoten enthält, scheint es mit dem folgenden Fehler in einen ungültigen/nicht wiederbeschreibbaren Zustand zu geraten:
Die erste Begegnung des Interrupt scheint gut zu laufen, aber nach der zweiten Begegnung tritt dies auf. />
Dies führt jedoch zu dem gleichen Fehler wie oben, nur dass es nach dem ersten Interrupt statt der zweiten Interrupt . Der Workflow habe ich gültig definiert? Gibt es einen besseren Weg, um es zu strukturieren? />[*]https://langchain-ai.github.io/langgrap ... -or-reject
https /> < /ul>
Hauptdiagramm: < /p>
Code: Select all
return chatGeneration.message;
^
TypeError: Cannot read properties of undefined (reading 'message')
at ChatOpenAI.invoke (file:///Users/user/code/lgdemo//node_modules/@langchain/core/dist/language_models/chat_models.js:64:31)
- Wenn der Benutzer mit y antwortet, fährt er zu dem ToolsNode , den der AgentNode angefordert hat. End ,
Der nachfolgende Aufruf von Graph.invoke Ergebnisse in diesem Fehler. angefordert. (Wie zuvor) - Wenn der Benutzer mit etwas anderem reagiert (z. B. n ), fährt er zu AgentNode zurück. (Dies hat sich geändert)
Dies führt jedoch zu dem gleichen Fehler wie oben, nur dass es nach dem ersten Interrupt statt der zweiten Interrupt . Der Workflow habe ich gültig definiert? Gibt es einen besseren Weg, um es zu strukturieren? />[*]https://langchain-ai.github.io/langgrap ... -or-reject
https /> < /ul>
Hauptdiagramm: < /p>
Code: Select all
const workflow = new StateGraph(MessagesAnnotation)
.addNode('agent', agentNode)
.addNode('tools', toolsNode)
.addNode('approve', approveNode, {
ends: ['tools', END],
})
.addEdge(START, 'agent')
.addEdge('tools', 'agent')
.addConditionalEdges('agent', agentRouter, ['approve', END]);
const checkpointer = new MemorySaver();
const graph = workflow.compile({
checkpointer,
});
const graphConfig = {
configurable: { thread_id: '0x0004' },
};
< /code>
Tools, Knoten und Router: < /p>
const cmdFooTool = tool(async function(inputs) {
console.log('===TOOL CMD_FOO===');
return inputs.name;
}, {
name: 'CMD_FOO',
description: 'Invoke when you want to do a Foo.',
schema: z.object({
name: z.string('Any string'),
}),
});
const cmdBarTool = tool(async function(inputs) {
console.log('===TOOL QRY_BAR===');
return inputs.name;
}, {
name: 'QRY_BAR',
description: 'Invoke when you want to query a Bar.',
schema: z.object({
name: z.string('Any string'),
}),
});
const tools = [cmdFooTool, cmdBarTool];
const llmWithTools = llm.bindTools(tools);
const toolsNode = new ToolNode(tools);
async function agentNode(state) {
console.log('===AGENT NODE===');
const response = await llmWithTools.invoke(state.messages);
console.log('=RESPONSE=',
'\ncontent:', response.content,
'\ntool_calls:', response.tool_calls.map((toolCall) => (toolCall.name)));
return { messages: [response] };
}
async function approveNode (state) {
console.log('===APPROVE NODE===');
const lastMsg = state.messages.at(-1);
const toolCall = lastMsg.tool_calls.at(-1);
const interruptMessage = `Please review the following tool invocation:
${toolCall.name} with inputs ${JSON.stringify(toolCall.args, undefined, 2)}
Do you approve (y/N)`;
console.log('=INTERRUPT PRE=');
const interruptResponse = interrupt(interruptMessage);
console.log('=INTERRUPT POST=');
const isApproved = (interruptResponse.trim().charAt(0).toLowerCase() === 'y');
const goto = (isApproved) ? 'tools' : END;
console.log('=RESULT=\n', { isApproved, goto });
return new Command({ goto });
}
function hasToolCalls(message) {
return message?.tool_calls?.length > 0;
}
async function agentRouter (state) {
const lastMsg = state.messages.at(-1);
if (hasToolCalls(lastMsg)) {
return 'approve';
}
return END;
}
< /code>
Simulieren Sie einen Lauf: < /p>
let state;
let agentResult;
let inputText;
let invokeWith;
// step 1: prompt
inputText = 'Pls perform a Foo with name "ASDF".';
console.log('===HUMAN PROMPT===\n', inputText);
invokeWith = { messages: [new HumanMessage(inputText)] };
agentResult = await graph.invoke(invokeWith, graphConfig);
state = await graph.getState(graphConfig);
console.log('===STATE NEXT===\n', state.next);
console.log('=LAST MSG=\n', agentResult.messages.at(-1).content);
console.log('=LAST TOOL CALLS=\n', agentResult.messages.at(-1).tool_calls);
// step 2: interrupted in the 'approve' node, human in the loop authorises
inputText = 'yes'
console.log('===HUMAN INTERRUPT RESPONSE===\n', inputText);
invokeWith = new Command({ resume: inputText });
agentResult = await graph.invoke(invokeWith, graphConfig);
state = await graph.getState(graphConfig);
console.log('===STATE NEXT===\n', state.next);
console.log('=LAST MSG=\n', agentResult.messages.at(-1).content);
console.log('=LAST TOOL CALLS=\n', agentResult.messages.at(-1).tool_calls);
// step 3: prompt
inputText = 'Pls perform a Foo with name "ZXCV".';
console.log('===HUMAN PROMPT===\n', inputText);
invokeWith = { messages: [new HumanMessage(inputText)] };
agentResult = await graph.invoke(invokeWith, graphConfig);
state = await graph.getState(graphConfig);
console.log('===STATE NEXT===\n', state.next);
console.log('=LAST MSG=\n', agentResult.messages.at(-1).content);
console.log('=LAST TOOL CALLS=\n', agentResult.messages.at(-1).tool_calls);
// step 4: interrupted in the 'approve' node, human in the loop does not authorise
inputText = 'no';
console.log('===HUMAN INTERRUPT RESPONSE===\n', inputText);
invokeWith = new Command({ resume: inputText });
agentResult = await graph.invoke(invokeWith, graphConfig);
state = await graph.getState(graphConfig);
console.log('===STATE NEXT===\n', state.next);
console.log('=LAST MSG=\n', agentResult.messages.at(-1).content);
console.log('=LAST TOOL CALLS=\n', agentResult.messages.at(-1).tool_calls);
// step 5: prompt
inputText = 'Pls perform a Foo with name "GHJK".';
console.log('===HUMAN PROMPT===\n', inputText);
invokeWith = { messages: [new HumanMessage(inputText)] };
agentResult = await graph.invoke(invokeWith, graphConfig);
state = await graph.getState(graphConfig);
console.log('===STATE NEXT===\n', state.next);
console.log('=LAST MSG=\n', agentResult.messages.at(-1).content);
console.log('=LAST TOOL CALLS=\n', agentResult.messages.at(-1).tool_calls);
< /code>
Vollständige Ausgabe: < /p>
===HUMAN PROMPT===
Pls perform a Foo with name "ASDF".
===AGENT NODE===
(node:58990) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
=RESPONSE=
content:
tool_calls: [ 'CMD_FOO' ]
===APPROVE NODE===
=INTERRUPT PRE=
===STATE NEXT===
[ 'approve' ]
=LAST MSG=
=LAST TOOL CALLS=
[
{
name: 'CMD_FOO',
args: { name: 'ASDF' },
type: 'tool_call',
id: 'call_u7CIyWdTesFATZ5bGG2uaVUZ'
}
]
===HUMAN INTERRUPT RESPONSE===
yes
===APPROVE NODE===
=INTERRUPT PRE=
=INTERRUPT POST=
=RESULT=
{ isApproved: true, goto: 'tools' }
===TOOL CMD_FOO===
===AGENT NODE===
=RESPONSE=
content: The Foo operation has been performed with the name "ASDF".
tool_calls: []
===STATE NEXT===
[]
=LAST MSG=
The Foo operation has been performed with the name "ASDF".
=LAST TOOL CALLS=
[]
===HUMAN PROMPT===
Pls perform a Foo with name "ZXCV".
===AGENT NODE===
=RESPONSE=
content:
tool_calls: [ 'CMD_FOO' ]
===APPROVE NODE===
=INTERRUPT PRE=
===STATE NEXT===
[ 'approve' ]
=LAST MSG=
=LAST TOOL CALLS=
[
{
name: 'CMD_FOO',
args: { name: 'ZXCV' },
type: 'tool_call',
id: 'call_kKF91c8G6enWwlrLFON8TYLJ'
}
]
===HUMAN INTERRUPT RESPONSE===
no
===APPROVE NODE===
=INTERRUPT PRE=
=INTERRUPT POST=
=RESULT=
{ isApproved: false, goto: '__end__' }
===STATE NEXT===
[]
=LAST MSG=
=LAST TOOL CALLS=
[
{
name: 'CMD_FOO',
args: { name: 'ZXCV' },
type: 'tool_call',
id: 'call_kKF91c8G6enWwlrLFON8TYLJ'
}
]
===HUMAN PROMPT===
Pls perform a Foo with name "GHJK".
===AGENT NODE===
file:///Users/user/code/lgdemo/node_modules/@langchain/core/dist/language_models/chat_models.js:64
return chatGeneration.message;
^
TypeError: Cannot read properties of undefined (reading 'message')
at ChatOpenAI.invoke (file:///Users/user/code/lgdemo//node_modules/@langchain/core/dist/language_models/chat_models.js:64:31)
at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
at async RunnableCallable.agentNode [as func] (file:///Users/user/code/lgdemo//test.js:51:20)
at async RunnableCallable.invoke (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/utils.js:79:27)
at async RunnableSequence.invoke (file:///Users/user/code/lgdemo//node_modules/@langchain/core/dist/runnables/base.js:1274:33)
at async _runWithRetry (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/pregel/retry.js:67:22)
at async PregelRunner._executeTasksWithRetry (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/pregel/runner.js:217:33)
at async PregelRunner.tick (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/pregel/runner.js:45:40)
at async CompiledStateGraph._runLoop (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/pregel/index.js:1296:17)
at async createAndRunLoop (file:///Users/user/code/lgdemo//node_modules/@langchain/langgraph/dist/pregel/index.js:1195:17) {
pregelTaskId: '7bd60c12-4beb-54b7-85a7-9bc1461600f5'
}
Node.js v23.3.0