Flink Agents框架入门:基础组件解析

259 阅读11分钟

Flink Agents作为事件驱动型智能体系统的开发框架,不仅继承了Flink引擎分布式、大规模、容错性的结构化数据流式处理能力以及成熟的状态管理机制,还为智能体式人工智能(Agentic AI)的基础组件与功能提供了原生级抽象。这些抽象简化了智能体系统开发的复杂程度,对于开发者来说,能够降低学习难度,提升开发效率。了解这些基础组件,是基于Flink Agents开发事件驱动型智能体系统的必经之路。

1、Chat models(对话模型)

对话模型(Chat models)能让智能体(Agents)与大型语言模型(LLM)进行交互,从而实现自然语言理解、推理与生成功能。在Flink Agents中,对话模型扮演着智能体 “大脑” 的角色:它会处理输入消息,并根据上下文(Context)、提示词(Prompts)及可用工具,生成具有智能性的响应。

1.1 对话模型的创建

要在智能体中使用对话模型(Chat models),需通过装饰器(decorators)(注:本文阐述均基于Python开发语言来说)同时定义连接(connection)与配置(setup),随后通过事件(events)与该模型进行交互。

  • @chat_model_connection
    @chat_model_connection装饰器(decorators)用于标记一个 “创建对话模型连接” 的方法。该方法通常只需定义一次,即可在多个对话模型配置中共享使用。
  • @chat_model_setup
    @chat_model_setup装饰器(decorators)用于标记一个 “创建对话模型配置” 的方法。该方法会引用一个(已定义的)连接,并添加对话专属的配置项(如提示词、工具等)。

1.2 对话事件(Chat Events)

对话模型通过内置事件(events)实现通信,核心事件包括:

  • ChatRequestEvent(对话请求事件):由 “动作(actions)” 发送,用于向大型语言模型(LLM)请求生成对话结果(chat completion)。
  • ChatResponseEvent(对话响应事件):由 “动作(actions)” 接收,包含大型语言模型(LLM)返回的响应内容。

1.3 示例

class MyAgent(Agent):
    #定义对话模型连接
    @chat_model_connection
    @staticmethod
    def ollama_connection() -> ResourceDescriptor:
        return ResourceDescriptor(
            clazz=OllamaChatModelConnection,
            base_url="http://localhost:11434",
            request_timeout=30.0
        )

    #定义对话模型配置
    @chat_model_setup
    @staticmethod
    def ollama_chat_model() -> ResourceDescriptor:
        return ResourceDescriptor(
            clazz=OllamaChatModelSetup,
            connection="ollama_connection",
            model="qwen3:8b",
            temperature=0.7
        )

    @action(InputEvent)
    @staticmethod
    def process_input(event: InputEvent, ctx: RunnerContext) -> None:
        # Create a chat request with user message
        user_message = ChatMessage(
            role=MessageRole.USER,
            content=f"input: {event.input}"
        )
        ctx.send_event(
            ChatRequestEvent(model="ollama_chat_model", messages=[user_message]) 
        )    # 此Action发送对话请求事件

    @action(ChatResponseEvent)    # 此Action接收对话响应事件
    @staticmethod
    def process_response(event: ChatResponseEvent, ctx: RunnerContext) -> None:
        response_content = event.response.content
        # Handle the LLM's response
        # Process the response as needed for your use case

1.4 内置的对话模型

  • Anthropic:AnthropicChatModelConnection / AnthropicChatModelSetup
  • Ollama:OllamaChatModelConnection / OllamaChatModelSetup
  • OpenAI:OpenAIChatModelConnection / OpenAIChatModelSetup
  • Tongyi:TongyiChatModelConnection / TongyiChatModelSetup

1.5 自定义对话模型

如果你想使用内置对话模型以外的模型,可以扩展基础对话类自行实现!对话模型系统围绕两个主要的抽象类构建而成:BaseChatModelConnection和BaseChatModelSetup。

2、Action(行动)

Action(行动)是一段可执行的代码。每个行动至少监听一种事件类型。当所监听类型的事件发生时,该动作会被触发。动作还可以生成新的事件,以触发其他动作。

在智能体(Agent)中声明Action时,用户可使用@action装饰器修饰智能体类的函数,并将所监听的事件类型声明为装饰器的参数。被修饰的函数签名应为(Event, RunnerContext) -> None

Action示例如1.3节中process_input函数和process_response函数所示。

3、Event(事件)

请注意,这里Event的概念是就智能体内部通信而言的。
Event(事件)是在行动(Actions)之间传递的消息。事件可携带有效负载(payloads)。若多个动作均监听某一事件类型,则单个事件可触发这些动作。

