Hello-Agents 第二部分-第六章:框架开发实践

作者:逆境不可逃

技术永无止境

希望我的内容可以帮助到你!!!!!


大家吼 ! 我是 逆境不可逃 今天给大家带来文章**《Hello-Agents 第二部分-第四章总结:智能体经典范式构建》.**

Hello-Agents 官方地址: datawhalechina/hello-agents: 📚 《从零开始构建智能体》------从零开始的智能体原理与实践教程

1. 本章主线

第四章从零实现了 ReActPlan-and-SolveReflection,重点是理解 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:四个框架对比与设计哲学

题目要求选择两个框架,从协作模式、控制方式、适用场景对比,并解释 "涌现式协作" 和 "显式控制"。

AutoGenLangGraph 为例:

维度 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_idphase_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 是中心编排器,负责多智能体任务的分解、分配、执行、完成和失败恢复。它可以添加 SingleAgentWorkerRolePlayingWorker,并支持结构化输出、callbacks、共享记忆和人类在环。

与 AutoGen 群聊对比:

维度 CAMEL Workforce AutoGen 群聊
组织方式 任务编排器 + workers 多 Agent 消息对话
核心对象 WorkforceWorkerTask AssistantAgentUserProxyAgentGroupChat
协作逻辑 分解任务,分配给合适 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:科研论文辅助写作平台

需求:

  • 研究员智能体和写作智能体深度协作;
  • 文献综述、实验设计、数据分析、论文撰写;
  • 多轮讨论,自主推进。

推荐:CAMELCAMEL 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` 承载高并发消息和分布式运行。关键是先判断任务需要"涌现式协作"还是"显式控制",再决定框架,而不是先选框架再硬套任务。

相关推荐
效能革命笔记6 小时前
DevOps工具链选型推荐:聚焦本土适配与安全可控
人工智能·安全·devops
怪祝浙6 小时前
AI学习-LangChain实战-多模态识别agent
人工智能·学习·langchain
火星资讯6 小时前
优艾智合荣获“国际具身智能技术突破奖”
人工智能
小袁说公考6 小时前
2026公考培训机构硬核测评 | 师资、退费、管理三大核心指标横向对比
人工智能·经验分享·笔记
YangYang9YangYan6 小时前
2026学习数据分析对产品经理的价值
学习·数据分析·产品经理
淞綰6 小时前
c语言的练习-字符串的练习-寻找最长连续字符以及出现次数
c语言·数据结构·学习·算法·c语言的练习
企服AI产品测评局6 小时前
2026委外加工管控实测:AI工具全流程跟踪能力横向对比与实在Agent深度测评
人工智能·ai·chatgpt
我不是8神6 小时前
面试题:Gorutine泄露的条件有哪些?
java·开发语言
爱好物理的一名程序员XiaoK6 小时前
搭建网站时遇到的只显示空白界面
java