乐于分享
好东西不私藏

AI时代企业的两种智能架构形态:LLM Agent与Graph

AI时代企业的两种智能架构形态:LLM Agent与Graph

SECTION

引言

各类产品界面中已集成形态各异的 AI 能力,例如语音转写纪要、AI 生成内容、拍照答疑、对话输入框等功能。

看似简单的产品界面,实则对应两种截然不同的 AI 应用实现形态:

1. 对话输入框代表 Agent:以大语言模型(LLM)作为决策核心,可自主规划任务、支持多轮交互,天然适配开放式、持续性任务,外在表现为「对话交互」形态。

2. 功能按钮 / API 代表 Graph:例如上文的「录音总结」按钮,其底层 Graph 流程大致为:录音文件输入→LLM 解析并生成摘要→保存录音记录,是一套固定执行流程。Graph 的核心在于流程确定性、任务闭环,依靠预先定义的节点与连线达成指定目标,外在表现为「功能组件」形态。视频生成类 API 同样属于 Graph 形态 AI 应用。

SECTION

核心概念解析

基础定义

1. Graph流程编排):由开发者预先定义拓扑结构的流程图,节点可承载代码函数、API 调用、LLM 调用等逻辑,输入输出通常为结构化数据。核心特征是确定性:相同输入下,执行路径与最终输出可完全预判。

2. Agent智能体):以 LLM 为核心的执行实体,能够自主规划、决策并完成任务;通过与外部环境(工具、用户、其他智能体)动态交互达成目标,行为具备不确定性。核心特征是自主性

3. Tool(工具):可供 Agent 调用的外部能力载体,一般是封装独立功能的函数或 API。工具支持同步 / 异步、有状态 / 无状态等类型,仅负责执行逻辑,不具备自主决策能力。

4. Orchestration(编排):协调调度多个计算单元(节点、智能体)协同工作的过程;本文语境下特指通过 Graph 实现静态流程预定义。

深度对比表

对比维度
Agent智能体Graph流程编排
核心驱动
LLM
 自主决策
开发者预设固定流程
输入数据
非结构化自然语言、图片等
结构化标准数据
交付重点
过程与结果同等重要
仅聚焦最终输出结果
状态管理
长周期、跨轮次持久记忆
单次执行、无状态隔离
运行模式
偏向异步流式执行
偏向同步阻塞执行

Key Point

Agent 是由 LLM 全权驱动、具备自主能力的智能实体,通过调用工具使用外部能力;Graph 具备执行确定性,通过拓扑连线串联各类外部能力,节点内可嵌入 LLM 完成生成、判断逻辑。

SECTION

发展历程:从确定性流程到自主智能

2022 年 Langchain 框架首次发布时,大模型行业主流 API 仍是 OpenAI 文本补全接口,仅支持简单「文本输入 - 文本输出」模式。Langchain 最初的定位是「连接 LLM 与外部计算、数据源」,典型链式结构如下。

此后 ReAct(思考 - 行动)范式被正式提出,首次系统性论证 LLM 可通过「思考 - 执行 - 观测」循环与外部世界交互,为 Agent 自主规划能力奠定理论基础。几乎同期,OpenAI 推出对话补全 API,将大模型交互模式从单轮文本输入输出升级为多轮对话;函数调用(Function Calling)能力随之落地,赋予 LLM 标准化调用外部函数、API 的能力。至此,具备多轮对话、自主调用外部能力的应用形态 ——Agent 正式成型。

行业两大技术路线同步演化:

1. 编排框架分支:Langchain 推出 LangGraph,简单链式编排升级为复杂拓扑流程。这类编排框架完美适配 Graph 形态 AI 应用:支持结构化输入、以最终结果为核心输出、对话历史等状态与编排逻辑解耦,灵活编排 LLM、知识库等各类节点组件。

2. 智能体框架分支:AutoGenCrewAI、谷歌 ADK 等大量 Agent / 多智能体框架涌现。这类框架核心解决「LLM 驱动动态流程」「上下文传递」「记忆管理」「多智能体协作」等问题,与编排框架聚焦的「复杂流程串联外部系统」属于两类不同诉求。

尽管定位不同,编排框架也能实现 ReAct智能体、多智能体等模式,但本质属于模拟实现,好比用 Word 编写代码:功能可实现,但底层设计存在天然适配缺陷编排框架原生面向确定性Graph 流程,而 Agent 核心是动态变化的思考链路,强行用流程框架承载智能体逻辑,会在输出形态、运行模式上产生适配痛点

