📘 **TELUS Agriculture & Consumer Goods** 如何通过 **Haystack Agents** 转变促销交易

使用 Haystack 进行 Agent 驱动的检索


笔记本作者:Bilge Yücel

在本笔记本中,您将构建一个由 **Haystack** 和 Qdrant 驱动的智能电影推荐助手。您将学习如何结合稀疏向量搜索元数据过滤 (payload)LLM 驱动的代理,来创建一个能够理解自然语言查询并从精选数据集中推荐相关电影的系统。

到本笔记本结束时,您将拥有一个功能齐全的助手,可以回答如下查询:

  • “给我找一部评分很高、关于赛车动作的电影。”
  • “你能推荐五部日本惊悚片吗?”
  • “有什么关于动物、适合孩子看的电影?”

该助手将实现为一个工具调用代理,并可以访问一个 retrieval_tool,该工具可以根据生成的查询过滤器从数据库中检索信息。

🧱 设置开发环境

安装必要的依赖项

  • haystack-ai:用于构建管道和代理
  • datasets:用于从 Hugging Face 加载电影数据集
  • qdrant-haystack:用于向量数据库访问
  • fastembed-haystack:用于使用 FastEmbed 库生成稀疏嵌入
!pip install -q -U haystack-ai qdrant-haystack fastembed-haystack datasets

添加 OpenAI APIQdrant Cloud 的凭据

import os
from google.colab import userdata

os.environ["OPENAI_API_KEY"] = userdata.get('OPENAI_API_KEY')
os.environ["QDRANT_API_KEY"] = userdata.get('QDRANT_API_KEY')

🎬 加载电影数据集

从 Hugging Face 加载 Pablinho/movies-dataset 数据集

from datasets import load_dataset

data = load_dataset('Pablinho/movies-dataset', split="train")
data[14]
{'Release_Date': '2022-01-28',
 'Title': 'The Ice Age Adventures of Buck Wild',
 'Overview': "The fearless one-eyed weasel Buck teams up with mischievous possum brothers Crash & Eddie as they head off on a new adventure into Buck's home: The Dinosaur World.",
 'Popularity': 1431.307,
 'Vote_Count': '737',
 'Vote_Average': '7.1',
 'Original_Language': 'en',
 'Genre': 'Animation, Comedy, Adventure, Family',
 'Poster_Url': 'https://image.tmdb.org/t/p/original/zzXFM4FKDG7l1ufrAkwQYv2xvnh.jpg'}

转换为 Haystack 文档

将所有电影转换为 Haystack Document 对象。Document 数据类除了元数据外,还包含文本、表格和二进制数据作为内容。

from haystack.dataclasses import Document

def movie_to_document(movie_dataset):
    docs = []
    for movie in movie_dataset:
        # Process votes
        vote_avg_raw = movie.get('Vote_Average')
        try:
            rating = float(vote_avg_raw)
        except (TypeError, ValueError):
            rating = 5.0 # average value

        # Process genre
        genre_raw = movie.get("Genre")
        if isinstance(genre_raw, str):
            genre_list = [g.lower() for g in genre_raw.split(", ")]
        else:
            genre_list = []

        # Create Document
        doc = Document(
            content=movie.get('Overview', ''),
            meta={
                'title': movie.get('Title', ''),
                'rating': rating,
                'language': movie.get('Original_Language', ''),
                'genre': genre_list,
            }
        )
        docs.append(doc)
    return docs
documents = movie_to_document(data)
documents[14]
Document(id=1f5260fdf65717876901d115502f2d3286cc76ba582a05ca38249b16fb7423d7, content: 'The fearless one-eyed weasel Buck teams up with mischievous possum brothers Crash & Eddie as they he...', meta: {'title': 'The Ice Age Adventures of Buck Wild', 'rating': 7.1, 'language': 'en', 'genre': ['animation', 'comedy', 'adventure', 'family']})

📄 创建稀疏嵌入

使用 Qdrant/minicoil-v1 稀疏神经网络嵌入模型,配合 FastembedSparseDocumentEmbedder 组件。我们将使用 meta_fields_to_embed 参数将 title 元数据包含在内容中。初始化 FastembedSparseDocumentEmbedder 后,运行 .warm_up() 来加载嵌入模型并使其准备好运行。然后,使用文档运行该组件以创建每个文档的嵌入。

