【AI Agent 与 Super Agent 构建实战】第 13 篇:Plan-and-Execute 模式 — 先规划后执行

本系列定位:用 Go 语言从零构建各类 AI Agent,覆盖 ReAct、Plan-and-Execute、Multi-Agent、Super Agent 等核心设计模式的完整工程实现。


本篇你将学到

  • Plan-and-Execute 模式的核心架构与"先规划后执行"的设计哲学
  • Planner(规划器)与 Executor(执行器)的职责分离实现
  • Go 语言实现 Plan 数据结构——支持步骤列表、依赖关系、状态追踪
  • 完整的 Plan-and-Execute 执行引擎,并与 ReAct 做适用场景对比

学完本篇,你将进入模块 3 的核心:让 Agent 从"走一步看一步"升级为"先想清楚再动手"。


一、为什么需要 Plan-and-Execute

1.1 ReAct 的局限性

在模块 2 中,我们实现了完整的 ReAct Agent。ReAct 的核心循环是 Thought → Action → Observation,每一步都依赖 LLM 的实时推理来决定下一步做什么。这种"边想边做"的模式简单直接,但在复杂任务面前有明显短板。

ReAct 模式

Thought 1

Action 1

Observation 1

Thought 2

Action 2

Observation 2

Thought 3

Action 3

ReAct 有三个结构性问题:

问题表现影响
短视决策每步只看上一步结果,无全局视图任务执行到中途才发现方向错了
Token 浪费每一步都把完整上下文塞给 LLM10 步任务的 Token 消耗线性增长
不可复用计划隐含在推理过程中,无法提取无法缓存、无法并行、无法预审

1.2 Plan-and-Execute 的核心思想

Plan-and-Execute 模式来自 LangChain 团队提出的经典架构,核心思路是将规划和执行解耦为两个独立阶段

用户任务

Planner 规划器

完整任务计划
步骤 1→2→3→4→5

Executor 执行器

执行步骤 1

执行步骤 2

执行步骤 3

执行步骤 4

执行步骤 5

任务完成

关键区别在于:Planner 一次性生成完整的步骤列表,Executor 则像流水线一样逐步执行。规划只调用一次 LLM(或少量几次),执行阶段则可以高效推进。

1.3 两种模式适用场景对比

维度ReActPlan-and-Execute
决策频率每步都调 LLM规划时调,执行时少调
全局视野弱,逐步推进强,先看全局
Token 成本高(N 步 N 次完整推理)低(1 次规划 + N 次轻量执行)
适合任务探索性、不可预测的任务步骤清晰、可预见的任务
典型例子“帮我调试这个报错”“调研 3 个竞品并写报告”

一句话总结:ReAct 适合探险,Plan-and-Execute 适合工程。 实际生产中两者经常组合使用——大计划用 Plan-and-Execute,每个步骤内部用 ReAct。


二、Plan 数据结构设计

2.1 核心模型

一个计划由多个步骤组成,每个步骤有依赖关系、状态、执行结果。我们先定义清晰的数据模型:

contains

has

1

*

Plan

+string ID

+string Goal

[]PlanStep Steps

+time.Time CreatedAt

+string Status

PlanStep

+int Index

+string Task

[]string DependsOn

[]string ToolNames

+StepStatus Status

+string Result

+string Error

+int Retries

«enumeration»

StepStatus

Pending

Running

Done

Failed

Skipped

2.2 Go 实现

// internal/planner/plan.go
package planner

import (
	"time"
)

// StepStatus 步骤状态枚举
type StepStatus string

const (
	StatusPending StepStatus = "pending" // 等待执行
	StatusRunning StepStatus = "running" // 正在执行
	StatusDone    StepStatus = "done"    // 执行成功
	StatusFailed  StepStatus = "failed"  // 执行失败
	StatusSkipped StepStatus = "skipped" // 被跳过(依赖失败等)
)

