This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Dapr Agents

用于大规模构建持久且有弹性的 AI Agent 系统的框架

Concepts Agents

什么是 Dapr Agents?

Dapr Agents 是一个 Python 框架,用于利用 Dapr 的分布式系统能力构建由 LLM 驱动的自主 Agent 应用。它提供工具来创建能够执行持久任务、做决策并通过工作流协作的 AI Agent,同时利用 Dapr 的状态管理、消息传递和可观测性特性来实现大规模可靠执行。

1 - 简介

Dapr Agents 及其核心功能概述

Agent Overview

Dapr Agents 是一个用于构建持久且弹性的 AI agent 系统的开发者框架,由大语言模型(LLMs)驱动。基于久经考验的 Dapr 项目构建,它使开发者能够创建具有身份、能够推理问题、做出动态决策并无缝协作的自主系统。它包含内置的可观测性和有状态工作流执行,确保 agent 工作流无论复杂度如何都能成功完成。无论您是开发单 agent 应用还是复杂的多 agent 工作流,Dapr Agents 都为智能、自适应系统提供可在跨环境中扩展的基础设施。

核心能力

  • Agent 身份:使用 Dapr Agents,每个 agent 都被分配一个唯一的加密身份,用于对 agent 交互进行身份认证并在服务和基础设施间强制执行授权。
  • 持久执行:使用 Dapr Agents 创建的 agent 由 Dapr 的工作流引擎支持,该引擎将每个 agent 与 LLMs 和工具的交互持久化到持久状态存储中,即使在 agent 重启后也能恢复并继续执行。
  • 弹性:Dapr Agents 可以通过自动重试策略、超时和熔断器从临时故障中恢复,还可以应用由工作流状态支持的持久重试以从持续时间更长的故障中恢复。
  • 规模与效率:在单核上高效运行数千个 agent。Dapr 在机器集群间透明地分发单 agent 和多 agent 应用并处理它们的生命周期。
  • 数据驱动 Agent:通过连接到数十种不同的数据源,直接与数据库、文档和非结构化数据集成。
  • 多 Agent 系统:默认安全和可观测,支持 agent 之间的协作。
  • Kubernetes 原生:在 Kubernetes 环境中轻松部署和管理 agent。
  • 平台就绪:访问范围和声明式资源使平台团队能够将 Dapr Agents 集成到其系统中。
  • 供应商中立与开源:避免供应商锁定,并在云和本地部署间获得灵活性。

核心功能

Dapr Agents 提供专为创建智能、自主系统设计的专用模块。每个模块都设计为独立工作,允许您使用适合应用程序需求的任何组合。

功能描述
LLM 集成它使用 Dapr Conversation API 抽象了用于聊天补全的 LLM 推理 API,使您能够在不更改高级 agent 代码的情况下更换 LLM 提供商,并包括用于 embeddings、音频和其他专用集成的原生客户端。
结构化输出利用 OpenAI 的 Function Calling 等功能生成符合 JSON Schema 和 OpenAPI 标准的可预测、可靠的结果,用于工具集成。
工具选择基于需求、最佳动作和通过 Function Calling 功能执行的动态工具选择。
MCP 支持内置对 Model Context Protocol 的支持,使 agent 能够通过标准化接口动态发现和调用外部工具。
Agent 即工具在 DurableAgent 的推理循环中调用其他 Dapr Agents——或来自其他框架(如 OpenAI Agents、LangGraph 和 CrewAI)的 agent——作为工具,以实现可组合的多 agent 系统。
内存管理在交互间保留上下文,选项从简单的内存列表到向量数据库(Chroma、PostgreSQL、Redis),与 Dapr 状态存储 集成以实现可扩展、持久的内存。
持久 Agent由工作流支持的 agent,通过持久状态管理和自动重试机制为长时间运行的进程提供容错执行。
Agent 运行器通过 HTTP 暴露 agent 或订阅 PubSub 以执行长时间运行的任务,使 agent 能够被 API 访问而无需用户界面或人工干预。
事件驱动通信通过 发布订阅消息传递 实现 agent 协作,用于分布式系统中的事件驱动通信、任务分发和实时协调。
Agent 编排使用 Dapr 工作流 进行确定性 agent 编排,通过更高级别的任务与 LLMs 交互以处理复杂的多步骤流程。

Agent 模式

Dapr Agents 支持一组全面的模式,代表构建智能系统的不同方法。

这些模式从确定性的、工作流驱动的设计到完全自主的 agent,能够进行动态规划和执行;每种模式都解决不同的用例并在可预测性与自主性之间取得平衡。

模式描述
增强型 LLM通过内存和工具等外部功能增强语言模型,为 AI 驱动的应用程序提供基础。
持久 Agent通过使用 Dapr 状态存储为 agent 交互添加持久性和持久化来扩展增强型 LLM。
提示链接将复杂任务分解为一系列步骤,其中每个 LLM 调用处理前一个调用的输出。
评估器-优化器实现双 LLM 流程,其中一个模型生成响应,而另一个在迭代循环中提供评估和反馈。
并行化同时处理问题的多个维度,输出通过编程方式聚合以提高效率。
路由对输入进行分类并将其定向到专门的后续任务,实现关注点分离和专家专业化。
编排器-工作者具有中央编排器 LLM,动态分解任务,将其委派给工作者 LLMs,并综合结果。

开发者体验

Dapr Agents 是基于 Python Dapr SDK 构建的 Python 框架,为构建 agent 系统提供全面的开发体验。

入门指南

按照 入门页面 上的说明开始使用 Dapr Agents。

框架集成

Dapr Agents 与流行的 Python 框架和工具集成。有关详细的集成指南和示例,请参阅 集成页面

运维支持

Dapr Agents 继承了 Dapr 的企业级运维能力,为 agent 系统的持久可靠部署提供全面支持。

内置运维功能

  • 可观测性 - 分布式追踪、指标收集和日志记录,用于 agent 交互和工作流执行
  • 安全性 - mTLS 加密、访问控制和密钥管理,用于安全的 agent 通信
  • 弹性 - 自动重试、熔断器和超时策略,用于容错的 agent 操作
  • 基础设施抽象 - Dapr 组件抽象了 LLM 提供商、内存存储、存储和消息传递后端,实现在不同环境间的无缝转换

这些功能使团队能够监控 agent 性能、保护多 agent 通信,并确保复杂 agent 工作流的可靠执行。

贡献

无论您是有兴趣增强框架、添加新集成还是改进文档,我们都欢迎社区的贡献。

有关开发设置和指南,请参阅我们的贡献者指南

2 - 入门指南

如何安装 Dapr Agents 并运行你的第一个 Agent

安装 Dapr CLI

虽然 Dapr Agents 中的简单示例可以在不使用边车的情况下运行,但推荐模式是使用 Dapr 边车。要充分利用 Dapr Agents 的全部功能,请安装 Dapr CLI,用于在本地或 Kubernetes 上运行 Dapr 进行开发。完整的分步指南,请参阅 Dapr CLI 安装页面

通过重启终端/命令提示符并运行以下命令来验证 CLI 是否已安装:

dapr -h

在本地模式下初始化 Dapr

在本地初始化 Dapr 以设置用于开发的自托管环境。此过程会获取并安装 Dapr 边车二进制文件、将必要的服务作为 Docker 容器运行,并为你的应用程序准备一个默认组件文件夹。详细步骤,请参阅官方本地初始化 Dapr 指南

Dapr 初始化

要初始化 Dapr 控制平面容器并创建默认配置文件,请运行:

dapr init

验证你拥有运行中的容器实例,这些实例使用 daprio/dapropenzipkin/zipkinredis 镜像:

docker ps

安装 Python

安装 uv

Dapr Agents 快速入门使用 uv 作为 Python 包管理器。按照 uv 安装指南 进行安装。

配置 LLM

快速入门默认使用 Ollama,因此你可以在本地运行所有内容而无需 API 密钥。

默认配置:Ollama(本地)

  1. 安装并启动 Ollama:
curl -fsSL https://ollama.com/install.sh | sh
brew install ollama

ollama.com/download 下载并运行安装程序。

  1. 拉取一个支持工具调用的模型:
ollama serve    # 启动服务器(如果已在运行则跳过)
ollama pull qwen3:0.6b
  1. 在运行任何快速入门之前,导出所需的环境变量:
export OLLAMA_ENDPOINT=http://localhost:11434/v1
export OLLAMA_MODEL=qwen3:0.6b
$env:OLLAMA_ENDPOINT = "http://localhost:11434/v1"
$env:OLLAMA_MODEL = "qwen3:0.6b"

resources/llm-provider.yaml 组件会自动从你的环境中解析 {{OLLAMA_ENDPOINT}}{{OLLAMA_MODEL}}

备选方案:OpenAI

要改用 OpenAI,请将 resources/llm-provider.yaml 替换为:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: llm-provider
spec:
  type: conversation.openai
  version: v1
  metadata:
  - name: key
    value: "{{OPENAI_API_KEY}}"
  - name: model
    value: "gpt-4o-mini"

Dapr 还通过 Conversation API 支持 Anthropic、Mistral 和其他提供商。替换组件类型和元数据,同时保持 name: llm-provider 不变。

准备你的环境

在这个入门指南中,你将直接使用 Dapr Agents 快速入门 中的示例。你将主要使用 02_durable_agent_http.py——一个基于 Dapr 工作流引擎的可靠持久化 Agent,并通过 HTTP 暴露。

1. 克隆仓库

git clone https://github.com/dapr/dapr-agents.git
cd dapr-agents/quickstarts

2. 创建虚拟环境并安装依赖

quickstarts 文件夹中:

uv venv

# 激活虚拟环境
# 在 Windows 上:
.venv\Scripts\activate
# 在 macOS/Linux 上:
source .venv/bin/activate

# 安装依赖
uv sync --active

这将安装 dapr-agents 以及示例所需的所有额外库。

了解应用程序

此示例创建了一个协助处理天气信息的 Agent,并使用 Dapr 处理 LLM 交互、持久化对话历史记录,以及提供可靠、持久的 Agent 步骤执行。

对于这个快速入门,你将主要使用:

  • 02_durable_agent_http.py – 通过 HTTP 暴露的主持久化天气 Agent 应用程序
  • function_tools.py – 包含 slow_weather_func,即 Agent 使用的工具
  • resources/llm-provider.yaml – Conversation API 和 LLM 配置
  • resources/agent-memory.yaml – 对话记忆状态存储
  • resources/agent-workflow.yaml – 工作流和持久执行状态存储

打开 02_durable_agent_http.py

from dapr_agents.llm import DaprChatClient

from dapr_agents import DurableAgent
from dapr_agents.agents.configs import AgentMemoryConfig, AgentStateConfig
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner
from function_tools import slow_weather_func


def main() -> None:
    weather_agent = DurableAgent(
        name="WeatherAgent",
        role="Weather Assistant",
        instructions=["Help users with weather information"],
        tools=[slow_weather_func],
        # Configure this agent to use Dapr Conversation API.
        llm=DaprChatClient(component_name="llm-provider"),
        # Configure the agent to use Dapr State Store for conversation history.
        memory=AgentMemoryConfig(
            store=ConversationDaprStateMemory(
                store_name="agent-memory",
            )
        ),
        # This is where the execution state is stored
        state=AgentStateConfig(
            store=StateStoreService(store_name="agent-workflow"),
        ),
    )

    runner = AgentRunner()
    try:
        runner.serve(weather_agent, port=8001)
    finally:
        runner.shutdown()


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\nInterrupted by user. Exiting gracefully...")

