作者:逆境不可逃
技术永无止境
希望我的内容可以帮助到你!!!!!
大家吼 ! 我是 逆境不可逃 今天给大家带来文章**《Hello-Agents 第二部分-第四章总结:智能体经典范式构建》.**
Hello-Agents 官方地址: datawhalechina/hello-agents: 📚 《从零开始构建智能体》------从零开始的智能体原理与实践教程
1. 本章主线
第四章从零实现了 ReAct、Plan-and-Solve、Reflection,重点是理解 Agent Loop 的底层机制;第五章体验低代码平台,重点是快速搭建。本章进入框架开发实践:使用成熟智能体框架,把通用能力交给框架,把开发者注意力集中到角色、流程、工具、状态和业务逻辑上。
一个智能体框架的价值,不只是少写代码,而是提供一套可复用的工程规范:
- 模型层:统一接入不同 LLM 后端;
- 工具层:标准化工具定义、注册、调用和错误处理;
- 状态层:管理对话历史、中间结果、任务进度和持久化;
- 协作层:组织单智能体或多智能体之间的消息交互;
- 可观测层:记录事件、消息、工具调用和执行轨迹;
- 部署层:支持异步、并发、分布式、恢复和监控。
第六章选择了四个代表性框架:
| 框架 | 核心思想 | 更适合的任务 |
|---|---|---|
AutoGen |
用对话驱动多智能体协作 | 软件开发团队、角色分工、群聊式协作 |
AgentScope |
工程化优先的消息驱动多智能体平台 | 大规模、高并发、分布式、多角色交互 |
CAMEL |
角色扮演和引导性提示激发协作 | 双智能体深度协作、创作、研究、头脑风暴 |
LangGraph |
把智能体流程建模为状态机和有向图 | 可控流程、循环、反思、测试修复、审计场景 |
可以用一句话记住它们的差异:
AutoGen: 把任务变成一场多角色会议
AgentScope: 把多智能体系统做成工程化消息网络
CAMEL: 用角色扮演让专家智能体自主协作
LangGraph: 用图和状态机精确控制每一步
2. 为什么需要智能体框架
手写 Agent Loop 适合教学和原型,但当任务变复杂后,重复问题会迅速出现:
- 每个 Agent 都要写模型调用、历史管理、工具分发;
- 多智能体之间需要消息路由、状态同步、终止条件;
- 工具调用失败、模型输出格式错误、循环失控需要统一处理;
- 线上系统需要日志、追踪、恢复、并发和权限边界;
- 复杂流程需要可视化、可测试、可审计。
框架的本质是把这些重复部分封装为稳定抽象。第六章的核心不是 "哪个框架最好",而是理解不同框架背后的设计取舍:
| 取舍维度 | 涌现式协作 | 显式控制 |
|---|---|---|
| 代表框架 | AutoGen、CAMEL | LangGraph |
| 开发重点 | 定义角色和目标 | 定义状态、节点和边 |
| 优点 | 自然、灵活、像人类协作 | 可控、可测、可审计 |
| 风险 | 不确定性高,调试依赖对话历史 | 前期样板代码更多 |
AgentScope 则强调另一个维度:工程化。它关注的是多智能体系统从 "能跑" 到 "能稳定服务" 的问题,包括异步、消息驱动、分布式和容错。
3. chapter6 代码目录
官方代码目录包含四个示例:
chapter6/
AutoGenDemo/
autogen_software_team.py
output.py
README.md
requirements.txt
AgentScopeDemo/
main_cn.py
prompt_cn.py
game_roles.py
structured_output_cn.py
utils_cn.py
README.md
requirements.txt
CAMEL/
DigitalBookWriting.py
requirements.txt
Langgraph/
Dialogue_System.py
requirements.txt
依赖摘要:
AutoGenDemo: autogen-agentchat, autogen-ext[openai,azure], streamlit, requests
AgentScopeDemo: agentscope==1.0.2
CAMEL: camel-ai==0.2.75
Langgraph: langgraph==1.0.0a3, langchain_openai, tavily-python
4. AutoGen:对话驱动的软件开发团队
4.1 核心机制
AutoGen 的核心抽象是 "可对话智能体"。开发者定义多个角色,让它们通过消息自动协作。第六章以 AutoGen 0.7.4 的新架构为例,强调两点:
- 分层架构:
autogen-core负责底层模型交互和消息传递,autogen-agentchat提供对话式智能体高级接口; - 异步优先:多智能体协作中 LLM 调用是主要耗时点,
async/await能避免阻塞。
案例目标:构建一个软件开发团队,自动完成 "比特币价格显示应用" 的需求分析、代码实现、审查和测试。
团队角色:
| 角色 | 类 | 职责 |
|---|---|---|
| 产品经理 | AssistantAgent |
需求分析、功能拆解、验收标准 |
| 软件工程师 | AssistantAgent |
编写完整可运行代码 |
| 代码审查员 | AssistantAgent |
审查代码质量、安全性、最佳实践 |
| 用户代理 | UserProxyAgent |
代表用户测试,负责终止流程 |
4.2 模型客户端配置
来自 AutoGenDemo/autogen_software_team.py:
import os
from dotenv import load_dotenv
from autogen_ext.models.openai import OpenAIChatCompletionClient
load_dotenv()
def create_openai_model_client():
"""创建 OpenAI 兼容模型客户端。"""
return OpenAIChatCompletionClient(
model=os.getenv("LLM_MODEL_ID", "gpt-4o"),
api_key=os.getenv("LLM_API_KEY"),
base_url=os.getenv("LLM_BASE_URL", "https://api.openai.com/v1"),
)
这个函数把模型服务隔离成统一客户端,后续每个 Agent 都复用它。这样可以切换 OpenAI、Azure OpenAI、本地兼容服务或第三方模型服务。
4.3 定义团队角色
产品经理的系统消息:
from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
def create_product_manager(model_client):
system_message = """你是一位经验丰富的产品经理,专门负责软件产品的需求分析和项目规划。
你的核心职责包括:
1. 需求分析:深入理解用户需求,识别核心功能和边界条件
2. 技术规划:基于需求制定清晰的技术实现路径
3. 风险评估:识别潜在的技术风险和用户体验问题
4. 协调沟通:与工程师和其他团队成员进行有效沟通
当接到开发任务时,请按以下结构进行分析:
1. 需求理解与分析
2. 功能模块划分
3. 技术选型建议
4. 实现优先级排序
5. 验收标准定义
请简洁明了地回应,并在分析完成后说"请工程师开始实现"。"""
return AssistantAgent(
name="ProductManager",
model_client=model_client,
system_message=system_message,
)
工程师和审查员同样通过 system_message 定义职责。AutoGen 的关键不是写复杂状态机,而是把团队中 "谁负责什么" 描述清楚。
用户代理:
def create_user_proxy():
"""创建用户代理智能体。"""
return UserProxyAgent(
name="UserProxy",
description="""用户代理,负责以下职责:
1. 代表用户提出开发需求
2. 执行最终的代码实现
3. 验证功能是否符合预期
4. 提供用户反馈和建议
完成测试后请回复 TERMINATE。""",
)
UserProxyAgent 是 AutoGen 中很重要的设计:它既可以代表人类发起需求,也可以作为执行器或人工在环接口,让对话式协作不完全脱离真实执行。
4.4 组织群聊与终止条件
import asyncio
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.ui import Console
async def run_software_development_team():
model_client = create_openai_model_client()
product_manager = create_product_manager(model_client)
engineer = create_engineer(model_client)
code_reviewer = create_code_reviewer(model_client)
user_proxy = create_user_proxy()
termination = TextMentionTermination("TERMINATE")
team_chat = RoundRobinGroupChat(
participants=[
product_manager,
engineer,
code_reviewer,
user_proxy,
],
termination_condition=termination,
max_turns=20,
)
task = """我们需要开发一个比特币价格显示应用,具体要求如下:
核心功能:
- 实时显示比特币当前价格(USD)
- 显示24小时价格变化趋势(涨跌幅和涨跌额)
- 提供价格刷新功能
技术要求:
- 使用 Streamlit 框架创建 Web 应用
- 界面简洁美观,用户友好
- 添加适当的错误处理和加载状态
请团队协作完成这个任务,从需求分析到最终实现。"""
result = await Console(team_chat.run_stream(task=task))
return result
if __name__ == "__main__":
asyncio.run(run_software_development_team())
这里的关键组件:
RoundRobinGroupChat:按固定顺序让成员轮流发言;TextMentionTermination("TERMINATE"):检测到终止词后结束协作;Console(team_chat.run_stream(...)):流式展示协作过程。
4.5 团队生成的应用代码
AutoGenDemo/output.py 是团队协作生成的 Streamlit 比特币价格应用:
import streamlit as st
import requests
def get_bitcoin_price():
try:
response = requests.get(
"https://api.coingecko.com/api/v3/simple/price"
"?ids=bitcoin&vs_currencies=usd&include_24hr_change=true"
)
data = response.json()
current_price = data["bitcoin"]["usd"]
price_change_percentage = data["bitcoin"]["usd_24h_change"]
return current_price, price_change_percentage
except requests.exceptions.RequestException as e:
st.error(f"Error fetching data: {e}")
return None, None
st.title("实时比特币价格")
st.subheader("获取最新的比特币价格信息及其24小时价格变化趋势")
if st.button("刷新价格"):
st.experimental_rerun()
with st.spinner("加载中..."):
current_price, price_change_percentage = get_bitcoin_price()
if current_price is not None:
st.metric(label="当前比特币价格 (USD)", value=f"${current_price}")
if price_change_percentage is not None:
st.metric(label="24小时变化 (%)", value=f"{price_change_percentage:.2f}%")
else:
st.error("无法获取数据,请稍后重试。")
这个应用能体现 AutoGen 案例的闭环:产品经理拆解需求,工程师生成代码,审查员给出质量建议,用户代理验证功能。
4.6 AutoGen 适用性
优势:
- 角色分工自然,适合模拟人类团队;
system_message复用性强,可以沉淀角色模板;- 群聊机制降低复杂协作建模门槛;
UserProxyAgent方便接入人类在环和执行反馈;- 异步架构适合多 Agent 远程 LLM 调用。
局限:
- 对话式协作不完全可控,可能偏题或循环;
- 调试依赖长对话历史,定位问题比传统堆栈更难;
- 轮询群聊适合固定流程,不适合复杂动态路由;
- 角色提示词质量直接影响协作质量。
适合:
- 软件开发协作;
- 方案评审;
- 多角色头脑风暴;
- 需要人类在环的任务处理。
5. AgentScope:消息驱动的三国狼人杀
5.1 核心机制
AgentScope 的设计更偏工程化。它不是单纯组织几个 Agent 聊天,而是围绕消息、生命周期、分布式和可观测性构建多智能体平台。
核心组件:
| 组件 | 作用 |
|---|---|
Msg |
统一消息结构 |
AgentBase / ReActAgent |
智能体基础抽象 |
MsgHub |
消息中心,负责路由、广播、组播 |
pipeline |
编排顺序、并发等执行模式 |
| 结构化输出 | 用 Pydantic 约束 Agent 行为 |
第六章案例是 "三国狼人杀":每个 Agent 同时有游戏身份和三国人物人格,例如 "狼人曹操""预言家诸葛亮"。
5.2 角色定义
来自 AgentScopeDemo/game_roles.py:
class GameRoles:
"""游戏角色管理类。"""
ROLES = {
"狼人": {
"description": "狼人",
"ability": "夜晚可以击杀一名玩家",
"win_condition": "消灭所有好人或与好人数量相等",
"team": "狼人阵营",
},
"预言家": {
"description": "预言家",
"ability": "每晚可以查验一名玩家的身份",
"win_condition": "消灭所有狼人",
"team": "好人阵营",
},
"女巫": {
"description": "女巫",
"ability": "拥有解药和毒药各一瓶,可以救人或杀人",
"win_condition": "消灭所有狼人",
"team": "好人阵营",
},
"村民": {
"description": "村民",
"ability": "无特殊技能,依靠推理和投票",
"win_condition": "消灭所有狼人",
"team": "好人阵营",
},
}
CHARACTER_TRAITS = {
"刘备": "仁德宽厚,善于团结众人,说话温和有礼",
"关羽": "忠义刚烈,言辞直接,重情重义",
"张飞": "性格豪爽,说话大声直接,容易冲动",
"诸葛亮": "智慧超群,分析透彻,言辞谨慎",
"曹操": "雄才大略,善于权谋,话语犀利",
"司马懿": "深谋远虑,城府极深,言辞含蓄",
}
这个文件把 "游戏规则" 和 "人物人格" 分开维护,便于扩展新身份和新角色。
5.3 角色提示词
来自 AgentScopeDemo/prompt_cn.py:
class ChinesePrompts:
"""中文提示词管理类。"""
@staticmethod
def get_role_prompt(role: str, character: str) -> str:
base_prompt = f"""你是{character},在这场三国狼人杀游戏中扮演{role}。
请严格按照以下JSON格式回复,不要添加任何其他文字:
{{
"reach_agreement": true/false,
"confidence_level": 1-10的数字,
"key_evidence": "你的证据或观点"
}}
角色特点:
"""
if role == "狼人":
return base_prompt + f"""
- 你是狼人阵营,目标是消灭所有好人
- 夜晚可以与其他狼人协商击杀目标
- 白天要隐藏身份,误导好人
- 以{character}的性格说话和行动
"""
elif role == "预言家":
return base_prompt + f"""
- 你是好人阵营的预言家,目标是找出所有狼人
- 每晚可以查验一名玩家的真实身份
- 要合理公布查验结果,引导好人投票
- 以{character}的智慧和洞察力分析局势
"""
else:
return base_prompt + f"""
- 你是好人阵营的村民
- 没有特殊技能,只能通过推理和投票
- 要仔细观察,找出狼人的破绽
- 以{character}的性格参与讨论
"""
这里的提示词有双重约束:
- 游戏身份:狼人、预言家、女巫、村民;
- 人格风格:刘备、曹操、司马懿等。
这正是多智能体角色扮演案例的关键:同样是狼人,曹操和张飞的策略风格应当不同。
5.4 结构化输出约束
来自 AgentScopeDemo/structured_output_cn.py:
from typing import Literal, Optional
from pydantic import BaseModel, Field
from agentscope.agent import AgentBase
class DiscussionModelCN(BaseModel):
"""讨论输出格式。"""
reach_agreement: bool = Field(description="是否已达成一致意见")
confidence_level: int = Field(
description="对当前推理的信心程度(1-10)",
ge=1,
le=10,
)
key_evidence: Optional[str] = Field(
description="支持你观点的关键证据",
default=None,
)
def get_vote_model_cn(agents: list[AgentBase]) -> type[BaseModel]:
"""根据存活玩家动态生成投票模型。"""
class VoteModelCN(BaseModel):
vote: Literal[tuple(_.name for _ in agents)] = Field(
description="你要投票淘汰的玩家姓名",
)
reason: str = Field(description="投票理由,简要说明为什么选择此人")
suspicion_level: int = Field(
description="对被投票者的怀疑程度(1-10)",
ge=1,
le=10,
)
return VoteModelCN
结构化输出解决了游戏类 Agent 的核心问题:模型不能随便说一段自然语言,而必须输出符合规则的决策对象。例如投票目标必须是存活玩家之一,信心等级必须在 1-10。
女巫动作:
class WitchActionModelCN(BaseModel):
"""女巫行动模型。"""
use_antidote: bool = Field(description="是否使用解药救人", default=False)
use_poison: bool = Field(description="是否使用毒药杀人", default=False)
target_name: Optional[str] = Field(
description="目标玩家姓名",
default=None,
)
action_reason: Optional[str] = Field(description="行动理由", default=None)
5.5 游戏主控制器
来自 AgentScopeDemo/main_cn.py,主类维护全局状态:
class ThreeKingdomsWerewolfGame:
"""三国狼人杀游戏主类。"""
def __init__(self):
self.players = {}
self.roles = {}
self.moderator = GameModerator()
self.alive_players = []
self.werewolves = []
self.villagers = []
self.seer = []
self.witch = []
self.hunter = []
self.witch_has_antidote = True
self.witch_has_poison = True
创建玩家:
from agentscope.agent import ReActAgent
from agentscope.model import DashScopeChatModel
from agentscope.formatter import DashScopeMultiAgentFormatter
async def create_player(self, role: str, character: str) -> ReActAgent:
name = get_chinese_name(character)
self.roles[name] = role
agent = ReActAgent(
name=name,
sys_prompt=ChinesePrompts.get_role_prompt(role, character),
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.environ["DASHSCOPE_API_KEY"],
enable_thinking=True,
),
formatter=DashScopeMultiAgentFormatter(),
)
await agent.observe(
await self.moderator.announce(
f"【{name}】你在这场三国狼人杀中扮演{GameRoles.get_role_desc(role)},"
f"你的角色是{character}。{GameRoles.get_role_ability(role)}"
)
)
self.players[name] = agent
return agent
这里可以看到 AgentScope 的编程模型:Agent 可以 observe 消息,也可以在后续阶段被调用产生结构化响应。
5.6 MsgHub:狼人私密讨论
狼人阶段是消息驱动架构最典型的体现:
from agentscope.pipeline import MsgHub, fanout_pipeline
async def werewolf_phase(self, round_num: int):
"""狼人阶段。"""
if not self.werewolves:
return None
async with MsgHub(
self.werewolves,
enable_auto_broadcast=True,
announcement=await self.moderator.announce(
f"狼人们,请讨论今晚的击杀目标。存活玩家:{format_player_list(self.alive_players)}"
),
) as werewolves_hub:
for _ in range(MAX_DISCUSSION_ROUND):
for wolf in self.werewolves:
await wolf(structured_model=DiscussionModelCN)
werewolves_hub.set_auto_broadcast(False)
kill_votes = await fanout_pipeline(
self.werewolves,
msg=await self.moderator.announce("请选择击杀目标"),
structured_model=WerewolfKillModelCN,
enable_gather=False,
)
votes = {}
for i, vote_msg in enumerate(kill_votes):
if vote_msg is not None and getattr(vote_msg, "metadata", None) is not None:
votes[self.werewolves[i].name] = vote_msg.metadata.get("target")
else:
valid_targets = [
p.name
for p in self.alive_players
if p.name not in [w.name for w in self.werewolves]
]
votes[self.werewolves[i].name] = random.choice(valid_targets) if valid_targets else None
killed_player, _ = majority_vote_cn(votes)
return killed_player
关键点:
MsgHub创建一个临时通信空间;enable_auto_broadcast=True表示狼人发言会自动广播给同频道成员;fanout_pipeline并发收集多个 Agent 的决策;- 对无效返回做兜底,保证游戏流程继续。
5.7 工具函数与胜负判断
来自 AgentScopeDemo/utils_cn.py:
from collections import Counter
def majority_vote_cn(votes: dict[str, str]) -> tuple[str, int]:
"""多数投票统计。"""
if not votes:
return "无人", 0
vote_counts = Counter(votes.values())
most_voted = vote_counts.most_common(1)[0]
return most_voted[0], most_voted[1]
def check_winning_cn(alive_players, roles: dict[str, str]) -> str | None:
"""检查游戏胜利条件。"""
alive_roles = [roles.get(p.name, "村民") for p in alive_players]
werewolf_count = alive_roles.count("狼人")
villager_count = len(alive_roles) - werewolf_count
if werewolf_count == 0:
return "好人阵营胜利!所有狼人已被淘汰!"
if werewolf_count >= villager_count:
return "狼人阵营胜利!狼人数量已达到或超过好人!"
return None
这些函数把游戏规则从 Agent 推理中抽离出来,避免让模型自己裁定胜负。重要规则应该由确定性代码执行,而不是交给 LLM 猜。
5.8 AgentScope 适用性
优势:
- 消息驱动天然适合多智能体通信;
- 异步和并发能力强;
MsgHub支持广播、私聊、组播等通信模式;- 结构化输出能把规则约束落到代码层;
- 容错和分布式能力适合生产级系统。
局限:
- 学习成本高于简单对话框架;
- 对异步、消息、分布式概念有要求;
- 对简单原型可能显得重;
- 生态相对新,需要更多工程沉淀。
适合:
- 多人游戏模拟;
- 分布式 Agent 服务;
- 高并发客服 / 任务调度;
- 长期运行的多智能体系统。
6. CAMEL:角色扮演的电子书创作
6.1 核心机制
CAMEL 的核心是 RolePlaying:通过为两个智能体设定互补角色和共同任务,让它们在 "引导性提示" 下自主协作。
两个关键概念:
- 角色扮演:例如 "心理学家" 和 "作家";
- 引导性提示:在对话开始前,为双方注入任务目标、行为约束、协作协议和终止标志。
与 AutoGen 相比,CAMEL 更轻量;与 LangGraph 相比,CAMEL 更依赖角色和提示词,而不是显式流程控制。
6.2 电子书创作任务
来自 CAMEL/DigitalBookWriting.py:
from colorama import Fore
from camel.societies import RolePlaying
from camel.utils import print_text_animated
from camel.models import ModelFactory
from camel.types import ModelPlatformType
from dotenv import load_dotenv
import os
load_dotenv()
LLM_API_KEY = os.getenv("LLM_API_KEY")
LLM_BASE_URL = os.getenv("LLM_BASE_URL")
LLM_MODEL = os.getenv("LLM_MODEL")
model = ModelFactory.create(
model_platform=ModelPlatformType.QWEN,
model_type=LLM_MODEL,
url=LLM_BASE_URL,
api_key=LLM_API_KEY,
)
任务定义:
task_prompt = """
创作一本关于"拖延症心理学"的短篇电子书,目标读者是对心理学感兴趣的普通大众。
要求:
1. 内容科学严谨,基于实证研究
2. 语言通俗易懂,避免过多专业术语
3. 包含实用的改善建议和案例分析
4. 篇幅控制在8000-10000字
5. 结构清晰,包含引言、核心章节和总结
"""
这个任务明确了产物、读者、风格、长度和结构,是 CAMEL 能稳定协作的前提。如果任务过泛,两个角色会很容易发散。
6.3 RolePlaying 会话
role_play_session = RolePlaying(
assistant_role_name="心理学家",
user_role_name="作家",
task_prompt=task_prompt,
model=model,
)
chat_turn_limit, n = 30, 0
input_msg = role_play_session.init_chat()
while n < chat_turn_limit:
n += 1
assistant_response, user_response = role_play_session.step(input_msg)
print_text_animated(Fore.BLUE + f"作家:\n\n{user_response.msg.content}\n")
print_text_animated(Fore.GREEN + f"心理学家:\n\n{assistant_response.msg.content}\n")
if "CAMEL_TASK_DONE" in user_response.msg.content:
print(Fore.MAGENTA + "电子书创作完成!")
break
input_msg = assistant_response.msg
这里的对话结构是:
作家:提出章节结构、写作要求、修订请求
心理学家:提供专业知识、研究依据、概念解释
作家:把知识转化为大众化文本
心理学家:继续补充和校验
6.4 协作阶段
电子书案例可以拆成四个阶段:
| 阶段 | 主要行为 |
|---|---|
| 框架搭建 | 作家提出目录,心理学家补齐核心理论模块 |
| 内容生成 | 心理学家提供专业知识,作家转译成通俗语言 |
| 迭代优化 | 作家打磨可读性,心理学家检查科学准确性 |
| 总结收尾 | 共同完成实用建议、案例和结论 |
CAMEL 的亮点在于:开发者没有显式写流程图,协作行为从角色和任务中 "涌现" 出来。
6.5 CAMEL 适用性
优势:
- API 简洁,启动成本低;
- 角色扮演直观,适合模拟专家协作;
- 适合创作、研究、头脑风暴、跨领域协同;
- 对话可以自然推进,不需要开发者编写复杂状态机。
局限:
- 高度依赖初始提示;
- 调试不佳时难判断是角色、任务还是协议问题;
- 双智能体很强,但大规模协作需要更复杂模块;
- 对严格流程和审计要求不如 LangGraph。
6.6 CAMEL Workforce 补充
习题要求查阅 CAMEL 最新 Workforce。根据 CAMEL 官方文档,Workforce 是 CAMEL-AI 的多智能体协作引擎,用于组装、管理和扩展多个专门 Agent 来处理单 Agent 难以完成的复杂任务。官方文档说明其核心包括:
Workforce:中心编排器,管理多智能体任务生命周期;SingleAgentWorker:由一个ChatAgent加系统提示和工具组成的 worker;RolePlayingWorker:内部使用两个 Agent 的RolePlaying会话,适合辩论、设计、探索;- 任务生命周期:分解、分配、执行、完成、失败恢复;
- 可选能力:共享记忆、结构化输出处理、callbacks、人类在环。
官方示例的典型结构:
from camel.societies.workforce import Workforce
from camel.agents import ChatAgent
workforce = Workforce("My Research Team")
general_agent = ChatAgent(
system_message="You are a helpful research assistant."
)
workforce.add_single_agent_worker(
description="A worker for general research tasks",
worker=general_agent,
)
这说明 CAMEL 已经不只是最初的 "双智能体 RolePlaying",而是在向多 worker 编排系统发展。
资料来源:CAMEL 官方 Workforce 文档,https://docs.camel-ai.org/key_modules/workforce。
7. LangGraph:状态机与图工作流
7.1 核心机制
LangGraph 把智能体流程建模为状态机和有向图:
State:全局状态,记录消息、搜索词、结果、最终答案等;Node:计算步骤,接收状态,返回状态增量;Edge:节点之间的跳转;Conditional Edge:根据状态动态选择下一步;Checkpointer:保存会话状态,支持多轮和恢复。
与对话式框架不同,LangGraph 要求开发者显式定义流程,因此更适合对可靠性、可审计性要求高的系统。
7.2 状态结构
来自 Langgraph/Dialogue_System.py:
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class SearchState(TypedDict):
messages: Annotated[list, add_messages]
user_query: str
search_query: str
search_results: str
final_answer: str
step: str
messages 使用 add_messages,表示新消息会追加到历史,而不是覆盖。这是 LangGraph 状态管理的一个关键点。
7.3 初始化模型和 Tavily
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from tavily import TavilyClient
load_dotenv()
llm = ChatOpenAI(
model=os.getenv("LLM_MODEL_ID", "gpt-4o-mini"),
api_key=os.getenv("LLM_API_KEY"),
base_url=os.getenv("LLM_BASE_URL", "https://api.openai.com/v1"),
temperature=0.7,
)
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
这个示例结合了 LLM 和真实搜索工具 Tavily,流程是 "理解问题 -> 搜索 -> 生成答案"。
7.4 节点 1:理解查询
from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
def understand_query_node(state: SearchState) -> SearchState:
"""理解用户查询并生成搜索关键词。"""
user_message = ""
for msg in reversed(state["messages"]):
if isinstance(msg, HumanMessage):
user_message = msg.content
break
understand_prompt = f"""分析用户的查询:"{user_message}"
请完成两个任务:
1. 简洁总结用户想要了解什么
2. 生成最适合搜索的关键词
格式:
理解:[用户需求总结]
搜索词:[最佳搜索关键词]"""
response = llm.invoke([SystemMessage(content=understand_prompt)])
response_text = response.content
search_query = user_message
if "搜索词:" in response_text:
search_query = response_text.split("搜索词:")[1].strip()
elif "搜索关键词:" in response_text:
search_query = response_text.split("搜索关键词:")[1].strip()
return {
"user_query": response.content,
"search_query": search_query,
"step": "understood",
"messages": [AIMessage(content=f"我理解您的需求:{response.content}")],
}
节点函数只做一件事:读状态,处理,返回状态更新。
7.5 节点 2:搜索
def tavily_search_node(state: SearchState) -> SearchState:
"""使用 Tavily API 搜索信息。"""
search_query = state["search_query"]
try:
response = tavily_client.search(
query=search_query,
search_depth="basic",
include_answer=True,
include_raw_content=False,
max_results=5,
)
search_results = ""
if response.get("answer"):
search_results = f"综合答案:\n{response['answer']}\n\n"
if response.get("results"):
search_results += "相关信息:\n"
for i, result in enumerate(response["results"][:3], 1):
title = result.get("title", "")
content = result.get("content", "")
url = result.get("url", "")
search_results += f"{i}. {title}\n{content}\n来源:{url}\n\n"
if not search_results:
search_results = "抱歉,没有找到相关信息。"
return {
"search_results": search_results,
"step": "searched",
"messages": [AIMessage(content="搜索完成,正在整理答案。")],
}
except Exception as e:
return {
"search_results": f"搜索失败:{e}",
"step": "search_failed",
"messages": [AIMessage(content="搜索遇到问题,将基于已有知识回答。")],
}
这里通过 step 字段把成功和失败写入状态,为后续分支或回退留出空间。
7.6 节点 3:回答
def generate_answer_node(state: SearchState) -> SearchState:
"""基于搜索结果生成最终答案。"""
if state["step"] == "search_failed":
fallback_prompt = f"""搜索 API 暂时不可用,请基于已有知识回答用户问题:
用户问题:{state['user_query']}
请提供有用回答,并说明这是基于已有知识的回答。"""
response = llm.invoke([SystemMessage(content=fallback_prompt)])
return {
"final_answer": response.content,
"step": "completed",
"messages": [AIMessage(content=response.content)],
}
answer_prompt = f"""基于以下搜索结果为用户提供完整、准确的答案:
用户问题:{state['user_query']}
搜索结果:
{state['search_results']}
要求:
1. 综合搜索结果,提供准确、有用的回答
2. 如果是技术问题,提供具体方案或代码
3. 引用重要信息来源
4. 回答结构清晰
5. 如果搜索结果不完整,请说明并给出补充建议"""
response = llm.invoke([SystemMessage(content=answer_prompt)])
return {
"final_answer": response.content,
"step": "completed",
"messages": [AIMessage(content=response.content)],
}
7.7 构建图
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
def create_search_assistant():
workflow = StateGraph(SearchState)
workflow.add_node("understand", understand_query_node)
workflow.add_node("search", tavily_search_node)
workflow.add_node("answer", generate_answer_node)
workflow.add_edge(START, "understand")
workflow.add_edge("understand", "search")
workflow.add_edge("search", "answer")
workflow.add_edge("answer", END)
memory = InMemorySaver()
app = workflow.compile(checkpointer=memory)
return app
图结构:
START
↓
understand
↓
search
↓
answer
↓
END
7.8 运行工作流
initial_state = {
"messages": [HumanMessage(content=user_input)],
"user_query": "",
"search_query": "",
"search_results": "",
"final_answer": "",
"step": "start",
}
config = {"configurable": {"thread_id": f"search-session-{session_count}"}}
async for output in app.astream(initial_state, config=config):
for node_name, node_output in output.items():
if "messages" in node_output and node_output["messages"]:
latest_message = node_output["messages"][-1]
if isinstance(latest_message, AIMessage):
print(node_name, latest_message.content)
thread_id 用来区分会话,astream 可以流式观察每个节点输出。
7.9 LangGraph 适用性
优势:
- 流程显式,可控、可测、可审计;
- 原生支持循环和条件分支;
- 状态结构清晰,便于调试;
- 适合可靠性要求高的生产流程;
- 可以自然实现 "生成 - 检查 - 修复" 类闭环。
局限:
- 前期样板代码多;
- 对简单聊天或创作任务可能偏重;
- 开发者必须明确建模状态和跳转;
- 节点内部错误、状态污染、条件边判断错误都需要系统调试。
适合:
- 金融审批;
- 工单处理;
- 搜索问答;
- 代码生成 - 测试 - 修复;
- 论文写作 - 审阅 - 修改;
- 任何需要明确流程和审计的 Agent 应用。
8. 四个框架横向选型
| 维度 | AutoGen | AgentScope | CAMEL | LangGraph |
|---|---|---|---|---|
| 协作方式 | 群聊式对话 | 消息驱动网络 | 角色扮演 | 图工作流 |
| 控制方式 | 中等,靠群聊规则 | 中等偏强,靠消息和编排 | 较弱,靠提示词 | 最强,显式节点和边 |
| 工程化 | 中等 | 强 | 中等 | 强 |
| 上手成本 | 中等 | 较高 | 低到中等 | 中等偏高 |
| 适合规模 | 小到中型团队协作 | 大规模多智能体 | 双智能体或小团队深度协作 | 复杂流程 |
| 典型风险 | 对话偏移、循环 | 过度工程化 | 提示词敏感 | 样板代码多 |
选型建议:
- 想模拟一个多人团队开会协作:选
AutoGen; - 想构建长期运行、高并发、多 Agent 系统:选
AgentScope; - 想让两个专家深度创作或研究:选
CAMEL; - 想做强流程、强审计、强控制:选
LangGraph; - 任务极简单、流程固定、没有多 Agent 需求:从零写脚本即可。
9. 本章习题解析
习题 1:四个框架对比与设计哲学
题目要求选择两个框架,从协作模式、控制方式、适用场景对比,并解释 "涌现式协作" 和 "显式控制"。
以 AutoGen 和 LangGraph 为例:
| 维度 | AutoGen | LangGraph |
|---|---|---|
| 协作模式 | 多 Agent 群聊,角色轮流发言 | 节点按图执行,状态在节点间流转 |
| 控制方式 | 通过发言顺序、终止条件、角色提示控制 | 通过状态、节点、边、条件边控制 |
| 适用场景 | 需求评审、代码开发、多人讨论、方案协作 | 审批流程、搜索问答、测试修复、反思循环 |
| 调试方式 | 查看对话历史 | 查看状态变化和节点输出 |
| 风险 | 对话跑偏、不稳定 | 前期设计复杂、灵活性较低 |
"涌现式协作" 指开发者只定义角色、目标和基本规则,让复杂协作行为从 Agent 对话中自然产生。AutoGen 和 CAMEL 更接近这种模式。
"显式控制" 指开发者明确规定每一步流程、状态字段和跳转条件。LangGraph 更接近这种模式。
核心取舍:
涌现式协作: 更自然、更灵活,但难预测
显式控制: 更可靠、更可审计,但开发成本更高
习题 2:AutoGen 动态回退、测试工程师和对话监控
动态回退机制
当前 RoundRobinGroupChat 是固定顺序:
ProductManager -> Engineer -> CodeReviewer -> UserProxy
如果代码审查发现需求理解错误,应该回退到产品经理重新澄清:
ProductManager -> Engineer -> CodeReviewer
↓
发现需求偏差
↓
ProductManager 重新审核
实现思路:
- 简单方案:保留轮询,但要求审查员输出 "RETURN_TO_PM";
- 框架检测到该标志后,将下一轮任务描述明确交给产品经理;
- 更强方案:使用 Selector/Router 类团队,让路由器根据消息选择下一个发言者。
伪代码:
def route_next_agent(last_message: str) -> str:
if "RETURN_TO_PM" in last_message:
return "ProductManager"
if "请工程师开始实现" in last_message:
return "Engineer"
if "请代码审查员检查" in last_message:
return "CodeReviewer"
if "代码审查完成" in last_message:
return "QAEngineer"
return "UserProxy"
添加测试工程师
测试工程师系统消息:
def create_qa_engineer(model_client):
system_message = """你是一位严谨的测试工程师,负责验证工程师提交的代码是否满足需求。
你的职责:
1. 阅读产品需求和工程师代码
2. 设计测试用例,包括正常路径、异常路径和边界情况
3. 检查依赖、运行方式和错误处理
4. 如果代码可执行,给出测试步骤和预期结果
5. 如果发现问题,明确指出失败原因和修复建议
输出格式:
1. 测试范围
2. 测试用例列表
3. 发现的问题
4. 是否通过验收
如果测试通过,请回复"测试通过,请用户代理最终确认"。
如果测试失败,请回复"测试失败,请工程师修复"。"""
return AssistantAgent(
name="QAEngineer",
model_client=model_client,
system_message=system_message,
)
团队成员调整:
team_chat = RoundRobinGroupChat(
participants=[
product_manager,
engineer,
code_reviewer,
qa_engineer,
user_proxy,
],
termination_condition=TextMentionTermination("TERMINATE"),
max_turns=30,
)
对话质量监控
监控指标:
- 是否连续多轮没有新信息;
- 是否偏离原始任务;
- 是否重复同一观点;
- 是否超过最大轮次;
- 是否出现无效工具调用;
- 是否未按角色职责发言;
- 是否没有输出可执行产物。
干预策略:
轻度异常: 给当前 Agent 注入提醒
中度异常: 路由到产品经理重新澄清
严重异常: 终止流程并请求人工介入
伪代码:
class ConversationMonitor:
def __init__(self):
self.no_progress_turns = 0
def evaluate(self, history: list[str]) -> str:
if len(history) > 30:
return "terminate"
if self._is_repeating(history[-3:]):
return "intervene"
if self._is_off_topic(history[-1]):
return "redirect"
return "continue"
def _is_repeating(self, recent: list[str]) -> bool:
return len(set(recent)) <= 1
def _is_off_topic(self, message: str) -> bool:
keywords = ["比特币", "价格", "Streamlit", "API", "代码"]
return not any(keyword in message for keyword in keywords)
习题 3:AgentScope 消息驱动、猎人模型和分布式挑战
消息驱动相比函数调用的优势
传统函数调用:
A 调用 B -> 等待 B 返回 -> A 继续
消息驱动:
A 发送消息 -> 消息中心路由 -> B 接收处理 -> 返回消息
优势:
- 发送方和接收方解耦;
- 支持异步和并发;
- 支持广播、组播、点对点;
- 容易记录和追踪消息;
- 智能体可以分布在不同进程或服务器;
- 某个智能体失败时,系统可重试或降级。
特别有价值的场景:
- 多智能体游戏;
- 群体辩论;
- 分布式客服;
- 并行任务处理;
- 长时间运行的模拟系统。
设计猎人结构化输出
本地代码已经有 get_hunter_model_cn,可以进一步增强验证:
from typing import Optional
from pydantic import BaseModel, Field, model_validator
from agentscope.agent import AgentBase
def get_hunter_model_cn(agents: list[AgentBase]) -> type[BaseModel]:
"""猎人开枪行为模型。"""
valid_names = {agent.name for agent in agents}
class HunterModelCN(BaseModel):
shoot: bool = Field(description="是否使用开枪技能")
target: Optional[str] = Field(
description="开枪目标玩家姓名;如果不使用技能则为空",
default=None,
)
shoot_reason: Optional[str] = Field(
description="开枪理由;使用技能时必须说明",
default=None,
)
confidence_level: int = Field(
description="对目标是狼人的信心程度(1-10)",
ge=1,
le=10,
)
@model_validator(mode="after")
def validate_target(self):
if self.shoot and not self.target:
raise ValueError("选择开枪时必须指定 target。")
if self.shoot and self.target not in valid_names:
raise ValueError(f"target 必须是存活玩家之一:{sorted(valid_names)}")
if self.shoot and not self.shoot_reason:
raise ValueError("选择开枪时必须说明 shoot_reason。")
if not self.shoot and self.target:
raise ValueError("不使用技能时 target 应为空。")
return self
return HunterModelCN
分布式部署挑战
实时狼人杀分布式部署会遇到:
- 消息顺序问题:不同节点网络延迟不同;
- 状态一致性问题:玩家生死、投票结果必须一致;
- 并发冲突:多个动作同时更新状态;
- 失败恢复:某个 Agent 节点宕机;
- 时钟同步:夜晚 / 白天阶段切换必须统一;
- 幂等性:重复消息不能导致重复投票或重复击杀。
解决方案:
| 挑战 | 方案 |
|---|---|
| 消息顺序 | 为消息加 round_id、phase_id、递增序号 |
| 状态一致性 | 主持人节点作为权威状态源 |
| 并发冲突 | 投票收集后统一结算,不直接改状态 |
| 节点失败 | 超时默认弃权或使用 fallback 响应 |
| 重复消息 | 使用 message_id 做幂等去重 |
| 可追溯 | 所有消息写入事件日志 |
习题 4:CAMEL 冲突解决与 Workforce 对比 AutoGen
任务完成冲突处理
如果一个智能体认为应终止,另一个认为不应终止,可以加入 "仲裁机制":
Writer/Psychologist
↓
Completion Judge
↓
判断是否满足任务验收标准
↓
继续协作 or 终止
验收标准可结构化:
{
"content_complete": true,
"scientific_accuracy": 8,
"readability": 8,
"has_cases": true,
"has_practical_advice": true,
"should_stop": true,
"missing_parts": []
}
伪代码:
def should_stop_by_judge(writer_msg: str, psychologist_msg: str, task_spec: str) -> bool:
judge_prompt = f"""
请判断当前电子书协作是否可以终止。
任务要求:
{task_spec}
作家最新意见:
{writer_msg}
心理学家最新意见:
{psychologist_msg}
请输出 JSON:
{{
"should_stop": true/false,
"reason": "...",
"missing_parts": ["..."]
}}
"""
result = judge_llm(judge_prompt)
return result["should_stop"]
更稳妥的终止条件:
- 双方都输出完成标志;
- 或仲裁 Agent 判定满足验收标准;
- 或达到最大轮数后输出未完成清单;
- 或人类确认终止。
CAMEL Workforce 与 AutoGen 群聊的区别
根据 CAMEL 官方 Workforce 文档,Workforce 是中心编排器,负责多智能体任务的分解、分配、执行、完成和失败恢复。它可以添加 SingleAgentWorker 和 RolePlayingWorker,并支持结构化输出、callbacks、共享记忆和人类在环。
与 AutoGen 群聊对比:
| 维度 | CAMEL Workforce | AutoGen 群聊 |
|---|---|---|
| 组织方式 | 任务编排器 + workers | 多 Agent 消息对话 |
| 核心对象 | Workforce、Worker、Task |
AssistantAgent、UserProxyAgent、GroupChat |
| 协作逻辑 | 分解任务,分配给合适 worker | 让角色在群聊中轮流或路由发言 |
| 适合任务 | 多子任务、多 worker、可分配工作 | 讨论式协作、评审、团队模拟 |
| 控制感 | 更偏任务调度 | 更偏对话推进 |
| 冲突处理 | 可通过 coordinator /recovery | 依赖群聊规则或外部监控 |
简单理解:
AutoGen 像会议室:多个角色围绕同一话题交流。
CAMEL Workforce 像项目经理:拆任务、分派 worker、收集结果、处理失败。
习题 5:LangGraph 图结构、反思节点与复杂循环
理解 - 搜索 - 回答图结构
当前案例:
START
↓
understand
↓
search
↓
answer
↓
END
状态字段:
messages 对话历史
user_query 用户需求总结
search_query 优化后的搜索词
search_results 搜索结果
final_answer 最终答案
step 当前步骤
添加反思节点
扩展图:
START
↓
understand
↓
search
↓
answer
↓
reflect
├── good -> END
├── weak_search -> search
└── weak_answer -> answer
反思节点:
def reflect_answer_node(state: SearchState) -> SearchState:
answer = state["final_answer"]
search_results = state["search_results"]
if len(answer) < 300:
quality = "weak_answer"
elif "来源:" not in search_results and "搜索失败" not in search_results:
quality = "weak_search"
else:
quality = "good"
return {
"step": quality,
"messages": [AIMessage(content=f"答案质量检查结果:{quality}")],
}
def route_after_reflection(state: SearchState) -> str:
if state["step"] == "good":
return END
if state["step"] == "weak_search":
return "search"
return "answer"
图注册:
workflow.add_node("reflect", reflect_answer_node)
workflow.add_edge("answer", "reflect")
workflow.add_conditional_edges(
"reflect",
route_after_reflection,
{
"search": "search",
"answer": "answer",
END: END,
},
)
生产系统还需要增加最大循环次数:
class SearchState(TypedDict):
messages: Annotated[list, add_messages]
user_query: str
search_query: str
search_results: str
final_answer: str
step: str
retry_count: int
代码生成 - 测试 - 修复循环
START
↓
generate_code
↓
run_tests
↓
analyze_result
├── tests_pass -> END
└── tests_fail -> fix_code -> run_tests
节点职责:
| 节点 | 功能 |
|---|---|
generate_code |
根据需求生成初版代码 |
run_tests |
执行单元测试或静态检查 |
analyze_result |
判断测试是否通过,提取错误信息 |
fix_code |
根据错误修复代码 |
END |
输出最终代码和测试结果 |
这个场景非常适合 LangGraph,因为它天然需要循环、条件分支、状态追踪和审计。
习题 6:三个产品的框架选型
应用 A:高并发智能客服系统
需求:
- 每秒 1000+ 请求;
- 响应时间低于 2 秒;
- 7x24 稳定运行;
- 支持水平扩展。
推荐:AgentScope,部分模块可自研。
理由:
- 消息驱动和异步架构适合高并发;
- 分布式部署能力更贴近生产环境;
- 消息可追踪,利于监控和排障;
- 客服系统需要任务路由、状态管理、失败重试和容错;
- 简单群聊式框架不适合 1000+ QPS 的线上客服主链路。
架构建议:
API Gateway
↓
Session Router
↓
AgentScope MsgHub
↓
Intent Agent / Policy Agent / Tool Agent / Reply Agent
↓
Human Handoff
注意:如果延迟硬性小于 2 秒,不能让每个请求都走复杂多 Agent。应把高频简单问题用检索、规则、小模型处理,复杂问题再进入多 Agent。
应用 B:科研论文辅助写作平台
需求:
- 研究员智能体和写作智能体深度协作;
- 文献综述、实验设计、数据分析、论文撰写;
- 多轮讨论,自主推进。
推荐:CAMEL 或 CAMEL Workforce。
理由:
- 研究员和写作者是典型互补角色;
- 任务需要深度讨论和知识转译;
- CAMEL 的 RolePlaying 很适合双专家协作;
- 如果后续扩展统计专家、审稿人、引用检查员,可升级到 Workforce。
架构建议:
RolePlaying:
User Role: 研究员
Assistant Role: 写作智能体
Workforce 扩展:
Literature Worker
Method Worker
Data Analysis Worker
Writing Worker
Reviewer Worker
如果平台需要严格流程和版本审计,可以在外层用 LangGraph 管控阶段,内部单个节点调用 CAMEL 协作。
应用 C:金融风控审批系统
需求:
- 资料审核 -> 风险评估 -> 额度计算 -> 合规检查 -> 人工复核 -> 最终决策;
- 明确判断标准和分支逻辑;
- 要求可追溯、可审计。
推荐:LangGraph。
理由:
- 金融审批是强流程、强合规场景;
- 每一步都要显式记录输入、输出、判断依据;
- 条件边适合表达风险等级、额度区间、人工复核;
- 状态机便于审计和回放;
- 对话式涌现不适合直接控制金融决策主流程。
图结构:
START
↓
document_check
├── missing_docs -> request_more_info -> END
↓
risk_assessment
├── high_risk -> manual_review
↓
limit_calculation
↓
compliance_check
├── compliance_fail -> reject
↓
manual_review
↓
final_decision
↓
END
关键工程要求:
- 每个节点输出结构化 JSON;
- 所有状态写入审计日志;
- 高风险节点必须人工复核;
- 模型只能给建议,最终决策需规则和人工兜底;
- 版本化保存规则、提示词和模型参数。
10. 总结
第六章的重点不是学会某一个框架的 API,而是建立框架选型判断:
- `AutoGen` 用对话组织协作,适合多角色团队任务;
- `AgentScope` 用消息驱动和工程化能力支撑生产级多智能体系统;
- `CAMEL` 用角色扮演和引导性提示激发深度协作;
- `LangGraph` 用状态机和图结构提供强控制、可追溯和循环能力。
真实项目里,框架往往可以组合使用:用 `LangGraph` 管主流程,用 `AutoGen` 或 `CAMEL` 完成局部协作,用 `AgentScope` 承载高并发消息和分布式运行。关键是先判断任务需要"涌现式协作"还是"显式控制",再决定框架,而不是先选框架再硬套任务。