// PlanStep 单个计划步骤
type PlanStep struct {
	Index     int        `json:"index"`      // 步骤序号,从 0 开始
	Task      string     `json:"task"`       // 步骤描述(自然语言)
	DependsOn []int      `json:"depends_on"` // 依赖的步骤序号列表
	ToolNames []string   `json:"tool_names"` // 预计使用的工具
	Status    StepStatus `json:"status"`     // 当前状态
	Result    string     `json:"result"`     // 执行结果
	Error     string     `json:"error"`      // 错误信息
	Retries   int        `json:"retries"`    // 已重试次数
	StartedAt *time.Time `json:"started_at"` // 开始时间
	DoneAt    *time.Time `json:"done_at"`    // 完成时间
}

// Plan 完整任务计划
type Plan struct {
	ID        string     `json:"id"`         // 计划唯一标识
	Goal      string     `json:"goal"`       // 原始目标
	Steps     []PlanStep `json:"steps"`      // 步骤列表
	Status    string     `json:"status"`     // plan 整体状态
	CreatedAt time.Time  `json:"created_at"` // 创建时间
}

// NewPlan 创建新计划
func NewPlan(goal string, steps []PlanStep) *Plan {
	// 初始化每个步骤的状态
	for i := range steps {
		steps[i].Index = i
		steps[i].Status = StatusPending
	}
	return &Plan{
		ID:        generateID(),
		Goal:      goal,
		Steps:     steps,
		Status:    "pending",
		CreatedAt: time.Now(),
	}
}

// generateID 生成简易唯一 ID
func generateID() string {
	return time.Now().Format("20060102-150405.000")
}

// IsDone 判断计划是否全部完成
func (p *Plan) IsDone() bool {
	for i := range p.Steps {
		s := &p.Steps[i]
		if s.Status != StatusDone && s.Status != StatusSkipped {
			return false
		}
	}
	return true
}

// IsFailed 判断是否有致命失败
func (p *Plan) IsFailed() bool {
	for i := range p.Steps {
		if p.Steps[i].Status == StatusFailed {
			return true
		}
	}
	return false
}

// NextReady 找出下一个可执行步骤(依赖已全部完成)
func (p *Plan) NextReady() []int {
	var ready []int
	for i := range p.Steps {
		s := &p.Steps[i]
		if s.Status != StatusPending {
			continue
		}
		if p.depsSatisfied(s) {
			ready = append(ready, i)
		}
	}
	return ready
}

// depsSatisfied 检查步骤依赖是否全部满足
func (p *Plan) depsSatisfied(s *PlanStep) bool {
	for _, depIdx := range s.DependsOn {
		if depIdx < 0 || depIdx >= len(p.Steps) {
			return false // 依赖越界
		}
		status := p.Steps[depIdx].Status
		if status != StatusDone && status != StatusSkipped {
			return false
		}
	}
	return true
}

// MarkRunning 标记步骤开始执行
func (p *Plan) MarkRunning(idx int) {
	now := time.Now()
	p.Steps[idx].Status = StatusRunning
	p.Steps[idx].StartedAt = &now
}

// MarkDone 标记步骤成功完成
func (p *Plan) MarkDone(idx int, result string) {
	now := time.Now()
	p.Steps[idx].Status = StatusDone
	p.Steps[idx].Result = result
	p.Steps[idx].DoneAt = &now
}

// MarkFailed 标记步骤失败
func (p *Plan) MarkFailed(idx int, errMsg string) {
	p.Steps[idx].Status = StatusFailed
	p.Steps[idx].Error = errMsg
}

// MarkSkipped 标记步骤被跳过(通常因为依赖失败)
func (p *Plan) MarkSkipped(idx int, reason string) {
	p.Steps[idx].Status = StatusSkipped
	p.Steps[idx].Error = reason
}

