google ADK VS Langchain-ai

架构设计

Google ADK

Langchain-ai

核心流程设计

Google ADK

Langchain-ai

核心功能设计

Skill

Google ADK

阶段 核心动作 关键类
Phase 1: Skill 定义 Skill = 文件夹 (SKILL.md + references/ + assets/ + scripts/);元数据由 Frontmatter (YAML) 表示 Frontmatter, SkillSource 接口
Phase 2: 加载 (Source 构造) 三种 SkillSource 实现从不同介质加载 skill;共享解析逻辑在 AbstractSkillSource ClassPathSkillSource, LocalSkillSource, InMemorySkillSource, AbstractSkillSource
Phase 3: 注册 SkillToolset 包装 SkillSource,创建 3 个工具,挂到 LlmAgent SkillToolset, BaseToolset
Phase 4: 发现 (每次 LLM 请求) processLlmRequest 自动列出所有 skill frontmatter,拼成 <available_skills> XML 注入系统提示 SkillToolset.processLlmRequest, AbstractSkillSource.listFrontmatters
Phase 5: 查找 (LLM 工具调用) LLM 调用 3 个工具之一:list_skills / load_skill / load_skill_resource ListSkillsTool, LoadSkillTool, LoadSkillResourceTool
Phase 6: Script 运行 run_skill_script RunSkillScriptTool, CodeExecution, BaseCodeExecutoradk java原生缺,adk python不缺。

langchain-ai

Skills 中间件实现了 渐进式披露(progressive disclosure) 模式:

  1. 会话开始时 一次性加载所有 skill 的元数据(name + description),写入 SkillsState
  2. 每次模型调用时把 skill 列表格式化进系统提示,告诉模型「有哪些 skill 可用」;
  3. 运行时由模型驱动 :模型根据任务决定是否调用某个 skill,再按需 read_file 读取完整的 SKILL.md、辅助脚本与资源,最后执行脚本或按 SKILL.md 中的工作流完成任务。

中间件只负责前两步(加载 + 注入),第三步的「查找 → 读资源 → 读脚本 → 运行脚本 → 运行 Skill」是模型在运行时通过工具调用自主完成的------中间件不直接执行这些动作,而是通过系统提示引导模型完成。

LangChain deepagents skills 内置了常见的CLi命令,需要注入:FileSystemMiddleware,使用FilteSystemBackend执行。

自定义的CLI命令,需要自己实现Tool,或者自定义Middleware,自定义Backend

自己实现Tool,参考:

python 复制代码
@tool
def run_calc_funnel(command: str, flow_id: int) -> str:
    """Execute a calc_funnel.py CLI subcommand and return the output.

    This tool runs the marketing funnel analysis script as a CLI command.
    The script must be called with a subcommand and a flow_id.

    Available subcommands:
    - get_funnel_data: Get raw funnel stage data for a given flow_id
    - check_funnel_data: Validate funnel data consistency for a given flow_id
    - calc_funnel_rate: Calculate overall conversion rates for each funnel stage
    - calc_funnel_decay: Calculate step-by-step conversion rates and find the biggest decay stage

    Args:
        command: The subcommand to execute (get_funnel_data, check_funnel_data, calc_funnel_rate, calc_funnel_decay)
        flow_id: The marketing flow ID to analyze (e.g., 1, 2, 3)
    """
    valid_commands = {"get_funnel_data", "check_funnel_data", "calc_funnel_rate", "calc_funnel_decay"}
    if command not in valid_commands:
        return f"Invalid command '{command}'. Valid commands: {', '.join(sorted(valid_commands))}"

    cmd = [sys.executable, str(CALC_FUNNEL_SCRIPT), command, "--flow_id", str(flow_id)]

    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            encoding="utf-8",
            errors="replace",
            timeout=30,
        )
        output = result.stdout.strip() if result.stdout.strip() else result.stderr.strip()
        return output if output else "(no output)"
    except subprocess.TimeoutExpired:
        return "Error: command timed out after 30 seconds"
    except Exception as e:
        return f"Error executing command: {e}"

自定义实现Middleware,使用sandbox

python 复制代码
import asyncio
from pathlib import Path
from typing import Any

from deepagents import create_deep_agent
from deepagents.backends import CompositeBackend, StoreBackend
from deepagents.backends.langsmith import LangSmithSandbox
from deepagents.backends.utils import create_file_data
from langchain.agents.middleware import AgentMiddleware, AgentState

