AI Agent的主流设计模式之规划模式

在这种模式下,AI Agent根据任务的复杂程度,设计任务计划流程,对任务进行细分,再对细分子任务动用ReAct 模式进行处理。可以说这种模式是一种战略思维,可以更有效地解决战略级复杂任务。

python 复制代码
import json
import re
from typing import Dict, List, Any, Callable, Optional, Tuple
from enum import Enum
from datetime import datetime
import openai
from dataclasses import dataclass, field


class PlanStatus(Enum):
    """计划状态枚举"""
    PENDING = "pending"  # 待执行
    IN_PROGRESS = "progress"  # 执行中
    COMPLETED = "completed"  # 已完成
    FAILED = "failed"  # 失败
    BLOCKED = "blocked"  # 阻塞


@dataclass
class Step:
    """计划步骤数据类"""
    id: str
    description: str
    action: str = None
    parameters: Dict = field(default_factory=dict)
    dependencies: List[str] = field(default_factory=list)
    status: PlanStatus = PlanStatus.PENDING
    result: Any = None
    error: str = None


@dataclass
class Plan:
    """完整计划数据类"""
    goal: str
    steps: List[Step]
    current_step_idx: int = -1
    status: PlanStatus = PlanStatus.PENDING
    context: Dict = field(default_factory=dict)  # 共享上下文