// Progress 返回进度信息(已完成/总数)
func (p *Plan) Progress() (done, total int) {
	total = len(p.Steps)
	for i := range p.Steps {
		s := p.Steps[i].Status
		if s == StatusDone || s == StatusSkipped {
			done++
		}
	}
	return done, total
}

这套数据结构有几个设计要点:

  • 指针接收者MarkDoneMarkFailed 等方法修改状态,用指针接收者避免拷贝
  • 内部取地址:遍历 range p.Steps 时对 &p.Steps[i] 取地址而非循环变量,避免经典的 Go 闭包陷阱
  • 依赖检查depsSatisfied 是并行执行的基础,下一章会基于它实现 DAG 调度
  • 进度追踪Progress 方法支持运行时可视化

三、Planner — 让 LLM 生成完整计划

3.1 Planner 的职责

Planner 的核心任务是接收用户的自然语言目标,输出结构化的步骤列表。我们用结构化输出(Structured Output)技术让 LLM 直接返回 JSON。

LLM Planner PlanEngine 用户 LLM Planner PlanEngine 用户 "帮我调研 3 个竞品并写报告" CreatePlan(goal) 构建规划 Prompt(含可用工具) 请求生成结构化计划 返回 JSON 计划 解析 + 校验 + 补全依赖 返回 *Plan 逐步执行计划...

3.2 Planner 实现

// internal/planner/planner.go
package planner

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/yourname/agentforge/internal/provider"
)

// Planner 规划器:调用 LLM 生成任务计划
type Planner struct {
	llm       provider.LLMProvider
	model     string
	toolsInfo []ToolInfo // 可用工具的描述信息
}

// ToolInfo 工具元信息(用于提示 LLM)
type ToolInfo struct {
	Name        string
	Description string
}

// NewPlanner 创建规划器
func NewPlanner(llm provider.LLMProvider, model string, tools []ToolInfo) *Planner {
	return &Planner{
		llm:       llm,
		model:     model,
		toolsInfo: tools,
	}
}

// planRequest LLM 需要返回的结构
type planRequest struct {
	Thought string     `json:"thought"` // 规划思路
	Steps   []rawStep  `json:"steps"`   // 步骤列表
}

// rawStep LLM 返回的原始步骤(可能缺字段,需后处理)
type rawStep struct {
	Task      string   `json:"task"`
	DependsOn []int    `json:"depends_on"`
	ToolNames []string `json:"tool_names"`
}

// CreatePlan 生成完整任务计划
func (pl *Planner) CreatePlan(ctx context.Context, goal string) (*Plan, error) {
	prompt := pl.buildPrompt(goal)

	// 使用结构化输出模式请求 LLM
	resp, err := pl.llm.Chat(ctx, provider.ChatRequest{
		Model: pl.model,
		Messages: []provider.Message{
			{Role: "system", Content: plannerSystemPrompt(pl.toolsInfo)},
			{Role: "user", Content: prompt},
		},
		ResponseFormat: provider.ResponseFormatJSON,
	})
	if err != nil {
		return nil, fmt.Errorf("规划请求失败: %w", err)
	}

	// 解析 LLM 返回的 JSON
	var pr planRequest
	if err := json.Unmarshal([]byte(resp.Content), &pr); err != nil {
		return nil, fmt.Errorf("计划解析失败: %w, 原始内容: %s", err, resp.Content)
	}

	// 转换为 Plan 结构并校验
	steps, err := normalizeSteps(pr.Steps)
	if err != nil {
		return nil, err
	}

	return NewPlan(goal, steps), nil
}

// buildPrompt 构建规划提示
func (pl *Planner) buildPrompt(goal string) string {
	return fmt.Sprintf(`请为以下目标制定详细的执行计划:

目标:%s

要求:
1. 将目标分解为 3-8 个具体、可执行的步骤
2. 每个步骤用一句话清晰描述要做什么
3. 用 depends_on 字段标明步骤间的依赖(值为前置步骤的序号,从 0 开始)
4. 没有依赖的步骤 depends_on 设为空数组 []
5. tool_names 填写该步骤预计用到的工具名

输出 JSON 格式:
{
  "thought": "你的规划思路(1-2 句话)",
  "steps": [
    {
      "task": "步骤描述",
      "depends_on": [],
      "tool_names": ["tool_a"]
    }
  ]
}`, goal)
}

// plannerSystemPrompt 构建系统提示(含工具清单)
func plannerSystemPrompt(tools []ToolInfo) string {
	var sb strings.Builder
	sb.WriteString("你是一个任务规划专家。你的职责是将用户目标分解为清晰的执行步骤。\n\n")
	sb.WriteString("可用工具:\n")
	for _, t := range tools {
		sb.WriteString(fmt.Sprintf("- %s: %s\n", t.Name, t.Description))
	}
	sb.WriteString("\n请确保计划合理、步骤之间依赖关系正确。")
	return sb.String()
}

// normalizeSteps 校验并规范化 LLM 返回的步骤
func normalizeSteps(raw []rawStep) ([]PlanStep, error) {
	if len(raw) == 0 {
		return nil, fmt.Errorf("计划为空,至少需要一个步骤")
	}
	if len(raw) > 20 {
		return nil, fmt.Errorf("步骤过多(%d),建议不超过 20 个", len(raw))
	}

	steps := make([]PlanStep, len(raw))
	for i, r := range raw {
		if strings.TrimSpace(r.Task) == "" {
			return nil, fmt.Errorf("第 %d 步缺少任务描述", i)
		}
		// 校验依赖索引合法性
		for _, dep := range r.DependsOn {
			if dep < 0 || dep >= len(raw) {
				return nil, fmt.Errorf("第 %d 步依赖了非法索引 %d", i, dep)
			}
			if dep >= i {
				return nil, fmt.Errorf("第 %d 步依赖了自身或后续步骤 %d", i, dep)
			}
		}
		steps[i] = PlanStep{
			Index:     i,
			Task:      r.Task,
			DependsOn: r.DependsOn,
			ToolNames: r.ToolNames,
			Status:    StatusPending,
		}
	}
	return steps, nil
}

normalizeSteps 做了三层校验:步骤数量、描述非空、依赖索引合法且不形成前向依赖(dep >= i 即指向自己或后续步骤,必然构成环)。这是防止 LLM 输出混乱数据的第一道防线。


四、Executor — 逐步执行计划

4.1 执行器职责

Executor 接收一个 Plan,按依赖顺序逐步执行每个步骤。每个步骤本质上是一次 ReAct 子循环——但范围被限定在该步骤的 Task 描述内。

成功

失败

开始执行 Plan

还有可执行步骤?

Plan 完成

取出 NextReady 步骤

执行单个步骤
调用 LLM + 工具

执行结果

标记 Done

标记 Failed

失败处理:跳过依赖项

4.2 Executor 实现

// internal/planner/executor.go
package planner

import (
	"context"
	"fmt"
	"strings"

	"github.com/yourname/agentforge/internal/provider"
)

// StepExecutor 单步执行函数类型
// 输入:步骤描述 + 已完成步骤的结果摘要
// 输出:步骤执行结果
type StepExecutor func(ctx context.Context, step *PlanStep, context_ string) (string, error)

// Executor 计划执行器
type Executor struct {
	llm         provider.LLMProvider
	model       string
	maxStepIter int // 单步最大迭代
}

// NewExecutor 创建执行器
func NewExecutor(llm provider.LLMProvider, model string) *Executor {
	return &Executor{
		llm:         llm,
		model:       model,
		maxStepIter: 5, // 单步最多 5 次 LLM 调用
	}
}

// Execute 执行整个计划(同步顺序执行)
func (ex *Executor) Execute(ctx context.Context, plan *Plan) error {
	plan.Status = "running"

	for !plan.IsDone() && !plan.IsFailed() {
		ready := plan.NextReady()
		if len(ready) == 0 {
			// 没有可执行步骤且未全部完成 → 死锁(可能有环)
			return fmt.Errorf("计划陷入死锁,无法继续")
		}

		for _, idx := range ready {
			step := &plan.Steps[idx]
			plan.MarkRunning(idx)

			// 构建上下文:已完成步骤的结果摘要
			stepCtx := ex.buildStepContext(plan, idx)

			// 执行该步骤
			result, err := ex.executeStep(ctx, step, stepCtx)
			if err != nil {
				plan.MarkFailed(idx, err.Error())
				// 级联跳过依赖此步骤的后续步骤
				ex.cascadeSkip(plan, idx)
			} else {
				plan.MarkDone(idx, result)
			}
		}
	}

	if plan.IsFailed() {
		plan.Status = "failed"
		return fmt.Errorf("计划执行失败")
	}
	plan.Status = "done"
	return nil
}

// executeStep 执行单个步骤(内部 ReAct 循环)
func (ex *Executor) executeStep(ctx context.Context, step *PlanStep, stepCtx string) (string, error) {
	prompt := fmt.Sprintf(`你正在执行一个多步骤计划的第 %d 步。

步骤任务:%s

前置步骤结果:
%s

请完成这一步并返回结果。`, step.Index+1, step.Task, stepCtx)

	// 简化版:直接调用 LLM(实际项目中可接入工具调用)
	resp, err := ex.llm.Chat(ctx, provider.ChatRequest{
		Model: ex.model,
		Messages: []provider.Message{
			{Role: "system", Content: "你是一个任务执行器,专注完成分配给你的单一步骤。"},
			{Role: "user", Content: prompt},
		},
	})
	if err != nil {
		return "", err
	}
	return resp.Content, nil
}

// buildStepContext 构建步骤上下文(汇总前置步骤结果)
func (ex *Executor) buildStepContext(plan *Plan, idx int) string {
	var sb strings.Builder
	for _, depIdx := range plan.Steps[idx].DependsOn {
		dep := &plan.Steps[depIdx]
		sb.WriteString(fmt.Sprintf("步骤 %d(%s)的结果:\n%s\n\n",
			depIdx+1, dep.Task, dep.Result))
	}
	if sb.Len() == 0 {
		return "(无前置步骤)"
	}
	return sb.String()
}

// cascadeSkip 级联跳过依赖失败步骤的后续步骤
func (ex *Executor) cascadeSkip(plan *Plan, failedIdx int) {
	for i := range plan.Steps {
		s := &plan.Steps[i]
		if s.Status != StatusPending {
			continue
		}
		for _, dep := range s.DependsOn {
			if dep == failedIdx {
				plan.MarkSkipped(i, fmt.Sprintf("依赖步骤 %d 失败", failedIdx))
				// 递归跳过依赖此步骤的步骤
				ex.cascadeSkip(plan, i)
				break
			}
		}
	}
}

执行器有几个关键设计:

  • 同步顺序执行:当前版本按顺序执行,下一章会升级为基于 DAG 的并行执行
  • 级联跳过:某步失败后,所有直接或间接依赖它的步骤会被标记为 Skipped,避免无效执行
  • 死锁检测:如果 NextReady 返回空但计划未完成,说明依赖关系有环(正常情况不应出现,但 LLM 生成的计划可能出错)

五、Plan-and-Execute 引擎整合

5.1 引擎封装

把 Planner 和 Executor 组合为统一入口:

// internal/planner/engine.go
package planner

import (
	"context"
	"fmt"

	"github.com/yourname/agentforge/internal/provider"
)

// PlanEngine Plan-and-Execute 引擎
type PlanEngine struct {
	planner   *Planner
	executor  *Executor
	callbacks EngineCallbacks // 执行回调(用于日志/UI)
}