SECTION

融合路径探索:Agent 与 Graph 的关系

以上痛点根源在于底层架构设计差异:专为确定性流程打造的 Graph 框架,很难原生支撑以动态思考链路为核心的自主智能体系统。

Eino 框架同时覆盖 Graph 编排与 Agent 两大场景,技术演进路径为先推出 Graph 编排组件 eino-compose,再独立开发智能体组件 eino-adk。两套模块看似割裂,开发体验无法互通,且该分离架构会长期保留,但部分能力会深度打通。下文从三个维度拆解 Eino 框架内 Agent 与 Graph 的关联。

智能体场景与流程编排

Agent 与 Graph 存在本质差异,但多智能体场景属于二者交叉领域,无法简单二选一。多智能体即多个智能体通过特定规则协作,对外整体表现为一个完整智能体,其协作规则能否用 Graph 编排实现?我们分三类主流协作模式分析:

1. 层级调用智能体作为工具):行业最通用方案(谷歌 ADK 标准范式):顶层主智能体处理用户交互,遇到代码执行、数据分析等细分任务时,调用专用子智能体。子智能体通常无状态、不与主智能体共享记忆,交互逻辑仅为简单函数调用。主智能体与子智能体仅存在调用与被调用关系,不属于 Graph 编排的节点流转逻辑。

2. 预设固定协作流程:针对成熟标准化协作模式,例如「规划 - 执行 - 重规划」(Plan-Execute-Replan),智能体间交互顺序、角色分工固定。Eino-adk 等框架会将该类模式封装为预制多智能体模板,开发者开箱即用,无需手动配置智能体流转关系。底层 Graph 编排仅作为模板内部实现细节,对开发者完全透明。

智能体协作可通过「智能体作为工具」简化实现,或使用框架预制固定协作模板;完全动态协作场景则和 Graph 静态编排诉求完全割裂,二者底层编排逻辑不存在交集。

将 Agent 作为 Graph 内节点

换个视角思考:Graph 编排流程中是否有嵌入 Agent 的需求?智能体能否作为 Graph 的一个节点?

先梳理二者核心冲突:

1. Agent 输入来源多元:除上游节点结构化数据外,强依赖自身对话记忆;Graph 节点仅以上游输出作为唯一输入源。

2. Agent 输出为异步完整过程数据,下游节点难以解析复用。

若强行将 Agent 嵌入 Graph 节点,等同于强制一个需要多轮对话、长期记忆、异步流式输出的智能体,适配确定性、同步阻塞的流程节点,实现方案繁琐且架构不优雅Graph 仅可启动智能体智能体内部复杂交互逻辑不应阻塞主流程。

实际 Graph 流程中,开发者真正需要的是轻量化「LLM 节点」:在确定流程中接收标准输入,完成意图识别、内容生成,输出结构化数据,为流程注入基础智能能力。

若业务场景确实需要完整 Agent 能力,更合理的方案不是将智能体塞入静态 Graph,而是为 Agent 增加预处理、后处理插件,将业务逻辑内嵌至智能体内部。

小结:直接把 Agent 作为 Graph 节点并非最优解;优先选用轻量化 LLM 节点;确需智能体能力时,通过插件向 Agent 注入业务逻辑。

最优融合方案:Graph 封装为 Agent 的工具

既然微观节点层面直接融合存在诸多缺陷,宏观层面是否存在更简洁的联动方案?答案是依托「工具」作为桥梁。对比 Graph 与 Tool 的核心特征,二者高度匹配:

对比维度
Graph流程编排Tool
 工具
输入格式
结构化数据
结构化数据
交付重点
仅输出最终结果
仅输出最终结果
状态管理
单次执行、无状态
单次执行、无状态
运行模式
整体同步执行
LLM
 视角下同步调用

高度重合的特性意味着:Graph 的运行形态完全适配工具标准,将 Graph 封装为工具逻辑直观、实现简单。绝大多数 Graph 流程都可通过工具机制接入 Agent,扩充智能体能力池,同时完整复用 Graph 的优势:复杂业务拓扑编排、丰富组件生态、流式处理 / 回调 / 断点续跑等框架治理能力。

至此,Agent 与 Graph 两条技术路线的取舍之争形成辩证统一的解决方案。

SECTION

总结

Agent 与 Graph 并非二选一的对立路线,而是两套互补的 AI 应用范式

1. Graph 是构建稳定、确定性 AI 功能的基础:擅长编排复杂业务逻辑、数据处理流水线、API 调用链路,产出可预测、易维护的工作流。需要功能按钮、稳定后端服务时,优先选用 Graph