class PlanningAgent:
    """
    规划模式(Planning Mode)AI Agent

    核心特点:
    1. 先制定详细计划,再执行
    2. 支持步骤依赖关系
    3. 可动态调整计划
    4. 维护执行上下文
    """

    def __init__(
            self,
            api_key: str,
            model: str = "qwen-max",
            base_url: str = "https://dashscope.aliyuncs.com/compatible-mode/v1",
            max_retries: int = 3,
            verbose: bool = True
    ):
        """
        初始化规划模式Agent

        Args:
            api_key: 阿里云API Key
            model: 模型名称
            base_url: API基础URL
            max_retries: 步骤最大重试次数
            verbose: 是否打印详细信息
        """
        self.client = openai.OpenAI(
            api_key=api_key,
            base_url=base_url
        )
        self.model = model
        self.max_retries = max_retries
        self.verbose = verbose
        self.tools: Dict[str, Callable] = {}
        self.current_plan: Optional[Plan] = None

        # 系统提示词模板
        self.planner_prompt = """你是一个专业的任务规划器。你的任务是将用户的复杂目标分解为可执行的步骤序列。

可用工具:
{tool_descriptions}

请分析用户的目标,并制定一个详细的执行计划。计划应该:

1. 将大目标分解为多个小步骤
2. 每个步骤应该是可执行的(可以使用可用工具)
3. 考虑步骤之间的依赖关系
4. 为每个步骤提供清晰的描述

请以JSON格式输出计划,格式如下:
{{
    "steps": [
        {{
            "id": "step_1",
            "description": "步骤描述",
            "action": "工具名称(如果不需要工具则填null)",
            "parameters": {{}} // 工具参数占位,后续动态填充
        }}
    ]
}}

注意:
- 如果某个步骤不需要工具,action设为null
- 参数可以在执行时动态确定
- 确保步骤逻辑连贯

用户目标:{goal}
"""

        self.executor_prompt = """你是一个计划执行者。你需要根据当前计划步骤和已有信息,执行具体操作。

当前计划进度:
{plan_progress}

当前步骤信息:
- ID: {step_id}
- 描述: {step_description}
- 需要工具: {step_action}

上下文信息:
{context}

请分析当前情况,决定如何执行这个步骤。
如果需要使用工具,请指定工具参数。
如果步骤已完成,请提供结果。

输出格式:
思考:[你的分析]
行动:[工具名称(参数JSON)] 或 提供结果
"""

        self.replanner_prompt = """你是一个计划调整专家。在执行过程中遇到了问题,需要调整原有计划。

原始计划:
{original_plan}

已完成步骤:
{completed_steps}

遇到的问题:
{issue}

当前上下文:
{context}

请分析是否需要调整计划。如果需要调整,提供新的计划。
如果不需调整,说明如何继续执行。

输出格式:
分析:[你的分析]
是否需要调整:[是/否]
新计划:[如果需要,提供JSON格式的新计划]
建议:[继续执行的建议]
"""

    def register_tool(self, name: str, func: Callable, description: str):
        """注册工具"""
        self.tools[name] = {
            "func": func,
            "description": description
        }

    def _get_tool_descriptions(self) -> str:
        """获取工具描述"""
        if not self.tools:
            return "暂无可用工具"

        descriptions = []
        for name, info in self.tools.items():
            descriptions.append(f"- {name}: {info['description']}")
        return "\n".join(descriptions)

    def _call_llm(self, messages: List[Dict], temperature: float = 0.7) -> str:
        """调用大模型"""
        try:
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                temperature=temperature,
                max_tokens=2000
            )
            return response.choices[0].message.content
        except Exception as e:
            raise Exception(f"调用大模型失败:{str(e)}")

    def _parse_json_response(self, text: str) -> Optional[Dict]:
        """从模型回复中解析JSON"""
        # 尝试提取JSON部分
        json_pattern = r'\{[^{}]*\}'
        matches = re.findall(json_pattern, text, re.DOTALL)

        for match in matches:
            try:
                return json.loads(match)
            except:
                continue

        # 尝试更宽松的匹配
        try:
            # 直接尝试解析整个文本
            return json.loads(text)
        except:
            pass

        return None

    def create_plan(self, goal: str) -> Plan:
        """
        创建执行计划

        Args:
            goal: 用户目标

        Returns:
            生成的计划
        """
        if self.verbose:
            print(f"\n📋 开始制定计划...")
            print(f"目标: {goal}")

        # 构建提示词
        prompt = self.planner_prompt.format(
            tool_descriptions=self._get_tool_descriptions(),
            goal=goal
        )

        messages = [
            {"role": "system", "content": "你是一个专业的任务规划助手。"},
            {"role": "user", "content": prompt}
        ]

        # 调用模型生成计划
        response = self._call_llm(messages, temperature=0.3)

        if self.verbose:
            print(f"\n🤖 模型回复:\n{response}")

        # 解析计划
        plan_json = self._parse_json_response(response)

        if not plan_json or "steps" not in plan_json:
            # 如果解析失败,创建简单计划
            if self.verbose:
                print("⚠️ 计划解析失败,创建默认计划")
            return self._create_default_plan(goal)

        # 构建步骤列表
        steps = []
        for i, step_data in enumerate(plan_json["steps"]):
            step = Step(
                id=step_data.get("id", f"step_{i + 1}"),
                description=step_data["description"],
                action=step_data.get("action"),
                parameters=step_data.get("parameters", {}),
                dependencies=step_data.get("dependencies", [])
            )
            steps.append(step)

        plan = Plan(
            goal=goal,
            steps=steps,
            context={}
        )

        if self.verbose:
            print(f"\n✅ 计划制定完成,共 {len(steps)} 个步骤")
            for i, step in enumerate(steps):
                print(f"  {i + 1}. [{step.id}] {step.description}")
                if step.action:
                    print(f"     工具: {step.action}")

        return plan

    def _create_default_plan(self, goal: str) -> Plan:
        """创建默认计划(当解析失败时)"""
        # 尝试用工具名称启发式创建计划
        steps = []

        # 简单规则:如果目标包含特定关键词,创建相应步骤
        if "计算" in goal or "等于" in goal:
            steps.append(Step(
                id="step_1",
                description="计算数学表达式",
                action="calculator",
                parameters={"expression": goal}
            ))
        elif "天气" in goal:
            # 提取城市名(简单启发式)
            import re
            cities = re.findall(r'[北京上海广州深圳]', goal)
            city = cities[0] if cities else "北京"

            steps.append(Step(
                id="step_1",
                description=f"查询{city}天气",
                action="weather",
                parameters={"city": city}
            ))
        elif "时间" in goal:
            steps.append(Step(
                id="step_1",
                description="获取当前时间",
                action="get_time",
                parameters={}
            ))
        else:
            # 通用步骤:先分析再执行
            steps.append(Step(
                id="step_1",
                description="分析用户需求",
                action=None,
                parameters={}
            ))

        return Plan(goal=goal, steps=steps)

    def _get_ready_steps(self, plan: Plan) -> List[Step]:
        """获取可以执行的步骤(依赖已完成的步骤)"""
        ready_steps = []

        for step in plan.steps:
            if step.status != PlanStatus.PENDING:
                continue

            # 检查所有依赖是否已完成
            deps_satisfied = True
            for dep_id in step.dependencies:
                dep_step = next((s for s in plan.steps if s.id == dep_id), None)
                if not dep_step or dep_step.status != PlanStatus.COMPLETED:
                    deps_satisfied = False
                    break

            if deps_satisfied:
                ready_steps.append(step)

        return ready_steps

    def _execute_step(self, step: Step, plan: Plan) -> Tuple[bool, Any]:
        """
        执行单个步骤

        Returns:
            (是否成功, 执行结果)
        """
        if self.verbose:
            print(f"\n🔧 执行步骤 [{step.id}]: {step.description}")

        # 如果不需要工具,直接标记为完成
        if not step.action or step.action == "null":
            step.status = PlanStatus.COMPLETED
            step.result = f"步骤 '{step.description}' 已完成(无需工具)"
            return True, step.result

        # 检查工具是否存在
        if step.action not in self.tools:
            error_msg = f"错误:未知工具 '{step.action}'"
            step.status = PlanStatus.FAILED
            step.error = error_msg
            return False, error_msg

        # 执行工具
        for attempt in range(self.max_retries):
            try:
                tool_func = self.tools[step.action]["func"]

                # 准备参数
                params = step.parameters.copy()

                # 如果参数是动态的,可以从上下文获取
                for key, value in params.items():
                    if isinstance(value, str) and value.startswith("$"):
                        # 从上下文获取变量
                        var_name = value[1:]
                        if var_name in plan.context:
                            params[key] = plan.context[var_name]

                if self.verbose:
                    print(f"  工具参数: {params}")

                # 执行工具
                result = tool_func(**params)

                # 更新步骤状态
                step.status = PlanStatus.COMPLETED
                step.result = result

                # 将结果保存到上下文
                plan.context[f"{step.id}_result"] = result

                return True, result

            except Exception as e:
                error_msg = f"执行失败 (尝试 {attempt + 1}/{self.max_retries}): {str(e)}"
                if attempt < self.max_retries - 1:
                    if self.verbose:
                        print(f"  ⚠️ {error_msg},重试中...")
                    continue
                else:
                    step.status = PlanStatus.FAILED
                    step.error = error_msg
                    return False, error_msg

    def _format_plan_progress(self, plan: Plan) -> str:
        """格式化计划进度"""
        progress = []
        for step in plan.steps:
            status_symbol = {
                PlanStatus.PENDING: "⏳",
                PlanStatus.IN_PROGRESS: "🔄",
                PlanStatus.COMPLETED: "✅",
                PlanStatus.FAILED: "❌",
                PlanStatus.BLOCKED: "🚫"
            }.get(step.status, "⏳")

            progress.append(f"{status_symbol} [{step.id}] {step.description}")
            if step.status == PlanStatus.COMPLETED and step.result:
                progress.append(f"    结果: {step.result[:50]}...")
            elif step.status == PlanStatus.FAILED and step.error:
                progress.append(f"    错误: {step.error}")

        return "\n".join(progress)

    def execute_plan(self, plan: Plan) -> Dict[str, Any]:
        """
        执行计划

        Args:
            plan: 待执行的计划

        Returns:
            执行结果
        """
        self.current_plan = plan
        plan.status = PlanStatus.IN_PROGRESS

        if self.verbose:
            print(f"\n🚀 开始执行计划...")

        # 循环直到所有步骤完成
        while True:
            # 获取可执行的步骤
            ready_steps = self._get_ready_steps(plan)

            if not ready_steps:
                # 检查是否所有步骤都完成了
                all_completed = all(
                    step.status == PlanStatus.COMPLETED
                    for step in plan.steps
                )

                if all_completed:
                    plan.status = PlanStatus.COMPLETED
                    break
                else:
                    # 有步骤阻塞,需要重新规划
                    if self.verbose:
                        print("\n⚠️ 计划可能阻塞,尝试重新规划...")

                    success = self._replan()
                    if not success:
                        plan.status = PlanStatus.BLOCKED
                        break
                    continue

            # 执行第一个准备好的步骤
            step = ready_steps[0]
            step.status = PlanStatus.IN_PROGRESS

            # 执行步骤
            success, result = self._execute_step(step, plan)

            if not success:
                if self.verbose:
                    print(f"\n❌ 步骤 [{step.id}] 执行失败")

                # 尝试重新规划
                if self.verbose:
                    print("尝试重新规划...")

                success = self._replan()
                if not success:
                    plan.status = PlanStatus.FAILED
                    break

        # 汇总结果
        summary = self._summarize_execution(plan)

        if self.verbose:
            print(f"\n📊 执行完成,状态: {plan.status.value}")
            print(summary)

        return {
            "status": plan.status.value,
            "summary": summary,
            "context": plan.context
        }

    def _replan(self) -> bool:
        """
        重新规划(当执行遇到问题时)

        Returns:
            是否成功重新规划
        """
        if not self.current_plan:
            return False

        plan = self.current_plan

        # 找出失败/阻塞的步骤
        failed_steps = [
            step for step in plan.steps
            if step.status in [PlanStatus.FAILED, PlanStatus.BLOCKED]
        ]

        if not failed_steps:
            return True

        # 构建重新规划提示
        completed_steps = [
            step for step in plan.steps
            if step.status == PlanStatus.COMPLETED
        ]

        prompt = self.replanner_prompt.format(
            original_plan=json.dumps({
                "steps": [
                    {
                        "id": s.id,
                        "description": s.description,
                        "action": s.action
                    }
                    for s in plan.steps
                ]
            }, ensure_ascii=False, indent=2),
            completed_steps="\n".join([
                f"- {s.id}: {s.result}" for s in completed_steps
            ]),
            issue=f"步骤 {failed_steps[0].id} 失败: {failed_steps[0].error}",
            context=json.dumps(plan.context, ensure_ascii=False, indent=2)
        )

        messages = [
            {"role": "system", "content": "你是一个计划调整专家。"},
            {"role": "user", "content": prompt}
        ]

        response = self._call_llm(messages, temperature=0.4)

        if self.verbose:
            print(f"\n🤖 重新规划建议:\n{response}")

        # 解析新计划
        if "新计划" in response:
            plan_json = self._parse_json_response(response)
            if plan_json and "steps" in plan_json:
                # 添加新步骤
                base_idx = len(plan.steps)
                for i, step_data in enumerate(plan_json["steps"]):
                    new_step = Step(
                        id=f"new_step_{base_idx + i + 1}",
                        description=step_data["description"],
                        action=step_data.get("action"),
                        parameters=step_data.get("parameters", {}),
                        dependencies=[failed_steps[0].id]  # 依赖失败的步骤
                    )
                    plan.steps.append(new_step)

                if self.verbose:
                    print(f"✅ 已添加 {len(plan_json['steps'])} 个新步骤")
                return True

        return False

    def _summarize_execution(self, plan: Plan) -> str:
        """生成执行总结"""
        lines = []
        lines.append(f"目标: {plan.goal}")
        lines.append(f"状态: {plan.status.value}")
        lines.append("\n执行记录:")

        for step in plan.steps:
            status_symbol = {
                PlanStatus.COMPLETED: "✅",
                PlanStatus.FAILED: "❌",
                PlanStatus.BLOCKED: "🚫",
                PlanStatus.PENDING: "⏳"
            }.get(step.status, "⏳")

            lines.append(f"{status_symbol} {step.description}")
            if step.result:
                lines.append(f"   结果: {step.result}")
            elif step.error:
                lines.append(f"   错误: {step.error}")

        if plan.context:
            lines.append("\n上下文信息:")
            for key, value in plan.context.items():
                lines.append(f"  {key}: {value}")

        return "\n".join(lines)

    def solve(self, goal: str) -> str:
        """
        一站式解决问题:制定计划并执行

        Args:
            goal: 用户目标

        Returns:
            最终答案
        """
        # 1. 创建计划
        plan = self.create_plan(goal)

        # 2. 执行计划
        result = self.execute_plan(plan)

        # 3. 生成最终答案
        if result["status"] == "completed":
            # 收集所有步骤的结果
            step_results = []
            for step in plan.steps:
                if step.result:
                    step_results.append(f"- {step.description}: {step.result}")

            answer = f"任务完成!\n\n执行过程:\n" + "\n".join(step_results)

            # 如果有最终上下文,也包含进去
            if "final_result" in plan.context:
                answer += f"\n\n最终结果:{plan.context['final_result']}"

            return answer
        else:
            return f"任务未能完成。状态:{result['status']}\n{result['summary']}"


