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

Haystack 2.14.0

在 Github 上查看

⭐️ Highlights

增强复杂代理系统

我们通过改进消息处理和流式传输支持,增强了代理工作流程。 Agent 组件现在返回一个 last_message 输出,以便快速访问最后一条消息,并且可以使用 streaming_callback 实时发出工具结果。您可以使用更新的 print_streaming_chunk 或编写自己的回调函数来在流式传输期间启用 ToolCall 详细信息。

from haystack.components.websearch import SerperDevWebSearch
from haystack.components.agents import Agent
from haystack.components.generators.utils import print_streaming_chunk
from haystack.tools import tool, ComponentTool
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack.dataclasses import ChatMessage

web_search = ComponentTool(name="web_search", component=SerperDevWebSearch(top_k=5))
wiki_search = ComponentTool(name="wiki_search", component=SerperDevWebSearch(top_k=5, allowed_domains=["https://www.wikipedia.org/"]))

research_agent = Agent(
    chat_generator=OpenAIChatGenerator(model="gpt-4o-mini"),
    system_prompt="""
    You are a research agent that can find information on web or specifically on wikipedia. 
    Use wiki_search tool if you need facts and use web_search tool for latest news on topics.
    Use one tool at a time, use the other tool if the retrieved information is not enough.
    Summarize the retrieved information before returning response to the user.
    """,
    tools=[web_search, wiki_search],
    streaming_callback=print_streaming_chunk
)

result = research_agent.run(messages=[ChatMessage.from_user("Can you tell me about Florence Nightingale's life?")])

使用 print_streaming_chunk 函数启用流式传输看起来像这样

[TOOL CALL]
Tool: wiki_search 
Arguments: {"query":"Florence Nightingale"}

[TOOL RESULT]
{'documents': [{'title': 'List of schools in Nottinghamshire', 'link': 'https://www.wikipedia.org/wiki/List_of_schools_in_Nottinghamshire', 'position': 1, 'id': 'a6d0fe00f1e0cd06324f80fb926ba647878fb7bee8182de59a932500aeb54a5b', 'content': 'The Florence Nightingale Academy, Eastwood; The Flying High Academy, Mansfield; Forest Glade Primary School, Sutton-in-Ashfield; Forest Town Primary School ...', 'blob': None, 'score': None, 'embedding': None, 'sparse_embedding': None}], 'links': ['https://www.wikipedia.org/wiki/List_of_schools_in_Nottinghamshire']}
...

打印 last_message

print("Final Answer:", result["last_message"].text)
>>> Final Answer: Florence Nightingale (1820-1910) was a pioneering figure in nursing and is often hailed as the founder of modern nursing. She was born...

此外,AnswerBuilder 将所有生成的_消息_存储在 GeneratedAnswer 的 all_messages 元字段中,并支持新的 last_message_only 模式,适用于只需要处理最后一条_消息_的轻量级流程。

使用 SuperComponents 可视化 Pipelines

我们扩展了 pipeline.draw()pipeline.show(),它们将 pipeline 图保存为图像文件或在 Jupyter notebooks 中显示它们。您现在可以传递 super_component_expansion=True 来展开任何 SuperComponents 并绘制更详细的可视化。

以下是包含 MultiFileConverterDocumentPreprocssor SuperComponents 的 pipeline 的示例。在安装 pip install haystack-ai pypdf markdown-it-py mdit_plain trafilatura python-pptx python-docx jq openpyxl tabulate pandas 所需的所有支持的文件格式的依赖项后,您可以运行

from pathlib import Path

from haystack import Pipeline
from haystack.components.converters import MultiFileConverter
from haystack.components.preprocessors import DocumentPreprocessor
from haystack.components.writers import DocumentWriter
from haystack.document_stores.in_memory import InMemoryDocumentStore

document_store = InMemoryDocumentStore()

pipeline = Pipeline()
pipeline.add_component("converter", MultiFileConverter())
pipeline.add_component("preprocessor", DocumentPreprocessor())
pipeline.add_component("writer", DocumentWriter(document_store = document_store))
pipeline.connect("converter", "preprocessor")
pipeline.connect("preprocessor", "writer")

