跳到正文
DEV Community· mech.app·· 3 小時前AI 評分71

LangChain Deep Agents 如何整合子智能體、檔案系統後端與持久記憶

Deep Agents: How LangChain's Batteries-Included Harness Wires Sub-Agents, Filesystem Backends, and Persistent Memory

AI 導讀

LangChain 發佈 Deep Agents,這是一套基於 LangGraph、可即用並可逐項替換組件的智能體執行骨架,內置子智能體、可插拔檔案系統、持久記憶及人工審批。子智能體使用獨立上下文,檔案系統支援本地、沙盒及遠端後端;記憶可跨工作階段保留,並提供串流、檢查點保存與 LangSmith 追蹤。

正文

LangChain just shipped Deep Agents as their answer to "how do I actually deploy an agent?" It's an opinionated harness that runs out of the box with sub-agents, filesystem abstraction, persistent memory, and human-in-the-loop approval. The project sits at 30,823 stars and is trending #5 on GitHub for Python. This is not a framework. It's a working agent you extend, override, or replace piece by piece.

The interesting parts are how sub-agents isolate context, how the filesystem abstraction handles local vs. sandboxed vs. remote backends, how persistent memory bridges sessions, and what "batteries-included" means for production deployment (streaming, checkpointing, human approval).

Architecture: LangGraph Foundation and Pluggable Layers

Deep Agents is built on LangGraph, which gives you streaming, persistence, and checkpointing as first-class primitives. The harness itself is a graph of nodes that handle tool execution, context management, memory retrieval, and sub-agent delegation.

Core layers:

  • Agent node: Invokes the LLM with tools, handles tool calls, manages context window.
  • Sub-agent node: Spawns isolated agents for delegated tasks, returns results to parent.
  • Filesystem abstraction: Pluggable backend (local, sandboxed, remote) for file operations.
  • Persistent memory: Pluggable state and store backends for cross-session recall.
  • Human-in-the-loop interceptor: Pauses execution before tool calls, waits for approval or edits.
  • Skills loader: Loads reusable behaviors (functions or prompts) on demand.
  • MCP server adapter: Wraps any Model Context Protocol server as a tool.

The harness is model-agnostic. You bring any LLM that supports tool calling: frontier (OpenAI, Anthropic), open-weight (Llama, Mistral), or local (Ollama).

Sub-Agents: Isolated Context Windows with Shared State

Sub-agents solve the problem of long-horizon tasks that blow out the parent agent's context window. When the parent delegates a task, it spawns a sub-agent with a fresh context window. The sub-agent gets its own tool set, its own memory, and its own execution graph.

Isolation mechanics:

  • Context window: Sub-agent starts with a clean slate. Parent passes only the task description and relevant artifacts.
  • Tool access: Sub-agent can have a different tool set. Parent might have shell access, sub-agent might only have read-only filesystem.
  • State boundary: Sub-agent writes to its own state store. Parent reads the final result, not intermediate steps.
  • Checkpointing: Sub-agent execution is checkpointed separately. If it fails, parent can retry or escalate.

The parent agent invokes the sub-agent via a tool call. The sub-agent runs to completion (or interruption), then returns a structured result. The parent decides whether to accept, retry, or delegate further.

Example delegation flow:

# Parent agent delegates a research task to a sub-agent
sub_agent_result = await parent_agent.invoke_tool(
    "delegate_to_sub_agent",
    {
        "task": "Research Python async best practices and summarize in 500 words",
        "tools": ["web_search", "filesystem_read"],
        "max_steps": 10
    }
)

# Sub-agent runs in isolated context, returns summary
# Parent agent continues with the result

The sub-agent's context window never leaks into the parent's. This keeps the parent's token budget under control and lets you run multiple sub-agents in parallel.

Filesystem Abstraction: Pluggable Backends for Local, Sandboxed, and Remote

The filesystem abstraction is a layer that decouples file operations from the underlying storage. The agent calls read_file, write_file, edit_file, or search_files. The backend decides where those operations happen.

Backend options:

Backend Use Case Security Boundary Latency
Local Development, single-user agents None (full filesystem access) ~1ms
Sandboxed Production, untrusted code execution Chroot, Docker, or VM isolation ~10ms
Remote Multi-tenant, cloud storage Network boundary, auth required ~100ms

The abstraction is a Python protocol. You implement read, write, edit, search, and the harness routes all file operations through it.

Sandboxed backend example:

class DockerFilesystemBackend:
    def __init__(self, container_id: str):
        self.container_id = container_id

    async def read_file(self, path: str) -> str:
        # Execute docker cp to read file from container
        result = await docker_exec(self.container_id, f"cat {path}")
        return result.stdout

    async def write_file(self, path: str, content: str) -> None:
        # Write to temp file, docker cp into container
        await docker_cp(content, self.container_id, path)