from haystack_integrations.components.embedders.fastembed import FastembedSparseDocumentEmbedder

sparse_doc_embedder = FastembedSparseDocumentEmbedder(model="Qdrant/minicoil-v1", meta_fields_to_embed=["title"])
sparse_doc_embedder.warm_up()

sparse_embedder_result = sparse_doc_embedder.run(documents=documents)
sparse_embedder_result["documents"][14]
Document(id=1f5260fdf65717876901d115502f2d3286cc76ba582a05ca38249b16fb7423d7, content: 'The fearless one-eyed weasel Buck teams up with mischievous possum brothers Crash & Eddie as they he...', meta: {'title': 'The Ice Age Adventures of Buck Wild', 'rating': 7.1, 'language': 'en', 'genre': ['animation', 'comedy', 'adventure', 'family']}, sparse_embedding: vector with 71 non-zero elements)

☁️ 将电影写入 Qdrant Cloud

初始化 QdrantDocumentStore 以连接到您的 Qdrant Cloud 集群。

您需要提供以下信息:

  • url:您的 Qdrant Cloud 实例的端点
  • index:存储文档的索引名称
  • api_key:您的 Qdrant API 密钥(通过环境变量安全设置)

确保设置:

  • use_sparse_embeddings=True 以启用稀疏向量支持
  • payload_fields_to_index 以定义要为过滤而索引的元数据字段(例如,genre、rating、language)

此设置确保您的向量存储支持稀疏检索和元数据过滤,这对于高级搜索场景至关重要。

from haystack_integrations.document_stores.qdrant import QdrantDocumentStore
from haystack.utils import Secret

document_store = QdrantDocumentStore(
    url="https://*******.us-west-1-0.aws.cloud.qdrant.io:6333",
    index="movie-assistant",
    api_key=Secret.from_env_var("QDRANT_API_KEY"),
    use_sparse_embeddings=True,
    recreate_index=True,
    payload_fields_to_index=[
        {"field_name":"meta.language", "field_schema":"keyword"},
        {"field_name":"meta.rating", "field_schema":"float"},
        {"field_name":"meta.genre", "field_schema":"keyword"},
        {"field_name":"meta.title", "field_schema":"keyword"}
    ]
)

现在,调用 .write_documents() 并传入 Document 列表。

document_store.write_documents(documents=sparse_embedder_result["documents"])

🔍 创建稀疏检索管道

构建一个稀疏检索管道,该管道接受用户关于所需电影类型的查询,并从我们的数据库中检索相关的电影。

首先,初始化并向管道添加一个使用 Qdrant/minicoil-v1 模型的 FastembedSparseTextEmbedder。此组件会将用户查询转换为稀疏向量表示。然后,我们添加一个 QdrantSparseEmbeddingRetriever,该组件连接到我们的 QdrantDocumentStore 并使用这些向量检索最相关的 5 部电影。

最后,我们将这两个组件连接起来,将嵌入器的输出传递给检索器的输入。这创建了一个功能性的查询管道,它将自然语言输入转换为稀疏嵌入,并从 Qdrant 返回匹配的结果。

from haystack_integrations.components.retrievers.qdrant import QdrantSparseEmbeddingRetriever
from haystack_integrations.components.embedders.fastembed import FastembedSparseTextEmbedder
from haystack import Pipeline

sparse_query_pipeline = Pipeline()
sparse_query_pipeline.add_component("sparse_text_embedder", FastembedSparseTextEmbedder(model="Qdrant/minicoil-v1"))
sparse_query_pipeline.add_component("sparse_retriever", QdrantSparseEmbeddingRetriever(document_store=document_store, top_k=5))
sparse_query_pipeline.connect("sparse_text_embedder.sparse_embedding", "sparse_retriever.query_sparse_embedding")
<haystack.core.pipeline.pipeline.Pipeline object at 0x7d5e78335e80>
🚅 Components
  - sparse_text_embedder: FastembedSparseTextEmbedder
  - sparse_retriever: QdrantSparseEmbeddingRetriever
🛤️ Connections
  - sparse_text_embedder.sparse_embedding -> sparse_retriever.query_sparse_embedding (SparseEmbedding)

使用查询运行管道

sparse_docs = sparse_query_pipeline.run({"text":"A movie about car race"})
Calculating sparse embeddings: 100%|██████████| 1/1 [00:00<00:00, 172.81it/s]

查看检索到的电影