# expanded pipeline that shows all components
path = Path("expanded_pipeline.png")
pipeline.draw(path=path, super_component_expansion=True)

# original pipeline
path = Path("original_pipeline.png")
pipeline.draw(path=path)

Extended vs Original Pipeline

SentenceTransformersSimilarityRanker 支持 PyTorch、ONNX 和 OpenVINO

我们添加了一个新的 SentenceTransformersSimilarityRanker 组件,它使用 Sentence Transformers 库根据文档与查询的语义相似度对文档进行排序。此组件将替换可能在未来版本中弃用的旧 TransformersSimilarityRanker 组件,并在弃用期结束后移除。 SentenceTransformersSimilarityRanker 还允许选择不同的推理后端:PyTorch、ONNX 和 OpenVINO。例如,在安装 sentence-transformers>=4.1.0 后,您可以运行

from haystack.components.rankers import SentenceTransformersSimilarityRanker
from haystack.utils.device import ComponentDevice

onnx_ranker = SentenceTransformersSimilarityRanker(
    model="sentence-transformers/all-MiniLM-L6-v2",
    token=None,
    device=ComponentDevice.from_str("cpu"),
    backend="onnx",
)
onnx_ranker.warm_up()
docs = [Document(content="Berlin"), Document(content="Sarajevo")]
output = onnx_ranker.run(query="City in Germany", documents=docs)
ranked_docs = output["documents"]

⬆️ 升级说明

  • 我们将 py.typed 文件添加到 Haystack 中,以便下游项目可以使用类型信息,这符合 PEP 561。这意味着 Haystack 的类型提示现在将对依赖它的项目中的类型检查器可见。Haystack 主要使用 mypy(而不是 pyright)进行类型检查,尽管我们付出了努力,但某些类型信息可能不完整或不可靠。如果您在自己的项目中使用静态类型检查,您可能会注意到一些变化:以前,Haystack 的类型实际上被视为 Any,但现在将提供并强制执行实际的类型信息。我们将在下一个版本中继续改进类型。
  • 已移除弃用的 deserialize_tools_inplace 实用函数。请使用 deserialize_tools_or_toolset_inplace,并像这样导入: from haystack.tools import deserialize_tools_or_toolset_inplace

🚀 新功能

  • ToolInvoker 类添加了 run_async 方法,以允许异步调用工具。

  • Agent 现在也可以通过 run_async 方法流式传输工具结果。

  • 引入了 serialize_valuedeserialize_value 实用方法,以实现跨模块的_一致_值(反)序列化。

  • State 类移动到 agents.state 模块,并添加了序列化和反序列化功能。

  • 为 ConditionalRouter 添加了对多个输出的支持

  • 实现 OpenAI 使用数据的 JSON 安全序列化,将_token_计数和详细信息(如 CompletionTokensDetails 和 PromptTokensDetails)转换为普通字典。

  • 添加了一个新的 SentenceTransformersSimilarityRanker 组件,它使用 Sentence Transformers 库根据文档与查询的语义相似度对文档进行排序。此组件是对旧 TransformersSimilarityRanker 组件的替代,该组件可能在未来版本中被弃用,并在弃用期结束后移除。 SentenceTransformersSimilarityRanker 还允许选择不同的推理后端:PyTorch、ONNX 和 OpenVINO。要使用 SentenceTransformersSimilarityRanker,您需要安装 sentence-transformers>=4.1.0

  • ToolInvoker 添加了 streaming_callback 参数,以启用工具结果的流式传输。请注意,tool_result 仅在工具执行完成后发出,并且不会增量流式传输。

  • 更新 print_streaming_chunk 以打印 ToolCall 信息(如果存在于块的元数据中)。

  • 更新 Agent 以将 streaming_callback 转发给 ToolInvoker,以便在工具调用期间发出工具结果。

  • 增强 SuperComponent 的类型兼容性检查,以返回检测到的两个输入类型之间的公共类型。