# ============= 工具函数定义 =============

import math


def calculator(expression: str) -> str:
    """计算器工具"""
    try:
        # 安全计算
        allowed_names = {
            k: v for k, v in math.__dict__.items()
            if not k.startswith("__")
        }
        allowed_names.update({"abs": abs, "round": round})

        result = eval(expression, {"__builtins__": {}}, allowed_names)
        return f"计算结果:{expression} = {result}"
    except Exception as e:
        return f"计算错误:{str(e)}"


def get_current_time(format: str = "%Y-%m-%d %H:%M:%S") -> str:
    """获取当前时间"""
    return datetime.now().strftime(format)


def search_weather(city: str) -> str:
    """模拟天气查询"""
    weather_data = {
        "北京": "晴,25°C,空气质量良",
        "上海": "多云,28°C,空气质量优",
        "广州": "阵雨,30°C,空气质量优",
        "深圳": "雷阵雨,29°C,空气质量良"
    }
    return weather_data.get(city, f"未找到{city}的天气信息")


def search_baidu(query: str) -> str:
    """模拟百度搜索"""
    # 实际可调用真实搜索API
    return f"关于'{query}'的搜索结果:这里是一些相关信息..."


def get_stock_price(code: str) -> str:
    """模拟股票查询"""
    stocks = {
        "000001": "平安银行 12.50元",
        "600036": "招商银行 35.80元",
        "9988": "阿里巴巴 110.50港元"
    }
    return stocks.get(code, f"未找到代码{code}的股票信息")


