返回首页
案例AgentFunction Calling

Prompt 驱动的自动化工作流

LLM Function Calling 构建自动化工作流

技术栈

PythonOpenAI APIAnthropic APIPydanticRetryTenacityStructlogFastAPI

项目概述

传统的 RPA(机器人流程自动化)需要预先定义每一步操作,灵活性差且维护成本高。随着 LLM 的 Function Calling 能力成熟,我们可以构建更智能的自动化工作流:LLM 作为"调度大脑",根据用户意图自主决定调用哪些工具、以什么顺序执行、如何处理异常。这种 Prompt 驱动的方式极大降低了工作流的配置门槛,同时具备了自我纠错和适应新场景的能力。

本案例以一个实际业务场景为例——智能客服与工单处理自动化:用户提出请求后,LLM 自动判断意图、查询知识库、创建工单、发送通知,全程无需人工干预。你将学会如何设计工具接口、编写 Prompt 模板、实现错误处理和重试机制、编排多步骤工作流。

技术架构

系统采用 Agent Loop 架构,核心是一个持续的"思考-行动-观察"循环:

用户输入 -> 意图识别 -> 工具选择 -> 参数填充 -> 执行工具 -> 结果验证 -> 自我纠错 -> 生成回复

详细步骤

步骤 1:工具设计

工具设计的核心原则是:每个工具职责单一、接口清晰、文档详细。LLM 通过工具的 name 和 description 来决定调用哪个工具,通过 parameters schema 来构造参数。description 写得越好,LLM 的工具选择准确率越高。避免一个工具做太多事情。

from pydantic import BaseModel, Field
from typing import Optional
from enum import Enum

class TicketPriority(str, Enum):
    LOW = "low"
    MEDIUM = "medium"
    HIGH = "high"
    URGENT = "urgent"

# --- 工具 1:查询知识库 ---
search_knowledge_base = {
    "type": "function",
    "function": {
        "name": "search_knowledge_base",
        "description": "在产品知识库中搜索相关信息。用于回答产品使用、配置、故障排查等问题。",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "搜索关键词,应简洁精炼"
                },
                "category": {
                    "type": "string",
                    "enum": ["产品使用", "配置指南", "故障排查", "计费问题"],
                    "description": "问题分类"
                }
            },
            "required": ["query"]
        }
    }
}

# --- 工具 2:创建工单 ---
class CreateTicketParams(BaseModel):
    title: str = Field(description="工单标题,简洁概括问题")
    description: str = Field(description="详细的问题描述")
    priority: TicketPriority = Field(description="优先级")
    customer_id: Optional[str] = Field(default=None, description="客户 ID(如果已提供)")
    assigned_team: str = Field(description="分配给哪个团队处理")

create_ticket = {
    "type": "function",
    "function": {
        "name": "create_ticket",
        "description": "创建客户支持工单。当知识库中没有相关答案,或问题需要人工处理时调用。",
        "parameters": CreateTicketParams.model_json_schema()
    }
}

# --- 工具 3:发送通知 ---
send_notification = {
    "type": "function",
    "function": {
        "name": "send_notification",
        "description": "向客户或内部团队发送通知(邮件/短信)。",
        "parameters": {
            "type": "object",
            "properties": {
                "recipient": {"type": "string", "description": "接收人"},
                "channel": {"type": "string", "enum": ["email", "sms"], "description": "通知渠道"},
                "message": {"type": "string", "description": "通知内容"},
                "ticket_id": {"type": "string", "description": "关联的工单 ID(如有)"}
            },
            "required": ["recipient", "channel", "message"]
        }
    }
}

步骤 2:Prompt 模板设计

System Prompt 定义了 Agent 的角色、行为规则和决策边界。好的 System Prompt 应该明确:(1) 你是谁、你能做什么;(2) 决策逻辑(什么时候用哪个工具);(3) 输出格式要求;(4) 安全边界(不能做什么)。

SYSTEM_PROMPT = """你是一个智能客服助手,负责处理客户的技术支持请求。

## 工作流程
1. 分析用户的问题,判断是否需要查询知识库
2. 如果能从知识库找到答案,直接回答用户
3. 如果知识库中没有相关信息,或问题需要人工介入:
   a. 创建工单,分配给合适的团队
   b. 向用户确认工单已创建
   c. 如果问题紧急,发送内部通知

## 决策规则
- 产品使用/配置问题:先查知识库,找不到再建工单
- 故障排查:查知识库获取临时解决方案,同时建工单跟进
- 计费问题:直接建工单,分配给财务团队
- 紧急问题(影响业务运行):建 URGENT 工单 + 内部通知

## 输出要求
- 回答要简洁、准确、友好
- 如果创建工单,必须提供工单编号
- 不要编造不确定的信息

## 安全边界
- 不能泄露其他客户的信息
- 不能承诺你无法保证的服务水平
- 不能绕过正常流程直接解决问题"""

步骤 3:错误处理与重试

