AI Agent的规划系统:让Agent更智能地完成任务

在前面的文章中,我们已经讨论了 AI Agent 的记忆系统和工具调用体系。今天,我想分享一下如何实现规划系统,让 AI Agent 能够更智能地完成复杂任务。

从一个真实需求说起

还记得前段时间,我在开发一个代码重构助手时遇到的场景:

用户:帮我重构这个项目的日志系统,统一使用结构化日志,添加链路追踪。

助手:好的,我来帮你重构。首先修改 logger.py...
(开始修改代码)

用户:等等,你这样直接改可能会影响到其他模块,能不能先分析一下影响范围?

助手:抱歉,你说得对。让我重新规划一下...

这个场景让我意识到:AI Agent 需要像人类工程师一样,在执行任务前先做好规划。不能一上来就写代码,而是要:

  1. 分析需求和影响范围
  2. 制定详细的执行计划
  3. 按步骤有序地执行
  4. 及时处理意外情况

规划系统的设计

经过几轮迭代,我设计了一个相对完善的规划系统:

from typing import List, Dict, Any
from enum import Enum
from datetime import datetime
from pydantic import BaseModel

class TaskStatus(Enum):
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"
    FAILED = "failed"
    BLOCKED = "blocked"

class TaskStep(BaseModel):
    id: str
    description: str
    status: TaskStatus
    dependencies: List[str]
    estimated_time: float
    actual_time: float = 0
    result: Any = None
    error: str = None

class Task(BaseModel):
    id: str
    name: str
    description: str
    steps: List[TaskStep]
    context: Dict[str, Any]
    created_at: datetime
    updated_at: datetime
    status: TaskStatus

class PlanningSystem:
    def __init__(
        self,
        llm,
        tool_registry,
        memory_system
    ):
        self.llm = llm
        self.tool_registry = tool_registry
        self.memory_system = memory_system
        self.tasks: Dict[str, Task] = {}

    async def plan_task(
        self,
        description: str,
        context: Dict[str, Any] = None
    ) -> Task:
        # 1. 分析任务,生成步骤
        steps = await self._generate_steps(
            description,
            context
        )

        # 2. 创建任务
        task = Task(
            id=self._generate_id(),
            name=self._extract_name(description),
            description=description,
            steps=steps,
            context=context or {},
            created_at=datetime.now(),
            updated_at=datetime.now(),
            status=TaskStatus.PENDING
        )

        # 3. 存储任务
        self.tasks[task.id] = task

        return task

    async def execute_task(
        self,
        task_id: str
    ) -> Task:
        task = self.tasks[task_id]
        task.status = TaskStatus.IN_PROGRESS

        try:
            # 1. 获取可执行的步骤
            steps = self._get_executable_steps(task)

            # 2. 并行执行步骤
            results = await asyncio.gather(*[
                self._execute_step(task, step)
                for step in steps
            ])

            # 3. 更新任务状态
            task.updated_at = datetime.now()
            if all(r.status == TaskStatus.COMPLETED for r in results):
                task.status = TaskStatus.COMPLETED
            elif any(r.status == TaskStatus.FAILED for r in results):
                task.status = TaskStatus.FAILED

            return task

        except Exception as e:
            task.status = TaskStatus.FAILED
            raise

    async def _generate_steps(
        self,
        description: str,
        context: Dict[str, Any]
    ) -> List[TaskStep]:
        # 使用 LLM 分析任务,生成步骤
        response = await self.llm.plan(
            task=description,
            context=context,
            available_tools=self.tool_registry.list_tools()
        )

        # 解析响应,创建步骤
        steps = []
        for step in response.steps:
            steps.append(TaskStep(
                id=self._generate_id(),
                description=step.description,
                status=TaskStatus.PENDING,
                dependencies=step.dependencies,
                estimated_time=step.estimated_time
            ))

        return steps

    def _get_executable_steps(
        self,
        task: Task
    ) -> List[TaskStep]:
        # 找出所有依赖已完成的步骤
        executable = []
        for step in task.steps:
            if step.status != TaskStatus.PENDING:
                continue

            if all(
                self._get_step_by_id(task, dep).status == TaskStatus.COMPLETED
                for dep in step.dependencies
            ):
                executable.append(step)

        return executable

    async def _execute_step(
        self,
        task: Task,
        step: TaskStep
    ) -> TaskStep:
        start_time = datetime.now()

        try:
            # 1. 准备执行上下文
            context = self._prepare_context(task, step)

            # 2. 使用 LLM 决定使用哪些工具
            tools = await self.llm.select_tools(
                step=step,
                context=context,
                available_tools=self.tool_registry.list_tools()
            )

            # 3. 执行工具调用
            results = []
            for tool in tools:
                result = await self.tool_registry.get_tool(
                    tool.name
                ).execute(**tool.parameters)
                results.append(result)

            # 4. 更新步骤状态
            step.status = TaskStatus.COMPLETED
            step.result = results

        except Exception as e:
            step.status = TaskStatus.FAILED
            step.error = str(e)

        finally:
            step.actual_time = (
                datetime.now() - start_time
            ).total_seconds()

        return step

    def _prepare_context(
        self,
        task: Task,
        step: TaskStep
    ) -> Dict[str, Any]:
        # 合并任务上下文和前置步骤的结果
        context = task.context.copy()

        # 添加依赖步骤的结果
        for dep_id in step.dependencies:
            dep_step = self._get_step_by_id(task, dep_id)
            if dep_step.result:
                context[f"step_{dep_id}_result"] = dep_step.result

        return context