def send_email(to: str, subject: str, content: str) -> str:
    """模拟发送邮件"""
    return f"邮件已发送给{to},主题:{subject}"


# ============= 使用示例 =============

def main():
    """主函数示例"""
    import os

    # 获取API Key
    API_KEY = os.getenv("DASHSCOPE_API_KEY")
    if not API_KEY:
        print("请设置环境变量 DASHSCOPE_API_KEY")
        return

    # 创建规划模式Agent
    agent = PlanningAgent(
        api_key=API_KEY,
        model="qwen-max",
        verbose=True
    )

    # 注册工具
    agent.register_tool("calculator", calculator, "计算数学表达式")
    agent.register_tool("get_time", get_current_time, "获取当前时间")
    agent.register_tool("weather", search_weather, "查询天气")
    agent.register_tool("search", search_baidu, "搜索信息")
    agent.register_tool("stock", get_stock_price, "查询股票")
    agent.register_tool("email", send_email, "发送邮件")

    print("\n" + "=" * 60)
    print("规划模式Agent示例 - 先计划后执行")
    print("=" * 60)

    # 示例1:简单计算任务
    print("\n【示例1】简单计算")
    result = agent.solve("计算 3.14 * 5 的平方是多少?")
    print(f"\n最终答案:{result}")

    # 示例2:多步骤复杂任务
    print("\n【示例2】多步骤任务:查询天气并计算温度")
    result = agent.solve("查询北京的天气,然后计算温度数值加上15度是多少?")
    print(f"\n最终答案:{result}")

    # 示例3:需要搜索的任务
    print("\n【示例3】信息查询任务")
    result = agent.solve("查一下平安银行的股票代码,然后查询它的股价")
    print(f"\n最终答案:{result}")

    # 示例4:复杂组合任务
    print("\n【示例4】复杂组合任务:分析后执行")
    result = agent.solve("""
    请帮我完成以下任务:
    1. 查询当前时间
    2. 查询上海的天气
    3. 如果天气好(温度>25度),计算(35 + 20) * 2的结果
    4. 给我一个完整的总结
    """)
    print(f"\n最终答案:{result}")