⚡️ 增强说明

  • 在使用 HuggingFaceAPIChatGenerator 进行流式传输时,返回的 ChatMessage 现在在其元数据中包含 prompt _token_数和 completion _token_数。在内部,HuggingFaceAPIChatGenerator 请求一个包含使用数据的额外流式传输块。然后,它处理使用流式传输块,将使用元数据添加到返回的 ChatMessage 中。

  • 我们现在有一个 TextEmbedder 的协议。该协议使得创建需要任何 TextEmbedder 作为 init 参数的自定义组件或 SuperComponents 更加容易。

  • 我们添加了一个 Component 签名验证方法,该方法详细说明了 runrun_async 方法签名之间的不匹配。这使用户可以轻松调试自定义组件。

  • 增强了 AnswerBuilder 组件的两个面向代理的功能

    1. 所有生成的_消息_现在都存储在 GeneratedAnswer 对象 的 meta 字段下的 all_messages 键中,从而提高了可追溯性和调试能力。
    2. 添加了一个新的 last_message_only 参数,当设置为 True 时,它只处理回复中的最后一条_消息_,同时仍将完整的对话历史保存在元数据中。这对于只需要处理最后_一条_响应的代理工作流程特别有用。
  • 已进行多项改进,以便 Agent 组件可以直接用在 ComponentTool 中,从而能够直接构建多代理系统。这些改进包括

    • 在 Agent 的输出中添加一个 last_message 字段,该字段返回最后生成的 ChatMessage。
    • 改进 ToolInvoker 中的 _default_output_handler,使其首先尝试序列化工具结果中的输出,然后再将其转换为字符串。这对于在字符串化 ChatMessage 等数据类时获得更好的表示尤为重要。
  • component 装饰器添加了类型提示。这改善了对 Pyright/Pylance 的支持,使 VSCode 等 IDE 能够显示组件的文档字符串。

  • 更新了 pipeline 执行逻辑,使用新的实用方法 _deepcopy_with_exceptions,该方法尝试深度复制对象,并在复制失败时安全地回退到原始对象。此外,当 ComponentToolToolset 实例用作运行时参数时,_deepcopy_with_exceptions 会跳过对它们的深度复制。这可以防止由于尝试深度复制包含不可复制属性(例如 Jinja2 模板、客户端)的对象而导致的错误和意外行为。以前,对输入和输出使用了标准的 deepcopy,由于某些 Python 对象无法深度复制,这有时会导致错误。

  • 使用 Pydantic 的 model_json_schema 重构了 ComponentTool 参数的 JSON Schema 生成,从而支持了更广泛的类型(例如 Union、Enum、Dict 等)。我们还在调用 model_json_schema 之前将数据类转换为 Pydantic 模型,以保留 schema 中参数的文档字符串描述。这意味着 ChatMessage、Document 等数据类现在具有正确定义的 JSON schema。

  • Pipelinedraw()show() 方法现在有一个额外的布尔参数 super_component_expansion,当设置为 True 并且 pipeline 包含 SuperComponents 时,可视化图将显示 super-components 的内部结构,就像它们是 pipeline 的一部分组件一样,而不是一个带有 SuperComponent 名称的“黑盒子”。

  • 改进了 @componentComponent 协议的类型注解。类型检查器现在可以确保 @component 类提供兼容的 run() 方法,其必需的返回类型已从 Dict[str, Any](不变)更改为 Mapping[str, Any],以允许将 TypedDict 用于输出类型。

    • 更新了 ToolInvoker 中的 StreamingChunk 构建,以流式传输带有 finish reason 的块。这在使用 print_streaming_chunk 实用方法时很有用
    • 更新 print_streaming_chunk 以更好地格式化 _消息_,尤其是在与 Agent 一起使用时。
    • 还更新为与当前版本的 AWS Bedrock 集成兼容,方法是处理 ChoiceDeltaToolCall 的 dict 表示形式
  • ComponentTool 在包装 SuperComponent 时,现在可以保留并合并来自底层 pipeline 组件的文档字符串。当 ComponentTool 与 SuperComponent 一起使用时,会进行两项关键改进

    1. 参数描述现在从包装管道中的原始组件中提取。当一个输入映射到多个组件时,参数描述将从所有映射的组件中合并,提供关于参数如何在整个管道中使用的全面信息。
    2. 组件的总体描述现在是从所有底层组件的描述生成的,而不是使用通用的 SuperComponent 描述。这有助于 LLM 理解组件实际做什么,而不是仅仅看到“使用提供的输入运行包装的管道”。

    这些更改使得 SuperComponents 在 LLM 函数调用方面更加有用,因为 LLM 将获得有关组件目的及其参数的详细信息。

  • SentenceTransformersDocumentEmbedderSentenceTransformersTextEmbedder 添加了 local_files_only 参数,以允许在离线模式下加载模型。

  • 更新了 DocumentRecallEvaluator。现在,在 MULTI_HIT 模式下,除法是针对唯一真实文档进行的,而不是针对总真实文档数。我们还添加了空值检查。如果没有检索到文档或所有文档的内容都为空字符串,我们将返回 0.0 并记录警告。同样,如果没有真实文档或所有文档的内容都为空字符串,我们将返回 0.0 并记录警告。