from langgraph.runtime import Runtime
from langgraph.store.memory import InMemoryStore
from langsmith.sandbox import SandboxClient

# Identical skill bundles for every user: one shared store namespace.
SKILLS_SHARED_NAMESPACE = ("skills", "builtin")


class SkillSandboxSyncMiddleware(AgentMiddleware[AgentState, Any, Any]):
    """Copy shared skill files from the store into the sandbox before each agent run."""

    def __init__(self, backend: CompositeBackend) -> None:
        super().__init__()
        self.backend = backend

    async def abefore_agent(self, state: AgentState, runtime: Runtime[Any]) -> None:
        store = runtime.store

        files: list[tuple[str, bytes]] = []
        for item in await store.asearch(SKILLS_SHARED_NAMESPACE):
            key = str(item.key)
            if ".." in key or any(c in key for c in ("*", "?")):
                msg = f"Invalid key: {key}"
                raise ValueError(msg)
            normalized = key if key.startswith("/") else f"/{key}"
            # CompositeBackend routes paths and batches uploads to the right backend.
            files.append((f"/skills{normalized}", item.value["content"].encode()))

        if files:
            await self.backend.aupload_files(files)


async def seed_skill_store(store: InMemoryStore) -> None:
    """Load canonical skill files from disk into the shared store namespace (run once at deploy).
    You can retrieve skills from any source (local filesystem, remote URL, etc.).
    """
    skills_dir = Path(__file__).resolve().parent / "skills"
    for file_path in sorted(p for p in skills_dir.rglob("*") if p.is_file()):
        rel = file_path.relative_to(skills_dir).as_posix()
        key = f"/{rel}"
        await store.aput(
            SKILLS_SHARED_NAMESPACE,
            key,
            create_file_data(file_path.read_text(encoding="utf-8")),
        )


async def main() -> None:
    store = InMemoryStore()
    await seed_skill_store(store)

    client = SandboxClient()
    ls_sandbox = client.create_sandbox()
    sandbox_backend = LangSmithSandbox(sandbox=ls_sandbox)

    backend = CompositeBackend(
        default=sandbox_backend,
        routes={
            "/skills/": StoreBackend(
                store=store,
                namespace=lambda _rt: SKILLS_SHARED_NAMESPACE,
            ),
        },
    )

    try:
        agent = create_deep_agent(
            model="openai:gpt-5.5",
            backend=backend,
            skills=["/skills/"],
            store=store,
            middleware=[SkillSandboxSyncMiddleware(backend)],
        )

    finally:
        client.delete_sandbox(ls_sandbox.name)


if __name__ == "__main__":
    asyncio.run(main())

MultiAgent

Google adk

langchain-ai

create_deep_agent 是 Deep Agents 的主入口,负责把模型、工具、子代理、中间件、权限、内存等配置装配成一个可运行的 CompiledStateGraph。整个过程可划分为 6 个阶段,按顺序依次执行:

  1. 模型与 Profile 解析
  2. 工具与 Backend 初始化
  3. 子代理(Subagents)处理
  4. 通用子代理(General-Purpose)自动注入
  5. 主代理中间件栈装配
  6. 状态聚合与最终构造

Human In The Loop

Google adk

HITL 确认是一个跨两次 runAsync 调用的流程,图中的虚线分隔标记了 Turn 1 和 Turn 2 的边界。

Turn 1:工具请求确认

  1. LLM 调用工具 (如 delete_file),Functions.handleFunctionCalls 调用 FunctionTool.runAsync
  2. 确认检查 (FunctionTool.java:248):requireConfirmation=truetoolContext.toolConfirmation() 为空(首次调用,无确认)。
  3. 请求确认toolContext.requestConfirmation(hint)ToolConfirmation(hint, confirmed=false) 写入 EventActions.requestedToolConfirmations[callId]
  4. 返回错误响应 :工具返回 {"error": "requires confirmation"},作为 FunctionResponse Event。
  5. 生成确认请求事件Functions.generateRequestConfirmationEvent 生成一个 request_confirmation FunctionCall Event,其 args 中携带 originalFunctionCall(原始调用信息)。
  6. 流程暂停:确认请求事件流式返回给调用方,流程暂停,等待用户响应。

用户响应(两次 runAsync 之间)

  1. 用户决定批准或拒绝,以 request_confirmation FunctionResponse(含 ToolConfirmation(confirmed=true/false))的形式回传。
  2. 该响应作为 user event 追加到 session。

