基于图的智能体工作流¶
ADK 中基于图的智能体工作流让你能够更精确地控制智能体的构建, 创建结合代码逻辑和 AI 推理能力的确定性流程。基于图的工作流允许你将智能体逻辑定义为 由执行节点和边组成的图,将 AI 驱动的智能体推理与确定性工具和代码相结合。
图 1. 基于图的航班升级智能体设计,组合了不同类型的工作流节点, 包括函数、人工输入、工具和大语言模型能力。
ADK 提供了预置的模板工作流, 例如顺序智能体, 它们仅在一组智能体之间提供定义好的流程控制。你可以继续使用 冗长的提示词和工具来构建标准 ADK 智能体,并在基于图的工作流智能体中使用它们。当你需要更精确的控制时,工作流智能体图可以让你 更灵活地决定任务的路由和执行方式。基于图的工作流具有以下优势:
- 定义精确的逻辑: 显式映射路由逻辑来管理不同节点之间的转换。
- 实现复杂结构: 构建支持分支和状态管理的智能体工作流。
- 无需 AI 即可运行函数链: 调用智能体工具和你自己的代码,而无需调用生成式 AI 模型。
- 增强可靠性: 通过依赖结构化的节点定义而非仅依赖提示词来提高智能体的可预测性。
ADK 中的工作流风格
ADK 提供了三种互补的方式来组合多步骤工作:
开始使用¶
本节介绍如何开始使用基于图的智能体。以下示例展示了如何创建一个顺序执行的基于图的智能体工作流, 该工作流生成一个城市名称,使用代码函数查询该城市的当前时间,最后由智能体报告信息。
from google.adk import Agent
from google.adk import Workflow
from google.adk import Event
from pydantic import BaseModel
city_generator_agent = Agent(
name="city_generator_agent",
model="gemini-flash-latest",
instruction="""Return the name of a random city.
Return only the name, nothing else.""",
output_schema=str,
)
class CityTime(BaseModel):
time_info: str # 时间信息
city: str # 城市名称
def lookup_time_function(node_input: str):
"""模拟返回指定城市的当前时间。"""
return CityTime(time_info="10:10 AM", city=node_input)
city_report_agent = Agent(
name="city_report_agent",
model="gemini-flash-latest",
input_schema=CityTime,
instruction="""Output following line:
It is {CityTime.time_info} in {CityTime.city} right now.""",
output_schema=str,
)
def completed_message_function(node_input: str):
return Event(
message=f"{node_input}\n WORKFLOW COMPLETED.",
)
root_agent = Workflow(
name="root_agent",
edges=[
("START", city_generator_agent, lookup_time_function,
city_report_agent, completed_message_function)
],
)
在 ADK Go v2.0.0 中,顺序工作流使用图引擎:
workflow.NewFunctionNode 包装每个步骤,workflow.Chain 将
节点连接成一个顺序的 edges 切片。框架自动通过 event.Output
将每个节点的类型化返回值传递给下一个节点——无需写入会话状态。整个图被
包装在 workflowagent.New 中,它会生成一个标准的 agent.Agent。
// cityTime holds the data passed from the lookup step to the report step.
type cityTime struct {
City string
TimeInfo string
}
// newSequentialGetStarted builds a three-node sequential workflow using the
// v2 graph engine. Each node is a workflow.NewFunctionNode whose return value
// is automatically wrapped in session.Event.Output and forwarded to the next
// node as its typed input.
//
// This is the Go equivalent of the Python Workflow example:
//
// root_agent = Workflow(
// name="root_agent",
// edges=[("START", city_generator_agent, lookup_time_function,
// city_report_agent, completed_message_function)],
// )
func newSequentialGetStarted() (agent.Agent, error) {
// Step 1: return a city name. The string is set as event.Output and
// becomes the typed input of the next node.
cityGeneratorNode := workflow.NewFunctionNode("city_generator_agent",
func(_ agent.Context, _ any) (string, error) {
return "Tokyo", nil
},
workflow.NodeConfig{},
)
// Step 2: receive the city name and return structured time data.
lookupTimeNode := workflow.NewFunctionNode("lookup_time_function",
func(_ agent.Context, city string) (cityTime, error) {
return cityTime{City: city, TimeInfo: "10:10 AM"}, nil
},
workflow.NodeConfig{},
)
// Step 3: receive the cityTime struct and produce the final report string.
cityReportNode := workflow.NewFunctionNode("city_report_agent",
func(_ agent.Context, ct cityTime) (string, error) {
return fmt.Sprintf("It is %s in %s right now.\nWORKFLOW COMPLETED.",
ct.TimeInfo, ct.City), nil
},
workflow.NodeConfig{},
)
// workflow.Chain wires START → cityGeneratorNode → lookupTimeNode → cityReportNode.
// Data flows through event.Output: no session state writes needed.
return workflowagent.New(workflowagent.Config{
Name: "root_agent",
Description: "Sequential workflow: generate city → look up time → report.",
Edges: workflow.Chain(workflow.Start, cityGeneratorNode, lookupTimeNode, cityReportNode),
})
}
这段示例代码演示了如何组装一个简单的顺序工作流, 并在智能体处理和代码执行之间交替进行。虽然你可以使用单个智能体配合更长的提示词和工具调用来执行这些步骤, 但基于图的方法可以让你精确控制任务的执行顺序以及每个步骤的数据输出。
有关基于图的工作流中数据处理的更多信息,请参阅工作流节点和智能体的数据处理。
使用图构建流程¶
你可以使用基于提示词的智能体来定义多步骤流程, 通过 ADK 智能体的 instructions 字段描述任务和流程。然而,随着你的指令和流程变得更长更复杂, 确保智能体遵循每个步骤和指南也变得更加复杂且可靠性降低。
基于图的工作流智能体相比基于提示词的智能体具有显著优势, 它允许你在代码中明确定义整体流程工作流。通过基于图的智能体工作流, 流程的每个步骤都可以被定义为图中的执行节点,每个节点可以是 AI 智能体、工具或你编写的代码。下图展示了 一个简单的基于提示词的智能体如何转化为工作流智能体图:
图 2. 基于提示词的智能体指令被转化为基于图的工作流的结构。
从基于提示词的智能体转向基于图的工作流智能体, 使你能够明确地分解流程中的任务以定义特定的执行流。一旦定义完成, 智能体应用程序将按照图中的步骤流转,根据需要在非确定性的 AI 驱动智能体和确定性代码之间切换。
以下代码示例展示了图 2 中的工作流图如何被转化为基于图的智能体:
process_message = Agent(
name="process_message",
model="gemini-flash-latest",
instruction="""Classify user message into either "BUG", "CUSTOMER_SUPPORT",
or "LOGISTICS". If you think a message applies to more than one category,
reply with a comma separated list of categories.
""",
output_schema=str,
)
def router(node_input: str):
routes = node_input.split(",")
routes = [route.strip() for route in routes]
return Event(route=routes)
def response_1_bug():
return Event(message="Handling bug...")
def response_2_support():
return Event(message="Handling customer support...")
def response_3_logistics():
return Event(message="Handling logistics...")
root_agent = Workflow(
name="routing_workflow",
edges=[
("START", process_message, router),
( router,
{
"BUG": response_1_bug,
"CUSTOMER_SUPPORT": response_2_support,
"LOGISTICS": response_3_logistics,
}
)
],
)
在 ADK Go v2.0.0 中,条件路由使用 workflow.NewEmittingFunctionNode
来设置 event.Routes,并使用 workflow.StringRoute 边来分发到
匹配的处理器——这与 Python 的 router 函数和字典分发直接对应。workflow.Concat 将链和条件边
合并为传递给 workflowagent.New 的单个 edges 切片。
// classifyMessage is the router node. It emits ev.Routes to select which
// branch to follow — the Go equivalent of Python's:
//
// def router(node_input: str):
// return Event(route=["BUG"])
func classifyMessage(ctx agent.Context, msg string, emit func(*session.Event) error) (any, error) {
// In a real workflow this step calls an LLM; here we classify by keyword.
category := "LOGISTICS"
lower := strings.ToLower(msg)
switch {
case strings.Contains(lower, "bug") || strings.Contains(lower, "error"):
category = "BUG"
case strings.Contains(lower, "help") || strings.Contains(lower, "support"):
category = "CUSTOMER_SUPPORT"
}
ev := session.NewEvent(ctx, ctx.InvocationID())
ev.Routes = []string{category} // drives edge dispatch
ev.Output = msg // forward original message to the chosen handler
if err := emit(ev); err != nil {
return nil, err
}
return nil, nil // nil suppresses the automatic terminal event
}
// newProcessPipeline builds a classification + conditional-routing workflow
// using the v2 graph engine. The classifyMessage emitting node sets
// ev.Routes, and the graph engine dispatches to the matching handler via
// workflow.StringRoute.
//
// This is the Go equivalent of the Python Workflow example:
//
// root_agent = Workflow(
// name="routing_workflow",
// edges=[
// ("START", process_message, router),
// (router, {
// "BUG": response_1_bug,
// "CUSTOMER_SUPPORT": response_2_support,
// "LOGISTICS": response_3_logistics,
// }),
// ],
// )
func newProcessPipeline() (agent.Agent, error) {
classifyNode := workflow.NewEmittingFunctionNode(
"process_message", classifyMessage, workflow.NodeConfig{},
)
bugNode := workflow.NewFunctionNode("response_1_bug",
func(_ agent.Context, _ any) (string, error) {
return "Handling bug...", nil
},
workflow.NodeConfig{},
)
supportNode := workflow.NewFunctionNode("response_2_support",
func(_ agent.Context, _ any) (string, error) {
return "Handling customer support...", nil
},
workflow.NodeConfig{},
)
logisticsNode := workflow.NewFunctionNode("response_3_logistics",
func(_ agent.Context, _ any) (string, error) {
return "Handling logistics...", nil
},
workflow.NodeConfig{},
)
// workflow.Concat merges the sequential chain with the conditional edges.
// Each workflow.Edge carries a workflow.StringRoute matcher that the engine
// checks against ev.Routes emitted by classifyNode.
edges := workflow.Concat(
workflow.Chain(workflow.Start, classifyNode),
[]workflow.Edge{
{From: classifyNode, To: bugNode, Route: workflow.StringRoute("BUG")},
{From: classifyNode, To: supportNode, Route: workflow.StringRoute("CUSTOMER_SUPPORT")},
{From: classifyNode, To: logisticsNode, Route: workflow.StringRoute("LOGISTICS")},
},
)
return workflowagent.New(workflowagent.Config{
Name: "routing_workflow",
Description: "Classifies a message and routes it to the appropriate handler.",
Edges: edges,
})
}
这段示例代码演示了如何组合一系列智能体来定义一个在一组节点之间具有路由的图, 这些节点是离散的任务,可以包含智能体、工具、你的代码,甚至其他工作流智能体。有关构建高级流水线的信息,请参阅 为工作流智能体构建图路由。
已知限制¶
基于图的工作流存在一些已知限制。它们与以下 ADK 功能不兼容:
- 实时流: 基于图的工作流不支持实时流。
- 集成: 部分第三方集成可能与基于图的工作流不兼容。
Go:图工作流 API
ADK Go v2.0.0 中的 workflow 包与 Python 的 Workflow 类直接对应。使用 workflow.NewFunctionNode 和
workflow.NewAgentNode 定义节点,使用 workflow.Chain 或
workflow.Concat 配合 []workflow.Edge 连接它们,并使用
workflowagent.New 将图包装为可运行的智能体。条件路由使用
workflow.StringRoute、workflow.IntRoute 或
workflow.BoolRoute 与 event.Routes 匹配。扇入由
workflow.NewJoinNode 处理。
有关高级路由模式和扇出/合并示例,请参阅 为工作流智能体构建图路由。有关预置的更高层级替代方案(顺序、并行、循环),请参阅 预置工作流智能体。