for d in sparse_docs["sparse_retriever"]["documents"]:
  print("Title:", d.meta["title"])
  print("Content:", d.content)
  print("Rating:", d.meta["rating"])
  print("Genres:", d.meta["genre"])
  print("---")
Title: Cannonball Run II
Content: The original characters from the first Cannonball Run movie compete in an illegal race across the country once more in various cars and trucks.
Rating: 5.5
Genres: ['action', 'comedy']
---
Title: Ford v Ferrari
Content: American car designer Carroll Shelby and the British-born driver Ken Miles work together to battle corporate interference, the laws of physics, and their own personal demons to build a revolutionary race car for Ford Motor Company and take on the dominating race cars of Enzo Ferrari at the 24 Hours of Le Mans in France in 1966.
Rating: 8.0
Genres: ['drama', 'action', 'history']
---
Title: Driven
Content: Talented rookie race-car driver Jimmy Bly has started losing his focus and begins to slip in the race rankings. It's no wonder, with the immense pressure being shoveled on him by his overly ambitious promoter brother as well as Bly's romance with his arch rival's girlfriend Sophia. With much riding on Bly, car owner Carl Henry brings former racing star Joe Tanto on board to help Bly. To drive Bly back to the top of the rankings, Tanto must first deal with the emotional scars left over from a tragic racing accident which nearly took his life.
Rating: 5.1
Genres: ['action']
---
Title: New Initial D the Movie - Legend 2: Racer
Content: The planned film trilogy retells the beginning of the story from Shuuichi Shigeno's original car-racing manga. High school student Takumi Fujiwara works as a gas station attendant during the day and a delivery boy for his father's tofu shop during late nights. Little does he know that his precise driving skills and his father's modified Toyota Sprinter AE86 Trueno make him the best amateur road racer on Mt. Akina's highway. Because of this, racing groups from all over the Gunma prefecture issue challenges to Takumi to see if he really has what it takes to be a road legend.
Rating: 8.1
Genres: ['animation', 'drama', 'action']
---
Title: The Art of Racing in the Rain
Content: A family dog - with a near-human soul and a philosopher's mind - evaluates his life through the lessons learned by his human owner, a race-car driver.
Rating: 8.3
Genres: ['comedy', 'drama', 'romance']
---

🗑️ 添加过滤器

让我们通过结合语义搜索和过滤器来丰富用户查询。

文本查询“一部关于赛车的电影”首先被转换为稀疏嵌入,以便在电影描述中进行搜索。此外,我们应用两个过滤器:电影的评分必须至少为 7,并且其类型必须包含“动作”。通过结合语义相似性和结构化过滤,我们确保结果不仅在主题上相关,而且符合特定的质量和类型约束。

sparse_docs = sparse_query_pipeline.run({
    "sparse_text_embedder": {"text": "A movie about car race"},
    "sparse_retriever": {"filters":{
        "operator": "AND",
        "conditions": [
            {"field":"meta.rating", "operator":">=", "value":7},
            {"field":"meta.genre", "operator": "==", "value": "action"},
        ],
    }}
})
Calculating sparse embeddings: 100%|██████████| 1/1 [00:00<00:00, 236.82it/s]
for d in sparse_docs["sparse_retriever"]["documents"]:
  print("Title:", d.meta["title"])
  print("Content:", d.content)
  print("Rating:", d.meta["rating"])
  print("Genres:", d.meta["genre"])
  print("---")