// EngineCallbacks 执行过程中的回调钩子
type EngineCallbacks struct {
	OnPlanCreated  func(plan *Plan)
	OnStepStart    func(step *PlanStep)
	OnStepDone     func(step *PlanStep)
	OnStepFailed   func(step *PlanStep)
	OnPlanComplete func(plan *Plan)
}

// NewPlanEngine 创建引擎
func NewPlanEngine(llm provider.LLMProvider, model string, tools []ToolInfo) *PlanEngine {
	return &PlanEngine{
		planner:  NewPlanner(llm, model, tools),
		executor: NewExecutor(llm, model),
	}
}

// WithCallbacks 设置回调
func (e *PlanEngine) WithCallbacks(cb EngineCallbacks) *PlanEngine {
	e.callbacks = cb
	return e
}

// Run 一键运行:规划 + 执行
func (e *PlanEngine) Run(ctx context.Context, goal string) (*Plan, error) {
	// 阶段一:规划
	plan, err := e.planner.CreatePlan(ctx, goal)
	if err != nil {
		return nil, fmt.Errorf("规划阶段失败: %w", err)
	}
	if e.callbacks.OnPlanCreated != nil {
		e.callbacks.OnPlanCreated(plan)
	}

	// 阶段二:执行
	plan.Status = "running"
	for !plan.IsDone() && !plan.IsFailed() {
		ready := plan.NextReady()
		if len(ready) == 0 {
			break
		}
		for _, idx := range ready {
			step := &plan.Steps[idx]
			plan.MarkRunning(idx)
			if e.callbacks.OnStepStart != nil {
				e.callbacks.OnStepStart(step)
			}

			stepCtx := e.executor.buildStepContext(plan, idx)
			result, err := e.executor.executeStep(ctx, step, stepCtx)
			if err != nil {
				plan.MarkFailed(idx, err.Error())
				if e.callbacks.OnStepFailed != nil {
					e.callbacks.OnStepFailed(step)
				}
			} else {
				plan.MarkDone(idx, result)
				if e.callbacks.OnStepDone != nil {
					e.callbacks.OnStepDone(step)
				}
			}
		}
	}

	if e.callbacks.OnPlanComplete != nil {
		e.callbacks.OnPlanComplete(plan)
	}
	return plan, nil
}

// PrintPlan 打印计划(调试用)
func PrintPlan(plan *Plan) {
	fmt.Printf("\n📋 计划: %s\n", plan.Goal)
	fmt.Println("──────────────────────────────")
	for i := range plan.Steps {
		s := &plan.Steps[i]
		depStr := "无"
		if len(s.DependsOn) > 0 {
			deps := make([]string, len(s.DependsOn))
			for j, d := range s.DependsOn {
				deps[j] = fmt.Sprintf("%d", d+1)
			}
			depStr = fmt.Sprintf("步骤 %s", joinInts(s.DependsOn))
		}
		statusIcon := map[StepStatus]string{
			StatusPending: "⏳",
			StatusRunning: "🔄",
			StatusDone:    "✅",
			StatusFailed:  "❌",
			StatusSkipped: "⏭️",
		}[s.Status]
		fmt.Printf("  %s [%d] %s (依赖: %s)\n", statusIcon, i+1, s.Task, depStr)
		if s.Result != "" {
			fmt.Printf("       → %s\n", truncate(s.Result, 80))
		}
	}
	fmt.Println("──────────────────────────────")
	done, total := plan.Progress()
	fmt.Printf("进度: %d/%d\n\n", done, total)
}

// joinInts 辅助函数
func joinInts(nums []int) string {
	result := ""
	for i, n := range nums {
		if i > 0 {
			result += ", "
		}
		result += fmt.Sprintf("%d", n+1)
	}
	return result
}

// truncate 截断字符串
func truncate(s string, max int) string {
	if len(s) <= max {
		return s
	}
	return s[:max] + "..."
}

5.2 运行示例