事件包含两种特殊类型:

  • InputEvent(输入事件):由框架生成,携带在 “输入字段(input field)” 中到达智能体(agent)的一条输入数据记录。监听 InputEvent 的Acion将成为智能体的入口点(entry points)。
  • OutputEvent(输出事件):框架会监听OutputEvent,并将其 “输出字段(output field)” 中的有效负载转换为智能体的输出。Action可通过生成OutputEvent来发送输出数据。

4、RunnerContext(运行时上下文)

RunnerContext是模型上下文的抽象基类,为智能体(agent)执行提供上下文。 运行时上下文支持对事件处理的访问。

  • RunnerContext.send_event() # 发送事件
  • RunnerContext.short_term_memory() # 获取短期记忆对象

5、Prompts(提示词)

提示词(Prompts)是定义智能体(agents)如何与大型语言模型(LLMs)进行交互的模板。它们提供结构化的指令、上下文信息以及格式规范,这些内容会影响大型语言模型的响应生成。在Flink Agents中,提示词属于 “一等资源”(first-class resources),可在不同智能体与对话模型(chat models)之间进行定义、复用和引用。

Flink Agents支持两种类型的提示词:本地提示词(Local Prompt)和MCP提示词(MCP Prompt)。

5.1 本地提示词(Local Prompt)

本地提示词是直接在代码中定义的模板。它们支持使用 {variable_name} 语法进行变量替换,且可由文本字符串或消息序列创建。

本地提示词有两种创建方式:Prompt.from_text()和Prompt.from_messages()

product_suggestion_prompt_str = """
Based on the rating distribution and user dissatisfaction reasons, generate three actionable suggestions for product improvement.

Input format:
{{
    "id": "1",
    "score_histogram": ["10%", "20%", "10%", "15%", "45%"],
    "unsatisfied_reasons": ["reason1", "reason2", "reason3"]
}}

Ensure that your response can be parsed by Python json, use the following format as an example:
{{
    "suggestion_list": [
        "suggestion1",
        "suggestion2",
        "suggestion3"
    ]
}}

input:
{input}
"""

product_suggestion_prompt = Prompt.from_text(product_suggestion_prompt_str)
review_analysis_prompt = Prompt.from_messages(
    messages=[
        ChatMessage(
            role=MessageRole.SYSTEM,
            content="""
            Analyze the user review and product information to determine a
            satisfaction score (1-5) and potential reasons for dissatisfaction.

            Example input format:
            {{
                "id": "12345",
                "review": "The headphones broke after one week of use."
            }}

            Ensure your response can be parsed by Python JSON:
            {{
                "id": "12345",
                "score": 1,
                "reasons": ["poor quality"]
            }}
            """,
        ),
        ChatMessage(
            role=MessageRole.USER,
            content="""
            "input":
            {input}
            """,
        ),
    ],
)

本地提示词的使用: 提示词(Prompts)使用 {variable_name} 语法定义模板变量。变量值从 ChatMessage.extra_args 中获取填充。当对话模型(chat model)被调用时,该提示词会自动生效。

class ReviewAnalysisAgent(Agent):

    @prompt
    @staticmethod
    def review_analysis_prompt() -> Prompt:
        """Prompt for review analysis."""
        return Prompt.from_messages(
            messages=[
                ChatMessage(
                    role=MessageRole.SYSTEM,
                    content="""
            Analyze the user review and product information to determine a
            satisfaction score (1-5) and potential reasons for dissatisfaction.

            Example input format:
            {{
                "id": "12345",
                "review": "The headphones broke after one week of use."
            }}

            Ensure your response can be parsed by Python JSON:
            {{
                "id": "12345",
                "score": 1,
                "reasons": ["poor quality"]
            }}
            """,
                ),
                ChatMessage(
                    role=MessageRole.USER,
                    content="""
            "input":
            {input}
            """,
                ),
            ],
        )

    @chat_model_setup
    @staticmethod
    def review_analysis_model() -> ResourceDescriptor:
        """ChatModel which focus on review analysis."""
        return ResourceDescriptor(
            clazz=OllamaChatModelSetup,
            connection="ollama_server",
            model="qwen3:8b",
            prompt="review_analysis_prompt",
            extract_reasoning=True,
        )

    @action(InputEvent)
    @staticmethod
    def process_input(event: InputEvent, ctx: RunnerContext) -> None:
        """Process input event and send chat request for review analysis."""
        input: ProductReview = event.input
        ctx.short_term_memory.set("id", input.id)

        content = f"""
            "id": {input.id},
            "review": {input.review}
        """
        msg = ChatMessage(role=MessageRole.USER, extra_args={"input": content})
        ctx.send_event(ChatRequestEvent(model="review_analysis_model", messages=[msg]))