工作流在执行过程中可能遇到各种异常:工具调用失败、参数不合法、外部服务不可用等。需要设计健壮的错误处理机制:自动重试(指数退避)、参数修正、降级策略。使用 tenacity 库可以优雅地实现重试逻辑。

from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
import structlog

logger = structlog.get_logger()

class ToolExecutionError(Exception):
    """工具执行失败"""
    pass

class MaxRetriesExceeded(Exception):
    """超过最大重试次数"""
    pass

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10),
    retry=retry_if_exception_type((ToolExecutionError, TimeoutError)),
    before_sleep=lambda retry_state: logger.warning(
        "工具调用失败,正在重试",
        attempt=retry_state.attempt_number,
        error=str(retry_state.outcome.exception())
    )
)
def execute_tool(tool_name: str, tool_args: dict) -> dict:
    """执行工具调用,带自动重试"""
    try:
        logger.info("执行工具", tool=tool_name, args=tool_args)
        result = available_tools[tool_name](**tool_args)

        # 验证返回结果
        if not validate_result(result):
            raise ToolExecutionError(f"工具 {tool_name} 返回结果异常")

        return result
    except TypeError as e:
        # 参数类型错误,尝试修正
        logger.warning("参数错误,尝试修正", error=str(e))
        corrected_args = fix_tool_args(tool_name, tool_args)
        return execute_tool(tool_name, corrected_args)
    except Exception as e:
        logger.error("工具执行失败", tool=tool_name, error=str(e))
        raise ToolExecutionError(str(e))

步骤 4:工作流编排

Agent Loop 是整个系统的核心调度引擎。它持续运行直到任务完成或达到最大迭代次数。每次迭代:(1) 将对话历史发送给 LLM;(2) LLM 决定调用工具或返回最终回答;(3) 如果调用工具,执行并将结果追加到对话历史;(4) 循环。需要设置最大迭代次数防止无限循环。

from openai import OpenAI
import json

client = OpenAI()
MAX_ITERATIONS = 10

def run_workflow(user_message: str) -> str:
    """执行 Agent 工作流"""
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_message}
    ]

    for iteration in range(MAX_ITERATIONS):
        logger.info("Agent 迭代", iteration=iteration + 1)

        response = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            tools=[search_knowledge_base, create_ticket, send_notification],
            temperature=0
        )

        msg = response.choices[0].message
        messages.append(msg)

        # 如果没有工具调用,说明 LLM 给出了最终回答
        if not msg.tool_calls:
            return msg.content

        # 执行所有工具调用
        for tool_call in msg.tool_calls:
            fn_name = tool_call.function.name
            try:
                fn_args = json.loads(tool_call.function.arguments)
                result = execute_tool(fn_name, fn_args)
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": json.dumps(result, ensure_ascii=False)
                })
            except MaxRetriesExceeded:
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": json.dumps({"error": "工具执行失败,请尝试其他方案"})
                })

    return "抱歉,处理过程中遇到了问题,已为您创建人工工单。"

# 测试
result = run_workflow("我的服务突然无法访问了,急!")
print(result)

步骤 5:应用场景扩展

掌握了基础框架后,可以将工作流扩展到更多业务场景。关键是理解"LLM + 工具"的通用模式:定义问题域的工具集、设计决策 Prompt、实现执行引擎。以下是几个可扩展的应用方向。

数据报表自动化

用户用自然语言描述需求 -> LLM 生成 SQL -> 执行查询 -> 生成图表 -> 发送邮件

DevOps 自动运维

监控告警 -> LLM 分析根因 -> 查询日志 -> 执行修复脚本 -> 通知相关人员

内容发布流水线

原始素材 -> LLM 生成文案 -> 人工审核 -> 自动排版 -> 多平台发布

常见问题与解决方案

Q: LLM 选错了工具怎么办?

优化方向:(1) 改进工具 description,更清晰地描述适用场景;(2) 在 System Prompt 中添加具体的决策示例(Few-shot);(3) 对于复杂决策,可以先用 LLM 做一次"规划",再执行具体工具调用。

Q: Agent 循环无限运行怎么办?

必须设置最大迭代次数(建议 5-10 次)。此外,监控每次迭代的工具调用和结果,如果检测到"循环调用同一工具"或"结果没有进展",主动中断并降级为人工处理。

Q: 如何处理敏感操作?

对于有副作用的操作(如创建工单、发送通知、修改数据),建议添加"确认"环节:LLM 生成操作计划 -> 展示给用户确认 -> 确认后执行。对于高危操作,可以要求人工审批后才能执行。

学习要点总结

  1. Function Calling 的核心是"LLM 选择工具,代码执行工具"的协作模式
  2. 工具 description 的质量直接决定 LLM 的决策准确率
  3. 错误处理和重试是生产环境必备的能力,不能假设所有调用都会成功
  4. Agent Loop 必须有最大迭代次数保护,防止无限循环
  5. 对于有副作用的操作,必须加入确认或审批环节