这个单文件应用程序展示了如何创建一个生产级的持久化 Agent:

  • DurableAgent 将 LLM 和工具封装在基于工作流的执行模型中。推理和工具调用的每个步骤都会被持久化。
  • slow_weather_func(来自 function_tools.py)代表一个缓慢的外部调用,允许你观察持久化工作流如何在中断后恢复。
  • AgentRunner 通过 HTTP 在端口 8001 上暴露 Agent,使其他服务(或 curl)可以启动和查询持久化任务。

下面的部分将分解关键配置区域,并展示每个 Python 配置如何映射到 Dapr 组件。

通过 Dapr Conversation API 进行 LLM 调用

在 Agent 定义中:

llm=DaprChatClient(component_name="llm-provider"),

这通过 llm-provider 组件使用 Dapr Conversation API。对应的 Dapr 组件定义在 resources/llm-provider.yaml 中:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: llm-provider
spec:
  type: conversation.openai
  version: v1
  metadata:
  - name: key
    value: "ollama"
  - name: model
    value: "{{OLLAMA_MODEL}}"
  - name: endpoint
    value: "{{OLLAMA_ENDPOINT}}"
  • conversation.openai 组件类型用于 Ollama 兼容的 OpenAI API。
  • key 设置为 "ollama" 用于本地 Ollama 推理;使用云提供商时请替换为真实的 API 密钥。
  • modelendpoint 在运行时从环境变量中解析。

通过此设置,你可以通过编辑组件 YAML 来切换模型或提供商,而无需更改 Agent 代码。

使用 Dapr 状态存储的对话记忆

在 Agent 定义中,对话记忆配置如下:

memory=AgentMemoryConfig(
  store=ConversationDaprStateMemory(
      store_name="agent-memory",
  )
),

这告诉 Agent 将对话历史存储在 agent-memory Dapr 状态存储中。匹配的 Dapr 组件是 resources/agent-memory.yaml

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: agent-memory
spec:
  type: state.redis
  version: v1
  metadata:
    - name: redisHost
      value: localhost:6379
    - name: redisPassword
      value: ""
  • 状态存储使用 Redis 来持久化对话轮次。
  • Agent 在此处读取和写入消息,以便 LLM 可以在多个 HTTP 调用之间保持上下文。

你可以稍后浏览此状态(例如使用 Redis Insight)来查看对话历史的存储方式。

使用工作流状态存储的持久执行状态

Agent 的持久执行状态配置如下:

state=AgentStateConfig(
  store=StateStoreService(store_name="agent-workflow"),
),

这使用 agent-workflow Dapr 状态存储。对应的组件是 resources/agent-workflow.yaml

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: agent-workflow
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
  - name: actorStateStore
    value: "true"
  • actorStateStore: "true" 是必需的设置,启用适合 Dapr 工作流的存储。
  • 如果进程在执行中途停止,工作流引擎使用此状态从最后一个持久化步骤恢复,而不是从头开始。这可以防止复杂的 Agent 工作流重新执行已经完成的 LLM 和工具调用。

这些特性共同使 Agent 具有持久性可靠性提供商无关性,同时保持 Agent 代码本身专注于行为和工具。

使用 Dapr 运行持久化 Agent

quickstarts 文件夹中,激活虚拟环境后:

uv run dapr run --app-id durable-agent --resources-path resources -- python 02_durable_agent_http.py

这将:

  • 使用 resources/ 中的组件启动 Dapr 边车。
  • 使用持久化 WeatherAgent 运行 02_durable_agent_http.py
  • 在端口 8001 上暴露 Agent 的 HTTP API。

使用提示触发 Agent

在另一个终端中,向 Agent 询问天气。

curl -i -X POST http://localhost:8001/agent/run \
  -H "Content-Type: application/json" \
  -d '{"task": "What is the weather in London?"}'

响应包含一个 WORKFLOW_ID,代表工作流执行。

查询工作流状态或结果

使用 POST 响应中的 WORKFLOW_ID 来查询进度或最终结果:

curl -i -X GET http://localhost:8001/agent/instances/WORKFLOW_ID

WORKFLOW_ID 替换为你从 POST 请求中收到的值。

预期行为

  • Agent 在 /agent/run 暴露一个 REST 端点。

  • /agent/run 的 POST 请求接受提示、调度工作流执行并返回工作流 ID。

  • 你可以随时 GET /agent/instances/{WORKFLOW_ID}(即使停止并重新启动 Agent 后),以检查状态或获取最终答案。

  • 工作流编排:

    • LLM 调用来解释任务并决定是否需要工具。
    • 工具调用(使用 slow_weather_func)来获取天气数据。
    • 最终的 LLM 步骤,将工具结果整合到响应中。
  • 每个步骤都被持久化,因此除非失败,否则不会重复 LLM 或工具调用。

通过中断 Agent 测试持久性

要查看持久执行的实际效果:

  1. 开始运行 按上述方式向 /agent/run 发送 POST 请求,并记下 WORKFLOW_ID

  2. 终止 Agent 进程 当请求正在处理时(在 slow_weather_func 期间,它被故意延迟 5 秒),停止 Agent 进程:

    • 转到运行 uv run dapr run ... 的终端。
    • Ctrl+C 停止应用程序和边车。
  3. 重新启动 Agent 使用相同的命令再次启动:

   uv run dapr run --app-id durable-agent --resources-path resources -- python 02_durable_agent_http.py
  1. 查询相同的工作流 在另一个终端中,查询相同的工作流 ID:

    curl -i -X GET http://localhost:8001/agent/instances/WORKFLOW_ID
    

你会看到工作流从其最后一个持久化步骤继续,而不是从头开始。工具调用或 LLM 调用不会被重新执行,除非需要,并且你不需要发送新的提示。一旦工作流完成,GET 请求将返回最终结果。

总之,Dapr 工作流引擎在重启之间保留 Agent 的执行状态,实现了结合 LLM 调用、工具和有状态推理的可靠长时间运行交互。

使用 Diagrid Dashboard 检查工作流执行

在使用 Dapr 启动持久化 Agent 后,你可以使用本地 Diagrid Dashboard 来可视化和检查你的工作流状态,包括每次运行的详细执行历史。仪表板作为容器运行,并连接到 Dapr 工作流使用的相同状态存储(默认情况下是本地 Redis 实例)。

Diagrid Dashboard showing local workflow executions

使用 Docker 启动 Diagrid Dashboard 容器:

docker run -p 8080:8080 ghcr.io/diagridio/diagrid-dashboard:latest

在浏览器中打开仪表板,访问 http://localhost:8080 以探索你的本地工作流执行。

使用 Redis Insight 检查对话历史

Dapr 默认使用 Redis 进行状态管理和发布订阅消息,这些是 Dapr Agents Agent 工作流的基础。要检查 Redis 实例并查看此持久化 Agent 的对话状态,你可以使用 Redis Insight。

运行 Redis Insight:

docker run --rm -d --name redisinsight -p 5540:5540 redis/redisinsight:latest

运行后,在 http://localhost:5540/ 访问 Redis Insight 界面。