5.2 MCP提示词(MCP Prompt)

MCP 提示词(MCP Prompt)由外部 MCP 服务器(MCP Server)管理,当您在智能体(Agent)中定义 MCP 服务器连接后,这些提示词会被自动发现。这类提示词支持动态获取提示词、集中式提示词管理,以及与外部提示词仓库的集成。

使用 FastMCP 库创建一个可对外提供提示词(Prompts)访问的 MCP 服务器:

# mcp_server.py
mcp = FastMCP("ReviewServer")

@mcp.prompt()
def review_analysis_prompt(product_id: str, review: str) -> str:
    """Prompt for analyzing product reviews."""
    return f"""
    Analyze the following product review and provide a satisfaction score (1-5).

    Product ID: {product_id}
    Review: {review}

    Output format: {{"score": 1-5, "reasons": ["reason1", "reason2"]}}
    """

mcp.run("streamable-http")

MCP提示词的使用:

  • 使用@mcp_server装饰器定义 MCP 服务器连接
  • 通过 MCP 提示词的函数名引用该提示词(例如:“review_analysis_prompt”)
  • 使用ChatMessage.extra_args提供提示词参数
  • MCP 服务器中的所有提示词(Prompts)和工具(Tools)会被自动注册
class ReviewAnalysisAgent(Agent):

    @mcp_server
    @staticmethod
    def review_mcp_server() -> MCPServer:
        """Connect to MCP server."""
        return MCPServer(endpoint="http://127.0.0.1:8000/mcp")

    @chat_model_connection
    @staticmethod
    def ollama_server() -> ResourceDescriptor:
        """Ollama connection."""
        return ResourceDescriptor(clazz=OllamaChatModelConnection)

    @chat_model_setup
    @staticmethod
    def review_model() -> ResourceDescriptor:
        return ResourceDescriptor(
            clazz=OllamaChatModelSetup,
            connection="ollama_server",
            model="qwen3:8b",
            prompt="review_analysis_prompt",  # Reference MCP prompt by name
        )

    @action(InputEvent)
    @staticmethod
    def process_input(event: InputEvent, ctx: RunnerContext) -> None:
        input_data = event.input

        # Provide prompt variables via extra_args
        msg = ChatMessage(
            role=MessageRole.USER,
            extra_args={
                "product_id": input_data.product_id,
                "review": input_data.review
            }
        )
        ctx.send_event(ChatRequestEvent(model="review_model", messages=[msg]))

6、Tool Use(工具使用)

Flink Agents提供了灵活且可扩展的工具使用机制。开发者既可以将工具定义为本地 Python 函数,也可以与远程 MCP 服务器集成,以使用 MCP 服务器提供的工具。

  • 本地函数作为工具
    开发者可将工具定义为本地Python函数,且将本地函数定义并注册为工具。存在两种方式:
    • 将工具定义为智能体类中的静态方法
      开发者在定义智能体时,可将工具定义为智能体类(agent class)中的静态方法(static method),并在 Python 中使用@tool装饰器(decorator)将该函数标记为工具。在智能体中创建对话模型(chat model)时,可在资源描述符(ResourceDescriptor)的工具列表(tools list)中通过工具名称引用该工具。

      class ReviewAnalysisAgent(Agent):
      
          @tool
          @staticmethod
          def notify_shipping_manager(id: str, review: str) -> None:
              """Notify the shipping manager when product received a negative review due to
              shipping damage.
      
              Parameters
              ----------
              id : str
                  The id of the product that received a negative review due to shipping damage
              review: str
                  The negative review content
              """
              notify_shipping_manager(id=id, review=review)
      
          @chat_model_setup
          @staticmethod
          def review_analysis_model() -> ResourceDescriptor:
              """ChatModel which focus on review analysis."""
              return ResourceDescriptor(
                  clazz=OllamaChatModelSetup,
                  ...,
                  tools=["notify_shipping_manager"], # reference the tool by its name
              )
      
          ...
      
    • 向执行环境注册工具
      开发者可将工具注册到执行环境(execution environment)中,之后通过工具名称引用该工具。这种方式能让多个智能体(agents)复用该工具。

      def notify_shipping_manager(id: str, review: str) -> None:
          """Notify the shipping manager when product received a negative review due to
          shipping damage.
      
          Parameters
          ----------
          id : str
              The id of the product that received a negative review due to shipping damage
          review: str
              The negative review content
          """
          ...
      
      ...
      
      # Add notify shipping manager tool to the execution environment.
      agents_env.add_resource(
          "notify_shipping_manager", Tool.from_callable(notify_shipping_manager)
      )
      
      ...
      
      # Create react agent with notify shipping manager tool.
      review_analysis_react_agent = ReActAgent(
          chat_model=ResourceDescriptor(
              clazz=OllamaChatModelSetup,
              tools=["notify_shipping_manager"], # reference the tool by its name
          ),
          ...
      )
      
  • MCP 工具
    MCP 工具(MCP Tools)由外部 MCP 服务器(Model Context Protocol Server,模型上下文协议服务器)管理,当您在智能体(Agent)中定义 MCP 服务器连接后,这些工具会被自动发现。
    定义带工具的 MCP 服务器
    使用 FastMCP 库创建一个可对外提供工具访问的 MCP 服务器:
# mcp_server.py
mcp = FastMCP("ReviewServer")

@mcp.tool()
async def notify_shipping_manager(id: str, review: str) -> None:
    """Notify the shipping manager when product received a negative review due to
    shipping damage.

    Parameters
    ----------
    id : str
        The id of the product that received a negative review due to shipping damage
    review: str
        The negative review content
    """
    ...

mcp.run("streamable-http")

使用MCP工具:

class ReviewAnalysisAgent(Agent):
    ...

    @mcp_server
    @staticmethod
    def review_mcp_server() -> MCPServer:
        """Connect to MCP server."""
        return MCPServer(endpoint="http://127.0.0.1:8000/mcp")

    @chat_model_setup
    @staticmethod
    def review_model() -> ResourceDescriptor:
        return ResourceDescriptor(
            clazz=OllamaChatModelSetup,
            connection="ollama_server",
            model="qwen3:8b",
            tools=["notify_shipping_manager"],  # Reference MCP tool by name
        )

7、Memory(记忆)

记忆(Memory)是在多个行动(Actions)和智能体运行(agent runs)过程中可被保留的数据。

  • 短期记忆(Short-Term Memory)
    短期记忆在一次智能体运行(agent run)内的所有动作间共享,同时也在 “具有相同输入键(input key)” 的多次智能体运行间共享。 此处的 “智能体运行(agent run)” 指智能体的一次完整执行过程。上游(upstream)传来的每一条数据记录,都会触发一次新的智能体运行。 短期记忆对应 Flink 的 “键控状态(Keyed State)”:键控状态对 “同一键控分区(keyed partition)内多条数据记录的处理过程” 可见,而对 “其他键控分区中数据的处理过程” 不可见(即具有分区隔离性)。
    基本使用方法:
@action(InputEvent)
@staticmethod
def first_action(event: InputEvent, ctx: RunnerContext) -> None:
    ...
    ctx.short_term_memory.set("id", input.id)
    ...

@action(ChatResponseEvent)
@staticmethod
def second_action(event: ChatResponseEvent, ctx: RunnerContext) -> None:
    ...
    id = ctx.short_term_memory.get("id"),
    ...

存储为嵌套对象:

@action(InputEvent)
@staticmethod
def first_action(event: InputEvent, ctx: RunnerContext) -> None:
    ...
    stm = ctx.short_term_memory
    
    # create nested memory object, and then set the leaf value
    nested_obj = stm.new_object("a")
    nested_obj.set("b", input.id)
    
    # directly set leaf value, will auto crate the nested object    
    stm.set("x.y", input.user)
    ...
    
@action(ChatResponseEvent)
@staticmethod
def second_action(event: InputEvent, ctx: RunnerContext) -> None:
    ...
    stm = ctx.short_term_memory
    
    # directly get leaf value, will auto parse the nested object
    id = stm.get("a.b")
    
    # get the nested object, and then get the leaf value
    nested_obj = stm.get("x")
    user = nested_obj.get("y")
    ...

记忆引用:

@staticmethod
def first_action(event: Event, ctx: RunnerContext):  # noqa D102
    ...
    stm = ctx.get_short_term_memory()
    
    data_ref = stm.set(data_path, data_to_store)
    ctx.send_event(MyEvent(value=data_ref))
    ...

@action(MyEvent)
@staticmethod
def second_action(event: Event, ctx: RunnerContext):  # noqa D102
    ...
    stm = ctx.get_short_term_memory()
    
    content_ref: MemoryRef = event.value
    processed_data: ProcessedData = stm.get(content_ref)
    ...

总结

本文介绍了Flink Agents框架的基础组件,了解这些基础组件是开发者基于Flink Agents构建智能体系统的基石。后面我们将通过应用实例探讨基于Flink Agents的智能体开发以及怎样和Flink引擎集成在一起成为一个事件驱动型智能体系统。