# ============= 进阶示例:动态规划 =============

class DynamicPlanningAgent(PlanningAgent):
    """
    动态规划Agent - 在每一步都可以调整计划
    """

    def execute_plan_dynamic(self, plan: Plan) -> Dict[str, Any]:
        """
        动态执行计划:每步之后都可能调整后续计划
        """
        self.current_plan = plan
        plan.status = PlanStatus.IN_PROGRESS

        step_index = 0
        while step_index < len(plan.steps):
            step = plan.steps[step_index]

            if step.status != PlanStatus.PENDING:
                step_index += 1
                continue

            # 检查依赖
            deps_met = True
            for dep_id in step.dependencies:
                dep_step = next((s for s in plan.steps if s.id == dep_id), None)
                if not dep_step or dep_step.status != PlanStatus.COMPLETED:
                    deps_met = False
                    break

            if not deps_met:
                step_index += 1
                continue

            # 执行当前步骤
            if self.verbose:
                print(f"\n📌 执行步骤 {step_index + 1}/{len(plan.steps)}: {step.description}")

            step.status = PlanStatus.IN_PROGRESS
            success, result = self._execute_step(step, plan)

            if success:
                # 执行后评估,决定是否需要调整
                should_adjust, new_plan = self._evaluate_and_adjust(plan, step_index)

                if should_adjust and new_plan:
                    if self.verbose:
                        print("🔄 动态调整计划...")

                    # 替换剩余步骤
                    plan.steps = plan.steps[:step_index + 1] + new_plan.steps

                    if self.verbose:
                        print(f"   新计划共 {len(new_plan.steps)} 步")

            step_index += 1

        plan.status = PlanStatus.COMPLETED
        return self._summarize_execution(plan)

    def _evaluate_and_adjust(self, plan: Plan, current_idx: int) -> Tuple[bool, Optional[Plan]]:
        """
        评估执行情况并决定是否调整计划
        """
        # 简单的评估逻辑:检查是否需要更多步骤
        if current_idx >= len(plan.steps) - 1:
            return False, None  # 已经是最后一步

        # 获取当前结果
        current_step = plan.steps[current_idx]
        if not current_step.result:
            return False, None

        # 根据结果决定是否需要额外步骤
        result_str = str(current_step.result).lower()

        # 示例:如果天气查询结果包含"雨",添加带伞提醒
        if "weather" in current_step.action and "雨" in result_str:
            new_step = Step(
                id="dynamic_step_1",
                description="提醒用户带伞",
                action=None,
                parameters={}
            )
            new_plan = Plan(goal=plan.goal, steps=[new_step])
            return True, new_plan

        return False, None


