feat(deepagents): allow overriding default middleware for deepagent and subagents#204
feat(deepagents): allow overriding default middleware for deepagent and subagents#204alvedder wants to merge 1 commit intolangchain-ai:mainfrom
Conversation
hntrl
left a comment
There was a problem hiding this comment.
Thanks @alvedder! I think we're fine to merge this, but we're likely not going to document this anywhere. We are of the opinion that customization with a harness like this is actually a footgun. What exactly are you trying to customize with the summarization logic? Anything we can upstream?
|
@hntrl thanks for reviewing! basically I have 2 cases: Case 1I want to separate backends for filesystem, skills, memory and some subagents so that the deep agent can do whatever it wants on the filesystem and at the same time cannot override its skills and can only controllably override its memory and some subagents also have restrictions, hence I need different backends for filesystem, skills and memory middlewares and some subagents. While I can in fact specify different backends for the deep agent like that: createDeepAgent({
backend: sandboxWorkspace,
middleware: [
// works becase `memory` is not set
createMemoryMiddleware({
backend: restrictedHostFilesystem,
sources: ['memory/AGENTS.md'],
}),
// works because `skills` is not set
createSkillsMiddleware({
backend: readonlyHostFilesystem,
sources: ['skills/'],
}),
],
})I cannot do the same for subagents because they always inhert parent's backend. Also I don't think that in the current state it was intended to override middleware like that since, although possible, it changes the execution order of middlewares. Case 2Even if one goes with the default "same backend for deep agent and subagents", current interface FilesystemMiddlewareOptions {
backend?: BackendProtocol | BackendFactory;
systemPrompt?: string | null;
customToolDescriptions?: Record<string, string> | null;
toolTokenLimitBeforeEvict?: number | null;
}so I can technically override deep agent's filesystem middleware with my custom options, but subagents do not inherit the middleware instance itself, they inherit only Same goes for example for |
|
@alvedder We let you compose different backends like this already without you having to override middleware: createDeepAgent({
backend: new CompositeBackend(sandboxWorkspace,
"/memory/": restrictedHostFilesystem,
"/skills/": readonlyHostFilesystem,
}),
memory: ["memory/AGENTS.md"],
skills: ["skills/"]
});What's the motivation for overriding the backends in subagents? |
|
@hntrl Thanks for pointing out the option utilizing Regarding the motivation for overriding the backends in subagents Case 1there might be a QA-like subagent that should be able to read but not write to the fs Case 2Some agents including the main agent (deep agent) are provided with a docker container sandbox. Some specific subagents are provided with a different, less restricted, sandbox with no workspace mounted. Examplea data-scientist subagent is assigned with a task "write a python script that does X Y Z, install any dependencies that are needed to accomplish the task, then run the script with data A B C and return the result". This subagent is not restricted to use one specific folder or only available CLI tooling and we don't need to worry about it recklessly overriding user data. SummarySo my main points are:
|
|
Anyway, even without those advanced scenarios it would be nice to set custom params for filesystem middleware, especially |
Adds middleware override capability for DeepAgents and subagents, allowing users to replace specific default middleware by name while preserving the middleware execution order.
Motivation
Previously, middleware arrays were merged through simple concatenation (
[...defaults, ...custom]), making it difficult to replace or customize default middleware behavior as langchain'screateAgentthrows an error if detects multiple same-named middleware.Users who wanted to modify a specific middleware (e.g., filesystem, skills, memory) had to either:
This commit introduces a smarter merge strategy that:
namepropertyThis gives users fine-grained control over agent behavior without requiring deep knowledge of the internal middleware ordering.
Changes
libs/deepagents/src/middleware/utils.tsAdded the
mergeMiddlewarefunction that implements the merge strategy:Example behavior:
libs/deepagents/src/middleware/utils.test.tsAdded test coverage for
mergeMiddlewarewith 10 test cases covering:libs/deepagents/src/agent.tsUpdated
createDeepAgentfunction:mergeMiddlewarefor the main agent's runtime middlewareBefore:
After:
libs/deepagents/src/middleware/subagents.tsUpdated
getSubagentsfunction:mergeMiddlewarefor subagent middlewareBefore:
After: