ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

基于Deer-Go框架构建多智能体协作系统:从架构设计到实战调优

2026/8/12 20:23:30 拓冰建站 浏览量
基于Deer-Go框架构建多智能体协作系统:从架构设计到实战调优

1. 项目缘起:从字节的Agent框架到开源社区的Go移植

最近在深度研究AI Agent的实现框架,发现了一个很有意思的现象:很多前沿的、经过大规模生产验证的Agent框架,最初都诞生于大厂内部,比如百度的LangChain(虽然现在社区化了,但其核心思想在百度内部早有实践)、阿里的AgentScope,以及我们今天要拆解的——字节跳动的Deer-Flow。

Deer-Flow这个名字,在字节内部的技术分享和部分开源社区的讨论中时有耳闻,它代表了字节在复杂任务编排、多智能体协作以及工作流自动化方面的一套成熟解决方案。但遗憾的是,它并未完全开源,其核心实现与设计哲学,外界只能通过零星的论文、技术博客和API文档管中窥豹。这对于我们这些希望深入理解其内部机制,甚至想在自家项目中借鉴其思想的开发者来说,无疑是一道门槛。

就在这时,Deer-Go项目进入了我的视野。简单来说,这是一个社区驱动的、用Go语言对字节Deer-Flow框架核心思想与架构的“逆向工程”与重新实现。它不是一个简单的API封装或客户端,而是一个从零开始、深度解构原框架设计,并用Go语言特性重新诠释的完整项目。对于Go开发者,尤其是对构建企业级、高并发、可观测的AI Agent系统感兴趣的工程师来说,这无疑是一座宝藏。

我花了近两周的时间,深入研读了Deer-Go的源码、设计文档,并基于其架构搭建了几个实验性的Agent。这篇文章,就是我这次深度研究的全记录。我会带你一起,从零开始拆解Deer-Go,不仅看它“是什么”,更要弄明白它“为什么这么设计”,以及在实际使用中会遇到哪些“坑”,如何避开。无论你是想学习先进的Agent框架设计,还是打算在你的Go项目中引入Agent能力,相信这篇近万字的拆解都能给你带来实实在在的启发。

2. 核心架构透视:Deer-Go如何模拟“鹿群”的协作智能

Deer-Go的命名本身就蕴含了其设计哲学。“Deer”(鹿)象征着敏捷、协作的智能体(Agent),而“Flow”则代表了任务像水流一样被有序地编排、执行。在Go的实现中,这套哲学被转化为几个清晰的核心模块。理解这些模块及其交互,是掌握Deer-Go的关键。

2.1 模块构成与数据流转

Deer-Go的核心架构可以抽象为五个层次,自底向上分别是:

  1. 基础能力层(Capability Layer):这是Agent的“手”和“脚”。主要包括与大模型(如OpenAI GPT、智谱GLM、月之暗面Kimi等)交互的LLM Client,与外部工具(Tool)交互的Tool Executor,以及用于存储和检索对话、知识的内存模块Memory。这一层决定了Agent能“做什么”。

  2. 智能体核心层(Agent Core Layer):这是单个Agent的“大脑”。核心是Agent结构体,它封装了角色的定义(Role)、拥有的工具列表(Tools)、决策逻辑(Reasoning Engine)以及内部状态。一个Agent知道自己的目标,能根据输入和记忆进行思考,并决定调用工具或直接输出。

  3. 编排与路由层(Orchestration & Routing Layer):这是Deer-Go的“神经系统”,也是其精髓所在。它负责管理多个Agent之间的协作。核心组件是Coordinator(协调器)和Router(路由器)。Coordinator接收一个总任务,并将其分解为子任务;Router则根据子任务的内容、当前上下文和各Agent的能力描述,将任务动态分配给最合适的Agent去执行。这个过程模拟了人类团队中“项目经理”和“调度员”的角色。

  4. 工作流引擎层(Workflow Engine Layer):这是任务的“流水线”。Workflow定义了任务的执行蓝图,它是一个有向无环图(DAG),节点是Task(可以是调用一个Agent,也可以是一个简单的函数),边定义了任务之间的依赖关系(如顺序、并行、条件分支)。Workflow Engine负责解析这个蓝图,并驱动其按既定逻辑执行,处理重试、超时等。

  5. 可观测性与控制层(Observability & Control Layer):这是系统的“眼睛”和“遥控器”。主要包括贯穿始终的Logger(结构化日志)、Metrics(指标收集,如任务耗时、Agent调用次数)和Tracer(分布式链路追踪)。这层保证了在复杂的多Agent协作中,我们能清晰地看到每个环节发生了什么,性能瓶颈在哪里,方便调试和运维。