使用示例:

# 初始化规划系统
planner = PlanningSystem(
    llm=ChatGPT(),
    tool_registry=tool_registry,
    memory_system=memory_system
)

# 创建任务计划
task = await planner.plan_task(
    description="""
    重构项目的日志系统:
    1. 统一使用结构化日志
    2. 添加链路追踪
    3. 确保向后兼容
    """,
    context={
        "project_root": "./src",
        "current_logger": "logging",
        "target_logger": "structlog"
    }
)

# 查看生成的步骤
for step in task.steps:
    print(f"步骤:{step.description}")
    print(f"依赖:{step.dependencies}")
    print(f"预计耗时:{step.estimated_time}分钟")
    print("---")

# 输出:
# 步骤:分析当前日志系统的使用情况
# 依赖:[]
# 预计耗时:15分钟
# ---
# 步骤:设计新的日志接口
# 依赖:['step_1']
# 预计耗时:30分钟
# ---
# 步骤:实现日志适配器
# 依赖:['step_2']
# 预计耗时:45分钟
# ---
# 步骤:修改日志配置
# 依赖:['step_3']
# 预计耗时:20分钟
# ---
# 步骤:更新依赖项
# 依赖:['step_4']
# 预计耗时:10分钟
# ---
# 步骤:添加单元测试
# 依赖:['step_3', 'step_4']
# 预计耗时:40分钟
# ---
# 步骤:进行集成测试
# 依赖:['step_5', 'step_6']
# 预计耗时:60分钟

# 执行任务
result = await planner.execute_task(task.id)

关键设计决策

在实现这个规划系统时,我做了几个重要的设计决策:

1. 任务分解

class TaskAnalyzer:
    def __init__(self, llm):
        self.llm = llm

    async def analyze(
        self,
        description: str,
        context: Dict[str, Any]
    ) -> List[Dict]:
        # 使用 LLM 分析任务
        response = await self.llm.analyze(
            prompt=self._generate_prompt(
                description,
                context
            )
        )

        # 验证步骤的合理性
        steps = self._validate_steps(response.steps)

        # 检查依赖关系
        self._check_dependencies(steps)

        return steps

    def _generate_prompt(
        self,
        description: str,
        context: Dict[str, Any]
    ) -> str:
        return f"""
        请分析以下任务,将其分解为具体的执行步骤:

        任务描述:
        {description}

        上下文信息:
        {json.dumps(context, indent=2)}

        要求:
        1. 每个步骤要具体且可执行
        2. 明确步骤之间的依赖关系
        3. 估计每个步骤的执行时间
        4. 考虑可能的风险和回退方案

        请以 JSON 格式返回结果。
        """

2. 并行执行

class StepExecutor:
    def __init__(
        self,
        tool_registry,
        max_concurrent: int = 5
    ):
        self.tool_registry = tool_registry
        self.semaphore = asyncio.Semaphore(max_concurrent)

    async def execute_steps(
        self,
        steps: List[TaskStep],
        context: Dict[str, Any]
    ) -> List[TaskStep]:
        # 创建执行任务
        tasks = [
            self._execute_with_semaphore(step, context)
            for step in steps
        ]

        # 并行执行
        return await asyncio.gather(*tasks)

    async def _execute_with_semaphore(
        self,
        step: TaskStep,
        context: Dict[str, Any]
    ) -> TaskStep:
        async with self.semaphore:
            return await self._execute_step(step, context)

3. 错误处理

class ErrorHandler:
    def __init__(self, llm):
        self.llm = llm

    async def handle_error(
        self,
        step: TaskStep,
        error: Exception,
        context: Dict[str, Any]
    ) -> TaskStep:
        # 分析错误
        analysis = await self.llm.analyze_error(
            step=step,
            error=str(error),
            context=context
        )

        if analysis.can_retry:
            # 修改参数重试
            step.parameters = analysis.fixed_parameters
            return await self._retry_step(step)
        elif analysis.can_workaround:
            # 使用替代方案
            return await self._execute_workaround(
                step,
                analysis.workaround
            )
        else:
            # 标记失败
            step.status = TaskStatus.FAILED
            step.error = str(error)
            return step

实践心得

在实现和使用这个规划系统的过程中,我总结了几点经验:

  1. 任务分解要合理

    • 粒度要适中
    • 依赖要清晰
    • 考虑并行可能
  2. 执行要有弹性

    • 支持并行执行
    • 优雅处理错误
    • 允许动态调整
  3. 状态管理很重要

    • 及时更新状态
    • 保存执行历史
    • 支持断点恢复

写在最后

一个好的规划系统能让 AI Agent 的行为更加可控和高效。它就像是一个项目经理,懂得如何拆分任务、分配资源、控制风险。

在下一篇文章中,我会讲解如何实现 AI Agent 的多轮对话系统。如果你对规划系统的设计有什么想法,欢迎在评论区交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值