2. Agent 是实现通用自主智能的核心方案:以 LLM 为中心,依托动态规划与工具调用解决开放式复杂任务。需要可对话、自主完成复杂任务的智能助手时,Agent 是核心选型。

二者最佳融合模式将 Graph 封装为 Agent 的工具。该方案可充分发挥 Graph 强大的流程编排与生态集成能力,扩充 Agent 的工具集。一套完整 RAG 检索链路、数据分析流水线等复杂 Graph 业务,均可封装为智能体的原子能力,由 LLM 在合适时机动态调用。

SECTION

Eino 框架开发者落地指引

1. 使用 eino-compose 编写 Graph 流程,将确定性业务逻辑封装为标准化功能模块;

2. 使用 eino-adk 搭建 Agent智能体,赋予其思考规划、用户交互能力;

3. 将 Graph 封装为工具供给 Agent 调用,实现 1+1>2 的协同效果

SECTION

代码示例

CODE

package tooladapter

import (

"context"

"encoding/json"

"fmt"

"time"

"yourorg/sdk/audit"

"yourorg/sdk/authz"

"yourorg/sdk/mask"

"yourorg/sdk/observability"

"yourorg/sdk/approval"

"yourorg/schema"

)

// SchemaProvider 编译期类型安全约束,替代运行时反射

type SchemaProvider interface {

ToolSchema() *schema.ToolInfo

Validate(input string) error

}

// SensitiveField 敏感字段标记接口,用于自动脱敏

type SensitiveField interface {

Masked() string

SensitiveFields() []string

}

// InterruptHandler 中断回调接口,对接外部审批流

type InterruptHandler func(ctx context.Context, state *InterruptState) error

// InterruptState 中断状态,包含审批所需上下文

type InterruptState struct {

NodeID      string                 `json:"node_id"`

Input       string                 `json:"input"`

Metadata    map[string]interface{} `json:"metadata"`

CreatedAt   time.Time              `json:"created_at"`

ResolvedAt  *time.Time             `json:"resolved_at,omitempty"`

ApprovedBy  string                 `json:"approved_by,omitempty"`

}

// GraphCompileOption 编译选项,扩展支持企业级配置

type GraphCompileOption struct {

Timeout         time.Duration

MaxConcurrency  int

EnableTracing   bool

Classification  string // 密级:内部/秘密/机密/绝密

Department      string // 归属部门

DataCategory    string // 数据分类:个人/财务/业务/技术

}

// InvokableGraphTool 企业级适配器

type InvokableGraphTool[I, O any] struct {

graph           interface{} // 原始图引用,具体类型由使用者保证

name            string

desc            string

tInfo           *schema.ToolInfo

classification  string

department      string

dataCategory    string

schemaProvider  SchemaProvider

maskEngine      *mask.Engine

interruptHandler InterruptHandler

tracer          *observability.Tracer

authzClient     *authz.Client

auditLogger     *audit.Logger

}

// NewEnterpriseGraphTool 企业级构造函数,注入所有基础设施依赖

func NewEnterpriseGraphTool[I, O any](

graph interface{},

name, desc string,

provider SchemaProvider,

maskEngine *mask.Engine,

interruptHandler InterruptHandler,

tracer *observability.Tracer,

authzClient *authz.Client,

auditLogger *audit.Logger,

opts ...GraphCompileOption,

) (*InvokableGraphTool[I, O], error) {

if provider == nil {

return nil, fmt.Errorf("schemaProvider is required for compliance")

}

// 从 SchemaProvider 获取编译期确定的工具信息,替代运行时反射

tInfo := provider.ToolSchema()

tInfo.Name = name

tInfo.Description = desc

// 合并编译选项

compileOpt := GraphCompileOption{

Timeout:        30 * time.Second,

MaxConcurrency: 1,

EnableTracing:  true,

}

if len(opts) > 0 {

compileOpt = opts[0]

}

return &InvokableGraphTool[I, O]{

graph:           graph,

name:            name,

desc:            desc,

tInfo:           tInfo,

classification:  compileOpt.Classification,

department:      compileOpt.Department,

dataCategory:    compileOpt.DataCategory,

schemaProvider:  provider,

maskEngine:      maskEngine,

interruptHandler: interruptHandler,

tracer:          tracer,

authzClient:     authzClient,

auditLogger:     auditLogger,

}, nil

}

// InvokableRun 企业级执行入口,包含安全与合规全链路