数据在这五层中的典型流转路径是:用户请求 ->Workflow(定义流程)->Coordinator(分解任务)->Router(分配任务)-> 目标Agent(思考与执行)->Tool/LLM(具体操作)-> 结果返回并更新Memory-> 下一个任务或最终输出。

2.2 关键设计模式:为什么是“Actor模型”与“事件驱动”?

在阅读源码时,你会发现Deer-Go大量运用了Go的并发原语(Goroutine, Channel)来实现异步和非阻塞。其底层协调逻辑深受Actor模型事件驱动思想的影响。

为什么选择Actor模型?在传统的多线程共享内存模型中,协调多个高度自治且状态独立的Agent是复杂且容易出错的(锁竞争、死锁)。Actor模型将每个Agent视为一个独立的“演员”(Actor),它有自己的私有状态,只能通过发送和接收消息(Message)来与其他Actor交互。这完美契合了Agent的设定:每个Agent有自己的目标、记忆和工具,它们通过“对话”(消息传递)来协作。在Deer-Go中,每个Agent实例在运行时都可以看作一个轻量级的Actor,CoordinatorRouter通过Channel向它们发送任务消息,并异步接收处理结果。这种设计带来了更好的隔离性、容错性和横向扩展能力。

事件驱动如何工作?整个工作流的执行是被事件驱动的。一个Task的完成会触发一个“任务完成”事件。Workflow Engine监听这些事件,并根据DAG依赖关系判断哪些后续Task满足了执行条件(其所有前置任务均已完成),然后触发它们的执行。这种模式使得系统非常松散耦合,易于添加新的任务类型或改变工作流结构,同时也为实现复杂的分支、循环逻辑提供了基础。

注意:Deer-Go并没有严格实现一个学术意义上的Actor框架(如Erlang的进程或Akka),而是用Goroutine和Channel借鉴了其核心思想。这种“轻量级Actor”模式在Go中非常高效,但也要求开发者对Go的并发模型有深刻理解,否则容易陷入Channel阻塞或Goroutine泄漏的陷阱。

3. 从零构建一个多Agent协作系统:实战步骤详解

理论说得再多,不如亲手搭一个。下面,我将以构建一个“智能旅行规划助手”为例,带你一步步使用Deer-Go实现一个包含多个Agent协作的系统。这个系统需要完成:理解用户需求、查询天气、查找景点、规划行程、生成预算报告。

3.1 环境准备与基础定义

首先,确保你的Go版本在1.18以上,并初始化项目:

go mod init travel-planner go get github.com/community-driven/deer-go # 假设Deer-Go的仓库地址

接下来,定义我们需要的几个Agent角色。在Deer-Go中,角色定义通常放在一个配置文件中(如agents.yaml)或直接在代码中初始化。

# config/agents.yaml agents: - name: "需求分析员" role: "你是一个专业的旅行需求分析师,擅长从用户的模糊描述中提取关键信息,如目的地、时间、人数、预算、兴趣偏好等。" capabilities: ["text_understanding", "information_extraction"] default_tools: [] - name: "天气查询员" role: "你负责查询指定城市和日期的天气情况,并给出穿衣和活动建议。" capabilities: ["weather_query"] default_tools: ["weather_tool"] - name: "景点研究员" role: "你负责查找目的地的热门景点、文化地标、餐厅和娱乐活动,并了解其开放时间、门票和特色。" capabilities: ["local_search", "knowledge_retrieval"] default_tools: ["search_tool", "knowledge_base_tool"] - name: "行程规划师" role: "你是一个高效的行程规划师。根据目的地、时间、用户兴趣和景点信息,安排出合理、舒适且丰富的每日行程。" capabilities: ["scheduling", "optimization"] default_tools: [] - name: "预算分析师" role: "你根据行程、交通、住宿、餐饮和门票信息,估算出大致的旅行花费,并提供省钱建议。" capabilities: ["calculation", "cost_estimation"] default_tools: ["calculator_tool"]

在Go代码中,我们需要加载这个配置,并初始化对应的Agent实例。这里会用到Deer-Go的AgentBuilder模式。

