From 73a4f3afa17d3c4809f7c63baa7e9f24867b35d5 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Thu, 18 Sep 2025 20:17:43 -0400 Subject: [PATCH 1/8] basic examples w/ user roles and convo length --- src/oss/langchain/agents.mdx | 73 ++++++++++++++++++- src/oss/langchain/middleware.mdx | 119 +++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 1 deletion(-) diff --git a/src/oss/langchain/agents.mdx b/src/oss/langchain/agents.mdx index 5f18b8df6..40b74df8c 100644 --- a/src/oss/langchain/agents.mdx +++ b/src/oss/langchain/agents.mdx @@ -465,8 +465,79 @@ const agent = createAgent({ When no `prompt` is provided, the agent will infer its task from the messages directly. +#### Dynamic prompts with middleware + +For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the `DynamicSystemPromptMiddleware`. This is especially useful for personalizing prompts based on user roles, conversation context, or other dynamic factors: + +:::python +```python wrap +from langchain.agents import create_agent +from langchain.agents.middleware import DynamicSystemPromptMiddleware +from langgraph.runtime import Runtime +from typing import TypedDict + +class Context(TypedDict): + user_role: str + +def dynamic_system_prompt(state, runtime: Runtime[Context]) -> str: + user_role = runtime.context.get("user_role", "user") + base_prompt = "You are a helpful assistant." + + if user_role == "expert": + return f"{base_prompt} Provide detailed technical responses." + elif user_role == "beginner": + return f"{base_prompt} Explain concepts simply and avoid jargon." + return base_prompt + +agent = create_agent( + model="openai:gpt-4o", + tools=tools, + middleware=[DynamicSystemPromptMiddleware(dynamic_system_prompt)], +) + +# The system prompt will be set dynamically based on context +result = agent.invoke( + {"messages": [{"role": "user", "content": "Explain machine learning"}]}, + {"context": {"user_role": "expert"}} +) +``` +::: + +:::js +```typescript wrap +import { createAgent } from "langchain"; +import { dynamicSystemPromptMiddleware } from "langchain/middleware"; + +const agent = createAgent({ + model: "openai:gpt-4o", + tools, + middleware: [ + dynamicSystemPromptMiddleware({ + systemPromptFunction: (state, runtime) => { + const userRole = runtime?.context?.userRole || "user"; + const basePrompt = "You are a helpful assistant."; + + if (userRole === "expert") { + return `${basePrompt} Provide detailed technical responses.`; + } else if (userRole === "beginner") { + return `${basePrompt} Explain concepts simply and avoid jargon.`; + } + return basePrompt; + } + }), + ], +}); + +// The system prompt will be set dynamically based on context +const result = await agent.invoke( + { messages: [{ role: "user", content: "Explain machine learning" }] }, + { context: { userRole: "expert" } } +); +``` +::: + -For more details on message types and formatting, see [Messages](/oss/langchain/messages). +For more details on message types and formatting, see [Messages](/oss/langchain/messages). For comprehensive middleware documentation, see [Middleware](/oss/langchain/middleware). ## Advanced configuration diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index 9a528dc88..db2281f03 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -151,6 +151,7 @@ LangChain provides several built in middleware to use off-the-shelf - [Summarization](#summarization) - [Human-in-the-loop](#human-in-the-loop) - [Anthropic prompt caching](#anthropic-prompt-caching) +- [Dynamic system prompt](#dynamic-system-prompt) ### Summarization @@ -467,6 +468,124 @@ const result = await agent.invoke({ messages: [HumanMessage("What's my name?")] ``` ::: +### Dynamic system prompt + +`DynamicSystemPromptMiddleware` allows you to set the system prompt dynamically right before each model invocation. This middleware is particularly useful when the prompt depends on the current agent state or runtime context. + +For example, you can adjust the system prompt based on the user's expertise level: + +:::python +```python +from typing import TypedDict + +from langchain.agents import create_agent +from langchain.agents.middleware import DynamicSystemPromptMiddleware +from langgraph.runtime import Runtime + +class Context(TypedDict): + user_role: str + +def dynamic_system_prompt(state, runtime: Runtime[Context]) -> str: + user_role = runtime.context.get("user_role", "user") + base_prompt = "You are a helpful assistant." + + if user_role == "expert": + return f"{base_prompt} Provide detailed technical responses." + elif user_role == "beginner": + return f"{base_prompt} Explain concepts simply and avoid jargon." + return base_prompt + +agent = create_agent( + model="openai:gpt-4o", + tools=[web_search], + middleware=[ + DynamicSystemPromptMiddleware(dynamic_system_prompt), + ], +) + +# Use with context +result = agent.invoke( + {"messages": [{"role": "user", "content": "Explain async programming"}]}, + {"context": {"user_role": "expert"}} +) +``` +::: + +:::js +```typescript +import { createAgent } from "langchain"; +import { dynamicSystemPromptMiddleware } from "langchain/middleware"; + +const agent = createAgent({ + model: "openai:gpt-4o", + tools: [weatherTool, calculatorTool], + middleware: [ + dynamicSystemPromptMiddleware({ + systemPromptFunction: (state, runtime) => { + const userRole = runtime?.context?.userRole || "user"; + const basePrompt = "You are a helpful assistant."; + + if (userRole === "expert") { + return `${basePrompt} Provide detailed technical responses.`; + } else if (userRole === "beginner") { + return `${basePrompt} Explain concepts simply and avoid jargon.`; + } + return basePrompt; + } + }), + ], +}); + +// Use with context +const result = await agent.invoke( + { messages: [{ role: "user", content: "Explain async programming" }] }, + { context: { userRole: "expert" } } +); +``` +::: + +Alternatively, you can adjust the system prompt based on the conversation length: + +:::python +```python +from langchain.agents.middleware import DynamicSystemPromptMiddleware + +def simple_prompt(state) -> str: + message_count = len(state["messages"]) + + if message_count > 10: + return "You are in an extended conversation. Be more concise." + return "You are a helpful assistant." + +agent = create_agent( + model="openai:gpt-4o", + tools=[search_tool], + middleware=[DynamicSystemPromptMiddleware(simple_prompt)], +) +``` +::: + +:::js +```typescript +const agent = createAgent({ + model: "openai:gpt-4o", + tools: [searchTool], + middleware: [ + dynamicSystemPromptMiddleware({ + systemPromptFunction: (state) => { + const messageCount = state.messages.length; + + if (messageCount > 10) { + return "You are in an extended conversation. Be more concise."; + } + return "You are a helpful assistant."; + } + }), + ], +}); +``` +::: + ## Custom Middleware Middleware for agents are subclasses of `AgentMiddleware`, which implement one or more of its hooks. From 93ac99f21a084d24493edcceb310e5f84f79acf6 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 18 Sep 2025 21:32:24 -0400 Subject: [PATCH 2/8] Update src/oss/langchain/agents.mdx Co-authored-by: Christian Bromann --- src/oss/langchain/agents.mdx | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/oss/langchain/agents.mdx b/src/oss/langchain/agents.mdx index 40b74df8c..86485059b 100644 --- a/src/oss/langchain/agents.mdx +++ b/src/oss/langchain/agents.mdx @@ -505,25 +505,29 @@ result = agent.invoke( :::js ```typescript wrap +import { z } from "zod"; import { createAgent } from "langchain"; import { dynamicSystemPromptMiddleware } from "langchain/middleware"; +const contextSchema = z.object({ + userRole: z.enum(["expert", "beginner"]), +}); + const agent = createAgent({ model: "openai:gpt-4o", - tools, + tools: [...], + contextSchema, middleware: [ - dynamicSystemPromptMiddleware({ - systemPromptFunction: (state, runtime) => { - const userRole = runtime?.context?.userRole || "user"; - const basePrompt = "You are a helpful assistant."; - - if (userRole === "expert") { - return `${basePrompt} Provide detailed technical responses.`; - } else if (userRole === "beginner") { - return `${basePrompt} Explain concepts simply and avoid jargon.`; - } - return basePrompt; + dynamicSystemPromptMiddleware>((state, runtime) => { + const userRole = runtime.context.userRole || "user"; + const basePrompt = "You are a helpful assistant."; + + if (userRole === "expert") { + return `${basePrompt} Provide detailed technical responses.`; + } else if (userRole === "beginner") { + return `${basePrompt} Explain concepts simply and avoid jargon.`; } + return basePrompt; }), ], }); From de254ee810683850ec7bba74a6bf8a6ee19fda4d Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Thu, 18 Sep 2025 21:32:43 -0400 Subject: [PATCH 3/8] Update src/oss/langchain/middleware.mdx Co-authored-by: Christian Bromann --- src/oss/langchain/middleware.mdx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index db2281f03..0f9cb7fc0 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -571,15 +571,13 @@ const agent = createAgent({ model: "openai:gpt-4o", tools: [searchTool], middleware: [ - dynamicSystemPromptMiddleware({ - systemPromptFunction: (state) => { - const messageCount = state.messages.length; + dynamicSystemPromptMiddleware((state) => { + const messageCount = state.messages.length; - if (messageCount > 10) { - return "You are in an extended conversation. Be more concise."; - } - return "You are a helpful assistant."; + if (messageCount > 10) { + return "You are in an extended conversation. Be more concise."; } + return "You are a helpful assistant."; }), ], }); From f2f5ae26141f6e9a57f16603e9cfc803e0b88f1e Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Fri, 19 Sep 2025 15:12:58 -0400 Subject: [PATCH 4/8] Update src/oss/langchain/middleware.mdx Co-authored-by: Christian Bromann --- src/oss/langchain/middleware.mdx | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index 0f9cb7fc0..6762e29eb 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -513,30 +513,34 @@ result = agent.invoke( :::js ```typescript +import { z } from "zod"; import { createAgent } from "langchain"; import { dynamicSystemPromptMiddleware } from "langchain/middleware"; +const contextSchema = z.object({ + userRole: z.enum(["expert", "beginner"]), +}); + const agent = createAgent({ model: "openai:gpt-4o", - tools: [weatherTool, calculatorTool], + tools: [...], + contextSchema, middleware: [ - dynamicSystemPromptMiddleware({ - systemPromptFunction: (state, runtime) => { - const userRole = runtime?.context?.userRole || "user"; - const basePrompt = "You are a helpful assistant."; - - if (userRole === "expert") { - return `${basePrompt} Provide detailed technical responses.`; - } else if (userRole === "beginner") { - return `${basePrompt} Explain concepts simply and avoid jargon.`; - } - return basePrompt; + dynamicSystemPromptMiddleware>((state, runtime) => { + const userRole = runtime.context.userRole || "user"; + const basePrompt = "You are a helpful assistant."; + + if (userRole === "expert") { + return `${basePrompt} Provide detailed technical responses.`; + } else if (userRole === "beginner") { + return `${basePrompt} Explain concepts simply and avoid jargon.`; } + return basePrompt; }), ], }); -// Use with context +// The system prompt will be set dynamically based on context const result = await agent.invoke( { messages: [{ role: "user", content: "Explain async programming" }] }, { context: { userRole: "expert" } } From 6fe4b0884f37f49a47fb65a972a086bc98b7fc6c Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 23 Sep 2025 17:36:56 -0400 Subject: [PATCH 5/8] use decorator for python --- src/oss/langchain/agents.mdx | 27 +++++++++++++++++---------- src/oss/langchain/middleware.mdx | 23 +++++++++++++---------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/oss/langchain/agents.mdx b/src/oss/langchain/agents.mdx index 40b74df8c..d1279fd8f 100644 --- a/src/oss/langchain/agents.mdx +++ b/src/oss/langchain/agents.mdx @@ -467,32 +467,39 @@ When no `prompt` is provided, the agent will infer its task from the messages di #### Dynamic prompts with middleware -For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the `DynamicSystemPromptMiddleware`. This is especially useful for personalizing prompts based on user roles, conversation context, or other dynamic factors: +For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the `modify_model_request` decorator to create a simple custom middleware. + +Dynamic system prompt is especially useful for personalizing prompts based on user roles, conversation context, or other changing factors: :::python ```python wrap -from langchain.agents import create_agent -from langchain.agents.middleware import DynamicSystemPromptMiddleware +from langchain.agents import create_agent, AgentState +from langchain.agents.middleware.types import modify_model_request from langgraph.runtime import Runtime from typing import TypedDict class Context(TypedDict): user_role: str -def dynamic_system_prompt(state, runtime: Runtime[Context]) -> str: +@modify_model_request +def dynamic_system_prompt(state: AgentState, request: ModelRequest, runtime: Runtime[Context]) -> ModelRequest: user_role = runtime.context.get("user_role", "user") base_prompt = "You are a helpful assistant." - + if user_role == "expert": - return f"{base_prompt} Provide detailed technical responses." + prompt = f"{base_prompt} Provide detailed technical responses." elif user_role == "beginner": - return f"{base_prompt} Explain concepts simply and avoid jargon." - return base_prompt + prompt = f"{base_prompt} Explain concepts simply and avoid jargon." + else: + prompt = base_prompt + + request.system_prompt = base_prompt + return request agent = create_agent( model="openai:gpt-4o", tools=tools, - middleware=[DynamicSystemPromptMiddleware(dynamic_system_prompt)], + middleware=[dynamic_system_prompt], ) # The system prompt will be set dynamically based on context @@ -516,7 +523,7 @@ const agent = createAgent({ systemPromptFunction: (state, runtime) => { const userRole = runtime?.context?.userRole || "user"; const basePrompt = "You are a helpful assistant."; - + if (userRole === "expert") { return `${basePrompt} Provide detailed technical responses.`; } else if (userRole === "beginner") { diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index db2281f03..68770363f 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -470,7 +470,7 @@ const result = await agent.invoke({ messages: [HumanMessage("What's my name?")] ### Dynamic system prompt -`DynamicSystemPromptMiddleware` allows you to set the system prompt dynamically right before each model invocation. This middleware is particularly useful when the prompt depends on the current agent state or runtime context. +A system prompt can be dynamically set right before each model invocation using the `@modify_model_request` decorator. This middleware is particularly useful when the prompt depends on the current agent state or runtime context. For example, you can adjust the system prompt based on the user's expertise level: @@ -478,29 +478,32 @@ For example, you can adjust the system prompt based on the user's expertise leve ```python from typing import TypedDict -from langchain.agents import create_agent -from langchain.agents.middleware import DynamicSystemPromptMiddleware +from langchain.agents import create_agent, AgentState +from langchain.agents.middleware.types import modify_model_request from langgraph.runtime import Runtime class Context(TypedDict): user_role: str -def dynamic_system_prompt(state, runtime: Runtime[Context]) -> str: +@modify_model_request +def dynamic_system_prompt(state: AgentState, request: ModelRequest, runtime: Runtime[Context]) -> ModelRequest: user_role = runtime.context.get("user_role", "user") base_prompt = "You are a helpful assistant." if user_role == "expert": - return f"{base_prompt} Provide detailed technical responses." + prompt = f"{base_prompt} Provide detailed technical responses." elif user_role == "beginner": - return f"{base_prompt} Explain concepts simply and avoid jargon." - return base_prompt + prompt = f"{base_prompt} Explain concepts simply and avoid jargon." + else: + prompt = base_prompt + + request.system_prompt = prompt + return request agent = create_agent( model="openai:gpt-4o", tools=[web_search], - middleware=[ - DynamicSystemPromptMiddleware(dynamic_system_prompt), - ], + middleware=[dynamic_system_prompt], ) # Use with context From b9d266800c8b847f2781ad2d07fd37349ee2b22e Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 23 Sep 2025 17:39:41 -0400 Subject: [PATCH 6/8] more updates --- src/oss/langchain/middleware.mdx | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index 0c956d04f..44b4c9bec 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -555,19 +555,24 @@ Alternatively, you can adjust the system prompt based on the conversation length :::python ```python -from langchain.agents.middleware import DynamicSystemPromptMiddleware +from langchain.agents.middleware.types import modify_model_request -def simple_prompt(state) -> str: +@modify_model_request +def simple_prompt(state: AgentState, request: ModelRequest) -> ModelRequest: message_count = len(state["messages"]) if message_count > 10: - return "You are in an extended conversation. Be more concise." - return "You are a helpful assistant." + prompt = "You are in an extended conversation. Be more concise." + else: + prompt = "You are a helpful assistant." + + request.system_prompt = prompt + return request agent = create_agent( model="openai:gpt-4o", tools=[search_tool], - middleware=[DynamicSystemPromptMiddleware(simple_prompt)], + middleware=[simple_prompt], ) ``` ::: From 668b0f06d7718705708e21d58f3d416d48b3af2e Mon Sep 17 00:00:00 2001 From: Christian Bromann Date: Wed, 24 Sep 2025 13:18:49 -0700 Subject: [PATCH 7/8] minor adjustments --- src/oss/langchain/agents.mdx | 16 ++++++++-------- src/oss/langchain/middleware.mdx | 8 ++++++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/oss/langchain/agents.mdx b/src/oss/langchain/agents.mdx index 59d3b1a39..280694da6 100644 --- a/src/oss/langchain/agents.mdx +++ b/src/oss/langchain/agents.mdx @@ -128,10 +128,10 @@ Model instances give you complete control over configuration. Use them when you #### Dynamic model -:::python - Dynamic models are selected at runtime based on the current state and context. This enables sophisticated routing logic and cost optimization. +:::python + To use a dynamic model, you need to provide a function that receives the graph state and runtime and returns an instance of `BaseChatModel` with the tools bound to it using `.bind_tools(tools)`, where `tools` is a subset of the `tools` parameter. ```python @@ -153,11 +153,6 @@ agent = create_agent(select_model, tools=tools) ``` ::: :::js - -**`state`**: The data that flows through your agent's execution, including messages, custom fields, and any information that needs to be tracked and potentially modified during processing (e.g. user preferences or tool usage stats). - - -Dynamic models are selected at runtime based on the current state and context. This enables sophisticated routing logic and cost optimization. To use a dynamic model, you need to provide a function that receives the graph state and runtime and returns an instance of `BaseChatModel` with the tools bound to it using `.bindTools(tools)`, where `tools` is a subset of the `tools` parameter. @@ -467,7 +462,12 @@ When no `prompt` is provided, the agent will infer its task from the messages di #### Dynamic prompts with middleware +:::python For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the `modify_model_request` decorator to create a simple custom middleware. +::: +:::js +For more advanced use cases where you need to modify the system prompt based on runtime context or agent state, you can use the `modifyModelRequest` decorator to create a simple custom middleware. +::: Dynamic system prompt is especially useful for personalizing prompts based on user roles, conversation context, or other changing factors: @@ -522,7 +522,7 @@ const contextSchema = z.object({ const agent = createAgent({ model: "openai:gpt-4o", - tools: [...], + tools: [/* ... */], contextSchema, middleware: [ dynamicSystemPromptMiddleware>((state, runtime) => { diff --git a/src/oss/langchain/middleware.mdx b/src/oss/langchain/middleware.mdx index 44b4c9bec..1a38ebd69 100644 --- a/src/oss/langchain/middleware.mdx +++ b/src/oss/langchain/middleware.mdx @@ -470,11 +470,11 @@ const result = await agent.invoke({ messages: [HumanMessage("What's my name?")] ### Dynamic system prompt +:::python A system prompt can be dynamically set right before each model invocation using the `@modify_model_request` decorator. This middleware is particularly useful when the prompt depends on the current agent state or runtime context. For example, you can adjust the system prompt based on the user's expertise level: -:::python ```python from typing import TypedDict @@ -513,8 +513,12 @@ result = agent.invoke( ) ``` ::: - :::js + +A system prompt can be dynamically set right before each model invocation using the `dynamicSystemPromptMiddleware` middleware. This middleware is particularly useful when the prompt depends on the current agent state or runtime context. + +For example, you can adjust the system prompt based on the user's expertise level: + ```typescript import { z } from "zod"; import { createAgent } from "langchain"; From fb2b2086de86249ecee4be4c0705911135fcf129 Mon Sep 17 00:00:00 2001 From: Sydney Runkle <54324534+sydney-runkle@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:22:44 -0400 Subject: [PATCH 8/8] Update src/oss/langchain/agents.mdx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/oss/langchain/agents.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/oss/langchain/agents.mdx b/src/oss/langchain/agents.mdx index 280694da6..bb516cbd9 100644 --- a/src/oss/langchain/agents.mdx +++ b/src/oss/langchain/agents.mdx @@ -493,7 +493,7 @@ def dynamic_system_prompt(state: AgentState, request: ModelRequest, runtime: Run else: prompt = base_prompt - request.system_prompt = base_prompt + request.system_prompt = prompt return request agent = create_agent(