The agent doesn't know it's talking to a Docker container. It just calls filesystem.read_file("/app/config.yaml") and gets the content back. This makes it trivial to swap backends without changing agent code.

Remote backend (S3 example):

For cloud storage, the backend translates file paths to S3 keys, handles auth, and streams large files. The agent still calls read_file, but the backend does s3.get_object under the hood.

The abstraction also handles context management. If a file is too large to fit in the context window, the backend can return a summary or a pointer. The agent can then request specific chunks.

Persistent Memory: Cross-Session Recall with Pluggable Stores

Persistent memory lets the agent remember facts, preferences, and past decisions across sessions. The harness separates short-term state (current conversation) from long-term memory (facts that persist).

Memory architecture:

  • State store: Holds the current conversation thread, tool call history, and intermediate results. Checkpointed after every step.
  • Memory store: Holds facts, summaries, and user preferences. Queried on demand, updated explicitly.
  • Retrieval strategy: Agent decides when to query memory (e.g., "What did I learn about this user last time?").

The memory store is pluggable. You can use an in-memory dict for development, SQLite for single-user agents, or Postgres/Redis for production.

Memory write example:

# Agent decides to remember a user preference
await memory_store.write({
    "key": "user_timezone",
    "value": "America/Los_Angeles",
    "context": "User mentioned they're in SF"
})

# Later session: agent retrieves the preference
timezone = await memory_store.read("user_timezone")

The harness includes a default retrieval strategy: the agent can call a recall_memory tool with a query. The memory store does semantic search (if backed by a vector DB) or keyword search (if backed by SQL).

Memory vs. state:

  • State is ephemeral. It's the current conversation, tool outputs, and intermediate reasoning. Checkpointed for resumption, but not meant to persist across sessions.
  • Memory is durable. It's facts the agent explicitly decides to remember. Survives session boundaries.

The agent decides what to remember. The harness doesn't auto-save everything. This keeps the memory store clean and avoids polluting it with noise.

Human-in-the-Loop: Approval, Editing, and Rejection

The human-in-the-loop flow intercepts tool calls before execution. The agent proposes a tool call, the harness pauses, and a human approves, edits, or rejects it.

Interception points:

  • Before execution: Agent proposes run_shell_command("rm -rf /"), harness pauses, human rejects.
  • After execution: Agent runs a command, harness shows the output, human decides whether to continue or rollback.
  • Conditional: Only high-risk tools (shell, filesystem write) require approval. Low-risk tools (web search, read) run automatically.

The harness uses LangGraph's interrupt mechanism. When the agent calls a tool, the graph node checks a policy. If the tool requires approval, it raises an interrupt. The graph pauses, serializes the state, and waits for a resume signal.

Approval flow:

  1. Agent calls write_file("/etc/passwd", "...").
  2. Harness checks policy: write_file requires approval.
  3. Graph raises interrupt, serializes state to checkpoint.
  4. Human reviews the tool call, edits the path to /tmp/passwd.
  5. Human resumes the graph with the edited tool call.
  6. Graph executes the edited call, continues.

The policy is a function you provide. It takes the tool name and arguments, returns approve, reject, or request_edit.

Policy example:

def approval_policy(tool_name: str, args: dict) -> str:
    if tool_name == "run_shell_command":
        if "rm" in args["command"]:
            return "reject"
        return "request_edit"
    if tool_name == "write_file":
        if args["path"].startswith("/etc"):
            return "reject"
        return "approve"
    return "approve"

The harness also supports async approval. The graph pauses, sends a webhook or message to a human, waits for a response, then resumes. This works for long-running agents that don't have a human in the loop synchronously.

Streaming and Checkpointing: Production Deployment Shape

Deep Agents inherits LangGraph's streaming and checkpointing. Every step is checkpointed. If the agent crashes, you resume from the last checkpoint. If the user disconnects, the agent keeps running and streams results when they reconnect.

Checkpointing mechanics:

  • After every LLM call: State is serialized (conversation history, tool outputs, memory pointers).
  • After every tool execution: Tool result is appended to state, checkpoint is written.
  • On interrupt: State is serialized with the pending tool call, waiting for human approval.

Checkpoints are stored in a pluggable backend: in-memory (dev), SQLite (single-user), Postgres (production). The checkpoint includes the full graph state, so you can resume mid-execution.

Streaming:

The harness streams tokens from the LLM, tool call proposals, tool execution results, and final answers. You get a stream of events:

  • llm_token: Partial LLM response.
  • tool_call_proposed: Agent wants to call a tool.
  • tool_call_approved: Human approved (or auto-approved).
  • tool_result: Tool execution finished.
  • final_answer: Agent is done.

