AI Agent 系统中的常用 Workflow 模式(1)

0 阅读8分钟

Agent 系统中的常用 Workflow 模式

引言

过去一年间,大语言模型(LLM)智能体的应用越来越广泛,从简单的任务处理到复杂的业务流程自动化,都能看到它们的身影。根据 Anthropic 的研究,最成功的智能体落地案例并非依赖复杂框架或专用库,而是采用简单可组合的模式构建。

本文将结合代码实例,介绍 Agent 系统中常用的 Workflow 模式,并探讨它们的应用场景和实现方法。

什么是 Agentic Systems?

在深入探讨 Workflow 模式之前,我们先明确一个概念:Agentic Systems。根据 Anthropic 的定义:

  • 工作流(Workflow):通过预定义代码路径编排 LLM 和工具的系统
  • 智能体(Agent):由 LLM 动态主导自身流程和工具使用,保持任务完成方式控制权的系统

本文将重点介绍工作流模式,这些模式是构建高效智能体的基础。

常用 Workflow 模式

1. 链式工作流(Chain Workflow)

链式工作流是最基础也是最常用的模式之一,它将任务分解为多个步骤,上一步的结果作为下一步的输入,形成一个处理链条。

实现代码
class ChainWorkFlow:
    @classmethod
    def run(cls):
        # 数据处理步骤
        data_processing_steps = [
            """仅从文本中提取数值及其相关指标。如果是金钱需要单位。将每一行设置为 value:metric
            
            示例格式:
            92:客户满意度
            45%:收入增长""",
            """尽可能将所有数值转换为百分比。如果是金钱需要保留单位。如果不是百分比或点数,则转换为小数(例如,92->92% , 0.002 -> 0.2%)。
            每行保留一个数字。
            
            示例格式:
            92%:客户满意度
            45%:收入增长""",
            """按数值降序排列所有行。每行保持 value:metric 格式。
            Example:
            92%: customer satisfaction
            87%: employee satisfaction""",
            """将排序后的数据格式化为带列的markdown表,仅输出指标和数据,不要描述
            | Metric | Value |
            |:--|--:|
            | 客户满意度 | 92% |""",
        ]

        report = """
        第三季度业绩总结:
        本季度,我们的客户满意度得分上升到92分。
        与去年相比,收入增长了45%。
        目前,我们在一级市场的市场份额为23%。
        客户流失率从8%降至5%。
        新用户获取成本为每位用户43美元。
        产品采用率提高到78%。
        员工满意度为87分。
        营业利润率提高到34%。
        """

        print("\nInput text:")
        print(report)
        formatted_result = cls.chain(report, data_processing_steps)

    @classmethod
    def chain(cls, input: str, prompts: list[str]) -> str:
        """
        链式调用
        result = prompt1(input)
        result2 = prompt2(result)
        result3 = prompt3(result2)
        ...

        """
        result = input
        for i, prompt in enumerate(prompts):
            result = util.llm_call(f"{prompt}\nInput:{result}", debug=False)
            print(f"Step {i} 结果\n:{result}\n")
        return result
应用场景

链式工作流适用于需要多步骤处理的任务,例如:

  • 数据提取和格式化
  • 文本分析和总结
  • 内容生成和编辑
  • 复杂问题的逐步解决

2. 并行工作流(Parallel Workflow)

并行工作流允许同时处理多个输入,使用相同的提示词模板,提高处理效率。

实现代码
class ParallelWorkFlow:
    @classmethod
    def run(cls):
        # 利益相关者分析
        stakeholders = [
            """消费者:
            -价格敏感
            -想要更好的技术
            -关注环境""",
            """雇员:
            -工作保障担忧
            -需要新技能
            -想要明确的方向""",
            """投资者:
            -期待增长
            -想要成本控制
            -风险问题""",
            """厂商:
            -库存限制
            -价格压力
            -技术转型""",
        ]

        impact_results = cls.parallel(
            """分析市场变化将如何影响这一利益相关者群体。
            提供具体影响和建议行动。
            格式有明确的分段和优先级。""",
            stakeholders,
        )

        for result in impact_results:
            print(result)
            print("+" * 80)

    @classmethod
    def parallel(cls, prompt: str, inputs: list[str], n_workers: int = 4) -> list[str]:
        """
        并行工作流,多个input使用同一套提示词
        """
        with ThreadPoolExecutor(max_workers=n_workers) as executor:
            futures = [
                executor.submit(util.llm_call, f"{prompt}\nInput:{data}", debug=True)
                for data in inputs
            ]
            return [f.result() for f in futures]
应用场景

并行工作流适用于以下场景:

  • 批量处理相似任务
  • 多维度分析(如不同利益相关者的影响分析)
  • 并行数据收集和处理
  • 大规模内容生成

3. 路由工作流(Route Workflow)

路由工作流根据输入内容的不同,将其路由到不同的处理流程或提示词模板,实现智能化的任务分发。