package main import ( "gopkg.in/yaml.v3" "io/ioutil" deer "github.com/community-driven/deer-go" ) type AgentConfig struct { Name string `yaml:"name"` Role string `yaml:"role"` Capabilities []string `yaml:"capabilities"` DefaultTools []string `yaml:"default_tools"` } func loadAgents(configPath string) ([]*deer.Agent, error) { data, err := ioutil.ReadFile(configPath) if err != nil { return nil, err } var configs []AgentConfig if err := yaml.Unmarshal(data, &configs); err != nil { return nil, err } var agents []*deer.Agent llmClient := deer.NewOpenAIClient(apiKey) // 初始化LLM客户端 for _, cfg := range configs { builder := deer.NewAgentBuilder(cfg.Name). WithRole(cfg.Role). WithLLM(llmClient) // 根据配置添加工具(这里需要你事先实现或集成这些工具) for _, toolName := range cfg.DefaultTools { switch toolName { case "weather_tool": builder.WithTool(NewWeatherTool()) case "search_tool": builder.WithTool(NewSearchTool()) // ... 其他工具 } } agent, err := builder.Build() if err != nil { return nil, err } agents = append(agents, agent) } return agents, nil }

3.2 工具(Tool)的实现与集成

Agent的强大之处在于能使用工具。在Deer-Go中,一个工具需要实现deer.Tool接口,通常包含Name(),Description(),ArgsSchema()Execute(ctx context.Context, input map[string]interface{})等方法。

WeatherTool为例:

package tools import ( "context" "encoding/json" "fmt" "net/http" deer "github.com/community-driven/deer-go" ) type WeatherTool struct{} func (w *WeatherTool) Name() string { return "get_weather" } func (w *WeatherTool) Description() string { return "查询指定城市在未来某日期的天气情况。输入需要包含'city'和'date'字段。" } func (w *WeatherTool) ArgsSchema() map[string]interface{} { return map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "city": map[string]interface{}{ "type": "string", "description": "城市名称,例如:北京", }, "date": map[string]interface{}{ "type": "string", "description": "日期,格式为YYYY-MM-DD", }, }, "required": []string{"city", "date"}, } } func (w *WeatherTool) Execute(ctx context.Context, input map[string]interface{}) (interface{}, error) { city, _ := input["city"].(string) date, _ := input["date"].(string) // 这里调用一个模拟的或真实的天气API apiUrl := fmt.Sprintf("https://api.weather.example?city=%s&date=%s", city, date) req, err := http.NewRequestWithContext(ctx, "GET", apiUrl, nil) if err != nil { return nil, fmt.Errorf("创建请求失败: %w", err) } resp, err := http.DefaultClient.Do(req) if err != nil { return nil, fmt.Errorf("请求天气API失败: %w", err) } defer resp.Body.Close() var result map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("解析API响应失败: %w", err) } // 返回结构化的天气信息 return map[string]interface{}{ "city": city, "date": date, "condition": result["condition"], "temperature": result["temp"], "humidity": result["humidity"], "suggestion": "建议穿着...", }, nil } // 在main函数中将其注册给“天气查询员”Agent weatherTool := &tools.WeatherTool{} // ... 在builder.WithTool时传入

关键点ArgsSchema()返回的是一个JSON Schema,这非常重要。Deer-Go的Agent在决定使用工具时,会利用LLM根据这个Schema来生成格式正确的调用参数。这保证了工具调用的类型安全和结构化。

3.3 工作流(Workflow)与协调器(Coordinator)配置

现在我们有了一群各司其职的Agent和它们的工具,下一步是告诉它们如何协作。我们需要定义一个Workflow

在Deer-Go中,Workflow可以通过YAML定义,也可以在代码中通过DSL(领域特定语言)构建。这里我们用YAML方式更清晰:

# workflows/travel_plan.yaml name: "智能旅行规划" description: "根据用户输入,协调多个Agent完成旅行规划" tasks: - id: "analyze_need" type: "agent_task" agent: "需求分析员" input: "{{.user_input}}" output_key: "extracted_info" - id: "query_weather" type: "agent_task" agent: "天气查询员" input: "目的地: {{.extracted_info.destination}}, 日期: {{.extracted_info.travel_date}}" depends_on: ["analyze_need"] output_key: "weather_info" - id: "research_attractions" type: "agent_task" agent: "景点研究员" input: "在 {{.extracted_info.destination}} 寻找关于 {{.extracted_info.interests}} 的景点和活动" depends_on: ["analyze_need"] output_key: "attractions_info" - id: "plan_itinerary" type: "agent_task" agent: "行程规划师" input: | 请基于以下信息规划行程: 需求:{{.extracted_info}} 天气:{{.weather_info}} 景点:{{.attractions_info}} depends_on: ["query_weather", "research_attractions"] output_key: "itinerary" - id: "estimate_budget" type: "agent_task" agent: "预算分析师" input: "根据行程 {{.itinerary}} 和用户预算偏好 {{.extracted_info.budget_preference}} 进行估算" depends_on: ["plan_itinerary"] output_key: "budget_report" - id: "compile_report" type: "function_task" function: "compileFinalReport" # 这是一个我们自定义的Go函数 input: "{{.itinerary}} ||| {{.budget_report}}" depends_on: ["estimate_budget"] output_key: "final_output"

这个YAML定义了一个DAG。depends_on字段清晰地定义了任务依赖。input字段中的{{.xxx}}是模板语法,用于引用上游任务的输出,实现了数据在任务间的自动传递。

接下来,我们需要在Go代码中加载这个工作流,并创建一个Coordinator来管理这些Agent和执行这个流程。

package main import ( "context" "fmt" deer "github.com/community-driven/deer-go" ) func main() { ctx := context.Background() // 1. 加载并初始化所有Agent agents, err := loadAgents("config/agents.yaml") if err != nil { panic(err) } // 创建一个Agent注册表,方便Coordinator查找 agentRegistry := make(map[string]*deer.Agent) for _, agent := range agents { agentRegistry[agent.Name()] = agent } // 2. 加载工作流定义 workflow, err := deer.LoadWorkflowFromFile("workflows/travel_plan.yaml") if err != nil { panic(err) } // 3. 创建协调器(Coordinator),并传入Agent注册表 coordinator := deer.NewCoordinator(agentRegistry) // 4. 创建路由器(Router),这里使用一个简单的基于能力描述匹配的路由器 router := deer.NewCapabilityRouter(agents) // 5. 将协调器、路由器与工作流引擎组装起来 engine := deer.NewWorkflowEngine(workflow, coordinator, router) // 6. 执行工作流 userInput := "我想下个月去杭州玩3天,喜欢自然风光和历史古迹,预算中等。" initialData := map[string]interface{}{ "user_input": userInput, } result, err := engine.Execute(ctx, initialData) if err != nil { fmt.Printf("工作流执行失败: %v\n", err) return } finalReport, _ := result["final_output"].(string) fmt.Printf("旅行规划报告生成完毕:\n%s\n", finalReport) }

3.4 自定义函数任务与最终报告编译

在上面的工作流中,最后一个任务compile_report是一个function_task类型。它不调用Agent,而是直接执行一个我们定义的Go函数。这展示了Deer-Go的灵活性:可以将传统的函数无缝嵌入到Agent工作流中。

我们需要实现这个函数并注册到工作流引擎中:

// 定义编译最终报告的函数 func compileFinalReport(ctx context.Context, input map[string]interface{}) (interface{}, error) { // input 包含了模板渲染后的字符串,这里我们按约定的分隔符拆分 inputStr, ok := input["_raw_input"].(string) // Deer-Go通常会将渲染后的模板放在一个特定字段 if !ok { return nil, fmt.Errorf("无效的输入格式") } parts := strings.Split(inputStr, "|||") if len(parts) != 2 { return nil, fmt.Errorf("输入格式错误,期望用'|||'分隔行程和预算") } itinerary := strings.TrimSpace(parts[0]) budgetReport := strings.TrimSpace(parts[1]) // 这里可以做一些格式美化、汇总等操作 finalReport := fmt.Sprintf("# 您的杭州三日游规划\n\n## 详细行程\n%s\n\n## 预算估算\n%s\n\n祝您旅途愉快!", itinerary, budgetReport) return finalReport, nil } // 在main函数中,创建引擎时需要注册这个函数 engine.RegisterFunction("compileFinalReport", compileFinalReport)

至此,一个完整的多Agent旅行规划系统就搭建起来了。当你运行程序,输入需求后,Deer-Go的引擎会驱动整个流程:需求分析员先提取关键信息,然后天气查询员和景点研究员并行工作,他们的结果汇总给行程规划师,最后预算分析师和报告编译函数收尾,生成一份完整的规划。

4. 深度踩坑与性能调优:从“能用”到“好用”

将系统跑起来只是第一步。在实际开发和压力测试中,我遇到了不少典型问题。下面分享几个关键的“坑”及其解决方案,这可能是官方文档里不会细说的部分。

4.1 坑一:Agent“胡言乱语”与提示词工程

现象:在初期测试中,“行程规划师”Agent有时会忽略天气和景点信息,凭空捏造一些不存在的活动,或者给出的时间安排完全不合理。

根因分析:这通常不是Deer-Go框架的问题,而是LLM提示词(Prompt)和上下文管理的问题。Deer-Go的Agent在调用LLM时,会组合角色描述(Role)、当前对话历史(Memory)、工具描述以及用户问题。如果组合后的提示词不够清晰,或者上下文窗口满了导致关键信息被截断,LLM就会“自由发挥”。

解决方案

  1. 精细化角色描述:不要只写“你是一个行程规划师”。要明确指令,例如:“你是一个严谨的行程规划师。你必须严格依据我提供的‘天气信息’和‘景点信息’来规划,不得自行添加未被提供的信息。如果信息不足,请明确指出。”
  2. 结构化输入:不要简单地将上游Agent的输出作为字符串模板拼接。最好将其转换为更结构化的形式(如JSON)再放入提示词,并明确指示LLM关注这些字段。
    // 改进后的input模板 input: | 请基于以下**结构化信息**规划行程: <需求> {{.extracted_info | toJson}} </需求> <天气> {{.weather_info | toJson}} </天气> <景点> {{.attractions_info | toJson}} </景点> 请首先确认你是否已完整收到以上三部分信息。
  3. 实现“短期记忆”窗口:Deer-Go的Memory模块可能默认保存所有历史。对于长流程,需要实现一个滑动窗口或总结式记忆,只保留最近几条或总结后的关键信息,防止上下文溢出。

4.2 坑二:工具调用失败与错误处理

现象WeatherTool调用外部API超时或返回错误,导致整个工作流卡住或失败。

根因分析:Deer-Go框架中,一个Task的失败默认会导致整个工作流失败。对于非核心任务(如天气查询),我们可能希望它能失败后提供降级方案(如使用默认天气),而不是让整个流程崩溃。

解决方案

  1. 任务级重试与超时:在定义Workflow的Task时,可以配置重试策略和超时时间。Deer-Go的Task配置应该支持这些参数(如果原生不支持,需要自己扩展)。
    - id: "query_weather" type: "agent_task" agent: "天气查询员" # ... 其他配置 retry_policy: max_attempts: 3 backoff: exponential # 指数退避 timeout: "10s" # 单个任务超时时间
  2. 实现降级逻辑:在工具Execute方法内部进行健壮的错误处理,并返回一个降级结果。
    func (w *WeatherTool) Execute(ctx context.Context, input map[string]interface{}) (interface{}, error) { // ... 调用API if err != nil || resp.StatusCode != 200 { log.Printf("天气API调用失败,使用默认数据: %v", err) // 返回一个合理的默认数据,而不是错误 return map[string]interface{}{ "city": city, "date": date, "condition": "数据暂缺", "temperature": "N/A", "suggestion": "请出行前自行查询最新天气。", }, nil // 注意,这里返回nil error,表示任务“成功”,但结果是降级的 } // ... 正常处理 }
  3. 工作流条件分支:在Workflow定义中引入条件判断。例如,如果weather_info包含“数据暂缺”,则走另一条分支,让行程规划师基于无天气信息的情况做规划。这需要更高级的Workflow DSL支持。

4.3 坑三:并发瓶颈与资源管理

现象:当并行任务增多(如同时为多个用户规划行程)时,系统响应变慢,甚至出现OOM(内存溢出)。

根因分析:每个Agent的思考(调用LLM)和Tool的执行(调用API)都是IO密集型操作,默认的简单并发控制可能导致: - 过多的Goroutine同时调用LLM API,触发速率限制。 - 大量HTTP连接同时打开,耗尽系统资源。 - 内存中同时保存过多中间结果。

解决方案

  1. 引入限流器(Rate Limiter):为LLM客户端和关键外部API工具(如搜索工具)添加限流。可以使用golang.org/x/time/rate包。
    // 为OpenAI客户端包装一个限流器 type RateLimitedLLMClient struct { client deer.LLMClient limiter *rate.Limiter } func (c *RateLimitedLLMClient) ChatCompletion(ctx context.Context, req deer.ChatCompletionRequest) (*deer.ChatCompletionResponse, error) { if err := c.limiter.Wait(ctx); err != nil { return nil, err } return c.client.ChatCompletion(ctx, req) }
  2. 使用工作池(Worker Pool):对于Tool的执行,特别是那些调用慢速外部服务的,可以使用工作池来限制并发数。避免为每个任务都启动一个无限制的Goroutine。
  3. 结果缓存:对于相同参数的查询(例如,同一城市同一日期的天气),可以在内存或Redis中缓存结果,在缓存有效期内直接返回,避免重复调用。这需要为Tool接口增加缓存层。
  4. 监控与指标:务必集成Deer-Go的Metrics模块,监控每个Agent的平均响应时间、任务队列长度、工具调用成功率等。通过这些指标能快速定位瓶颈所在。

4.4 坑四:调试与可观测性挑战

现象:一个复杂的多步骤工作流执行失败了,日志只显示“工作流执行失败”,很难定位是哪个Agent、哪一步出了问题。

根因分析:默认的日志可能不够结构化,缺乏统一的请求ID来串联整个流程,各个模块的日志分散。

解决方案

  1. 贯穿始终的TraceID:在engine.Execute入口处生成一个唯一的TraceID,并将其注入到context.Context中。之后所有Agent、Tool的调用日志都带上这个TraceID。这样可以通过一个ID在日志系统中检索出整个请求的全链路日志。
  2. 结构化日志:使用如logruszap等库,以JSON格式输出日志,包含level,timestamp,trace_id,agent_name,task_id,message,error等固定字段。方便后续用ELK等工具进行分析。
  3. 利用Deer-Go的Tracer:如果Deer-Go集成了OpenTelemetry之类的分布式追踪,一定要启用它。将追踪数据导出到Jaeger或Zipkin,可以可视化整个工作流的调用链,精确看到时间消耗在哪个环节。
  4. 增加检查点日志:在每个Task执行开始前、成功后、失败后,都记录清晰的日志。在Agent内部的关键决策点(如决定调用哪个工具时)也记录日志。

5. 进阶思考:Deer-Go的设计哲学与扩展方向

通过对Deer-Go的深度拆解和使用,我体会到了其背后一些值得玩味的设计哲学,也看到了一些可以进一步扩展的方向。

设计哲学

  1. 约定优于配置:通过标准的Tool接口、Agent构建器和YAML工作流定义,提供了快速搭建的原型。但深入定制时,又留有足够的扩展点(如自定义Router、自定义Memory实现)。
  2. 消息传递即协作:严格遵循通过消息(事件)来驱动Agent间协作,避免了共享状态的复杂性,使得系统各组件松耦合,易于测试和扩展。
  3. 可观测性是一等公民:从设计之初就将日志、指标、追踪考虑在内,这对于运维一个状态复杂的分布式AI系统至关重要。

可能的扩展方向

  1. 动态Agent注册与发现:目前的Agent注册是静态的。可以引入一个“Agent注册中心”,Agent启动后主动上报自己的能力,Router动态地从中心发现可用的Agent,实现更灵活的微服务化部署。
  2. 强化学习路由:当前的Router(如基于能力的路由)是静态规则。可以引入一个强化学习模型,根据历史任务的成功率、耗时等反馈,动态调整路由策略,让系统越用越“聪明”。
  3. Human-in-the-loop(人机回环):在工作流中插入“人工审核”或“人工提供信息”的节点。当Agent置信度不高或遇到边界情况时,自动暂停流程并通知人类介入,待人类输入后再继续。这对于高风险或高价值的场景非常必要。
  4. 子工作流嵌套:将复杂的工作流模块化,允许一个Task指向另一个子Workflow。这样可以构建出层次清晰、可复用的大型Agent应用。

Deer-Go作为对字节Deer-Flow思想的一次出色Go语言实践,为我们提供了一个绝佳的学习范本和开发起点。它可能不像一些全功能商业框架那样开箱即用,但其清晰的设计和可扩展的架构,让我们能够深入理解多Agent系统的每一块积木,并根据自己的业务需求进行定制和优化。这个过程本身,就是一次宝贵的技术深度之旅。