LangChain DeepAgents 速通指南(十)- DeepAgents Code 智能体服务核心源码解读
本文深入解析DeepAgents Code智能体服务的核心模块,从模型切换、工具管理到多智能体编排,逐一拆解其实现原理,帮助读者掌握构建生产级Agent Server的关键技巧。
前言
上篇文章对基于DeepAgents API搭建的生产级代码助手智能体DeepAgents Code进行了源码导读,重点剖析了其客户端与服务端分离的设计模式。在理解整体架构后,大家应已建立宏观认知——但这种认知还不够落地,真正关心的是:一个生产级智能体服务,其服务端核心具体长什么样?
在DeepAgents Code这个CLI智能体项目中,智能体核心服务(Agent Server)的设计与实现是整个系统的重中之重。借助DeepAgents SDK提供的标准化API,开发这样一个Agent Server的工作变得结构清晰且易于理解。不过,一个生产环境中的工业级项目仍有许多开发技巧:如何切换大模型?如何执行代码命令?如何进行多智能体开发?这些都是必须考虑的问题。
今天这篇文章,笔者将带大家深入DeepAgents Code项目的Agent Server模块,了解其核心实现与设计思路。
一、DeepAgents Code Agent Server核心设计
DeepAgents Code Agent Server的核心源码位于项目目录下的agent.py文件中。笔者认为,一个真正生产级别的智能体项目,至少需要妥善解决以下四个核心问题:
- 大模型的接入与切换:如何对接不同模型提供商?如何在不中断服务的前提下动态切换模型?
- 工具函数的调用:如何定义、注册和调用工具?如何保证工具执行的安全性与可控性?
- 会话记忆的存储复用:用户的对话历史和状态如何跨轮次持久化?如何实现跨会话的记忆共享?
- 多智能体系统的编排:当单一智能体能力不足时,如何通过子智能体委派来扩展能力边界?
DeepAgents Code基于DeepAgents API的create_deep_agent核心函数,通过其中间件机制、工具函数机制和子智能体机制等核心机制,实现了上述各项功能。
下图展示了DeepAgents Code智能体服务部分核心模块的结构关系图。在agent.py中,DeepAgent Code通过自定义的create_cli_agent函数完成了这一切的整合——大家先建立整体印象,接下来笔者会逐个模块展开讲解:
从上图可以清晰看到,借助DeepAgents API的标准化能力,DeepAgents Code的功能模块划分非常干净——每一层各司其职,互不越界。这正是优秀架构的魅力所在。
二、DeepAgents Code Agent Server核心模块讲解
有了宏观认知后,接下来笔者将深入各个核心模块,逐一拆解它们的实现原理与技巧。
2.1 Model Provider:模型服务
智能体最基础的能力是接入大模型。DeepAgents Code通过create_deep_agent的model参数传入模型实例,agent.py中的相关代码如下:
agent = create_deep_agent(model=model,system_prompt=system_prompt,tools=tools,backend=composite_backend,middleware=agent_middleware,interrupt_on=interrupt_on,context_schema=CLIContextSchema,checkpointer=checkpointer,subagents=all_subagents or None,name=_sanitize_agent_message_name(assistant_id),).with_config(config)
model参数用于传递模型,这是LangChain最标准的用法之一,相信大家已非常熟悉。但许多读者可能产生疑问:在像DeepAgents Code这样的生产级CLI工具中,用户经常需要在不重启会话的情况下动态切换模型(例如从GPT-4切换到Claude 3.5),这部分是如何实现的?从代码看,模型似乎在一开始就“写死”了?
DeepAgents Code运用了一个非常巧妙的设计,核心思路分为两步:
第一步:通过上下文(Context)传递模型切换指令
请留意create_deep_agent中的一个关键参数context_schema=CLIContextSchema。该参数定义了智能体运行时的动态上下文结构,其定义如下(位于_cli_context.py):
class CLIContext(TypedDict, total=False):model: str | None"""Model spec to swap at runtime (e.g. 'provider:model')."""model_params: dict[str, Any]"""Invocation params (e.g. temperature, max_tokens) to merge."""effective_model: str | None"""Resolved provider:model spec actually in use."""auto_approve: bool
在智能体运行时,例如通过agent.stream()执行时,可以在context参数中携带用户指定的模型(代码位于app.py):
context=CLIContext(model=self._model_override,model_params=self._model_params_override or {},...)
随后,这个context在textual_adapter.py中被传入agent.astream()调用,从而将“想要切换到的目标模型”传递给执行层:
async for chunk in agent.astream(stream_input,stream_mode=["messages", "updates"],subgraphs=True,config=config,context=context,durability="exit",):
第二步:通过ConfigurableMiddleware中间件在运行时替换模型
仅有上下文传递还不够——真正负责“在模型调用前一刻执行替换”的是DeepAgents Code自定义的ConfigurableModelMiddleware中间件(位于configurable_model.py):
class ConfigurableModelMiddleware(AgentMiddleware):"""Swap the model or per-call settings from `runtime.context`......."""def wrap_model_call(# noqa: PLR6301self,request: ModelRequest,handler: Callable[[ModelRequest], ModelResponse],) -> ModelResponse:
该中间件通过wrap_model_call钩子在每次模型调用前拦截请求。其内部核心逻辑包括:
- 从
runtime.context中读取要切换的模型; - 通过
model_matches_spec判断当前使用的模型是否与目标模型匹配; - 若不匹配,则调用
create_model()动态创建新模型实例; - 合并
model_params参数(如temperature、max_tokens等); - 跨提供商切换时自动清理不兼容的配置(例如从Anthropic切换到OpenAI时自动移除
cache_control相关参数); - 最后更新系统提示中的
### Model Identity部分,让新模型“知晓”自己的身份。
这套设计流程充分运用了LangChain的运行时上下文(Runtime Context)与中间件(Middleware)机制,实现了“模型切换不中断会话、无需重启服务”的流畅体验。对中间件机制还不熟悉的读者,可回顾相关文章。
2.2 Tool Registry:三种工具来源的统一管理
工具是智能体的“手脚”,DeepAgents Code的工具来源可分为以下三类:
| 来源 | 说明 | 典型工具 |
|---|---|---|
| 内置工具 | 硬编码在代码中的基础工具 | fetch_url、web_search、get_current_thread_id |
| MCP工具 | 通过MCP协议从远程服务加载 | 用户自定义的MCP服务器工具 |
| 中间件注入工具 | DeepAgents SDK运行时自动注入 | execute、read_file、write_file、edit_file |
内置工具定义在tools.py文件中,包含fetch_url(抓取网页内容)、web_search(网络搜索)和get_current_thread_id(获取当前会话ID)三个基础工具。
MCP工具通过mcp_tools.py实现。用户接入MCP服务后,DeepAgents Code会从MCP服务端解析可用的工具列表并注入智能体。关于MCP的具体使用方式,可参考相关文档。
中间件注入工具是三类工具中最丰富的一类。在之前的文章中曾提到,FileSystemMiddleware会自动注入ls、read_file、write_file、edit_file等文件操作工具。在DeepAgents Code中,除了文件系统中间件,还集成了代码执行中间件等其他组件,共同注入了诸如execute(执行Shell命令)等能力。
这里有必要提一下get_current_thread_id这个看似简单却很有意义的工具函数。它的使用场景主要有两个:
- LangSmith追踪关联:LangSmith是LangChain的可观测性平台,通过
thread_id关联同一会话的所有Trace。当智能体在运行过程中需要调用LangSmith API(例如上报自定义指标、查询历史Trace)时,它需要知道“我是谁”——这个工具让智能体可以主动获取thread_id,而非被动等待注入。 - MCP工具集成:某些MCP工具可能需要
thread_id来关联上下文。例如,一个外部记忆工具需要知道当前会话标识才能存取对应的状态数据。
这个设计的精妙之处在于:将“获取自身上下文信息”这件事从系统提示或工具参数的静态注入中解耦出来,变成一个可主动调用的工具。智能体“按需获取”而非“被动接收”,体现了更灵活的设计哲学。
2.3 Subagents:多智能体机制
在之前的文章中,已经详细介绍过DeepAgents SDK的子智能体机制。其核心思想是:通过task工具,主智能体可以将复杂任务委派给专门的子智能体执行,从而避免上下文过长、职责不清等问题。
DeepAgents Code充分利用了DeepAgents SDK的这一原生能力,相关代码位于subagents.py。用户可以在agents_dir/{subagent_name}/AGENTS.md中定义子智能体的能力描述,DeepAgents Code在创建主智能体时会自动将这些本地自定义的能力描述构建为对应的子智能体,并添加到subagents参数中。
除了本地自定义的子智能体,在agent.py中还可以看到如下代码:
all_subagents: list[SubAgent | CompiledSubAgent | AsyncSubAgent] = [*custom_subagents,*(async_subagents or []),]
其中的async_subagents是在config.toml中配置的、部署在远程平台上的智能体服务。这意味着DeepAgents Code的子智能体体系同时考虑了两种场景:
- 本地自定义:用户可以在本地通过Markdown文件随时定义新的子智能体;
- 远程复用:用户也可以接入部署在远程平台上的智能体服务,实现能力的远程共享。
这种“本地定义 + 远程复用”的设计既保证了灵活性,又兼顾了扩展性,非常值得借鉴。
三、DeepAgents Code中间件能力扩展
LangChain 1.0版本引入的中间件(Middleware)机制,是整个LangChain智能体体系框架中最具工程价值的能力之一。在前面的系列文章中,大家已经看到DeepAgents SDK通过一系列中间件获得了任务规划、工具选择调用、文件系统操作等核心能力。DeepAgents Code同样在此基础上做了大量扩展——甚至可以说,DeepAgents Code中相当一部分代码都是在定义和组装中间件,这足以说明中间件在LangChain智能体开发体系中的重要地位。
下面,笔者将逐一拆解DeepAgents Code中几个代表性的中间件,看看它们各自解决了什么问题、又是如何巧妙实现的。
3.1 LocalContextMiddleware:让智能体更懂项目
LocalContextMiddleware的设计目的是向智能体注入本地环境上下文,让它“知道”自己当前在哪个项目中工作,而非一个“对项目一无所知”的通用助手。该中间件定义在local_context.py中,核心机制如下:
在每次模型调用前(before_model钩子),中间件通过执行Bash检测脚本自动收集当前项目的环境信息,包括但不限于:
- Git状态:当前分支、是否有未提交的更改、远程仓库信息;
- 项目结构:目录布局、关键文件是否存在;
- 包管理器类型:检测项目使用的是npm、pip、cargo还是其他工具。
这些信息在首次交互时自动检测,并在每次总结事件后增量更新,检测结果会持久化存储在智能体状态中。每次模型调用时,中间件会将当前环境快照附加到系统提示中。
3.2 ResumeStateMiddleware:会话恢复的额外助手
当用户中途关闭终端、之后通过/threads命令恢复之前的会话时,需要解决两个棘手的问题:
- 模型信息丢失:LangGraph检查点中只保存了消息记录,却没有记录“这些消息是哪个模型生成的”。假如原会话使用的是上下文窗口较大的模型(如
gpt-4o),恢复时却使用了默认的qwen2.5-72b,就可能因上下文窗口不足而导致截断,严重影响会话连续性。 - Token计数丢失:界面状态栏需要显示当前会话的Token用量,但检查点中没有存储这个数据。重新计算需要遍历全部历史消息,性能开销大且不精确。
DeepAgents Code在resume_state.py中定义了ResumeStateMiddleware来解决上述问题。它在每次模型调用完成后的after_model钩子中,提取本次调用的Token用量和当前模型信息,写入一个名为ResumeState的扩展状态:
class ResumeState(AgentState):"""Extends agent state with per-checkpoint facts restored on resume."""_context_tokens: Annotated[NotRequired[int], PrivateStateAttr]"""Total context tokens from the model's last usage_metadata."""_model_spec: Annotated[NotRequired[str], PrivateStateAttr]"""provider:model spec effectively in use for the latest turn."""
ResumeState会随messages一起持久化到数据库中。当用户下次恢复会话时,中间件从检查点中还原_context_tokens、_model_spec和messages,实现会话状态的无损恢复——模型一致、Token计数准确,消息记录完全,用户感知不到任何中断。
3.3 MemoryMiddleware:跨会话的长期记忆
MemoryMiddleware负责从AGENTS.md文件加载长期记忆并将其注入系统提示,让智能体在跨会话的场景下依然能保持上下文连贯性。
该中间件定义在memory.py中,其核心逻辑在before_model钩子中执行:
- 从配置的文件系统路径(包括用户级
~/.deepagents/和项目级.deepagents/)读取AGENTS.md文件内容; - 剥离Markdown中的HTML注释(这些注释通常用于开发者备注,不应暴露给模型);
- 将处理后的内容包装成
<agent_memory>标签,注入到DeepAgents Code的系统提示中。
DeepAgents Code这种“文件即记忆”的设计让用户可以像编辑文档一样调整智能体的长期行为,无需修改任何代码。记忆内容跨会话生效,真正实现了“一次设定,处处生效”。
3.4 SkillsMiddleware:技能系统的按需加载
技能(Skill)已成为当今智能体能力的基石。DeepAgents Code中的SkillsMiddleware负责管理整个技能系统,其最大特色是渐进式披露(Progressive Disclosure)策略——先展示技能概览,用户显式调用时才加载完整指令,避免系统提示被海量技能详情撑爆。
该中间件的before_model钩子执行以下流程:
- 从多个目录(内置技能、用户级
~/.deepagents/<agent>/skills/、项目级.deepagents/skills/)扫描所有SKILL.md文件; - 解析每个文件的YAML Frontmatter,提取技能名称、描述和触发条件;
- 构建技能列表摘要,按以下格式注入系统提示:
Available skills:/deploy: Deploy application to production/analyze: Analyze code quality
当用户通过/skill_name显式调用某个技能时,中间件才从对应的SKILL.md中读取完整的指令内容并注入上下文——这就是“按需加载”的精髓所在。
3.5 FileSystemBackend、LocalShellBackend等:多优先级后端机制
在之前的文章中曾讲解过DeepAgents SDK内置的FileSystemMiddleware中间件,该中间件会自动注入ls、read_file、write_file、edit_file等文件操作工具,为DeepAgents Code提供最基础的文件系统交互能力。
在理解FileSystemMiddleware的过程中,大家也学习到了backend参数定义智能体的后端依赖。在agent.py中,DeepAgents Code按照创建逻辑优先级依次尝试三种后端:
- 远程沙箱模式(最高优先级)当
sandbox参数不为None时,直接使用远程沙箱后端(如ModalSandbox、DaytonaSandbox)。此时所有文件操作和Shell执行均由远程沙箱提供,本地不创建任何Backend。适用于需要隔离执行环境的场景,例如运行不可信代码或需要访问云端资源的任务。 - 本地Shell模式(默认)当
enable_shell=True且未配置沙箱时,创建LocalShellBackend。它同时提供文件读写和Shell命令执行能力,通过env参数注入精心策划的环境变量(如LANGSMITH_PROJECT),并通过inherit_env=False避免重复继承主进程环境,保证环境干净可控。这是本地开发的默认模式——DeepAgents Code执行Python代码时,本质上就是通过这个Backend中的execute工具调用python命令来运行编写的.py文件。 - 纯文件系统模式(受限)当
enable_shell=False时,退化为FilesystemBackend,仅提供文件读写能力,不执行任何Shell命令。适用于“只需编辑文件、无需运行命令”的场景,安全性更高。
综上所述,DeepAgents Code通过精心设计的后端策略、中间件机制与子智能体体系,实现了从模型动态切换、工具统一管理到会话无损恢复的全面能力,为构建生产级智能体服务提供了清晰的参考范式。