实现代码
class RouteWorkFlow:
    @classmethod
    def run(cls):
        support_routes = {
            "billing": """您是计费支持专家。遵循以下指南:
            1.始终以"计费支持响应:"开头
            2.首先确认具体的计费问题
            3.清楚地解释任何费用或差异
            4.列出具体的后续步骤和时间表
            5.如果相关,以付款选项结束

            保持专业但友好的回应。

            Input: """.strip(),
            "technical": """你是一名技术支持工程师。遵循以下指南:
            1.始终以"技术支持响应:"开头
            2.列出解决问题的确切步骤
            3.包括系统要求(如相关)
            4.为常见问题提供解决方法
            5.如果需要,以升级路径结束

            使用清晰、编号的步骤和技术细节。

            Input: """.strip(),
            "account": """您是帐户安全专家。遵循以下指南:
            1.始终以"帐户支持响应:"开头
            2.优先考虑账户安全和验证
            3.为帐户恢复/更改提供明确的步骤
            4.包括安全提示和警告
            5.对解决时间设定明确的期望

            保持严肃、以安全为重点的语气。

            Input: """.strip(),
            "product": """你是产品专家。遵循以下指南:
            1.始终以"产品支持响应:"开头
            2.注重特色教育和最佳做法
            3.包括使用的具体示例
            4.链接到相关文档部分
            5.建议可能有帮助的相关功能

            语气要有教育性和鼓励性。

            Input: """.strip(),
        }

        # 测试不同的支持工单
        tickets = [
            """主题:无法访问我的帐户
            消息:嗨,过去一个小时我一直在尝试登录,但一直收到"无效密码"错误。
            我确信我使用的密码是正确的。你能帮我重新获得访问权限吗?这很紧急,因为我需要
            在一天结束前提交一份报告。
            - John""".strip(),
            """主题:我卡上的意外费用
            留言:你好,我刚刚注意到贵公司从我的信用卡上收取了49.99美元的费用,但我想
            我在29.99美元的计划。你能解释一下这个费用吗?如果是错误的,你能调整一下吗?
            谢谢,
            Sarah""".strip(),
            """主题:如何导出数据?
            消息:我需要将所有项目数据导出到Excel。我已经看过文件了,但不能
            弄清楚如何进行批量导出。这可能吗?如果是这样,你能一步步的指导我吗?
            顺致敬意,
            Mike""".strip(),
        ]
        print("Processing support tickets...\n")
        for i, ticket in enumerate(tickets, 1):
            print(f"\nTicket {i}:")
            print("-" * 40)
            print(ticket)
            print("\nResponse:")
            print("-" * 40)
            response = cls.route(ticket, support_routes)
            print(response)
            print("+" * 80)

    @classmethod
    def route(cls, input: str, routes: dict[str, str]) -> str:
        """
        通过内容分类把输入路由到不同的提示词中
        """
        print(f"\n可用路由{list(routes.keys())}")
        selector_prompt = f"""
        分析输入并从以下选项中选择最合适的支持团队:{list(routes.keys())}
        首先解释您的推理,然后以XML格式提供您的选择:

        <reasoning>
        简要解释为什么应该将此票发送给特定团队。
        考虑关键术语、用户意图和紧急程度。
        </reasoning>
        
        <selection>
        所选团队名称
        </selection>
        
        Input: {input}
        """.strip()

        route_response = util.llm_call(selector_prompt)
        reasoning = util.extract_xml(route_response, "reasoning")
        route_key = util.extract_xml(route_response, "selection").strip().lower()

        print("路由分析:")
        print(reasoning)
        print(f"\n选中的路由: {route_key}")

        selected_prompt = routes[route_key]

        resulut = util.llm_call(f"{selected_prompt}\nInput:{input}")
        print(resulut)
应用场景

路由工作流适用于以下场景:

  • 客服工单分类和处理
  • 多领域问题的智能分发
  • 基于内容类型的不同处理逻辑
  • 个性化响应生成

核心工具函数

实现这些工作流模式需要一些核心工具函数,特别是与 LLM 交互的函数:

def llm_call(
    prompt: str, system_prompt: str = "", model="qwen-plus", debug=False
) -> str:
    messages = [
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": prompt},
    ]
    if debug:
        print(messages)
    reponse = dashscope.Generation.call(
        api_key="*******************",
        model=model,
        messages=messages,
        temperature=0.8,
        top_p=0.8,
        enable_thinking=False,
        stream=False,
        incremental_output=True,
        response_format={"type": "text"},
        result_format="message",  # or text
        enable_search=False,
        max_tokens=4096,
    )
    return reponse.output.choices[0].message.content


def extract_xml(text: str, tag: str) -> str:
    """
    Extracts the content of the specified XML tag from the given text. Used for parsing structured responses

    Args:
        text (str): The text containing the XML.
        tag (str): The XML tag to extract content from.

    Returns:
        str: The content of the specified XML tag, or an empty string if the tag is not found.
    """
    match = re.search(f"<{tag}>(.*?)</{tag}>", text, re.DOTALL)
    return match.group(1) if match else ""

实践建议

根据 Anthropic 的研究和我们的实践经验,构建高效的 Agent 系统时,应注意以下几点:

  1. 保持简单:优先使用简单可组合的模式,而不是复杂的框架
  2. 明确责任:清晰区分工作流和智能体的边界,根据任务性质选择合适的模式
  3. 模块化设计:将复杂任务分解为可管理的模块,便于维护和扩展
  4. 错误处理:添加适当的错误处理机制,确保系统的稳定性
  5. 持续优化:根据实际使用情况,不断调整和优化工作流

结论

工作流模式是构建高效 Agent 系统的基础。通过合理选择和组合链式、并行和路由等工作流模式,我们可以构建出功能强大、响应迅速的智能体系统。

正如 Anthropic 所强调的,最成功的智能体落地案例往往采用简单可组合的模式。因此,在设计和实现智能体系统时,我们应该注重基础工作流的设计和优化,而不是追求过于复杂的框架和工具。

通过本文介绍的工作流模式和代码示例,希望能够为开发者提供构建高效智能体的实用参考。