⚠️ 弃用说明

  • 弃用了 dataclasses 模块中的 State 类。鼓励用户迁移到新版本的 State,该版本现在位于 agents.state 模块中。已添加弃用警告以指导此迁移。

🔒 安全说明

  • 使 SentenceSplitter 中的 QUOTE_SPANS_RE 正则表达式变为 ReDoS 安全。这可以防止对恶意输入的潜在回溯。

🐛 Bug 修复

  • 修复了 SentenceSplitter 组件内使用的 QUOTE_SPANS_RE 正则表达式中潜在的 ReDoS 问题。
  • 将 OpenAITextEmbedder 和 OpenAIDocumentEmbedder 的 to_dict 方法添加了 init 参数 timeout 和 max_retries。这确保了在使用这些组件的 to_dict 方法时,这些值能够被正确序列化。
  • 在 LoggingTracer 中使用 coerce_tag_value 来序列化标签值
  • 更新 ComponentTool 的 __deepcopy__ 以优雅地处理尝试深度复制属性时发生的 NotImplementedError。
  • 修复了 OpenAIChatGenerator 和 OpenAIGenerator 在包装来自 Weave 等工具的流式响应时未正确处理的问题。
  • 修复了 RecursiveDocumentSplitter 中当 split_text 长于 split_length 并触发递归分块时出现的 bug。
  • 使 HuggingFaceAPICompatibleChatGenerator 中的内部工具转换与 huggingface_hub>=0.31.0 兼容。在 huggingface_hub 库中,ChatCompletionInputFunctionDefinitionarguments 属性已重命名为 parameters。我们的实现同时兼容旧版本和新版本。
  • HuggingFaceAPIChatGenerator 现在会检查 Hugging Face API 返回的工具调用中 arguments 变量的类型。如果 arguments 是 JSON 字符串,则将其解析为字典。以前,arguments 的类型未被检查,这有时会导致后续的工具工作流程失败。
  • 将 deserialize_tools_inplace 移回原始导入路径:from haystack.tools.tool import deserialize_tools_inplace。
  • 为了在 AsyncPipeline 与仅具有同步 run 方法的组件一起使用时正确保留上下文,我们使用 contextvars.copy_context() 复制上下文,并使用 ctx.run(...) 运行组件,以便保留活动跟踪 span 等上下文。现在这意味着如果您的组件 1) 仅具有同步 run 方法,并且 2) 它向跟踪器记录了内容,那么该跟踪将被正确地嵌套在父上下文中。
  • 修复了 LLMMetadataExtractor 在处理 Document 对象且内容为 None 或空字符串时发生的错误。该组件现在可以优雅地处理这些情况,将这些文档标记为失败并在其元数据中提供相应的错误消息,而无需尝试 LLM 调用。
  • 修复了 ComponentTool 使用的 component_invoker 在将 ChatMessage 等数据类直接传递给 component_tool.invoke(...) 时能够正常工作的问题。以前这会引发错误或静默跳过您的输入。

💙 非常感谢所有为本次发布做出贡献的人!

特别感谢并祝贺我们的首次贡献者!