func (g *InvokableGraphTool[I, O]) InvokableRun(ctx context.Context, input string, opts ...tool.Option) (output string, err error) {

// 1. 链路追踪埋点

span := g.tracer.StartSpan(ctx, fmt.Sprintf("graph_tool.%s", g.name))

defer span.End()

// 2. 输入校验与脱敏

if err := g.schemaProvider.Validate(input); err != nil {

g.auditLogger.Log(audit.Entry{

Action:    "INPUT_VALIDATION_FAILED",

ToolName:  g.name,

Input:     g.maskEngine.Mask(input),

Error:     err.Error(),

Timestamp: time.Now(),

})

return "", fmt.Errorf("input validation failed: %w", err)

}

// 3. 敏感数据脱敏记录审计日志(输入日志必须脱敏)

safeInput := g.maskEngine.Mask(input)

g.auditLogger.Log(audit.Entry{

Action:       "TOOL_INVOKE",

ToolName:     g.name,

Input:        safeInput,

Department:   g.department,

DataCategory: g.dataCategory,

Timestamp:    time.Now(),

})

// 4. 权限校验:基于ABAC的策略裁决

decision, err := g.authzClient.Check(ctx, authz.Request{

Subject:   getCallerIdentity(ctx), // 从上下文中提取用户身份

Action:    "execute",

Resource:  fmt.Sprintf("tool:%s", g.name),

Attribute: map[string]string{

"classification": g.classification,

"department":     g.department,

"data_category":  g.dataCategory,

},

})

if err != nil || !decision.Allowed {

g.auditLogger.Log(audit.Entry{

Action:   "ACCESS_DENIED",

ToolName: g.name,

Input:    safeInput,

Reason:   decision.Reason,

})

return "", fmt.Errorf("access denied: %s", decision.Reason)

}

// 5. 反序列化输入(带类型安全检查)

var typedInput I

if err := json.Unmarshal([]byte(input), &typedInput); err != nil {

return "", fmt.Errorf("input deserialization failed: %w", err)

}

// 6. 执行图,捕获中断

result, err := g.executeWithInterrupt(ctx, typedInput)

if err != nil {

g.auditLogger.Log(audit.Entry{

Action:   "EXECUTION_FAILED",

ToolName: g.name,

Input:    safeInput,

Error:    err.Error(),

})

return "", err

}

// 7. 输出脱敏与序列化

rawOutput, _ := json.Marshal(result)

safeOutput := g.maskEngine.Mask(string(rawOutput))

// 8. 输出审计日志

g.auditLogger.Log(audit.Entry{

Action:   "TOOL_COMPLETED",

ToolName: g.name,

Input:    safeInput,

Output:   safeOutput,

Duration: span.Elapsed(),

})

return string(rawOutput), nil

}

// executeWithInterrupt 支持审批中断的执行逻辑

func (g *InvokableGraphTool[I, O]) executeWithInterrupt(ctx context.Context, input I) (O, error) {

// 模拟图执行过程,实际场景中替换为真实的图运行引擎

// 此处仅为示意,展示中断处理流程

var result O

// 假设图在执行过程中遇到需要人工审批的节点

interruptState := &InterruptState{

NodeID:    "approval_node_001",

Input:     fmt.Sprintf("%v", input),

Metadata:  map[string]interface{}{"action": "transfer_funds", "amount": 50000},

CreatedAt: time.Now(),

}

// 调用外部审批处理器,阻塞等待审批结果

if err := g.interruptHandler(ctx, interruptState); err != nil {

return result, fmt.Errorf("interrupt not approved: %w", err)

}

// 审批通过后继续执行

// result = g.graph.Run(ctx, input)

return result, nil

}

// Info 返回工具元信息,包含分级分类字段

func (g *InvokableGraphTool[I, O]) Info(_ context.Context) (*schema.ToolInfo, error) {

// 扩展 ToolInfo,加入企业级属性

extendedInfo := &schema.ToolInfo{

Name:         g.tInfo.Name,

Description:  g.tInfo.Description,

Parameters:   g.tInfo.Parameters,

Classification: g.classification,

Department:     g.department,

DataCategory:   g.dataCategory,

}

return extendedInfo, nil

}

// getCallerIdentity 从上下文中提取调用者身份

func getCallerIdentity(ctx context.Context) string {

// 实际实现从 JWT 或 gRPC metadata 中提取

if identity, ok := ctx.Value("x-user-id").(string); ok {

return identity

}

return "anonymous"

}