def advanced_example():
    """动态规划示例"""
    import os

    API_KEY = os.getenv("DASHSCOPE_API_KEY")
    if not API_KEY:
        print("请设置API_KEY")
        return

    # 使用动态规划Agent
    agent = DynamicPlanningAgent(
        api_key=API_KEY,
        model="qwen-max",
        verbose=True
    )

    # 注册工具
    agent.register_tool("weather", search_weather, "查询天气")
    agent.register_tool("calculator", calculator, "计算")

    print("\n" + "=" * 60)
    print("动态规划示例 - 根据执行结果调整计划")
    print("=" * 60)

    # 这个任务可能会触发动态调整
    result = agent.solve("查询北京的天气,然后告诉我该怎么做")
    print(f"\n最终结果:{result}")


if __name__ == "__main__":
    # 运行基础示例
    main()

    # 运行动态规划示例
    # advanced_example()
相关推荐
weixin_440401691 小时前
Python数据分析-数据可视化(转置+折线图plot+柱状图bar+饼图pie)
python·信息可视化·数据分析
Alsian1 小时前
Day33 GPU及call方法
人工智能·python·深度学习
格林威1 小时前
Baumer相机金属粉末铺粉均匀性评估:用于增材制造过程监控的 7 个实用技巧,附 OpenCV+Halcon 实战代码!
人工智能·opencv·视觉检测·制造·工业相机·智能相机·堡盟相机
rainbow7242441 小时前
学AI的完整花费清单:从入门到进阶的投入预算
人工智能
清水白石0082 小时前
装饰器模式 vs Python 装饰器:同名背后的深度解析与实战融合
数据库·python·装饰器模式
ZPC82102 小时前
window 下使用docker
人工智能·python·算法·机器人
子午2 小时前
【岩石种类识别系统】Python+深度学习+人工智能+算法模型+图像识别+TensorFlow+2026计算机毕设项目
人工智能·python·深度学习
格林威2 小时前
Baumer相机镜面反射区域遮蔽重建:恢复缺失纹理的 6 个关键技术,附 OpenCV+Halcon 实战代码!
人工智能·opencv·计算机视觉·视觉检测·工业相机·智能相机·堡盟相机