// cmd/plan_demo/main.go
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/yourname/agentforge/internal/provider"
	"github.com/yourname/agentforge/internal/planner"
)

func main() {
	// 初始化 LLM Provider(复用第 02 篇的抽象层)
	llm, err := provider.New("openai", loadConfig())
	if err != nil {
		log.Fatal(err)
	}

	tools := []planner.ToolInfo{
		{Name: "web_search", Description: "搜索互联网获取最新信息"},
		{Name: "file_write", Description: "将内容写入文件"},
		{Name: "summarize", Description: "对长文本生成摘要"},
	}

	engine := planner.NewPlanEngine(llm, "gpt-4o", tools).
		WithCallbacks(planner.EngineCallbacks{
			OnPlanCreated: func(p *planner.Plan) {
				fmt.Println("✨ 计划已生成!")
				planner.PrintPlan(p)
			},
			OnStepStart: func(s *planner.PlanStep) {
				fmt.Printf("\n▶ 开始执行步骤 %d: %s\n", s.Index+1, s.Task)
			},
			OnStepDone: func(s *planner.PlanStep) {
				fmt.Printf("✔ 步骤 %d 完成\n", s.Index+1)
			},
			OnPlanComplete: func(p *planner.Plan) {
				fmt.Println("\n🎉 计划执行完毕!")
				planner.PrintPlan(p)
			},
		})

	// 运行一个复杂任务
	goal := "调研 LangChain、LlamaIndex、AutoGen 三个 AI Agent 框架的核心特性,对比它们的差异,生成一份中文分析报告"
	plan, err := engine.Run(context.Background(), goal)
	if err != nil {
		log.Printf("执行出错: %v", err)
	}

	fmt.Printf("\n最终状态: %s\n", plan.Status)
}

运行后预期输出大致如下:

$ go run cmd/plan_demo/main.go

✨ 计划已生成!

📋 计划: 调研 LangChain、LlamaIndex、AutoGen 三个 AI Agent 框架的核心特性...
──────────────────────────────
  ⏳ [1] 搜索 LangChain 的核心特性和架构 (依赖: 无)[2] 搜索 LlamaIndex 的核心特性和架构 (依赖: 无)[3] 搜索 AutoGen 的核心特性和架构 (依赖: 无)[4] 对比三个框架的差异和适用场景 (依赖: 步骤 1, 2, 3)[5] 生成中文分析报告 (依赖: 步骤 4)
──────────────────────────────
进度: 0/5

▶ 开始执行步骤 1: 搜索 LangChain 的核心特性和架构
✔ 步骤 1 完成
▶ 开始执行步骤 2: 搜索 LlamaIndex 的核心特性和架构
✔ 步骤 2 完成
...

🎉 计划执行完毕!
最终状态: done

注意:步骤 1/2/3 没有相互依赖,理论上可以并行执行。当前版本是顺序执行的,下一章我们会用 DAG 调度让它们并行跑起来。


本篇小结

知识点核心内容
Plan-and-Execute 架构规划与执行分离:Planner 一次生成完整计划,Executor 逐步执行
ReAct vs Plan-ExecuteReAct 适合探索性任务,Plan-Execute 适合步骤清晰的工程任务
Plan 数据结构步骤列表 + 依赖关系 + 五种状态(Pending/Running/Done/Failed/Skipped)
Planner 实现LLM + 结构化输出 + 依赖校验(防环、防越界)
Executor 实现按依赖顺序执行 + 级联跳过失败步骤的后续 + 死锁检测
引擎封装PlanEngine 整合 Planner/Executor + 回调钩子 + 可视化打印

下篇预告

第 14 篇:任务图与 DAG 执行 — 复杂任务的并行化
本篇的 Executor 是顺序执行的,步骤 1/2/3 明明可以并行却串行等待。下一篇我们将计划建模为 DAG 有向无环图,用拓扑排序 + goroutine 实现真正的并行执行引擎。


如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