Turn 2:恢复已确认工具(preprocess)

  1. 下一次 runAsync 触发 preprocess,RequestConfirmationLlmRequestProcessor.processRequest 执行(RequestConfirmationLlmRequestProcessor.java:60)。
  2. 扫描历史findMostRecentConfirmations 从 session 末尾向前找最近的 user event 中的 request_confirmation FunctionResponse(RequestConfirmationLlmRequestProcessor.java:148)。
  3. 找不到:跳过,走正常 LLM 调用流程。
  4. 找到 :提取 ToolConfirmation,并从 request_confirmation 的 args 中解析出 originalFunctionCall(原始工具调用)。
  5. 过滤已执行:扫描确认事件之后的 FunctionResponse ID,移除已经执行过的工具(避免重复执行)(RequestConfirmationLlmRequestProcessor.java:114)。
  6. 无剩余:跳过。
  7. 有剩余 :通过 Functions.handleFunctionCalls 重新执行,这次传入 toolConfirmations 映射。
  8. FunctionTool.runAsync 再次执行 (FunctionTool.java:246):这次 toolContext.toolConfirmation() 非空:
    • confirmed=true:调用 call() 真正执行工具,返回真实结果;
    • confirmed=false:返回 {"error": "rejected"}
  1. 结果作为 RequestProcessingResult 返回,flow 继续推进。

Langchain-ai

  1. 模型输出 :模型节点产出一条带 tool_callsAIMessage,图准备路由到 ToolNode,但在此之前先经过 after_model 钩子。
  2. 钩子触发after_model 读取 state["messages"],定位最后一条 AIMessage;若不存在或没有 tool_calls,直接返回 None,中间件「透明」。
  3. 逐个筛选 :遍历 tool_calls
    • 不在 interrupt_on → 自动放行(绿色)。
    • interrupt_onwhen(req) 返回 False → 自动放行(绿色)。
    • 两者都通过 → 生成 ActionRequest + ReviewConfig,记下原始下标 idx,加入中断列表。
  1. 是否需要中断 :若中断列表为空,返回 None,全部工具正常执行。
  2. 批量中断 :把所有 ActionRequest / ReviewConfig 打包成 HITLRequest,调用 interrupt(hitl_request)。整张图在此暂停,控制权交回调用方。
  3. 人工决策 :调用方恢复执行时传入 HITLResponse(decisions=[...])decisions 顺序与 action_requests 一一对应;数量不符则抛 ValueError
  4. 按类型改写 :对每个决策调用 _process_decision,按原始下标顺序重建 tool_calls
    • approve / edit:不生成 ToolMessageToolNode 会执行(edit 执行编辑后的版本)。
    • reject / respond:保留 tool_call 但追加合成的 ToolMessageToolNode 因已有响应而跳过执行,模型直接读到人工提供的内容。
  1. 写回并返回last_ai_msg.tool_calls = revised_tool_calls,返回 {"messages": [last_ai_msg, *artificial_tool_messages]}。图继续走向 ToolNode

综合比较

HITL(Human-In-The-Loop)

