深入认识 Agent —— 智能体开发框架

目录:
用 Copilot 学 Agent ------ AI 编程的新范式
深入认识 Agent ------ 实现你自己的 Agent
深入认识 Agent ------ 智能体开发框架


前文通过编码实现 Agent demo 的方式介绍了 ReAct、Plan-and-Solve、Reflection 三种模式的智能体,其主要目的是为了更好地理解 Agent 的工作方式。如果要投入到工程实践中去使用,从零开始写代码显然是不推荐的,更好的方式是使用框架。

主流 Agent 框架

本节将介绍 LangChainLangGraphAutoGenMicrosoft Agent Framework 等 LLM 应用框架。

LangChain

最早一批 LLM 应用框架中,比较有代表性的是 LangChain。

LangChain 旨在将"调用模型生成文本" 的简单模式扩展为构建具备推理、工具调用和数据整合能力的智能系统。它通过一套统一的抽象层,把 模型、提示词(Prompt)、外部工具(Tools)、记忆(Memory)和数据检索(Retriever)等组件组合在一起,使开发者能够以模块化的方式构建复杂的 AI 应用。

LangChain 是最早一批将 "Agent" 概念工程化的框架之一,它通过 Agent 引入了动态决策能力,让模型根据上下文自主选择是否调用工具、拆解问题并逐步完成任务。

在实际工程中, LangChain 常被用作快速原型开发和 AI 应用架构的基础层。不过由于其抽象层较多,在复杂系统中也可能带来可控性和调试上的挑战。

下面是一个简单的示例:

python 复制代码
# export OPENAI_API_KEY="your-api-key"

import os
from langchain.agents import create_agent

def get_weather(city: str) -> str:
    """Get weather for a given city."""
    return f"It's always sunny in {city}!"

agent = create_agent(
    model="azure_openai:gpt-5.4",
    tools=[get_weather],
    system_prompt="You are a helpful assistant",
    azure_deployment=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]}
)
print(result["messages"][-1].content_blocks)

AutoGen

AutoGen 是 Microsoft 推出的一个 Multi-Agent 框架,其核心思想是:将复杂任务拆解为多个子任务,让具备不同职责的 Agent 通过对话驱动的方式协作完成。

AutoGen 从设计上分为以下几层:

  • Core API,作为框架的底层基础,封装了与大语言模型交互、消息传递等核心功能,保证了框架的稳定性和未来扩展性。
  • AgentChat API,构建于 Core 之上,提供了用于开发对话式智能体应用的高级接口,简化了多智能体应用的开发流程。
  • Extensions API,允许通过各种第一方和第三方的扩展来增强框架的能力。

下面通过一个实际的例子来演示 AutoGen 的基本使用。

假设任务目标为:编写一个应用程序实时显示比特币当前价格。

为了实现这一任务,需要定义四个职责分明的智能体角色:产品经理,工程师,代码评审员,用户代理。

复制代码
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
    )


def create_engineer(model_client):
    """创建软件工程师智能体"""
    system_message = """
    你是一位资深的软件工程师,擅长 Python 开发和 Web 应用构建。

    你的技术专长包括:
    1. **Python 编程**:熟练掌握 Python 语法和最佳实践
    2. **Web 开发**:精通 Streamlit、Flask、Django 等框架
    3. **API 集成**:有丰富的第三方 API 集成经验
    4. **错误处理**:注重代码的健壮性和异常处理
    
    当收到开发任务时,请:
    1. 仔细分析技术需求
    2. 选择合适的技术方案
    3. 编写完整的代码实现
    4. 添加必要的注释和说明
    5. 考虑边界情况和异常处理
    
    请提供完整的可运行代码,并在完成后说"请代码审查员检查"。
    """

    return AssistantAgent(
        name="Engineer",
        model_client=model_client,
        system_message=system_message,
    )