Title: Ford v Ferrari
Content: American car designer Carroll Shelby and the British-born driver Ken Miles work together to battle corporate interference, the laws of physics, and their own personal demons to build a revolutionary race car for Ford Motor Company and take on the dominating race cars of Enzo Ferrari at the 24 Hours of Le Mans in France in 1966.
Rating: 8.0
Genres: ['drama', 'action', 'history']
---
Title: New Initial D the Movie - Legend 2: Racer
Content: The planned film trilogy retells the beginning of the story from Shuuichi Shigeno's original car-racing manga. High school student Takumi Fujiwara works as a gas station attendant during the day and a delivery boy for his father's tofu shop during late nights. Little does he know that his precise driving skills and his father's modified Toyota Sprinter AE86 Trueno make him the best amateur road racer on Mt. Akina's highway. Because of this, racing groups from all over the Gunma prefecture issue challenges to Takumi to see if he really has what it takes to be a road legend.
Rating: 8.1
Genres: ['animation', 'drama', 'action']
---
Title: Watch Out, We're Mad
Content: After a tied 1st place in a local stunt race, two drivers start a contest to decide who of them will own the prize, a dune buggy. But when a mobster destroys the car, they are determined to get it back.
Rating: 7.5
Genres: ['action', 'comedy']
---
Title: New Initial D the Movie - Legend 1: Awakening
Content: The first movie in a trilogy, focusing on the battle against the Takahashi brothers. High school student Takumi Fujiwara works as a gas station attendant during the day and a delivery boy for his father's tofu shop during late nights. Little does he know that his precise driving skills and his father's modified Toyota Sprinter AE86 Trueno make him the best amateur road racer on Mt. Akina's highway. Because of this, racing groups from all over the Gunma prefecture issue challenges to Takumi to see if he really has what it takes to be a road legend.
Rating: 8.3
Genres: ['animation', 'action']
---
Title: New Initial D the Movie - Legend 3: Dream
Content: Mt. Akina's new downhill racing hero Fujiwara Takumi prepares for the final showdown against Red Sun's unbeaten leader and Akagi's fastest driver, Takahashi Ryosuke.
Rating: 7.4
Genres: ['action', 'animation', 'drama']
---

📐 无查询过滤

初始化一个仅元数据的检索器(FilterRetriever),该检索器允许根据 rating、language 或 genre 等字段过滤电影,而无需语义匹配。

from haystack.components.retrievers.filter_retriever import FilterRetriever

filter_retriever = FilterRetriever(document_store=document_store)

运行 FilterRetriever 以获取所有评分为 9.0 及以上的电影

filter_retriever.run({
        "field":"meta.rating","operator":">=","value":9.0
    })

🤖 构建电影推荐代理

在本节中,我们将所有内容整合起来,构建一个利用工具、系统提示和 LLM 的电影推荐代理。

🛠️ 检索工具

为了与数据交互,代理使用了一个检索工具。Haystack 中的每个工具都需要一个名称、一个描述、一个要调用的函数和一个参数模式

我们首先定义一个检索函数,该函数根据输入动态地选择语义搜索还是基于元数据的过滤。如果用户提供查询,则工具使用稀疏检索管道。如果仅提供过滤器,则回退到元数据过滤器检索器。这种逻辑使代理能够灵活地处理各种用户查询—从简单的类型过滤器到更细致的语义提示。

from typing import Optional, Dict, Any, List

def retrieval_function(
    query: Optional[str] = None,
    metadata_filters: Optional[Dict[str, Any]] = None,
    top_k: int = 5
) -> List[Document]:

    retrieved_documents = []
    if not query and not metadata_filters:
        raise ValueError("At least one of 'query' or 'metadata_filters' must be provided.")

    # Retrieve documents based on the input provided
    if query:
        retrieved_documents = sparse_query_pipeline.run({
            "text": query,
            "filters": metadata_filters, # Can include metadata filters in semantic query
            "top_k": top_k
        })["sparse_retriever"]
    # If there's only `metadata_filters`
    else:
        retrieved_documents = filter_retriever.run(filters=metadata_filters)
        retrieved_documents["documents"] = retrieved_documents["documents"][:top_k]

    return retrieved_documents

定义工具参数的 JSON 模式,例如 querytop_kmetadata_filters,以确保工具调用得到正确结构化和验证。

在大多数情况下,您无需手动定义 parameters。当您使用 @tool 装饰器create_tool_from_function 时,Haystack 会自动从函数推断工具的名称、描述和参数,并为您生成匹配的 JSON 模式。

parameters = {
  "type": "object",
  "properties": {
    "top_k": {
      "type": "integer",
      "description": "number of movies to get",
      "default": 5,
    },
    "query": {
      "type": "string",
      "description": "query to retrieve movies based on semantic similarity of the description"
    },
    "metadata_filters": {
      "type": "object",
      "description": "metadata filters to filter the movies based on user's preference",
      "properties": {
        "operator": {
          "type": "string",
          "enum": ["AND", "OR", "NOT"],
          "default": "AND",
          "description": "operator to combine the conditions"
        },
        "conditions": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "field": {
                "type": "string",
                "enum": ["meta.title", "meta.rating", "meta.genre", "meta.language"]
              },
              "operator": {
                "type": "string",
                "enum": ["==", "!=", ">", "<", ">=", "<=", "not in"]
              },
              "value": {
                  "oneOf": [
                  { "type": "string" },
                  { "type": "integer" },
                  {
                    "type": "array",
                    "items": {"type": "string"}
                  }
                ]
              }
            },
            "required": ["field", "operator", "value"],
            "additionalProperties": False
          }
        }
      },
      "required": ["operator", "conditions"],
      "additionalProperties": False
    }
  },
  "additionalProperties": False,
}

将检索到的电影的输出格式化为可读文本,包括标题、内容、评分、类型和语言。然后,将检索函数包装为 Haystack Tool,指定代理应如何调用它以及输出应如何显示。

from haystack.tools import Tool

def movie_to_string(documents) -> str:
    result_str = ""
    for document in documents:
        result_str += f"Movie details for {document.meta['title']}:\n{document.content}\nRating:{document.meta['rating']}\nGenres:{document.meta['genre']}\nLanguage:{document.meta['language']}\n---\n"
    return result_str

retrieval_tool = Tool(
    name="retrieval_tool",
    description="Use this tool to get movies fitting to the user criteria",
    function=retrieval_function,
    parameters=parameters,
    outputs_to_string={"source": "documents", "handler": movie_to_string}
)

🧠 代理

首先,您需要为代理定义一个系统提示。此提示指示如何解释用户查询、生成工具输入以及处理回退策略。

prompt = """
You are a movie retrieval assistant that helps users find movies from a database using `retrieval_tool`. Users describe what they are looking for in natural language.

Each movie entry in the database contains the following fields:

* `content`: *str* — movie description
* `meta.title`: *str* — movie title
* `meta.rating`: *float* — rating score (e.g., 8.3)
* `meta.genre`: *List[str]* — genres (e.g., `["drama", "thriller"]`)
* `meta.language`: *str* — original language (options are: 'bn', 'te', 'pt', 'ml', 'ja', 'it', 'lv', 'ar', 'da', 'no', 'hu', 'la', 'el', 'he', 'fi', 'ta', 'sv', 'ms', 'sr', 'ro', 'ru', 'nb', 'nl', 'id', 'cs', 'fr', 'th', 'tl', 'de', 'uk', 'et', 'es', 'pl', 'fa', 'is', 'eu', 'tr', 'cn', 'zh', 'en', 'ko', 'ca', 'hi'

### Task

Your goal is to generate an input for the `retrieval_tool` using the user's query, which may include:

* `query`: a natural language string to match against the `content` field (movie description) — optional
* `metadata_filters`: dictionary of filtering conditions using fields in the `meta.*` namespace — optional
* `top_k`: number of movies to retrieve — default is 5 unless the user specifies otherwise

**Important:** At least one of `query` or `metadata_filters` **must** be included. Use the most appropriate one based on the user request.

### Generation Guidelines

- Use `metadata_filters` when the user specifies in the query:
  * **Genres** (e.g., "I'd like to watch an action movie", "A romance movie...")
  * **Rating thresholds** (e.g., "at least 7.5", "above 6", "a good movie", "quality movie")
  * **Language constraints** (e.g., "only English movies")

- Use `query` when the user describes:
  * **Plot or story themes** (e.g., "a movie about friendship and betrayal")
  * **Emotions or tone** (e.g., "feel-good", "dark comedy")
  * **Character types or settings** (e.g., "set in space", "female-led action")

- Use both `query` and `metadata_filters` if there is enough information in the user query for both `query` and `metadata_filters`.
- If the user asks for a specific number of movies, set that value as `top_k`.
- If the user only specifies a genre, it's acceptable to return only `metadata_filters` — no query needed.
- All metadata values must be lowercase
- If required, you can combine the conditions with "AND", "NOT" or "OR" operators.

### Examples

Here are the tool call parameters for user queries:

* User: "Can you recommned horror movies with bad ratings?"
{
  "metadata_filters": {
    "operator": "AND",
    "conditions": [
      {"field": "meta.genre", "operator": "==", "value": "horror"},
      {"field": "meta.rating", "operator": "<", "value": 5}
    ]
  }
}

* User: "I want a powerful courtroom drama, something emotionally intense, high rated"
{
  "query": "powerful courtroom drama, emotionally intense",
  "metadata_filters": {
    "operator": "AND",
    "conditions": [
      {"field": "meta.genre", "operator": "==", "value": "drama"},
      {"field": "meta.rating", "operator": ">=", "value": 7}
    ]
  }
}

* User: "Give me five Japanese thrillers."
{
  "top_k": 5,
  "metadata_filters": {
    "operator": "AND",
    "conditions": [
      {"field": "meta.language", "operator": "==", "value": "ja"},
      {"field": "meta.genre", "operator": "==", "value": "thriller"}
    ]
  }
}

* User: "I'd like to watch an action movie that is not drama"
{
  "metadata_filters": {
    "operator": "AND",
    "conditions": [
      {"field": "meta.genre", "operator": "==", "value": "action"},
      {"field": "meta.genre", "operator": "not in", "value": ["drama"]}
    ]
  }
}

#### Fallback Strategy
If the search space is too narrow (i.e., few or no results), relax the filters and/or simplify the query to allow broader but still relevant results. Aim to preserve the user’s intent while increasing recall.
Don't ask for confirmation from the user. Just tell that you broadened the search with the result and return the latest list.
"""

使用检索工具和系统提示初始化 Haystack Agent。此代理可以理解用户查询并响应电影推荐。

from haystack.components.agents import Agent
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage

movie_agent = Agent(
    chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
    system_prompt=prompt,
    tools=[retrieval_tool]
)

向代理发送一个示例查询。代理解释意图,调用检索工具,并返回合适的电影推荐。

query = "Find me a highly-rated action movie about car racing."
# query = "What can I watch with my kids, about animals?"
# query = "Can you recommend five japanese thrillers?"
# query = "I'd like to watch a german action movie but I don't like thriller and horror"
# query = "I love Titanic. Which movie should I watch next? It should be a highly rated movie though"

messages = movie_agent.run([ChatMessage.from_user(query)])
Calculating sparse embeddings: 100%|██████████| 1/1 [00:00<00:00, 198.07it/s]

打印 messages["last_message"].text 以查看代理的最终响应

print(messages["last_message"].text)
Here are some highly-rated action movies about car racing:

1. **Ford v Ferrari**
   - **Rating:** 8.0
   - **Genres:** Drama, Action, History
   - **Description:** American car designer Carroll Shelby and the British-born driver Ken Miles work together to build a revolutionary race car for Ford Motor Company and take on the dominating race cars of Enzo Ferrari at the 24 Hours of Le Mans in France in 1966.
   - **Language:** English

2. **New Initial D the Movie - Legend 2: Racer**
   - **Rating:** 8.1
   - **Genres:** Animation, Drama, Action
   - **Description:** High school student Takumi Fujiwara discovers his precise driving skills while working as a gas station attendant and becomes the best amateur road racer on Mt. Akina's highway.
   - **Language:** Japanese

3. **Watch Out, We're Mad**
   - **Rating:** 7.5
   - **Genres:** Action, Comedy
   - **Description:** After a tied 1st place in a local stunt race, two drivers start a contest to decide who will own a dune buggy. When a mobster destroys the car, they are determined to get it back.
   - **Language:** Italian

4. **New Initial D the Movie - Legend 1: Awakening**
   - **Rating:** 8.3
   - **Genres:** Animation, Action
   - **Description:** Focusing on the battle against the Takahashi brothers, this movie follows Takumi Fujiwara as he rises as the best amateur road racer on Mt. Akina's highway.
   - **Language:** Japanese

5. **Italian Spiderman**
   - **Rating:** 7.1
   - **Genres:** Comedy, Action
   - **Description:** A parody of Italian action–adventure films with an Italian take on the comic book superhero Spider-Man.
   - **Language:** Italian

Let me know if you need more information or recommendations!

🔉 启用流式输出

通过将 print_streaming_chunk 作为 streaming_callback 传递来启用流式输出。这会显示实时更新,包括代理处理查询时所有工具的调用。

from haystack.components.generators.utils import print_streaming_chunk

query = "Find me a highly-rated action movie about car racing."
# query = "What can I watch with my kids, about animals?"
# query = "Can you recommend five japanese thrillers?"
# query = "I'd like to watch a german action movie but I don't like thriller and horror"
# query = "I love Titanic. Which movie should I watch next? It should be a highly rated movie though"