This lets you build real-time UIs that show the agent's reasoning as it happens.

Skills: Reusable Behaviors the Agent Loads on Demand

Skills are reusable behaviors the agent can load when it needs them. A skill is a function, a prompt, or a sub-graph. The agent calls load_skill("code_review"), and the harness injects the skill into the tool set.

Skill types:

  • Function skill: A Python function the agent can call as a tool.
  • Prompt skill: A system prompt that changes the agent's behavior (e.g., "You are now a code reviewer").
  • Graph skill: A sub-graph that handles a specific task (e.g., multi-step debugging).

Skills are stored in a registry. The agent queries the registry, loads the skill, and uses it. When the task is done, the skill is unloaded to free up context.

Skill loading example:

# Agent decides it needs to review code
await agent.load_skill("code_review")

# Skill adds a code_review tool and a system prompt
# Agent uses the tool, then unloads the skill
await agent.unload_skill("code_review")

This keeps the agent's tool set lean. Instead of loading 50 tools upfront, the agent loads 5 and swaps them as needed.

MCP Server Support: Bring Any Model Context Protocol Server as a Tool

Deep Agents supports the Model Context Protocol (MCP). If you have an MCP server (a service that exposes tools, prompts, or resources), you can plug it into the harness as a tool.

The harness includes an MCP adapter. You point it at an MCP server URL, it fetches the server's tool definitions, and wraps them as LangChain tools. The agent can then call them like any other tool.

MCP adapter flow:

  1. Harness connects to MCP server, fetches tool schemas.
  2. Adapter wraps each tool as a LangChain tool.
  3. Agent calls the tool, adapter translates the call to MCP protocol.
  4. MCP server executes, returns result.
  5. Adapter translates result back to LangChain format.

This lets you use any MCP server (database query, API client, custom logic) without writing integration code.

Observability: Tracing, Evaluation, and Deployment via LangSmith

Deep Agents integrates with LangSmith for tracing, evaluation, and deployment. Every agent run is traced: LLM calls, tool executions, sub-agent delegations, memory queries. You get a flamegraph of the entire execution.

Tracing:

  • LLM calls: Input prompt, output tokens, latency, cost.
  • Tool calls: Tool name, arguments, result, latency.
  • Sub-agent calls: Task description, sub-agent trace, result.
  • Memory queries: Query, results, retrieval latency.

You can replay traces, compare runs, and debug failures. If the agent made a bad decision, you can see exactly what context it had and what tool it called.

Evaluation:

LangSmith lets you run the agent against a test set, compare outputs, and track metrics (success rate, latency, cost). You can version the agent, run A/B tests, and roll back if a new version regresses.

Deployment:

LangSmith can host the agent as a managed service. You push the agent code, LangSmith handles scaling, checkpointing, and streaming. You get an API endpoint and a web UI.

Failure Modes and Mitigation

Context window overflow:

Sub-agents help, but if the parent agent's context still overflows, the harness summarizes old messages and offloads tool outputs to disk. The agent can retrieve them later if needed.

Tool call loops:

If the agent gets stuck calling the same tool repeatedly, the harness tracks tool call history and raises an interrupt after N identical calls. You can then intervene or kill the loop.

Sub-agent failures:

If a sub-agent crashes, the parent agent gets an error result. The parent can retry, delegate to a different sub-agent, or escalate to a human.

Memory pollution:

If the agent writes too much to memory, retrieval gets slow and noisy. The harness includes a memory pruning policy: old, low-relevance facts are archived or deleted.

Approval fatigue:

If every tool call requires approval, humans get overwhelmed. The policy should auto-approve low-risk tools and only interrupt for high-risk ones.

Technical Verdict

Use Deep Agents when:

  • You need a working agent out of the box, not a framework to build one from scratch.
  • You want sub-agents for task delegation without managing context windows manually.
  • You need pluggable filesystem backends (local, sandboxed, remote) without rewriting agent code.
  • You want persistent memory across sessions with minimal setup.
  • You need human-in-the-loop approval for high-risk tool calls.
  • You're deploying to production and need streaming, checkpointing, and observability.

Avoid Deep Agents when:

  • You need full control over the orchestration graph and don't want opinionated defaults.
  • You're building a domain-specific agent that doesn't fit the harness's assumptions (long-horizon, multi-step work).
  • You need sub-second latency and can't afford the overhead of checkpointing and memory queries.
  • You're running in a constrained environment (edge device, embedded system) where LangGraph's dependencies are too heavy.

Deep Agents is opinionated. It makes decisions for you (how to delegate, how to manage context, how to persist state). If those decisions fit your use case, you save weeks of plumbing work. If they don't, you'll fight the harness.

Source Links

來源:DEV Community · dev.to