def create_code_reviewer(model_client):
    """创建代码审查员智能体"""
    system_message = """
    你是一位经验丰富的代码审查专家,专注于代码质量和最佳实践。

    你的审查重点包括:
    1. **代码质量**:检查代码的可读性、可维护性和性能
    2. **安全性**:识别潜在的安全漏洞和风险点,尤其注意网站不能暴露到公网或内网
    3. **最佳实践**:确保代码遵循行业标准和最佳实践
    4. **错误处理**:验证异常处理的完整性和合理性
    
    审查流程:
    1. 仔细阅读和理解代码逻辑
    2. 检查代码规范和最佳实践
    3. 识别潜在问题和改进点
    4. 提供具体的修改建议
    5. 评估代码的整体质量
    
    请提供具体的审查意见,完成后说"代码审查完成,请用户代理测试"。
    """

    return AssistantAgent(
        name="CodeReviewer",
        model_client=model_client,
        system_message=system_message,
    )


def create_user_proxy():
    """创建用户代理智能体"""
    system_message = """
    用户代理,负责以下职责:
    1. 代表用户提出开发需求
    2. 执行最终的代码实现
    3. 验证功能是否符合预期
    4. 提供用户反馈和建议
        
    完成测试后请回复 TERMINATE。
    """
    return UserProxyAgent(
        name="UserProxyAgent",
        description=system_message
    )

其中用到了两类智能体:

  • AssistantAgent 是主要的任务解决者,本质上是一个封装好的大语言模型。通过系统消息,可以为其赋予特定的"专家 assistant"角色。
  • UserProxyAgent,这是 AutoGen 中功能独特的组件。它扮演着双重角色:既是用户代理,负责发起任务和传达意图;又是执行器,可以执行代码或工具并将执行结果反馈给其他 Agent。

AsistentAgent 负责思考和推理,UserProxyAgent 负责执行和交互。

为了让以上智能体进行协作,需要一个机制来协调对话流程:

python 复制代码
async def run_development_team():
    """运行软件开发团队协作"""

    print("🔧正在初始化模型客户端")
    model_client = create_azureopenai_model_client()

    print("👥正在创建智能体")
    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 应用
    - 界面简洁美观,用户友好
    - 添加适当的错误处理和加载状态
    
    请团队协作完成这个任务,从需求分析到最终实现。
    """

    # 执行团队协作
    print("🚀启动 AutoGen 软件开发团队协作...\n" + "=" * 60)
    result = await Console(team_chat.run_stream(task=task))

    print("\n" + "=" * 60 + "\n✅团队协作完成")

    return result

这里用到了 RoundRobinGroupChat, 这是一种明确的、顺序化的对话协调机制。工作流如下:

  1. 创建 RoundRobinGroupChat 实例,将所有参与协作的智能体加入其中。
  2. 任务开始后,群聊会按照预设的顺序,激活相应的智能体。
  3. 被激活的智能体根据当前的对话上下文进行响应。
  4. 群聊将新的回复加入到对话历史,并激活下一个智能体。
  5. 这个过程会持续进行直到达到最大对话轮次或满足预设的终止条件。

通过这种方式,AutoGen 将复杂的协作关系简化成了一个流程清晰、易于管理的自动化圆桌会议。

接下来需要创建模型客户端,还是以 Azure OpenAI 为例:

python 复制代码
import os
import asyncio
from dotenv import load_dotenv
from autogen_ext.models.openai import AzureOpenAIChatCompletionClient
from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination
from autogen_agentchat.ui import Console

load_dotenv()

def create_azureopenai_model_client():
    """创建 AzureOpenAI 模型客户端"""
    return AzureOpenAIChatCompletionClient(
        azure_deployment=os.getenv("LLM_MODEL_ID"),
        model=os.getenv("LLM_MODEL_NAME"),
        api_version=os.getenv("LLM_API_VERSION"),
        azure_endpoint=os.getenv("LLM_BASE_URL"),
        api_key=os.getenv("LLM_API_KEY")
    )

最后在异步函数中启动并运行协作流程:

python 复制代码
if __name__ == '__main__':
    try:
        result = asyncio.run(run_development_team())
        print(f"\n🗒️协作结果摘要:")
        print(f"- 任务完成状态:{'成功' if result else '需要进一步处理'}")
    except ValueError as e:
        print(f"❌配置错误: {e}")
    except Exception as e:
        print(f"❌运行错误: {e}")

运行结果如下:

复制代码
🔧正在初始化模型客户端
👥正在创建智能体
🚀启动 AutoGen 软件开发团队协作...

============================================================
---------- TextMessage (user) ----------
我们需要开发一个比特币价格显示应用,具体要求如下:
...
请团队协作完成这个任务,从需求分析到最终实现。
---------- TextMessage (ProductManager) ----------
### 1. 需求理解与分析
...
请工程师开始实现。
---------- TextMessage (Engineer) ----------
### 开始实现
根据需求分析和功能模块划分,我们将按照优先级逐步实现功能。
...
请代码审查员检查。
---------- TextMessage (CodeReviewer) ----------
### 代码审查
...
代码审查完成,请用户代理测试。
Enter your response: TERMINATE
---------- TextMessage (UserProxyAgent) ----------
TERMINATE

============================================================
✅团队协作完成

🗒️协作结果摘要:
- 任务完成状态:成功

局限性

AutoGen 的对话式架构在复杂任务中可能陷入死循环,难以调试和控制,缺乏确定性。

截止到目前,MicroSoft 已经将其置于维护模式,仅接受社区贡献,不再开发新功能,并推荐新项目转向 Agent Framework。

尽管有上述局限,但对于短期 PoC,AutoGen 还是一个绝佳的低门槛选择。

LangGraph

LangGraph 是 LangChain 生态的一部分,作为 LangChain 生态系统的重要扩展,它代表了智能体框架设计的一个全新方向。与基于对话的框架不同,LangGraph 将智能体的执行流程建模为一种状态机,并将其表示为有向图(Directed Graph)。在这种设计中,图的节点代表一个具体的计算步骤(如调用 LLM、执行工具),而边则定义了从一个节点到另一个节点的跳转逻辑。这种设计的好处是天然支持循环、使得构建能够进行迭代、反思和自我修正的复杂智能体工作流变得前所未有的直观和简单。

LangGraph 带来的直观好处:

  • 断点续跑:内置的检查点(Checkpoint)机制能在任何节点保存状态,系统重启或出错都可以从任意点恢复。
  • 人工干预:可以原生地在流程特定节点暂停,等待人工审核或输入后再继续。
  • 易于调试:每一步执行都可追溯,并通过可视化界面调试。

LangGraph 三个基本构成要素分别是全局状态(State)、节点(Nodes)和边(Edges):

  • 全局状态被整个图共享,所有节点都能读取和更新这个全局状态,它通常被定义为一个字典类型,包含对话历史、中间结果、迭代次数等。
  • 节点实际上是一个函数,接收当前状态作为输入,并返回一个更新后的状态作为输出,它是执行具体工作的单元。
  • 边负责连接节点,定义工作流的方向。边有两种:简单边和常规边。简单边将两个节点简单地连接起来,而条件边则可以通过一个函数来判断当前的状态,然后根据条件函数的结果来将工作流路由到下一个节点。这正是实现循环和复杂逻辑分支的关键。

下面用 LangGraph 来实现一个讲笑话的工作流:

python 复制代码
import os
from langgraph.constants import START, END
from langgraph.graph import StateGraph
from typing_extensions import TypedDict
from dotenv import load_dotenv
from langchain_openai import AzureChatOpenAI

load_dotenv()

llm = AzureChatOpenAI(
    azure_deployment=os.getenv("LLM_MODEL_ID"),
    api_version=os.getenv("LLM_API_VERSION"),
    azure_endpoint=os.getenv("LLM_BASE_URL"),
    api_key=os.getenv("LLM_API_KEY"),
    temperature=0.9
)

# 全局状态
class State(TypedDict):
    topic: str
    joke: str
    improved_joke: str
    final_joke: str


# Nodes
def generate_joke(state: State):
    """调用 LLM 生成初始的笑话"""
    msg = llm.invoke(f"Write a short joke about {state['topic']}")
    return {"joke": msg.content}


def improve_joke(state: State):
    """二次调用 LLM 提升笑话水平"""

    msg = llm.invoke(f"Make this joke funnier by adding wordplay: {state['joke']}")
    return {"improved_joke": msg.content}


def polish_joke(state: State):
    """第三次调用 LLM 生成最终结果"""
    msg = llm.invoke(f"Add a surprising twist to this joke: {state['improved_joke']}")
    return {"final_joke": msg.content}


# 状态判断函数
def check_punchline(state: State):
    # Simple check - does the joke contain "?" or "!"
    if "?" in state["joke"] or "!" in state["joke"]:
        return "Pass"
    return "Fail"


workflow = StateGraph(State)
# add notes
workflow.add_node("generate_joke", generate_joke)
workflow.add_node("improve_joke", improve_joke)
workflow.add_node("polish_joke", polish_joke)

# add edge
workflow.add_edge(START, "generate_joke")
workflow.add_conditional_edges(
    "generate_joke", check_punchline, {"Fail": "improve_joke", "Pass": END}
)
workflow.add_edge("improve_joke", "polish_joke")
workflow.add_edge("polish_joke", END)

# compile the workflow
chain = workflow.compile()

png_data = chain.get_graph().draw_mermaid_png()
with open("graph.png", "wb") as f:
    f.write(png_data)
state = chain.invoke({"topic": "dog"})
print("😂Initial joke:")
print(state["joke"])
print("--- --- --- --- ---")
if "improved_joke" in state:
    print("😂Improved joke:")
    print(state["improved_joke"])
    print("--- --- --- --- ---")

    print("😂Final joke:")
    print(state["final_joke"])
else:
    print("😂Final joke:")
    print(state["joke"])

生成的有向图如下:

运行结果如下:

复制代码
😂Initial joke:
Why did the dog sit in the shade?  

Because it didn't want to be a **hot dog**! 🌭
--- --- --- --- ---
😂Final joke:
Why did the dog sit in the shade?  

Because it didn't want to be a **hot dog**! 🌭

因为在第一个节点 generate_joke 之后的条件边中进行状态判断时将工作流路由到了 END 节点,而没有经过剩下的两个节点,所以全局状态中只有 joke 字段。

优点与局限性

LangGraph 的节点都是独立的函数,因此可以非常方便地在流程中插入等待人工审核的节点,为实现可靠的人机协作(Human in the loop)提供了坚实的基础。

相比于基于对话的框架,LangGraph 的开发过程更为繁琐,开发者不仅仅要考虑做什么,还要考虑如何去控制工作流程。此外,LangGraph 的行为虽然可控,但也缺少了对话式智能体那种动态的、涌现式交互,其强项在于执行一个确定的、可靠的流程,而非模拟开放式的、不可预测的社会性协作。

Microsoft Agent Framework

上文介绍 AutoGen 时说过,该项目已进入维护模式,适合在原型系统中快速验证使用。而 MAF(Microsoft Agent Framework)是官方推出的新一代多智能体框架。

直接看一个简单的例子:

python 复制代码
import os
import asyncio

from agent_framework_openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
from agent_framework import Agent

load_dotenv()


async def main():
    agent = Agent(
        client=OpenAIChatCompletionClient(
            model=os.getenv("LLM_MODEL_ID"),
            api_version=os.getenv("LLM_API_VERSION"),
            azure_endpoint=os.getenv("LLM_BASE_URL"),
            api_key=os.getenv("LLM_API_KEY")
        ),
        name="HaikuAgent",
        instructions="You are an upbeat assistant that writes beautifully."
    )
    print(await agent.run("Write a haiku about Microsoft Agent Framework."))


if __name__ == '__main__':
    asyncio.run(main())

运行结果如下:

复制代码
Code whispers take form,  
Agents dance in quiet scope,  
Tasks bloom, seamless grace.

这个例子展示了 MAF 中 agent 的调用方式。接下来扩展一下,使用多 Agent 进行协作:

python 复制代码
import asyncio
import os

from agent_framework import Agent, workflow
from dotenv import load_dotenv
from agent_framework_openai import OpenAIChatCompletionClient

load_dotenv()

client = OpenAIChatCompletionClient(
    model=os.getenv("LLM_MODEL_ID"),
    api_version=os.getenv("LLM_API_VERSION"),
    azure_endpoint=os.getenv("LLM_BASE_URL"),
    api_key=os.getenv("LLM_API_KEY")
)

writer = Agent(
    name="WriterAgent",
    instructions="Write a short poem about the given topic in Chinese.",
    client=client
)

reviewer = Agent(
    name="ReviewerAgent",
    instructions="Review the given poem in Chinese.",
    client=client
)


@workflow
async def poem_workflow(topic: str) -> str:
    poem = (await writer.run(f"Write a poem about: {topic}")).text
    review = (await reviewer.run(f"Review this poem: {poem}")).text
    return f"Poem:\n{poem}\n\nReview:{review}"


async def main() -> None:
    result = await poem_workflow.run("思乡")
    print(result.get_outputs()[0])

if __name__ == '__main__':
    asyncio.run(main())

运行结果如下:

复制代码
Poem:
在异乡处思乡切,  
心随月影绕村阶。  
故土若梦藏心底,  
炊烟袅袅忆童年家。  

归雁声中春水涨,  
驿路遥遥心更牵。  
愿化微风归山谷,  
拂遍桑田与荷莲。  

Review:这是一首充满浓厚乡愁情感的诗,语言清丽流畅,意境优美深远,情感真挚动人。整首诗运用自然景色和情感描写,表现了远离故乡的游子对家乡的深深怀恋以及希望归乡的强烈愿望。以下是对这首诗的具体分析与点评:

**第一联:**
"在异乡处思乡切,  
心随月影绕村阶。"

开篇从"异乡"入手点题,"思乡切"直接流露出浓烈的情感,句意直白却触动人心。第二句描写"月影绕村阶",以月影表达心绪,将作者的思乡情感自然地嵌入情景之中,画面感十足。这一联意境悠远,为全诗奠定了感伤基调。

**第二联:**
"故土若梦藏心底,  
炊烟袅袅忆童年家。"

这一联从情感转向记忆,描绘故乡如梦般深藏于心。特别是"炊烟袅袅"这一景象,极具乡村特色,唤起了对温暖童年时光的记忆。文字生动,情景交融,让读者产生共鸣。

**第三联:**
"归雁声中春水涨,  
驿路遥遥心更牵。"

这里从现实场景出发,"归雁"与"春水"象征了季节的变化,同时为表达心绪提供了契机。"驿路遥遥"直接写离家的路途遥远,"心更牵"则简明而深刻地揭示了游子心中愈发浓烈的乡愁。自然景象与情感相辅相成,结构合理。

**第四联:**
"愿化微风归山谷,  
拂遍桑田与荷莲。"

结尾联情感达到了高潮,以"愿化微风"的形象化方式,表达出归乡的迫切与浓烈的情意,"桑田"与"荷莲"具体化了归乡后的画面,诗人希望自己回归乡土,融入天地自然。这一联不仅抒发了个人情感,也有某种超越具体情境的普遍性,令人动容。

**总体评价:**
这首诗在意象选择和情感表达上都非常用心,整篇意境优美,语言凝练,音韵和谐美好,使用工整的对仗手法,使得诗句节奏明快,同时在平易中蕴含深意,是一篇极为成功的五言近体诗。唯一可以进一步提升的地方是构建更为独特的意象,与其它类似的乡愁题材作品区分开来。但总体而言,这首诗非常动人,充满乡情,值得细细品味。

这个例子展示了多 Agent 通过 workflow 的方式来协作,但这个工作流仍然是链式工作流,无法实现人机协同。

下面这个例子演示图工作流:

python 复制代码
import asyncio
import os
from agent_framework_openai import OpenAIChatCompletionClient
from pydantic import BaseModel
from dotenv import load_dotenv
from agent_framework import Agent, Executor, WorkflowBuilder, WorkflowContext, handler
from typing_extensions import Never

load_dotenv()

client = OpenAIChatCompletionClient(
    model=os.getenv("LLM_MODEL_ID"),
    api_version=os.getenv("LLM_API_VERSION"),
    azure_endpoint=os.getenv("LLM_BASE_URL"),
    api_key=os.getenv("LLM_API_KEY")
)


class State(BaseModel):
    topic: str
    joke: str | None = None
    improved_joke: str | None = None


class GenerateJoke(Executor):
    def __init__(self, id: str, agent: Agent):
        super().__init__(id=id)
        self._agent = agent

    @handler
    async def run(self, state: State, ctx: WorkflowContext[State]) -> None:
        msg = await self._agent.run(f"Write a short joke about {state.topic}")
        next_state = state.model_copy(update={"joke": str(msg).strip()})
        await ctx.send_message(next_state)


class ImproveJoke(Executor):
    def __init__(self, id: str, agent: Agent):
        super().__init__(id=id)
        self._agent = agent

    @handler
    async def run(self, state: State, ctx: WorkflowContext[Never, State]) -> None:
        msg = await self._agent.run(
            f"Make this joke funnier by adding wordplay: {state.joke}"
        )
        final_state = state.model_copy(update={"improved_joke": str(msg).strip()})
        await ctx.yield_output(final_state)


def create_workflow():

    agent = Agent(name="joke-agent", client=client, instructions="You write and improve short jokes.")

    generate = GenerateJoke(id="generate_joke", agent=agent)
    improve = ImproveJoke(id="improve_joke", agent=agent)

    return (
        WorkflowBuilder(start_executor=generate)
        .add_edge(generate, improve)
        .build()
    )


async def main():
    workflow = create_workflow()
    events = await workflow.run(State(topic="dog"))
    state = events.get_outputs()[0]

    print("Initial joke:")
    print(state.joke)
    print("--- --- --- --- ---")
    print("Improved joke:")
    print(state.improved_joke)


if __name__ == "__main__":
    asyncio.run(main())

这是最简单的图:两个节点加上一条边。对其稍加改动,让工作流在第一个节点之后进行条件判断,动态地路由到下一个节点:

python 复制代码
import asyncio
import os
from agent_framework_openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
from pydantic import BaseModel
from agent_framework import Agent, Executor, WorkflowBuilder, WorkflowContext, handler

load_dotenv()

client = OpenAIChatCompletionClient(
    model=os.getenv("LLM_MODEL_ID"),
    api_version=os.getenv("LLM_API_VERSION"),
    azure_endpoint=os.getenv("LLM_BASE_URL"),
    api_key=os.getenv("LLM_API_KEY")
)


class State(BaseModel):
    topic: str
    joke: str | None = None
    improved_joke: str | None = None
    final_joke: str | None = None


class GenerateJoke(Executor):
    def __init__(self, id: str, agent: Agent):
        super().__init__(id=id)
        self._agent = agent

    @handler
    async def run(self, state: State, ctx: WorkflowContext[State]) -> None:
        msg = await self._agent.run(f"Write a short joke about {state.topic}")
        await ctx.send_message(state.model_copy(update={"joke": str(msg).strip()}))


class ImproveJoke(Executor):
    def __init__(self, id: str, agent: Agent):
        super().__init__(id=id)
        self._agent = agent

    @handler
    async def run(self, state: State, ctx: WorkflowContext[State]) -> None:
        msg = await self._agent.run(f"Make this joke funnier by adding wordplay: {state.joke}")
        await ctx.send_message(state.model_copy(update={"improved_joke": str(msg).strip()}))


class PolishJoke(Executor):
    def __init__(self, id: str, agent: Agent):
        super().__init__(id=id)
        self._agent = agent

    @handler
    async def run(self, state: State, ctx: WorkflowContext[State]) -> None:
        msg = await self._agent.run(f"Add a surprising twist to this joke: {state.improved_joke}")
        await ctx.send_message(state.model_copy(update={"final_joke": str(msg).strip()}))


class OutputState(Executor):
    def __init__(self, id: str):
        super().__init__(id=id)

    @handler
    async def run(self, state: State, ctx: WorkflowContext[None, State]) -> None:
        if state.final_joke is None:
            state = state.model_copy(update={"final_joke": state.joke})
        await ctx.yield_output(state)


def check_punchline_pass(state: State) -> bool:
    joke = state.joke or ""
    return ("?" in joke) or ("!" in joke)


def create_workflow():
    writer = Agent(name="writer", client=client, instructions="You write short jokes.")

    generate = GenerateJoke(id="generate_joke", agent=writer)
    improve = ImproveJoke(id="improve_joke", agent=writer)
    polish = PolishJoke(id="polish_joke", agent=writer)
    output = OutputState(id="output_state")

    return (
        WorkflowBuilder(start_executor=generate)
        .add_edge(generate, output, condition=check_punchline_pass)
        .add_edge(generate, improve, condition=lambda s: not check_punchline_pass(s))
        .add_edge(improve, polish)
        .add_edge(polish, output)
        .build()
    )


async def main():
    workflow = create_workflow()
    events = await workflow.run(State(topic="dog"))
    state = events.get_outputs()[0]

    print("😂Initial joke:")
    print(state.joke)
    print("--- --- --- --- ---")

    if state.improved_joke is not None:
        print("😂Improved joke:")
        print(state.improved_joke)
        print("--- --- --- --- ---")

    print("😂Final joke:")
    print(state.final_joke)


if __name__ == "__main__":
    asyncio.run(main())

Graph workflow 的例子实现的功能跟上文的 LangGraph 例子是一样的。

MAF 中的几个重要概念:

  • Executors,独立的处理单元,可以是 AI Agents 或者特定的逻辑组件,实际上就是图的节点。
  • Edges,连接两个节点的边,决定消息的流向。
  • Events,提供对工作流执行结果的可观测性,包括生命周期事件、节点事件和特定事件。

参考资料

1\]. https://github.com/microsoft/agent-framework

相关推荐
Only丿阿海1 小时前
当运维与AI结合 — 用 AI Agent 去维护 Nginx
运维·人工智能·nginx·agent·agent4j
qq_411262421 小时前
基于 ESP32-S3 的四博AI双目智能音箱方案:双目同显/异显、素材上传、触摸、G-sensor、舵机、TWS音频接入
人工智能·智能音箱
云上码厂1 小时前
卫星和航空影像的深度学习技术
人工智能·深度学习
吃好睡好便好1 小时前
在Matlab中绘制马鞍函数曲面图
开发语言·人工智能·学习·算法·matlab·信息可视化
tedcloud1231 小时前
OfficeCLI部署教程:让AI直接操作Word、Excel和PPT
服务器·人工智能·word·excel
测试员周周1 小时前
【Appium 系列】第01节-Appium 是什么 — 移动端自动化的行业标准
开发语言·人工智能·python·功能测试·appium·自动化·测试用例
工业机器人销售服务1 小时前
突破效率瓶颈:伯朗特大负载机器人实现连续模冲压多件同步取放
人工智能
前端小超人rui1 小时前
AI分类及AI大模型分类
人工智能·分类·数据挖掘·ai 大模型
薛定猫AI1 小时前
【深度解析】从 Gemini 3.2、Claude 限额变化到 AI Agent:大模型工程化选型与实战评估
人工智能·状态模式