在 Redis Insight 中,你可以连接到 Dapr 使用的 Redis 实例:

  • 端口:6379
  • 主机(Linux):172.17.0.1
  • 主机(Windows/Mac):host.docker.internal(例如 host.docker.internal:6379

Redis Insight 可以轻松检查状态存储(如 agent-memoryagent-workflow)中存储的键和值,这对于调试和理解持久化 Agent 的行为非常有用。

Redis 仪表板

在这里你可以浏览 Agent 使用的状态存储(agent-memory)并探索其数据。

下一步

现在你已经通过快速入门安装了 Dapr Agents,并端到端运行了一个持久化 HTTP Agent,请在快速入门部分探索更多示例和模式,了解多 Agent 工作流、发布订阅驱动的 Agent、追踪以及与 Dapr 构建块的更深层次集成。

3 - 为什么选择 Dapr Agents

了解 Dapr Agents 的优势与使用场景

Dapr Agents 是一个用于构建和编排基于 LLM 的自主代理的开源框架,它利用 Dapr 经过验证的分布式系统基础。与要求开发者从零开始构建基础设施的其他代理框架不同,Dapr Agents 通过提供企业级的可扩展性、状态管理和消息传递能力,使团队能够专注于代理智能。这种方法消除了重新创建分布式系统基础组件的复杂性,同时提供了由 Dapr 支持的代理工作流。

现有框架的挑战

当今许多代理框架试图通过开发自己的平台来重新定义微服务的构建和编排方式,以处理核心的分布式系统能力。虽然这些努力展示了创新,但在扩展或适应新环境时,往往导致学习曲线陡峭、系统分散和不必要的复杂性。

这些框架要求开发者采用全新的范式或重新创建基础架构,而不是基于现有的、被证明能够在规模上处理这些挑战的解决方案。这种额外的复杂性分散了对主要目标的关注:设计和实现智能、有效的代理。

Dapr Agents 如何解决这些问题

Dapr Agents 采用了一种不同的方法,它基于 Dapr 构建,利用其经过验证的 API 和模式,包括工作流发布订阅消息状态管理服务通信。这种集成消除了从零开始重新创建基础组件的需要。

通过与 Dapr 的运行时和模块化组件集成,Dapr Agents 赋予开发者构建和部署代理的能力,这些代理可以作为更大系统内的协作服务工作。无论是试验单个代理还是编排涉及多个代理的工作流,Dapr Agents 都允许团队专注于 LLM 驱动的代理的智能和行为,同时利用经过验证的框架来实现可扩展性和可靠性。

原则

代理中心设计

Dapr Agents 的设计理念是将由 LLM 驱动的代理置于任务执行和工作流编排的核心。这一原则强调:

  • LLM 驱动的代理:Dapr Agents 使创建能够利用 LLM 进行推理、动态决策和自然语言交互的代理成为可能。
  • 自适应任务处理:Dapr Agents 中的代理配备了灵活的模式,如工具调用和推理循环(例如 ReAct),使它们能够自主处理复杂且不断变化的任务。
  • 多代理系统:Dapr Agents 的框架允许代理作为模块化的、可复用的构建块,无缝集成到工作流中,无论是独立运行还是协作运行。

虽然 Dapr Agents 以代理为中心,但它也认识到在确定性工作流或更简单的任务序列中直接使用 LLM 的多功能性。在代理内置的任务处理模式(如 tool callingReAct 循环)不必要的场景中,LLM 可以作为推理和决策的核心组件。这种灵活性确保用户可以调整 Dapr Agents 以满足各种需求,而不局限于单一方法。

模块化原则

由持久化工作流支持

Dapr Agents 将持久性置于其架构的核心,利用 Dapr 工作流作为持久代理执行和确定性多代理编排的基础。

  • 持久化代理执行:DurableAgents 本质上由工作流支持,确保所有 LLM 调用和工具执行保持持久化、可审计和可恢复。工作流检查点保证代理可以从任何故障点恢复,同时保持状态一致性。
  • 确定性多代理编排:工作流提供对任务依赖和多个代理之间协调的集中控制。Dapr 的代码优先工作流引擎能够可靠地编排复杂的业务流程,同时在适当的地方保持代理自主性。

通过将工作流作为基础层集成,Dapr Agents 使系统能够结合确定性执行的可靠性和 LLM 驱动的代理的智能,确保可靠性和可扩展性。

模块化组件模型

Dapr Agents 利用 Dapr 的可插拔组件框架和构建块来简化开发并增强灵活性:

  • 核心功能的构建块:Dapr 提供 API 构建块,如发布订阅消息、状态管理、服务调用等,以解决常见的微服务挑战并促进最佳实践。
  • 可互换的组件:每个构建块运行在可互换的组件上(例如 Redis、Kafka、Azure CosmosDB),允许您在不更改应用程序代码的情况下替换实现。
  • 无缝转换:使用默认配置在本地开发,并通过简单地更新组件定义轻松部署到云环境。

消息驱动的通信

Dapr Agents 强调使用发布订阅消息进行代理之间的事件驱动通信。这一原则确保:

  • 解耦架构:用于可扩展性和模块化的异步通信。
  • 实时适应性:代理动态响应事件,实现更快、更灵活的任务执行。
  • 事件驱动工作流:通过将发布订阅消息与工作流能力相结合,代理可以通过事件流协作,同时参与更大的编排工作流,实现自主协调和结构化任务执行。

消息原则

解耦的基础设施设计

Dapr Agents 确保代理与底层基础设施之间的清晰分离,强调简单性、可扩展性和适应性:

  • 代理简单性:代理纯粹专注于推理和任务执行,而发布订阅消息、路由和验证由模块化基础设施组件外部管理。
  • 可扩展和适应性系统:通过卸载非代理特定的职责,Dapr Agents 允许代理独立扩展并无缝适应新的用例或集成。

解耦原则

Dapr Agents 的优势

作为一等公民的可扩展工作流

Dapr Agents 使用持久执行工作流引擎,保证每个代理任务都能执行完成,无论网络中断、节点崩溃和其他破坏性故障。开发者无需了解底层工作流引擎的概念——只需编写一个执行任意数量任务的代理,这些任务将自动分布在集群中。如果任何任务失败,它将被重试并从离开的地方恢复其状态。

成本效益的 AI 采用

Dapr Agents 基于 Dapr 的 Workflow API 构建,该 API 将每个代理表示为一个 actor,这是一个计算和状态的单一单元,是线程安全的且原生分布式的。这种设计实现了缩容至零的架构,最大限度地减少基础设施成本,使各规模的组织都能采用 AI。底层的虚拟 actor 模型允许数千个代理在单台机器上按需运行,并在从零扩展时保持低延迟。当不使用时,代理会被系统回收,但会保留其状态直到再次需要。这种设计消除了性能和资源效率之间的权衡。

以数据为中心的 AI 代理

通过内置的对超过 50 个企业数据源的连接,Dapr Agents 高效地处理结构化和非结构化数据。从基本的 PDF 提取到大规模数据库交互,它使数据驱动的 AI 工作流只需最少的代码更改即可实现。Dapr 的绑定状态存储,以及 MCP 支持,为代理数据摄取提供了对众多数据源的访问。

加速开发

Dapr Agents 提供 AI 功能,为开发者提供完整的 API 表面来解决常见问题,包括:

  • 灵活的提示
  • 结构化输出
  • 多个 LLM 提供商
  • 上下文记忆
  • 智能工具选择
  • MCP 集成
  • 多代理通信

集成的安全性和可靠性

通过基于 Dapr 构建,平台和基础设施团队可以将 Dapr 的弹性策略应用于 Dapr Agents 使用的数据库和消息代理组件。这些策略包括超时、重试/退避策略和断路器。对于安全性,Dapr 提供了将对特定数据库或消息代理的访问范围限定到一个或多个代理应用部署的选项。此外,Dapr Agents 使用 mTLS 加密其底层组件之间的通信。

内置的消息传递和状态基础设施

  • 服务到服务调用:通过内置的服务发现、错误处理和分布式跟踪,实现代理之间的直接通信。代理可以在多代理工作流中使用此功能进行同步消息传递。
  • 发布和订阅:通过共享消息总线支持代理之间的松散耦合协作。这实现了任务分发和协调的实时、事件驱动的交互。
  • 持久化工作流:定义长期的、持久的工作流,将确定性过程与基于 LLM 的决策相结合。Dapr Agents 使用它来编排复杂的多步骤代理工作流。
  • 状态管理:提供灵活的键值存储,使代理能够在交互之间保留上下文,确保工作流期间的连续性和适应性。
  • LLM 集成:使用 Dapr Conversation API抽象 LLM 推理 API 以进行聊天补全,并为其他 LLM 集成(如嵌入和音频处理)提供本机客户端。

供应商中立和开源

作为 CNCF 的一部分,Dapr Agents 是供应商中立的,消除了对锁定、知识产权风险或专有限制的担忧。组织可以使用他们可以审计和贡献的开源软件,对其 AI 应用程序获得完全的灵活性和控制权。

4 - 核心概念

了解 Dapr Agents 的核心概念

Dapr Agents 提供了一种结构化方式,用于构建和编排使用 LLM 的应用,让你无需陷入基础设施细节,同时具备持久性保证。它的核心目标,是通过抽象掉 LLM、工具、内存管理以及分布式系统的复杂性,使开发者能够把注意力集中在 AI 应用的业务逻辑上。在这一框架中,agent 是最基础的构建块。

Agents

Agent 是由大语言模型(LLM)驱动的自治单元,旨在执行任务、对问题进行推理,并在工作流中协作。作为智能构建块,agent 将推理能力与工具集成、内存和协作特性结合起来,以达成目标结果。

Agents 概念图

Dapr Agents 提供两类 agent,分别适用于不同场景:

Agent

Agent 类是一种会话式 agent,使用语言模型管理工具调用与对话。它提供同步执行,并内置对话记忆。

@tool
def my_weather_func() -> str:
    """获取当前天气。"""
    return "It's 72°F and sunny"

async def main():
    weather_agent = Agent(
        name="WeatherAgent",
        role="Weather Assistant",
        goal="Provide timely weather updates across cities",
        instructions=["Help users with weather information"],
        tools=[my_weather_func],
        memory = AgentMemoryConfig(
            store=ConversationDaprStateMemory(
                store_name="historystore",
                session_id="some-id",
            )
        ),
    )

    response1 = await weather_agent.run("What's the weather?")
    response2 = await weather_agent.run("How about now?")

这个示例展示了如何创建一个带工具集成的简单 agent。该 agent 会同步处理查询,并借助 Dapr State Store API 在多次交互之间保持对话上下文。

Durable Agent

DurableAgent 类是基于工作流的 agent。它在标准 Agent 的基础上结合了 Dapr Workflows,用于长时间运行、容错且具备持久性的执行。它提供持久状态管理、自动重试机制,以及跨故障场景的确定性执行。


from dapr_agents.workflow.runners import AgentRunner

async def main():
    travel_planner = DurableAgent(
        name="TravelBuddy",
        role="Travel Planner",
        goal="Help users find flights and remember preferences",
        instructions=["Help users find flights and remember preferences"],
        tools=[search_flights],
        memory = AgentMemoryConfig(
            store=ConversationDaprStateMemory(
                store_name="conversationstore",
                session_id="travel-session",
            )
        )
    )

    runner = AgentRunner()

    try:
        itinerary = await runner.run(
            travel_planner,
            payload={"task": "Plan a 3-day trip to Paris"},
        )
        print(itinerary)
    finally:
        runner.shutdown(travel_planner)

这个示例演示了如何创建一个由工作流支撑、可在后台自主运行的 agent。AgentRunner 会为你调度工作流、等待其完成,并确保该 agent 即使只被触发一次,也能在重启后继续执行。

关键特性:

  • 基于 Dapr Workflows 的工作流执行
  • 跨会话与故障场景的持久工作流状态管理
  • 自动重试与恢复机制
  • 带检查点的确定性执行
  • 内置消息路由与 agent 通信能力
  • 适用于 DurableAgent 的 AgentRunner 模式:临时执行(runner.run(...))、pub/sub 订阅(runner.subscribe(...))和 FastAPI 服务(runner.serve(...)
  • 支持复杂编排模式与多 agent 协作

适用场景:

  • 跨越时间或多个系统的多步骤工作流
  • 需要保证进度跟踪与状态持久化的任务
  • 操作可能暂停、失败,或需要在不丢数据的情况下恢复的场景
  • 复杂的 agent 编排与多 agent 协作
  • 需要容错与可扩展性的生产系统

总结如下:

Agent 类型内存类型执行交互模式状态
Agent内存或持久化临时嵌入式已弃用(v1.0.0-rc.1)
DurableAgent持久化持久PubSub / HTTP / 嵌入式推荐
  • 普通 Agent:交互是同步的——你发送对话提示并立即获得响应。对话可以保存在内存中,也可以持久化,但执行本身是临时的,重启后不会继续。

  • DurableAgent(由工作流支撑):交互是异步的——你只需触发一次 agent,它会在后台自主运行直到完成。对话状态和执行过程都会被持久化,并且可以在失败或重启后恢复。

Core Agent Features

Agent 系统本质上是分布式系统,需要多种行为模式及配套基础设施。

LLM Integration

Dapr Agents 提供统一接口,用于连接 LLM 推理 API。借助这一抽象,开发者可以把 agent 无缝接入先进语言模型,用于推理与决策。框架内置了适配不同提供方与模态的多个 LLM 客户端:

  • DaprChatClient:通过 Dapr 的 Conversation API 提供统一的 LLM 交互接口,内置安全能力(scopes、secrets、PII 混淆)、弹性能力(超时、重试、熔断器),并通过 OpenTelemetry 与 Prometheus 提供可观测性
  • OpenAIChatClient:全面支持 OpenAI 模型,包括聊天、embeddings 与音频
  • HFHubChatClient:面向 Hugging Face 模型,支持聊天与 embeddings
  • NVIDIAChatClient:面向 NVIDIA AI Foundation 模型,支持本地推理与聊天
  • ElevenLabs:支持语音与声音能力

Prompt Flexibility

Dapr Agents 支持灵活的提示模板,以塑造 agent 的行为和推理方式。用户可以在提示中定义占位符,为推理调用动态注入上下文。借助 Jinja 模板 与 Python f-string 格式化,用户可以加入循环、条件和变量,从而精确控制提示的结构与内容。这种灵活性保证了 LLM 的响应能够贴合当前任务,同时为各种场景带来模块化与适配性。

Structured Outputs

Dapr Agents 中的 agent 利用结构化输出能力(如 OpenAI 的 Function Calling)生成可预测且可靠的结果。这些输出遵循 JSON Schema Draft 2020-12OpenAPI Specification v3.1.0 标准,因而更易于互操作与工具集成。

# 定义数据模型
class Dog(BaseModel):
    name: str
    breed: str
    reason: str

# 初始化聊天客户端
llm = OpenAIChatClient()

# 获取结构化响应
response = llm.generate(
    messages=[UserMessage("One famous dog in history.")], response_format=Dog
)

print(json.dumps(response.model_dump(), indent=2))

这个示例展示了 LLM 如何按照某个 schema 生成结构化数据。Pydantic 模型(Dog)定义了期望的精确结构与数据类型,而 response_format 参数会指示 LLM 返回与该模型匹配的数据,从而为后续处理提供一致且可预测的输出。

Tool Calling

工具调用是自主 agent 设计中的关键模式,它允许 AI agent 基于用户输入,动态与外部工具交互。agent 会为特定任务动态选择合适工具,利用 LLM 分析需求并决定最佳动作。

@tool(args_model=GetWeatherSchema)
def get_weather(location: str) -> str:
    """根据地点获取天气信息。"""
    import random
    temperature = random.randint(60, 80)
    return f"{location}: {temperature}F."

每个工具都应带有清晰的 docstring,帮助 LLM 理解何时应使用它。@tool 装饰器将函数标记为工具,而 Pydantic 模型(GetWeatherSchema)则定义了用于结构化校验的输入参数。

工具调用流程

  1. 用户提交查询,说明任务以及可用工具。
  2. LLM 分析查询,并为当前任务选择合适工具。
  3. LLM 返回结构化 JSON 输出,其中包含工具的唯一 ID、名称和参数。
  4. AI agent 解析该 JSON,用提供的参数执行工具,并把结果作为 tool message 发回。
  5. 随后,LLM 会在用户上下文中总结工具执行结果,给出完整的最终响应。

这一能力既来自 LLM 的参数化知识,也通过 Function Calling 得到增强,从而确保工具被高效且准确地调用。

Tool Execution Modes

当 LLM 在单轮中返回多个工具调用时,DurableAgent 可通过 AgentExecutionConfig.tool_execution_mode 配置两种执行模式:

模式枚举值行为
并行(默认)ToolExecutionMode.PARALLEL来自单个 LLM 回合的所有工具调用会被并发分派并等待完成。适用于彼此独立的工具,可获得最佳延迟表现。
顺序ToolExecutionMode.SEQUENTIAL工具调用会按照 LLM 返回的顺序逐个执行。适用于存在副作用、且依赖同一轮前序结果的场景。
from dapr_agents.agents.configs import AgentExecutionConfig, ToolExecutionMode

travel_planner = DurableAgent(
    name="TravelBuddy",
    ...
    execution=AgentExecutionConfig(
        max_iterations=10,
        tool_execution_mode=ToolExecutionMode.SEQUENTIAL,
    ),
)

MCP Support

Dapr Agents 内置了对 Model Context Protocol (MCP) 的支持,使 agent 能通过标准化接口动态发现并调用外部工具。借助提供的 MCPClient,agent 可以通过三种传输方式连接 MCP 服务器:用于本地开发的 stdio、面向远程或分布式环境的 sse,以及可流式传输的 HTTP。

client = MCPClient()
await client.connect_sse("local", url="http://localhost:8000/sse")

# 将 MCP 工具转换为 AgentTool 列表
tools = client.get_all_tools()

一旦连接完成,MCP 客户端会从服务器拉取全部可用工具,并将其准备为可立即在 agent 工具集内使用的形式。这使 agent 无需硬编码或预加载,即可纳入外部进程暴露的能力——例如本地 Python 脚本或远程服务。agent 可以在运行时调用这些工具,并根据当前 MCP 服务器所提供的能力扩展自身行为。

Memory

Agent 会在多次交互之间保留上下文,从而提升响应的一致性与适应性。内存选项范围很广:从用于管理聊天历史的简单内存列表,到用于语义检索的向量数据库,再到与 Dapr 状态存储 集成的持久化内存,可覆盖 28 种状态存储提供程序的高级场景。

from dapr_agents import Agent, DurableAgent
from dapr_agents.agents.configs import AgentMemoryConfig
from dapr_agents.memory import (
    ConversationDaprStateMemory,
    ConversationListMemory,
    ConversationVectorMemory,
)

# 1. ConversationListMemory(简单内存)- 默认
memory_list = ConversationListMemory()

# 2. ConversationVectorMemory(向量存储)
memory_vector = ConversationVectorMemory(
    vector_store=your_vector_store_instance,
    distance_metric="cosine",
)

# 3. 通过 AgentMemoryConfig 使用 ConversationDaprStateMemory(Dapr 状态存储)
durable_memory = AgentMemoryConfig(
    store=ConversationDaprStateMemory(
        store_name="historystore",  # Dapr 组件名称
        session_id="my-session",
    )
)

# 与普通 Agent 一起使用(直接传入 memory 实例)
agent = Agent(
    name="MyAgent",
    role="Assistant",
    memory=memory_list,
)

# 与 DurableAgent 一起使用(传入 AgentMemoryConfig)
travel_planner = DurableAgent(
    name="TravelBuddy",
    memory=durable_memory,
    # ... 其他配置 ...
)

ConversationListMemory 是未显式指定内存时的默认实现。它使用 Python 列表提供快速、临时的存储,适合开发与测试。Dapr 提供的这些内存实现(都位于 dapr_agents.memory)彼此可互换,你无需修改 agent 逻辑或部署模型,就可以在它们之间切换。

内存实现类型持久化搜索使用场景
ConversationListMemory(默认)内存线性开发
ConversationVectorMemory向量存储语义RAG / AI 应用
ConversationDaprStateMemoryDapr 状态存储查询生产环境

ConversationVectorMemory 可由任意受支持的向量存储实现作为后端:

向量存储后端说明
ChromaChromaVectorStoreChromaDB可内存或持久化;无需额外基础设施
PostgreSQLPostgresVectorStorepgvector 扩展需要启用 pgvector 的 PostgreSQL
RedisRedisVectorStoreRedis Stack / Redis with Search需要 redisvl
from dapr_agents.storage.vectorstores import RedisVectorStore
from dapr_agents.document.embedder.openai import OpenAIEmbedder
from dapr_agents.memory import ConversationVectorMemory

vector_store = RedisVectorStore(
    url="redis://localhost:6379",
    index_name="my_agent",
    embedding_function=OpenAIEmbedder(),
    embedding_dimensions=1536,
)

memory = ConversationVectorMemory(
    vector_store=vector_store,
    distance_metric="cosine",
)

Agents as Tools

Dapr Agents 支持在 DurableAgent 的推理循环中,把其他 agent——无论是 Dapr Agents 自身,还是第三方 agent 框架——作为工具来调用。这样,父 agent 可以把子任务委派给专门的子 agent,并在不依赖 pub/sub 消息代理的情况下组合出多 agent 系统。

在同一注册表中注册的 agent,会自动作为可用工具出现。这同样适用于调用第三方框架中的 agent。或者,你也可以使用 dapr_agents.tool.workflow 中的 agent_to_tool,以进行显式接线、跨应用路由,或调用其他框架中的 agent:

from dapr_agents.tool.workflow import agent_to_tool

# 将独立 agent 作为一次工具调用来调用
aragorn_tool = agent_to_tool(
    "aragorn",
    description="Military Strategy. Goal: Lead the forces of Gondor.",
    target_app_id="aragorn-app",
)
# 在 DurableAgent 中把一个 agent 当作工具使用
frodo = DurableAgent(
    name="frodo",
    role="Ring Bearer",
    goal="Carry the One Ring to Mordor",
    tools=[aragorn_tool],
    ...
)

当 LLM 调用这些工具之一时,Dapr Agents 会把目标 agent 的工作流调度为一个 DurableAgent(子工作流),并返回结果——同时透明地处理跨应用路由与结果编组。

参数说明
agent_name目标 agent 的名称(用于派生工具名和工作流 ID)
description展示给父级 LLM 的工具 schema 中的人类可读描述
target_app_id跨应用路由时使用的 Dapr app-id;若为 None,则表示进程内调用
framework面向非 Dapr Agents 目标时的框架名称(例如 "openai""langgraph"
workflow_name显式指定的 Dapr 工作流名称;优先级高于 framework

完整可运行示例见 Agents as Tools 示例

Agent Runner

AgentRunner 为 DurableAgent 提供三种互补的托管模式:

  1. run:直接从 Python 触发持久工作流(适合 CLI、测试、notebook),并可选择等待完成。
  2. subscribe:自动为 agent 上所有使用 @message_router 装饰的方法(包括 DurableAgent.agent_workflow)完成注册,使配置主题上的 CloudEvent 在通过 message_model 校验后被调度为工作流运行。
  3. serve:把 subscribe 与 FastAPI 路由注册和自动启动的 Uvicorn 服务器组合起来,将 agent 作为 Web 服务托管。默认暴露 POST /agent/run(调度 @workflow_entry)和 GET /agent/instances/{instance_id}(获取工作流状态),你也可以传入自己的 FastAPI 应用,或自定义 host / port / path。
travel_planner = DurableAgent(
    name="TravelBuddy",
    role="Travel Planner",
    goal="Help humans find flights and remember preferences",
    instructions=[
        "Find flights to destinations",
        "Remember user preferences",
        "Provide clear flight info.",
    ],
    tools=[search_flights],
)
runner = AgentRunner()

下面的代码片段会复用这个 travel_planner 实例,分别说明每种模式。

1. Ad-hoc execution with runner.run(...)

当你希望直接从 Python 代码(测试、CLI、notebook 等)触发持久工作流时,可使用 run。runner 会定位 agent 的 @workflow_entry 并进行调度。.run() 是阻塞调用:它会触发 agent,并等待其完成。

result = await runner.run(
    travel_planner,
    payload={"task": "Plan a 3-day trip to Paris"},
)
print(result)

这种模式适合同步自动化场景,或你需要以编程方式拿到最终响应时使用。若想触发后立即返回,可传入 wait=False

2. Pub/Sub subscriptions with runner.subscribe(...)

subscribe 会扫描 agent 上所有带有 @message_router 的方法——包括内置的 agent_workflow——并根据 AgentPubSubConfig 中定义的主题与 schema,自动注册所需的 Dapr 订阅。每个传入的 CloudEvent 都会先依据声明的 message_model(例如 TriggerAction)校验,再由 runner 调度工作流入口。

runner.subscribe(travel_planner)
await wait_for_shutdown()

你可以增加自己的 @message_router 方法,以支持更多主题或广播通道——runner 会自动发现并把消息路由到正确处理器。可配合 wait_for_shutdown()(位于 dapr_agents.workflow.utils.core)等辅助方法,让进程持续运行,直到你手动停止。

3. FastAPI services with runner.serve(...)

serve 是把 DurableAgent 作为 Web 服务运行的一行式方法。它会先调用 subscribe(...),再启动一个 FastAPI 应用(除非你传入自定义应用),并默认提供两个端点:

  • POST /agent/run:根据 agent 的 @workflow_entry 签名校验 JSON 请求体,并调度新的工作流实例。
  • GET /agent/instances/{instance_id}:代理查询工作流状态(如有需要,也可包含 payload)。
runner.serve(
    travel_planner,
    port=8001,
)

由于工作流具备持久性,/run 端点会立即返回实例 ID,即使 agent 仍在后台继续工作。你既可以把生成的 FastAPI 路由挂载到更大的应用中,也可以让 serve 自己运行 Uvicorn 循环,用于独立部署。

Multi-agent Systems (MAS)

虽然构建一个完全自主、能处理各种任务的 agent 很诱人,但在实践中,更有效的做法通常是把问题拆分为多个具备适当工具与指令的专门 agent,再去协调它们之间的交互。

多 agent 系统(MAS)会把工作流执行分配给多个协同 agent,以高效达成共同目标。这个过程通常被称为 agent 编排。与单体式 agent 设计相比,它能带来更好的专业化、可扩展性与可维护性。

Agent 编排

Dapr Agents 主要通过 Dapr WorkflowsDapr PubSub 支持两种编排方式:

  • 基于确定性工作流的编排 —— 提供清晰、可重复的流程,具有预定义步骤与决策点
  • 事件驱动编排 —— 通过基于消息的协作,使 agent 之间形成动态、自适应配合

这两种方式都会使用中心编排器来协调多个专门 agent,每个 agent 负责特定任务或领域,从而实现高效任务分发与系统级协同。

Deterministic Workflows

工作流是结构化过程,其中 LLM agent 与工具按照预定义顺序协作,以完成复杂任务。与完全自主、独立做出所有决策的 agent 不同,工作流在工作流定义中提供结构性与可预测性,在 LLM agent 中提供智能与灵活性,在 Dapr 工作流引擎中提供可靠性与持久性。

这种方式尤其适合业务关键型应用:你既需要 LLM 的智能,也需要传统软件系统的可靠性。

import time

import dapr.ext.workflow as wf

wfr = wf.WorkflowRuntime()

@wfr.workflow(name="support_workflow")
def support_workflow(ctx: wf.DaprWorkflowContext, request: dict) -> str:
    triage_result = yield ctx.call_child_workflow(
        workflow="agent_workflow",
        input={"task": f"Assist with the following support request:\n\n{request}"},
        app_id="triage-agent",
    )
    if triage_result:
        print("Triage result:", triage_result.get("content", ""), flush=True)

    recommendation = yield ctx.call_child_workflow(
        workflow="agent_workflow",
        input={"task": triage_result.get("content", "")},
        app_id="expert-agent",
    )
    if recommendation:
        print("Recommendation:", recommendation.get("content", ""), flush=True)

    return recommendation.get("content", "") if recommendation else ""

wfr.start()
time.sleep(5)

client = wf.DaprWorkflowClient()
request = {
    "customer": "alice",
    "issue": "Unable to access dashboard after recent update",
}
instance_id = client.schedule_new_workflow(
    workflow=support_workflow,
    input=request,
)
client.wait_for_workflow_completion(instance_id, timeout_in_seconds=60)
wfr.shutdown()

这里使用 call_child_workflow 调用两个 Dapr Agent 的工作流,并把前一个的输出作为后一个的输入。为此,DurableAgent 需要按如下方式运行:

from dapr_agents import DurableAgent
from dapr_agents.agents.configs import AgentMemoryConfig
from dapr_agents.llm.dapr import DaprChatClient
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.workflow.runners.agent import AgentRunner

expert_agent = DurableAgent(
    name="expert_agent",
    role="Technical Support Specialist",
    goal="Provide recommendations based on customer context and issue.",
    instructions=[
        "Provide a clear, actionable recommendation to resolve the issue.",
    ],
    llm=DaprChatClient(component_name="llm-provider"),
    memory=AgentMemoryConfig(
        store=ConversationDaprStateMemory(
            store_name="agent-memory",
            session_id=f"expert-agent-session",
        )
    ),
)
runner = AgentRunner()
try:
    runner.serve(expert_agent, port=8001)
finally:
    runner.shutdown(expert_agent)

Workflow Patterns

工作流能够通过结构化编排实现多种 agent 模式,包括 Prompt Chaining、Routing、Parallelization、Orchestrator-Workers、Evaluator-Optimizer、Human-in-the-loop 等。若要查看这些模式的详细实现与示例,请参见 Patterns 文档

Message Router Workflows

@message_router 装饰器会把工作流直接绑定到 Dapr Pub/Sub 主题上,使每条通过校验的消息都会自动调度一个工作流实例。这一模式也用于 message-router quickstart:你只需把 CloudEvent 负载推送到主题,后续就会立即由 LLM 支撑的活动接手。

from pydantic import BaseModel
from dapr_agents.workflow.decorators.routers import message_router

class StartBlogMessage(BaseModel):
    topic: str

@message_router(
    pubsub="messagepubsub",
    topic="blog.requests",
    message_model=StartBlogMessage,
)
def blog_workflow(ctx: DaprWorkflowContext, wf_input: dict) -> str:
    outline = yield ctx.call_activity(
        create_outline, input={"topic": wf_input["topic"]}
    )
    post = yield ctx.call_activity(write_post, input={"outline": outline})
    return post

启动期间,可调用 register_message_routes(targets=[blog_workflow], dapr_client=client),以自动配置订阅、schema 校验与工作流调度。这让工作流定义同时成为编排与事件入口的单一事实来源。

Workflows vs. Durable Agents

DurableAgent 与基于工作流的 agent 编排,底层都使用 Dapr 工作流来获得持久性与可靠性,但两者在控制流由谁决定这一点上存在差异。

方面WorkflowsDurable Agents
控制方式开发者定义流程Agent 决定下一步
可预测性更高更低
灵活性整体结构固定、步骤内灵活完全灵活
可靠性很高(由工作流引擎保证)很高(由底层 agent 实现保证)
复杂度结构化工作流模式动态、灵活的执行路径
使用场景业务流程、受监管领域开放式研究、创造性任务

关键差异在于控制流的决定方式:使用 DurableAgent 时,底层工作流由 LLM 的规划决策动态生成,且整个过程在单个 agent 上下文中执行;而在确定性工作流中,开发者会显式定义一个或多个 LLM 交互之间的协调方式,从而为多任务或多 agent 提供结构化编排。

Event-Driven Orchestration

事件驱动的 agent 编排,使多个专门 agent 能通过异步 Pub/Sub 消息传递 进行协作。这种方式带来了强大的协同解题能力、并行处理能力,以及在多个专门 agent 之间划分职责的能力,并通过服务隔离获得弹性,通过独立扩缩容获得灵活性。

Core Participants

多 agent 协调系统中的核心参与者如下。

Durable Agents

每个 agent 都作为独立服务运行,拥有自己的生命周期,并以启用 pub/sub 的标准 DurableAgent 进行配置:

import asyncio

from dapr_agents.agents.configs import (
    AgentMemoryConfig,
    AgentProfileConfig,
    AgentPubSubConfig,
    AgentRegistryConfig,
    AgentStateConfig,
)
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner
from dapr_agents.workflow.utils.core import wait_for_shutdown

registry = AgentRegistryConfig(
    store=StateStoreService(store_name="agentregistrystore"),
    team_name="fellowship",
)

frodo = DurableAgent(
    profile=AgentProfileConfig(
        name="Frodo",
        role="Ring Bearer",
        instructions=["Speak like Frodo, with humility and determination."],
    ),
    pubsub=AgentPubSubConfig(
        pubsub_name="messagepubsub",
        agent_topic="fellowship.frodo.requests",
        broadcast_topic="fellowship.broadcast",
    ),
    state=AgentStateConfig(
        store=StateStoreService(store_name="workflowstatestore", key_prefix="frodo:")
    ),
    registry=registry,
    memory=AgentMemoryConfig(
        store=ConversationDaprStateMemory(
            store_name="memorystore",
            session_id="frodo-session",
        )
    ),
)

async def main():
    runner = AgentRunner()
    try:
        runner.subscribe(frodo)
        await wait_for_shutdown()
    finally:
        runner.shutdown(frodo)

asyncio.run(main())

Orchestrator

编排器负责协调 agent 之间的交互并管理对话流,包括选择合适的 agent、管理交互顺序以及跟踪进度。Dapr Agents 提供三种编排策略:Random、RoundRobin 和基于 LLM 的编排。

from dapr_agents.agents.configs import (
    AgentExecutionConfig,
    AgentPubSubConfig,
    AgentRegistryConfig,
    AgentStateConfig,
)
from dapr_agents.llm.openai import OpenAIChatClient
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner
import dapr.ext.workflow as wf

llm_orchestrator = LLMOrchestrator(
    name="LLMOrchestrator",
    llm=OpenAIChatClient(),
    pubsub=AgentPubSubConfig(
        pubsub_name="messagepubsub",
        agent_topic="llm.orchestrator.requests",
        broadcast_topic="fellowship.broadcast",
    ),
    state=AgentStateConfig(
        store=StateStoreService(
            store_name="workflowstatestore", key_prefix="llm.orchestrator:"
        )
    ),
    registry=AgentRegistryConfig(
        store=StateStoreService(store_name="agentregistrystore"),
        team_name="fellowship",
    ),
    execution=AgentExecutionConfig(max_iterations=3),
    runtime=wf.WorkflowRuntime(),
)

runner = AgentRunner()
runner.serve(llm_orchestrator, port=8004)

基于 LLM 的编排器会使用智能 agent 选择,实现上下文感知的决策;而 Random 与 RoundRobin 则为更简单的场景提供替代协调策略。runner 会把编排器作为 Dapr 应用或 HTTP 服务持续运行,使客户端能够通过 topic 发布任务,或通过 REST 调用它。

由于 DurableAgent.agent_workflow 与上述编排器都使用了 @message_router(message_model=TriggerAction) 装饰,runner.subscribe(...) 会自动根据 AgentPubSubConfig 中声明的主题完成接线,并在调度 @workflow_entry 之前,对每个传入的 CloudEvent 做 schema 校验。你还可以为同一个 agent 添加额外的 message router(每个都可拥有自己的 message_model);runner 下次启动时会自动发现它们,并扩展订阅列表。

Communication Flow

Agent 通过事件驱动的 pub/sub 系统通信,这种机制支持异步通信、解耦架构、可扩展交互与可靠消息投递。典型协作流程包括:客户端提交查询、编排器选择 agent、agent 处理并返回响应,以及在任务完成前持续进行迭代协调。

这种方式尤其适合需要多领域专长的复杂问题求解、来自不同视角的创造性协作、角色扮演场景,以及大任务的分布式处理。

How Messaging Works

消息传递把工作流中的 agent 连接起来,支持实时通信与协调。它是事件驱动交互的骨干,使 agent 无需直接连接,也能高效协同工作。

通过消息传递,agent 可以:

  • 跨任务协作:agent 交换消息以共享更新、广播事件或传递任务结果。
  • 编排工作流:任务通过已发布消息被触发和协调,使工作流能够动态调整。
  • 响应事件:agent 通过订阅相关主题并处理实时事件,适应不断变化的环境。

借助消息传递,工作流可以保持模块化与可扩展性;各 agent 专注自身职责,同时无缝参与更大的系统。

Message Bus and Topics

消息总线是负责管理主题与消息分发的中心系统。agent 通过消息总线发送和接收消息:

  • 发布消息:agent 将消息发布到指定主题,使所有订阅该主题的 agent 都能获得信息。
  • 订阅主题:agent 订阅与自身职责相关的主题,从而只接收它们真正需要的消息。
  • 广播更新:多个 agent 可以订阅同一主题,以便对共享事件或更新作出响应。

Why Pub/Sub Messaging for Agentic Workflows?

对于事件驱动的 agent 工作流而言,Pub/Sub 消息传递之所以关键,是因为它:

  • 解耦组件:agent 发布消息时无需知道由哪些 agent 接收,从而推动模块化与可扩展设计。
  • 实现实时通信:消息会在事件发生时送达,使 agent 能立即作出反应。
  • 促进协作:多个 agent 可以订阅同一主题,便于共享更新或拆分职责。
  • 支持可扩展性:消息总线能够让通信自然扩展,无论你是在增加新 agent、扩展工作流,还是适应不断变化的需求。agent 始终保持松耦合,使工作流能够平滑演进而不被打断。

这一消息传递框架保证了 agent 高效运行、工作流保持灵活,并使系统能够动态扩展。

5 - 代理模式

构建代理系统常用设计模式与使用场景

Dapr Agents 简化了代理系统的实现,从简单的增强型 LLM 到企业环境中的完全自主代理。以下各节描述了可以从 Dapr Agents 中受益的多种应用模式。

概述

代理系统使用设计模式(如反思、工具使用、规划和多代理协作)来实现比简单单次提示交互更好的结果。与其将"代理"视为二元分类,不如将系统视为具有不同程度代理性的系统更有用。

这一范围从简单的工作流(仅提示模型一次)到能够以更大自主性执行多个迭代步骤的复杂系统。 有两种基本的架构方法:

  • 工作流:通过预定义代码路径编排 LLM 和工具的系统(更具规范性)
  • 代理:LLM 动态引导自身流程和工具使用的系统(更具自主性)

一端是可预测的工作流,具有明确定义的决策路径和确定性结果。另一端是能够动态引导自身策略的 AI 代理。虽然完全自主的代理看似吸引人,但工作流通常为明确定义的任务提供更好的可预测性和一致性。这与企业对可靠性和可维护性的要求一致。

本文档中的模式从增强型 LLM 开始,然后介绍基于工作流的方法(提供可预测性和控制),再转向更具自主性的模式。每个模式都针对特定的使用场景,并在确定性结果和自主性之间提供不同的权衡。

增强型 LLM

增强型 LLM 模式是任何代理系统的基础构建块。它通过外部能力(如记忆和工具)增强语言模型,为 AI 驱动应用提供基本但强大的基础。

Diagram showing how the augmented LLM pattern works

此模式非常适合需要增强型 LLM 但不需要复杂编排或自主决策的场景。增强型 LLM 可以访问外部工具、维护对话历史,并在交互中提供一致的响应。

使用场景:

  • 记住用户偏好的个人助手
  • 访问产品信息的客户支持代理
  • 检索和分析信息的研究工具

使用 Dapr Agents 实现:

from dapr_agents import DurableAgent, tool

@tool
def search_flights(destination: str) -> List[FlightOption]:
    """Search for flights to the specified destination."""
    # Mock flight data (would be an external API call in a real app)
    return [
        FlightOption(airline="SkyHighAir", price=450.00),
        FlightOption(airline="GlobalWings", price=375.50)
    ]

# Create agent with memory and tools
travel_planner = DurableAgent(
    name="TravelBuddy",
    role="Travel Planner Assistant",
    instructions=["Remember destinations and help find flights"],
    tools=[search_flights],
)

Dapr Agents 自动处理:

  • 代理配置 - 通过角色和指令进行简单配置以引导 LLM 行为
  • 记忆持久化 - 代理管理对话记忆
  • 工具集成 - @tool 装饰器处理输入验证、类型转换和输出格式化

任何代理系统的基础构建块都是增强型 LLM——一种通过外部能力(如记忆、工具和检索)增强的语言模型。在 Dapr Agents 中,这由 DurableAgent 类表示。虽然简单的 Agent 类也存在,但自 v1.0.0-rc.1 起已弃用DurableAgent 是所有新开发推荐的选项。增强型 LLM 能力本身通常不足以应对复杂的企业场景,因此它们通常与工作流编排结合使用,为多步骤流程提供结构、可靠性和协调。

提示链

提示链模式通过将任务分解为一系列步骤来应对复杂需求,其中每个 LLM 调用处理前一个的输出。此模式允许更好地控制整个流程、步骤之间的验证以及每个步骤的专业化。

Diagram showing how the prompt chaining pattern works

使用场景:

  • 内容生成(先创建大纲,然后扩展,然后审核)
  • 多阶段分析(将复杂分析分解为顺序步骤)
  • 质量保证工作流(在处理步骤之间添加验证)

使用 Dapr Agents 实现:

from dapr_agents import DaprWorkflowContext, workflow

@workflow(name='travel_planning_workflow')
def travel_planning_workflow(ctx: DaprWorkflowContext, user_input: str):
    # Step 1: Extract destination using a simple prompt (no agent)
    destination_text = yield ctx.call_activity(extract_destination, input=user_input)
    
    # Gate: Check if destination is valid
    if "paris" not in destination_text.lower():
        return "Unable to create itinerary: Destination not recognized or supported."
    
    # Step 2: Generate outline with planning agent (has tools)
    travel_outline = yield ctx.call_activity(create_travel_outline, input=destination_text)
    
    # Step 3: Expand into detailed plan with itinerary agent (no tools)
    detailed_itinerary = yield ctx.call_activity(expand_itinerary, input=travel_outline)
    
    return detailed_itinerary

该实现展示了三种不同的方法:

  • 基于简单提示的任务(无代理)
  • 基于代理的任务(无工具)
  • 基于代理的任务(有工具)

Dapr Agents 的工作流编排提供:

  • 代码即工作流 - 以开发者友好的方式定义任务
  • 工作流持久化 - 长时间运行的链式任务在进程重启后依然存在
  • 混合执行 - 轻松混合提示、代理调用和配备工具的代理

路由

路由模式通过分类输入并将其引导到专门的后续任务来处理多样化的请求类型。这允许关注点分离,并为不同类型的查询创建专门的处理专家。

Diagram showing how the routing pattern works

使用场景:

  • 资源优化(将简单查询发送到较小的模型)
  • 多语言支持(将查询路由到语言特定的处理程序)
  • 客户支持(将不同查询类型引导到专门的处理程序)
  • 内容创建(将写作任务路由到主题专家)
  • 混合 LLM 系统(为不同任务使用不同模型)

使用 Dapr Agents 实现:

@workflow(name="travel_assistant_workflow")
def travel_assistant_workflow(ctx: DaprWorkflowContext, input_params: dict):
    user_query = input_params.get("query")
    
    # Classify the query type using an LLM
    query_type = yield ctx.call_activity(classify_query, input={"query": user_query})

    # Route to the appropriate specialized handler
    if query_type == QueryType.ATTRACTIONS:
        response = yield ctx.call_activity(
            handle_attractions_query,
            input={"query": user_query}
        )
    elif query_type == QueryType.ACCOMMODATIONS:
        response = yield ctx.call_activity(
            handle_accommodations_query,
            input={"query": user_query}
        )
    elif query_type == QueryType.TRANSPORTATION:
        response = yield ctx.call_activity(
            handle_transportation_query,
            input={"query": user_query}
        )
    else:
        response = "I'm not sure how to help with that specific travel question."
        
    return response

Dapr 方法的优势包括:

  • 熟悉的控制流 - 使用标准编程 if-else 结构进行路由
  • 可扩展性 - 控制流可以轻松扩展以满足未来需求
  • LLM 驱动的分类 - 使用 LLM 动态分类查询

并行化

并行化模式使问题的多个维度能够同时处理,输出以编程方式聚合。此模式提高了具有可并发处理的独立子任务的复杂任务的效率。

Diagram showing how the parallelization pattern works

使用场景:

  • 复杂研究(并行处理主题的不同方面)
  • 多方面规划(同时创建计划的各个元素)
  • 产品分析(并行分析产品的不同方面)
  • 内容创建(同时生成文档的多个部分)

使用 Dapr Agents 实现:

@workflow(name="travel_planning_workflow")
def travel_planning_workflow(ctx: DaprWorkflowContext, input_params: dict):
    destination = input_params.get("destination")
    preferences = input_params.get("preferences")
    days = input_params.get("days")

    # Process three aspects of the travel plan in parallel
    parallel_tasks = [
        ctx.call_activity(research_attractions, input={
            "destination": destination, 
            "preferences": preferences, 
            "days": days
        }),
        ctx.call_activity(recommend_accommodations, input={
            "destination": destination, 
            "preferences": preferences, 
            "days": days
        }),
        ctx.call_activity(suggest_transportation, input={
            "destination": destination, 
            "preferences": preferences, 
            "days": days
        })
    ]

    # Wait for all parallel tasks to complete
    results = yield wfapp.when_all(parallel_tasks)
    
    # Aggregate results into final plan
    final_plan = yield ctx.call_activity(create_final_plan, input={"results": results})
    
    return final_plan

使用 Dapr 进行并行化的好处包括:

  • 简化的并发 - 处理并行任务的复杂编排
  • 自动同步 - 等待所有并行任务完成
  • 工作流持久性 - 整个并行过程是持久且可恢复的

编排器-工作者

对于高度复杂的任务,当子任务的数量和性质无法预先知道时,编排器-工作者模式提供了一个强大的解决方案。此模式具有一个中央编排器 LLM,它动态分解任务、将任务委托给工作者 LLM,并综合它们的结果。

Diagram showing how the orchestrator-workers pattern works

与之前预定义工作流的模式不同,编排器根据特定输入动态确定工作流。

使用场景:

  • 跨越多个文件的软件开发任务
  • 从多个来源收集信息的研究
  • 评估复杂问题不同方面的业务分析
  • 结合各个领域专门内容的创作

使用 Dapr Agents 实现:

@workflow(name="orchestrator_travel_planner")
def orchestrator_travel_planner(ctx: DaprWorkflowContext, input_params: dict):
    travel_request = input_params.get("request")

    # Step 1: Orchestrator analyzes request and determines required tasks
    plan_result = yield ctx.call_activity(
        create_travel_plan,
        input={"request": travel_request}
    )

    tasks = plan_result.get("tasks", [])

    # Step 2: Execute each task with a worker LLM
    worker_results = []
    for task in tasks:
        task_result = yield ctx.call_activity(
            execute_travel_task,
            input={"task": task}
        )
        worker_results.append({
            "task_id": task["task_id"],
            "result": task_result
        })

    # Step 3: Synthesize the results into a cohesive travel plan
    final_plan = yield ctx.call_activity(
        synthesize_travel_plan,
        input={
            "request": travel_request,
            "results": worker_results
        }
    )

    return final_plan

Dapr 用于编排器-工作者模式的优势包括:

  • 动态规划 - 编排器可以根据输入动态创建子任务
  • 工作者隔离 - 每个工作者专注于解决问题的一个特定方面
  • 简化的综合 - 最终综合步骤将结果组合成连贯的输出

评估器-优化器

质量通常通过迭代和细化来实现。评估器-优化器模式实现了一个双 LLM 流程,其中一个模型生成响应,另一个模型在迭代循环中提供评估和反馈。

Diagram showing how the evaluator-optimizer pattern works

使用场景:

  • 需要遵守特定风格指南的内容创作
  • 需要细致理解和表达翻译
  • 满足特定需求和处理边缘情况的代码生成
  • 需要多轮信息收集和细化的复杂搜索

使用 Dapr Agents 实现:

@workflow(name="evaluator_optimizer_travel_planner")
def evaluator_optimizer_travel_planner(ctx: DaprWorkflowContext, input_params: dict):
    travel_request = input_params.get("request")
    max_iterations = input_params.get("max_iterations", 3)
    
    # Generate initial travel plan
    current_plan = yield ctx.call_activity(
        generate_travel_plan,
        input={"request": travel_request, "feedback": None}
    )

    # Evaluation loop
    iteration = 1
    meets_criteria = False

    while iteration <= max_iterations and not meets_criteria:
        # Evaluate the current plan
        evaluation = yield ctx.call_activity(
            evaluate_travel_plan,
            input={"request": travel_request, "plan": current_plan}
        )

        score = evaluation.get("score", 0)
        feedback = evaluation.get("feedback", [])
        meets_criteria = evaluation.get("meets_criteria", False)
        
        # Stop if we meet criteria or reached max iterations
        if meets_criteria or iteration >= max_iterations:
            break

        # Optimize the plan based on feedback
        current_plan = yield ctx.call_activity(
            generate_travel_plan,
            input={"request": travel_request, "feedback": feedback}
        )

        iteration += 1

    return {
        "final_plan": current_plan,
        "iterations": iteration,
        "final_score": score
    }

使用 Dapr 实现此模式的好处包括:

  • 迭代改进循环 - 管理生成和评估之间的反馈周期
  • 质量标准 - 能够清晰定义什么构成可接受的输出
  • 最大迭代控制 - 通过强制执行迭代限制防止无限循环

持久化代理

在代理性谱系的远端,持久化代理模式代表了从基于工作流方法的转变。不是预定义的步骤,而是一个自主代理,可以根据其对目标的理解规划自己的步骤并执行。

企业应用通常需要超出内存能力的持久化执行和可靠性。Dapr 的 DurableAgent 类帮助你实现具有工作流可靠性的自主代理,因为这些代理在幕后由 Dapr 工作流支持。DurableAgent 通过添加持久化到代理执行来扩展基本的 Agent 类。

Diagram showing how the durable agent pattern works

此模式不仅仅持久化消息历史——它为每次交互动态创建带有持久化活动的工作流,其中 LLM 调用和工具执行可靠地存储在 Dapr 的状态存储中。这使其非常适合可靠性和持久性至关重要的环境。

持久化代理还支持"无头代理"方法,即自主系统在没有直接用户交互的情况下运行。Dapr 的持久化代理暴露 REST 和发布订阅 API,使其非常适合由其他应用或外部事件触发的长时间运行操作。

使用场景:

  • 可能需要数分钟或数天完成的长时间运行任务
  • 跨多个服务运行的分布式系统
  • 处理复杂多会话工单的客户服务
  • 每一步都有 LLM 智能的业务流程
  • 处理日程安排和信息查询的个人助手
  • 由外部系统触发的自主后台进程

使用 Dapr Agents 实现:

import asyncio

from dapr_agents import DurableAgent
from dapr_agents.agents.configs import (
    AgentExecutionConfig,
    AgentMemoryConfig,
    AgentPubSubConfig,
    AgentRegistryConfig,
    AgentStateConfig,
)
from dapr_agents.memory import ConversationDaprStateMemory
from dapr_agents.storage.daprstores.stateservice import StateStoreService
from dapr_agents.workflow.runners import AgentRunner

travel_planner = DurableAgent(
    name="TravelBuddy",
    role="Travel Planner",
    goal="Help users find flights and remember preferences",
    instructions=[
        "Find flights to destinations",
        "Remember user preferences",
        "Provide clear flight info",
    ],
    tools=[search_flights],
    pubsub=AgentPubSubConfig(
        pubsub_name="messagepubsub",
        agent_topic="travel.requests",
        broadcast_topic="travel.broadcast",
    ),
    state=AgentStateConfig(
        store=StateStoreService(store_name="workflowstatestore"),
    ),
    registry=AgentRegistryConfig(
        store=StateStoreService(store_name="registrystatestore"),
        team_name="travel-team",
    ),
    execution=AgentExecutionConfig(max_iterations=3),
    memory=AgentMemoryConfig(
        store=ConversationDaprStateMemory(
            store_name="conversationstore",
            session_id="travel-session",
        )
    ),
)

async def main():
    runner = AgentRunner()
    try:
        result = await runner.run(
            travel_planner,
            payload={"task": "Find weekend flights to Paris"},
        )
        print(result)
    finally:
        runner.shutdown(travel_planner)

asyncio.run(main())

该实现遵循 Dapr 的边车架构模型,所有基础设施关注点由 Dapr 运行时处理:

  • 持久化记忆 - 代理状态存储在 Dapr 的状态存储中,在进程崩溃后依然存在
  • 工作流编排 - 所有代理交互通过 Dapr 的工作流系统管理
  • 服务暴露 - AgentRunner.serve() 暴露 REST 端点(如 POST /agent/run),用于调度代理的 @workflow_entry
  • 发布订阅输入/输出 - AgentRunner.subscribe() 扫描代理中的 @message_router 方法,并将配置的主题与模式验证连接

持久化代理支持"无头代理"概念——在没有直接用户交互的情况下运行的自主系统。根据场景,你可以:

  1. 运行持久化工作流以编程方式(runner.run 如上所示)
  2. 订阅代理到主题,以便其他服务可以通过发布订阅触发它(runner.subscribe
  3. 服务代理到 FastAPI 应用后面,提供内置的 /run 和状态端点(runner.serve

这些选项使得异步处理请求变得容易,并可以无缝集成到更大的分布式系统中。

重试策略

持久化代理支持 Dapr Workflow 的 RetryPolicy,使用其 WorkflowRetryPolicy

  • max_attempts: 工作流操作的最大重试次数。默认值为 1(无重试)。设置 DAPR_API_MAX_RETRIES 环境变量可覆盖默认值。
  • initial_backoff_seconds: 初始退避持续时间(秒)。默认值为 5 秒。
  • max_backoff_seconds: 最大退避持续时间(秒)。默认值为 30 秒。
  • backoff_multiplier: 指数退避的退避乘数。默认值为 1.5。
  • retry_timeout: 所有重试的总超时时间(秒)。

所有字段都是可选的。可以在实例化持久化代理时传递:

from dapr_agents.agents.configs import WorkflowRetryPolicy
travel_planner = DurableAgent(
    name="TravelBuddy",
    ...
    retry_policy=WorkflowRetryPolicy(
        max_attempts=5,
        initial_backoff_seconds=10,
        max_backoff_seconds=60,
        backoff_multiplier=2.0,
        retry_timeout=300,
    )
    ...
)

选择正确的模式

从简单的代理工作流到完全自主代理的旅程代表了将 LLM 集成到应用程序中的不同方法谱系。不同的使用场景需要不同程度的代理性和控制:

  • 从更简单的模式开始,如增强型 LLM 和提示链,用于可预测性至关重要的明确定义任务
  • 根据需求增长变得更复杂,逐步发展到更动态的模式,如并行化和编排器-工作者
  • 仅在开放式任务中考虑完全自主代理,当灵活性的好处超过严格控制的需求时

6 - 集成

Dapr Agents 中可用的各种集成和工具

开箱即用工具

文本分割器

文本分割器模块是 Dapr Agents 中的一个基础集成,专为 检索增强生成(RAG) 工作流和其他 上下文学习 应用预处理文档而设计。其主要目的是将大型文档拆分为更小的、有意义的块,以便进行嵌入、索引和基于用户查询的高效检索。

通过关注可管理的块大小并通过重叠保留上下文完整性,文本分割器确保文档的处理方式能够支持问答、摘要和文档检索等下游任务。

为什么要使用文本分割器?

在构建 RAG 管道时,将文本拆分为更小的块有以下几个关键目的:

  • 实现有效索引:块被嵌入并存储在向量数据库中,使其能够基于与用户查询的相似性进行检索。
  • 保持语义连贯性:重叠的块有助于在分割之间保留上下文,确保系统能够连接相关信息。
  • 处理模型限制:许多模型有输入大小限制。拆分确保文本在这些限制范围内同时保持有意义。

这一步对于将知识准备为可嵌入的搜索格式至关重要,是基于检索的工作流的基础。

文本拆分策略

文本分割器支持多种策略来有效处理不同类型的文档。这些策略在每个块的大小和保持上下文的需求之间取得平衡。

1. 基于字符的长度

  • 工作原理:计算每个块中的字符数。
  • 适用场景:简单有效,无需依赖外部分词工具即可进行文本拆分。

示例:

from dapr_agents.document.splitter.text import TextSplitter

# 基于字符的分割器(默认)
splitter = TextSplitter(chunk_size=1024, chunk_overlap=200)

2. 基于令牌的长度

  • 工作原理:计算令牌数量,即语言模型使用的语义单元(例如单词或子词)。
  • 适用场景:确保与 GPT 等模型的兼容性,令牌限制至关重要。

示例

import tiktoken
from dapr_agents.document.splitter.text import TextSplitter

enc = tiktoken.get_encoding("cl100k_base")

def length_function(text: str) -> int:
    return len(enc.encode(text))

splitter = TextSplitter(
    chunk_size=1024,
    chunk_overlap=200,
    chunk_size_function=length_function
)

定义块大小函数的灵活性使文本分割器能够适应各种场景。

块重叠

为了保留上下文,文本分割器包含块重叠功能。这确保了一个块的部分内容延续到下一个块,有助于在顺序处理块时保持连续性。

示例:

  • chunk_size=1024chunk_overlap=200 时,一个块的最后 200 个令牌或字符会出现在下一个块的开头。
  • 这种设计有助于文本生成等任务,在这些任务中跨块保持上下文至关重要。

如何使用文本分割器

以下是使用文本分割器处理 PDF 文档的实用示例:

步骤 1:加载 PDF

import requests
from pathlib import Path

# Download PDF
pdf_url = "https://arxiv.org/pdf/2412.05265.pdf"
local_pdf_path = Path("arxiv_paper.pdf")

if not local_pdf_path.exists():
    response = requests.get(pdf_url)
    response.raise_for_status()
    with open(local_pdf_path, "wb") as pdf_file:
        pdf_file.write(response.content)

步骤 2:读取文档

在此示例中,我们使用 Dapr Agents 的 PyPDFReader

pip install pypdf

然后,初始化读取器以加载 PDF 文件。

from dapr_agents.document.reader.pdf.pypdf import PyPDFReader

reader = PyPDFReader()
documents = reader.load(local_pdf_path)

步骤 3:拆分文档

splitter = TextSplitter(
    chunk_size=1024,
    chunk_overlap=200,
    chunk_size_function=length_function
)
chunked_documents = splitter.split_documents(documents)

步骤 4:分析结果

print(f"Original document pages: {len(documents)}")
print(f"Total chunks: {len(chunked_documents)}")
print(f"First chunk: {chunked_documents[0]}")

关键特性

  • 分层拆分:按分隔符(例如段落)拆分文本,然后在需要时进一步细化块。
  • 可自定义的块大小:支持基于字符和基于令牌的长度函数。
  • 重叠以保持上下文:在下一个块中保留前一个块的部分内容以保持连续性。
  • 元数据保留:每个块保留元数据,如页码和起始/结束索引,以便于映射。

通过理解和利用 文本分割器,您可以有效地预处理大型文档,确保它们准备好在 RAG 管道等高级工作流中进行嵌入、索引和检索。

Arxiv 获取器

Dapr Agents 中的 Arxiv 获取器模块提供了与 arXiv API 交互的强大接口。它旨在帮助用户以编程方式搜索、检索和下载来自 arXiv 的科学论文。凭借高级查询功能、元数据提取和 PDF 文件下载支持,Arxiv 获取器非常适合处理学术文献的研究人员、开发人员团队。

为什么要使用 Arxiv 获取器?

Arxiv 获取器简化了访问研究论文的过程,提供以下功能:

  • 自动化文献搜索:按特定主题、关键词或作者查询 arXiv。
  • 元数据检索:提取结构化元数据,如标题、摘要、作者、类别和提交日期。
  • 精确过滤:按日期范围限制搜索结果(例如,检索某一领域的最新研究)。
  • PDF 下载:获取论文的全文 PDF 以供离线使用。

如何使用 Arxiv 获取器

步骤 1:安装所需模块

pip install arxiv

步骤 2:初始化获取器

设置 ArxivFetcher 以开始与 arXiv API 交互。

from dapr_agents.document import ArxivFetcher

# Initialize the fetcher
fetcher = ArxivFetcher()

步骤 3:执行搜索

基于查询字符串的基本搜索

使用简单关键词搜索论文。结果以 Document 对象形式返回,每个对象包含:

  • text:论文的摘要。
  • metadata:结构化元数据,如标题、作者、类别和提交日期。
# Search for papers related to "machine learning"
results = fetcher.search(query="machine learning", max_results=5)

# Display metadata and summaries
for doc in results:
    print(f"Title: {doc.metadata['title']}")
    print(f"Authors: {', '.join(doc.metadata['authors'])}")
    print(f"Summary: {doc.text}\n")

高级查询

使用 AND、OR 和 NOT 等逻辑运算符细化搜索,或执行特定字段搜索(如按作者搜索)。

示例:

搜索关于"agents"和"cybersecurity"的论文:

results = fetcher.search(query="all:(agents AND cybersecurity)", max_results=10)

排除特定术语(例如"quantum"但不包括"computing"):

results = fetcher.search(query="all:(quantum NOT computing)", max_results=10)

搜索特定作者的论文:

results = fetcher.search(query='au:"John Doe"', max_results=10)

按日期过滤论文

将搜索结果限制在特定时间范围内,例如过去 24 小时内提交的论文。

from datetime import datetime, timedelta

# Calculate the date range
last_24_hours = (datetime.now() - timedelta(days=1)).strftime("%Y%m%d")
today = datetime.now().strftime("%Y%m%d")

# Search for recent papers
recent_results = fetcher.search(
    query="all:(agents AND cybersecurity)",
    from_date=last_24_hours,
    to_date=today,
    max_results=5
)

# Display metadata
for doc in recent_results:
    print(f"Title: {doc.metadata['title']}")
    print(f"Authors: {', '.join(doc.metadata['authors'])}")
    print(f"Published: {doc.metadata['published']}")
    print(f"Summary: {doc.text}\n")

步骤 4:下载 PDF

获取论文的全文 PDF 以供离线使用。元数据与下载的文件一起保存。

import os
from pathlib import Path

# Create a directory for downloads
os.makedirs("arxiv_papers", exist_ok=True)

# Download PDFs
download_results = fetcher.search(
    query="all:(agents AND cybersecurity)",
    max_results=5,
    download=True,
    dirpath=Path("arxiv_papers")
)

for paper in download_results:
    print(f"Downloaded Paper: {paper['title']}")
    print(f"File Path: {paper['file_path']}\n")

步骤 5:提取和处理 PDF 内容

使用 Dapr AgentsPyPDFReader 从下载的 PDF 中提取内容。每一页被视为一个单独的 Document 对象,并附带元数据。

from pathlib import Path
from dapr_agents.document import PyPDFReader

reader = PyPDFReader()
docs_read = []

for paper in download_results:
    local_pdf_path = Path(paper["file_path"])
    documents = reader.load(local_pdf_path, additional_metadata=paper)
    docs_read.extend(documents)

# Verify results
print(f"Extracted {len(docs_read)} documents.")
print(f"First document text: {docs_read[0].text}")
print(f"Metadata: {docs_read[0].metadata}")

实际应用

Arxiv 获取器为研究人员和开发人员实现了各种用例:

  • 文献综述:快速检索和组织关于给定主题或特定作者的相关论文。
  • 趋势分析:通过过滤近期提交内容来识别某一领域的最新研究。
  • 离线研究工作流:下载和处理 PDF 以供本地分析和归档。

下一步

虽然 Arxiv 获取器提供了检索和处理研究论文的强大功能,但其输出可以集成到高级工作流中:

  • 构建可搜索的知识库:将获取的论文与文本分割和向量嵌入等集成相结合,以实现高级搜索功能。
  • 检索增强生成(RAG):使用处理后的论文作为 RAG 管道的输入,为问答系统提供支持。
  • 自动化文献调查:基于获取和处理后的研究生成摘要或见解。

向量存储

Dapr Agents 包含内置的向量存储实现,可用于 ConversationVectorMemory 和 RAG 管道。每个存储都可以从 dapr_agents.storage.vectorstores 获取。

ChromaVectorStore

使用 ChromaDB 进行内存或持久化向量搜索。开发环境无需额外的基础设施。

from dapr_agents.storage.vectorstores import ChromaVectorStore
from dapr_agents.document.embedder.openai import OpenAIEmbedder

store = ChromaVectorStore(
    collection_name="my_collection",
    embedding_function=OpenAIEmbedder(),
)

PostgresVectorStore

使用 PostgreSQL 和 pgvector 进行生产级向量相似性搜索。

from dapr_agents.storage.vectorstores import PostgresVectorStore
from dapr_agents.document.embedder.openai import OpenAIEmbedder

store = PostgresVectorStore(
    connection_string="postgresql://user:pass@localhost:5432/mydb",
    embedding_function=OpenAIEmbedder(),
    embedding_dimensions=1536,
)

RedisVectorStore

通过 redisvl 库使用 Redis Stack 进行向量相似性搜索。

需要 redisvlpip install redisvl)。

from dapr_agents.storage.vectorstores import RedisVectorStore
from dapr_agents.document.embedder.openai import OpenAIEmbedder

store = RedisVectorStore(
    url="redis://localhost:6379",
    index_name="my_agent",
    embedding_function=OpenAIEmbedder(),
    embedding_dimensions=1536,
    distance_metric="cosine",  # "cosine", "l2", or "ip"
    storage_type="hash",       # "hash" or "json"
)

三种向量存储共享相同的接口,可互换地作为 ConversationVectorMemoryvector_store 参数使用:

from dapr_agents.memory import ConversationVectorMemory

memory = ConversationVectorMemory(
    vector_store=store,
    distance_metric="cosine",
)

工具

将 Agent 作为工具

Dapr Agents 支持在 DurableAgent 推理循环中将其他 Agent 作为工具调用,包括来自其他框架的 Agent(如 OpenAI Agents、LangGraph 和 CrewAI)。有关完整文档和代码示例,请参阅 将 Agent 作为工具

数据库的 MCP 工具箱

Dapr Agents 支持通过实现一个封装器来与 MCP Toolbox for Databases 集成,该封装器将可用工具加载到 Dapr Agents 使用的 Tool 模型中。

要集成该工具箱,请按如下方式加载工具:

from toolbox_core import ToolboxSyncClient
client = ToolboxSyncClient("http://127.0.0.1:5000")
agent_tools = AgentTool.from_toolbox_many(client.load_toolset("your-tools-name-here"))
agent = DurableAgent(
    ..
    tools=agent_tools
)

..
# Remember to close the tool
finally:
    client.close()

或将代码包装在 with 语句中:

from toolbox_core import ToolboxSyncClient
with ToolboxSyncClient("http://127.0.0.1:5000") as client:
    agent_tools = AgentTool.from_toolbox_many(client.load_toolset("your-tools-name-here"))
    agent = DurableAgent(
        ..
        tools=agent_tools
    )

7 - 快速入门

通过实用的分步示例开始使用 Dapr Agents

Dapr Agents 快速入门展示了如何使用 Dapr Agents 构建具有 LLM 驱动的自主代理和事件驱动工作流的应用程序。快速入门是一个单一渐进式教程,从基本 LLM 调用逐步构建到持久代理、工作流、多代理编排和可观测性。

开始之前

Dapr Agents 基础

Dapr Agents 基础快速入门在单个编号 Python 脚本目录中覆盖了完整的 Dapr Agents 编程模型。每一步都构建在前一步之上。

步骤文件你将学到
101_llm_client.py使用 DaprChatClient 通过 Dapr Conversation API 调用 LLM
202_durable_agent_http.py运行由 Dapr 工作流支持的持久代理,通过 HTTP 暴露
303_durable_agent_pubsub.py通过发布订阅而非 HTTP 触发持久代理
404_workflow_llm.py构建调用 LLM 作为活动的确定性 Dapr 工作流
505_workflow_agents.py将多个专业代理编排为子工作流
606_durable_agent_tracing.py使用 Zipkin 为代理和工作流启用分布式追踪
707_durable_agent_hot_reload.py通过 Dapr Configuration Store 在运行时热重载代理配置

有关完整的设置说明,包括 LLM 配置和先决条件,请参阅快速入门 README

示例

Dapr Agents 示例目录包含更高级和特定功能的场景,是对快速入门的补充:

示例你将学到
LLM 调用 – Dapr Chat Client通过 DaprChatClient 进行文本生成、LLM 提供商切换、韧性和 PII 混淆
LLM 调用 – OpenAI Client使用原生 OpenAI 客户端进行聊天完成、结构化输出、音频和嵌入。也适用于 ElevenLabsHugging FaceNVIDIA
独立代理工具调用使用 DurableAgentAgentRunner.run 构建具有工具的对话代理
持久代理工具调用使用 AgentRunner.run/subscribe/serve 升级到持久工作流支持的代理
基于 LLM 的工作流使用 LLM 活动的确定性多步骤工作流(链式调用、并行化、路由)
基于代理的工作流在 Dapr 工作流内编排代理活动
消息路由器工作流使用 @message_router 将工作流绑定到 Dapr 发布订阅主题
多代理工作流指环王主题的事件驱动多代理系统,具有 LLM、随机和轮询编排策略
Kubernetes 上的多代理工作流在 Kubernetes 中部署和编排多代理系统
使用 Chainlit 的文档代理可以上传和学习非结构化文档并具有长期记忆的对话代理
MCP 客户端 – SSE通过服务器发送事件连接到远程 MCP 服务器
MCP 客户端 – stdio通过 stdio 连接到本地 MCP 服务器
MCP 客户端 – Streamable HTTP通过 Streamable HTTP 传输连接到 MCP 服务器
使用 MCP 和 Chainlit 的数据代理通过 MCP 使用类 ChatGPT UI 对 Postgres 数据库进行自然语言查询
具有可观测性的代理作为活动使用 OpenTelemetry 和 Zipkin 端到端追踪代理活动
代理作为工具将其他 DurableAgent 实例——以及来自其他框架的代理——作为子工作流工具调用
持久代理热重载在运行时热重载代理角色和 LLM 设置而无需重启