feat(langgraph): use createReactAgent description for supervisor agent handoffs (#1515)

This commit is contained in:
David Duong
2025-08-09 01:56:09 +02:00
committed by GitHub
parent 72386a46cb
commit cb4b17af3d
6 changed files with 69 additions and 8 deletions
+6
View File
@@ -0,0 +1,6 @@
---
"@langchain/langgraph-supervisor": patch
"@langchain/langgraph": patch
---
feat(langgraph): use createReactAgent description for supervisor agent handoffs
+8 -2
View File
@@ -17,7 +17,13 @@ function _normalizeAgentName(agentName: string): string {
return agentName.trim().replace(WHITESPACE_RE, "_").toLowerCase();
}
const createHandoffTool = ({ agentName }: { agentName: string }) => {
const createHandoffTool = ({
agentName,
agentDescription,
}: {
agentName: string;
agentDescription?: string;
}) => {
/**
* Create a tool that can handoff control to the requested agent.
*
@@ -53,7 +59,7 @@ const createHandoffTool = ({ agentName }: { agentName: string }) => {
{
name: toolName,
schema: z.object({}),
description: "Ask another agent for help.",
description: agentDescription ?? "Ask another agent for help.",
}
);
return handoffTool;
+3 -5
View File
@@ -207,8 +207,8 @@ const createSupervisor = <
agentNames.add(agent.name);
}
const handoffTools = agents.map((agent) =>
createHandoffTool({ agentName: agent.name! })
const handoffTools = agents.map(({ name, description }) =>
createHandoffTool({ agentName: name!, agentDescription: description })
);
const allTools = [...(tools ?? []), ...handoffTools];
@@ -271,9 +271,7 @@ const createSupervisor = <
builder = builder.addNode(
agent.name!,
makeCallAgent(agent, outputMode, addHandoffBackMessages, supervisorName),
{
subgraphs: [agent],
}
{ subgraphs: [agent] }
);
builder = builder.addEdge(agent.name!, supervisorAgent.name!);
}
@@ -4,7 +4,7 @@ import { LanguageModelLike } from "@langchain/core/language_models/base";
import { tool } from "@langchain/core/tools";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
import { MessagesAnnotation } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { createReactAgent, ToolNode } from "@langchain/langgraph/prebuilt";
import { createSupervisor } from "../supervisor.js";
import { FakeToolCallingChatModel } from "./utils.js";
import { withAgentName, AgentNameMode } from "../index.js";
@@ -198,6 +198,7 @@ describe("Test supervisor basic workflow", () => {
llm: mathLLM,
tools: [add],
name: "math_expert",
description: "Math expert.",
prompt: "You are a math expert. Always use one tool at a time.",
});
@@ -205,6 +206,7 @@ describe("Test supervisor basic workflow", () => {
llm: researchLLM,
tools: [webSearch],
name: "research_expert",
description: "World class researcher with access to web search.",
prompt:
"You are a world class researcher with access to web search. Do not do any math.",
});
@@ -218,6 +220,20 @@ describe("Test supervisor basic workflow", () => {
includeAgentName,
});
const toolNode = (
workflow.nodes.supervisor.runnable as ReturnType<
typeof createReactAgent
>
).nodes.tools.bound as ToolNode;
expect(toolNode.tools).toMatchObject([
{ name: "transfer_to_math_expert", description: "Math expert." },
{
name: "transfer_to_research_expert",
description: "World class researcher with access to web search.",
},
]);
const app = workflow.compile();
expect(app).toBeDefined();
+27
View File
@@ -749,6 +749,7 @@ export class StateGraph<
interruptBefore,
interruptAfter,
name,
description,
}: {
checkpointer?: BaseCheckpointSaver | boolean;
store?: BaseStore;
@@ -756,6 +757,7 @@ export class StateGraph<
interruptBefore?: N[] | All;
interruptAfter?: N[] | All;
name?: string;
description?: string;
} = {}): CompiledStateGraph<
Prettify<S>,
Prettify<U>,
@@ -801,6 +803,7 @@ export class StateGraph<
store,
cache,
name,
description,
});
// attach nodes, edges and branches
@@ -880,9 +883,33 @@ export class CompiledStateGraph<
> {
declare builder: StateGraph<unknown, S, U, N, I, O, C, NodeReturnType>;
/**
* The description of the compiled graph.
* This is used by the supervisor agent to describe the handoff to the agent.
*/
description?: string;
/** @internal */
_metaRegistry: SchemaMetaRegistry = schemaMetaRegistry;
constructor({
description,
...rest
}: { description?: string } & ConstructorParameters<
typeof CompiledGraph<
N,
S,
U,
StateType<ToStateDefinition<C>>,
UpdateType<ToStateDefinition<I>>,
StateType<ToStateDefinition<O>>,
NodeReturnType
>
>[0]) {
super(rest);
this.description = description;
}
attachNode(key: typeof START, node?: never): void;
attachNode(key: N, node: StateGraphNodeSpec<S, U>): void;
@@ -564,6 +564,12 @@ export type CreateReactAgentParams<
*/
name?: string;
/**
* An optional description for the agent.
* This can be used to describe the agent to the underlying supervisor LLM.
*/
description?: string | undefined;
/**
* Use to specify how to expose the agent name to the underlying supervisor LLM.
@@ -684,6 +690,7 @@ export function createReactAgent<
preModelHook,
postModelHook,
name,
description,
version = "v1",
includeAgentName,
} = params;
@@ -987,5 +994,6 @@ export function createReactAgent<
interruptAfter,
store,
name,
description,
});
}