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 系统时,应注意以下几点:
- 保持简单:优先使用简单可组合的模式,而不是复杂的框架
- 明确责任:清晰区分工作流和智能体的边界,根据任务性质选择合适的模式
- 模块化设计:将复杂任务分解为可管理的模块,便于维护和扩展
- 错误处理:添加适当的错误处理机制,确保系统的稳定性
- 持续优化:根据实际使用情况,不断调整和优化工作流
结论
工作流模式是构建高效 Agent 系统的基础。通过合理选择和组合链式、并行和路由等工作流模式,我们可以构建出功能强大、响应迅速的智能体系统。
正如 Anthropic 所强调的,最成功的智能体落地案例往往采用简单可组合的模式。因此,在设计和实现智能体系统时,我们应该注重基础工作流的设计和优化,而不是追求过于复杂的框架和工具。
通过本文介绍的工作流模式和代码示例,希望能够为开发者提供构建高效智能体的实用参考。