维度 LangChain-ai 生态 Google ADK
中断机制 interrupt() 原语,抛出 GraphInterrupt 异常 FunctionTool (confirm)
恢复机制 Command(resume=value) 传入恢复值 通过 invocation_id + FunctionResponse 恢复调用链
决策类型 4 种:Approve / Edit / Reject / Respond 2 种:approve / reject(需手动扩展)
状态持久化 Checkpoint 自动保存中断点 Event Stream + Session.state 保存 confirm 等
多次中断 原生支持(同一节点多次 interrupt() 需覆盖 _get_declaration() 移除防重复调用指令(非默认行为
中断粒度 任意代码位置 仅在 FunctionTool 返回时
恢复后执行 从中断点继续执行(checkpoint 恢复) rerun_on_resume 重跑节点(DynamicNodeScheduler 惰性重建状态)

关键差异

  • LangGraph 的 interrupt()任意位置中断原语,可以停在代码任何地方,恢复时从断点继续。天然支持多步 HITL
  • ADK 的 HITL 仅在 FunctionTool 返回时 才能中断,且默认带"不要重复调用"指令,多步 HITL 需要手动覆盖(FunctionTool)。恢复后是 rerun_on_resume(重跑),而非断点续行

意图识别与动态路由

维度 LangChain-ai 生态 Google ADK
路由方式 conditional_edges(StateGraph 条件边) transfer_to_agent tool(LLM 自主路由)
动态节点 Send 实现动态 fan-out DynamicNodeScheduler + ctx.run_node()
意图识别 在 conditional_edge 函数中实现,可通过工程化代码,LLM,规则引擎,分词搜索等方式实现意图识别 Agent 自身即意图识别器,LLM 判断后调用 transfer
路由灵活性 编译时确定边,运行时条件选择 完全由 LLM 运行时决定
确定性 高(图结构编译时固定) 低(依赖 LLM 判断质量)

关键差异

  • LangGraph 的路由是编译时声明 + 运行时条件,图结构可见、可调试
  • ADK 的路由是LLM 运行时自主决策,灵活但不可预测,依赖 prompt engineering 约束

Skills

维度 LangChain-ai 生态(DeepAgents) Google ADK
Skill 格式 SKILL.md(YAML frontmatter + markdown) 目录结构(SKILL.md + scripts/)
加载方式 load_skill_from_dir load_skill_from_dir
渐进注入 SkillsMiddleware 实现 4 阶段:discovery → availability → activation → injection SkillToolset:list_skills → load_skill 两步
分层优先级 SDK → user → project → team 无显式分层
安全性 SkillsState 约束,references 限制 UnsafeLocalCodeExecutor(无安全约束)
稳定性 SkillsMiddleware 管理 SkillsState,可排除/重载 SkillToolset 一次性加载,无运行时管理
运行CLI 1. FileSystemMiddleware内置的一些常见的CLI 命令
  1. 自定义tool
  2. 自定义Middleware | 1. 运行 run_skill_script tool。adk java原生缺,adk_python原生有。
  3. adk python 运行不稳定,参数推断错 |

优缺点

Google ADK 优缺点

优点:

  1. 一体化与开箱即用:Runner → Flow → Agent → Tool 全链路封装,无需拼装多个库,部署成本低。
  2. Session / State / Event 一等公民 :内建 SessionServiceStateEvent 体系,会话持久化、状态增量(stateDelta)、Artifact 管理原生支持,无需额外选型。
  3. Plugin / Callback 扩展点丰富beforeModelCallback / afterModelCallback / beforeToolCallback / afterToolCallback / onEventCallback 等钩子覆盖全生命周期,且可通过 Plugin 接口统一管理。
  4. RequestProcessor / ResponseProcessor 管线清晰SingleFlow.REQUEST_PROCESSORS 链式处理 LlmRequest(Instructions / Identity / Compaction / CodeExecution / Tools),postprocess 阶段统一处理 functionCalls / AgentTransfer / OutputSchema,流程可追踪。
  5. Skill 体系规范 :6 阶段(定义 → 加载 → 注册 → 发现 → 查找 → 脚本运行),SkillToolset 自动注入 <available_skills> XML,渐进式披露由框架兜底。
  6. 多 Agent 转移语义统一transfer_to_agent 作为工具调用,LLM 自主路由,无需手工编排条件边,适合动态多 Agent 协作。
  7. Tracing 内建Tracing.traceCallLlm 等 span 原生集成,可观测性起步门槛低。

缺点:

  1. HITL 能力较弱 :中断仅在 FunctionTool 返回时触发,默认带"不要重复调用"指令,多步 HITL 需手动覆盖 _get_declaration();恢复后是 rerun_on_resume(重跑节点)而非断点续行,语义不直观。
  2. 路由确定性低 :完全依赖 LLM 判断 transfer_to_agent,缺乏编译时图结构约束,复杂流程难以静态验证与调试。
  3. 状态图能力缺失:无显式 StateGraph / conditional_edges / Send(fan-out)等图原语,复杂分支与并行编排需借助 Flow 与 RequestProcessor 间接实现,表达力弱于 LangGraph。
  4. Checkpointer / Time Travel 缺失:无 LangGraph 那样的状态回放与历史修改能力,长程任务的回溯与调试依赖 Event Stream 手工分析。
  5. Skill 脚本运行不完整run_skill_script 在 adk java 原生缺失,adk python 存在但参数推断不稳定;UnsafeLocalCodeExecutor 缺乏安全约束。
  6. 生态广度受限:相比 LangChain 社区的工具 / 向量库 / 检索器集成规模,ADK 的第三方集成相对较少,主要绑定 Google 生态(Gemini / Vertex)。
  7. 多步 HITL 与复杂中断场景需自行扩展:原生仅 approve / reject 两类决策,Approve / Edit / Reject / Respond 需手工补齐。

LangChain-AI 生态优缺点

优点:

  1. 分层清晰、按需选用:LangChain(原语)→ LangGraph(状态图)→ DeepAgents(自治框架)分层明确,可从原语层逐步升级,避免过度封装。
  2. LangGraph 状态图表达力强StateGraph + conditional_edges + Send 提供编译时可见、可调试的图结构;Checkpointer(Postgres / SQLite)原生支持状态持久化与 Time Travel(状态回放 / 修改)。
  3. HITL 原语强大interrupt() 可在任意代码位置 中断,Command(resume=value) 恢复;支持 4 类决策(Approve / Edit / Reject / Respond),天然适配多步审批与复杂人工流程。
  4. Middleware 钩子体系成熟before_agent / before_model / after_model / after_agent / wrap_model_call / wrap_tool_call 钩子粒度细,且支持同步 / 异步对称实现,易于横切关注点。
  5. DeepAgents 高级抽象完备create_deep_agent 一站式装配模型 / 工具 / 子代理 / 中间件 / 权限 / 内存 / Skills,Profile + Backend + Subagents + General-Purpose + 状态聚合 6 阶段流程化。
  6. Skills 渐进式披露规范SkillsMiddleware 实现 discovery → availability → activation → injection 4 阶段;分层优先级(SDK → user → project → team);SkillsState 约束 + 安全的 references 限制。
  7. 生态与可观测性最丰富:LangSmith / LangGraph Cloud 提供完整可观测性与托管;工具 / 向量库 / 检索器 / 模型适配器覆盖最广。
  8. 确定性路由与动态 fan-out :编译时声明条件边 + 运行时 Send 动态 fan-out,兼顾确定性与灵活性,适合复杂工作流。

缺点:

  1. 学习曲线与拼装成本高:三层生态需理解 LangChain LCEL、LangGraph StateGraph、DeepAgents Harness 三套抽象,入门门槛与初始工程化成本显著高于 ADK。
  2. Session / State 抽象分散:状态管理依赖 Checkpointer + Store + SkillsState 多处,缺少 ADK 那种统一的 Session / Event / Artifact 一等公民模型,跨层一致性需自行维护。
  3. Runner / 持久化需要额外组件:生产部署需引入 LangGraph Cloud 或自建 Postgres / SQLite Checkpointer,运维复杂度高于 ADK 的内建 SessionService。
  4. Skill 脚本运行需自配 Backend :DeepAgents 的 run_skill_script 依赖 FileSystemMiddleware + FileSystemBackend 或自定义 Sandbox(如 LangSmithSandbox),自定义 CLI 命令需手写 Tool 或 Middleware,工程量较 ADK 的 RunSkillScriptTool 更重。
  5. 中间件链复杂时调试困难wrap_model_call / wrap_tool_call 多层嵌套(_chain_model_call_handlers / _chain_tool_call_wrappers),顺序与异常传播路径不直观,易出错。
  6. 路由虽确定但灵活性受限:编译时固定边结构,动态意图识别需在 conditional_edge 函数中自行实现(规则 / LLM / 分词搜索),变更图结构需重新 compile,迭代成本高于 ADK 的 LLM 自主路由。
  7. Python 为主,多语言支持弱:LangChain 生态以 Python 为中心,Java / Go 等语言_binding 相对薄弱,跨语言团队需额外适配。
相关推荐
码流怪侠1 小时前
SuperAGI 技术深度解析:开发者优先的开源自主 AI Agent 框架
github·agent
名字还没想好☜1 小时前
Go 用 bufio.Scanner 读大文件踩坑:默认 64KB 行上限、Buffer 扩容与按 Token 切分
开发语言·后端·golang·go
元Y亨H1 小时前
大模型技术 LangGraph 概述
llm
愚农搬码1 小时前
AI Agent 目前最大的瓶颈是什么?
llm·agent·ai编程
用户6083089290472 小时前
SpringBoot自定义注解校验
后端
Keepreal4962 小时前
《从零构建大模型》——从头实现GPT模型进行文本生成
gpt·深度学习·llm
董员外2 小时前
RAG 系统进化论(六):GraphRAG(基于知识图谱的 RAG),从相似文本走向实体关系
人工智能·后端·设计模式
Conan在掘金2 小时前
ArkTS 进阶之道(31):状态联动深水区收官边界——为啥四级状态容器 + V1/V2 双轨是状态联动深水区收官根因
后端
Conan在掘金2 小时前
鸿蒙 7.0 空间美学开篇:沉浸式毛玻璃 + 底部 Sheet 面板——一行 backgroundBlurStyle 玩出物理材质感
后端