messages = movie_agent.run([ChatMessage.from_user(query)], streaming_callback=print_streaming_chunk)
[TOOL CALL]
Tool: retrieval_tool 
Arguments: {"query":"highly-rated action movie about car racing","metadata_filters":{"operator":"AND","conditions":[{"field":"meta.genre","operator":"==","value":"action"},{"field":"meta.rating","operator":">=","value":7}]}}



Calculating sparse embeddings: 100%|██████████| 1/1 [00:00<00:00, 190.17it/s]


[TOOL RESULT]
Movie details for New Initial D the Movie - Legend 2: Racer:
The planned film trilogy retells the beginning of the story from Shuuichi Shigeno's original car-racing manga. High school student Takumi Fujiwara works as a gas station attendant during the day and a delivery boy for his father's tofu shop during late nights. Little does he know that his precise driving skills and his father's modified Toyota Sprinter AE86 Trueno make him the best amateur road racer on Mt. Akina's highway. Because of this, racing groups from all over the Gunma prefecture issue challenges to Takumi to see if he really has what it takes to be a road legend.
Rating:8.1
Genres:['animation', 'drama', 'action']
Language:ja
---
Movie details for New Initial D the Movie - Legend 1: Awakening:
The first movie in a trilogy, focusing on the battle against the Takahashi brothers. High school student Takumi Fujiwara works as a gas station attendant during the day and a delivery boy for his father's tofu shop during late nights. Little does he know that his precise driving skills and his father's modified Toyota Sprinter AE86 Trueno make him the best amateur road racer on Mt. Akina's highway. Because of this, racing groups from all over the Gunma prefecture issue challenges to Takumi to see if he really has what it takes to be a road legend.
Rating:8.3
Genres:['animation', 'action']
Language:ja
---
Movie details for Ford v Ferrari:
American car designer Carroll Shelby and the British-born driver Ken Miles work together to battle corporate interference, the laws of physics, and their own personal demons to build a revolutionary race car for Ford Motor Company and take on the dominating race cars of Enzo Ferrari at the 24 Hours of Le Mans in France in 1966.
Rating:8.0
Genres:['drama', 'action', 'history']
Language:en
---
Movie details for Watch Out, We're Mad:
After a tied 1st place in a local stunt race, two drivers start a contest to decide who of them will own the prize, a dune buggy. But when a mobster destroys the car, they are determined to get it back.
Rating:7.5
Genres:['action', 'comedy']
Language:it
---
Movie details for Italian Spiderman:
This is an Australian-made parody of Italian action–adventure films of the 60s and 70s. and foreign movies that misappropriated popular American superheroes such as the Japanese TV series “Spider-Man”. (It should be noted that the Japanese Spider-Man was officially sanctioned by Marvel and is considered canon in the Marvel universe) This is an Italian take on the comic book superhero Spider-Man.
Rating:7.1
Genres:['comedy', 'action']
Language:it
---


[ASSISTANT]
Here are some highly-rated action movies related to car racing:

1. **New Initial D the Movie - Legend 1: Awakening**
   - **Rating:** 8.3
   - **Genres:** Animation, Action
   - **Language:** Japanese
   - **Description:** The first movie in a trilogy, focusing on the battle against the Takahashi brothers. High school student Takumi Fujiwara discovers his precise driving skills make him the best amateur road racer on Mt. Akina's highway.

2. **New Initial D the Movie - Legend 2: Racer**
   - **Rating:** 8.1
   - **Genres:** Animation, Drama, Action
   - **Language:** Japanese
   - **Description:** Continuing from the first film, Takumi Fujiwara's journey in car racing challenges from groups all over the Gunma prefecture unfolds.

3. **Ford v Ferrari**
   - **Rating:** 8.0
   - **Genres:** Drama, Action, History
   - **Language:** English
   - **Description:** Carroll Shelby and driver Ken Miles work to build a revolutionary race car for Ford to compete against Ferrari's dominating race cars at the 1966 24 Hours of Le Mans.

4. **Watch Out, We're Mad**
   - **Rating:** 7.5
   - **Genres:** Action, Comedy
   - **Language:** Italian
   - **Description:** After a tie in a local stunt race, two drivers contest for ownership of a dune buggy, but when a mobster destroys the car, they are determined to retrieve it.

5. **Italian Spiderman**
   - **Rating:** 7.1
   - **Genres:** Comedy, Action
   - **Language:** Italian
   - **Description:** A parody of Italian action/adventure films, this takes a humorous spin on superheroes and foreign adaptations.

Let me know if you need more information or further assistance!