创建 Agent 群
最后更新:2025 年 5 月 12 日
截至 Haystack 2.9.0,实验性数据类(重构的 ChatMessage 和 ChatRole、ToolCall 和 Tool)以及组件(重构的 OpenAIChatGenerator、ToolInvoker)已从
haystack-experimental中移除,并合并到 Haystack 核心中。
OpenAI 最近发布了 Swarm:一个教育性框架,提出了用于创建和协调多代理系统的轻量级技术。
在此 Notebook 中,我们将探讨 Swarm 的核心概念(例程和交接),并使用 Haystack 及其工具支持来实现它们。
这次探索不仅具有教育意义:我们将解锁原始实现中缺失的功能,例如使用来自不同提供商的模型的能力。事实上,我们的最终示例将包含 3 个代理:一个由 gpt-4o-mini(OpenAI)驱动,一个使用 Claude 3.5 Sonnet(Anthropic),第三个通过 Ollama 在本地运行 Llama-3.2-3B。
设置
我们安装了所需的依赖项。除了 Haystack,我们还需要 Anthropic 和 Ollama 的集成。
! pip install haystack-ai jsonschema anthropic-haystack ollama-haystack
接下来,我们配置 OpenAI 和 Anthropic 的 API 密钥。
from getpass import getpass
import os
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass("Enter your OpenAI API key:")
if not os.environ.get("ANTHROPIC_API_KEY"):
os.environ["ANTHROPIC_API_KEY"] = getpass("Enter your Anthropic API key:")
from typing import Annotated, Callable, Tuple
from dataclasses import dataclass, field
import random, re
from haystack.dataclasses import ChatMessage, ChatRole
from haystack.tools import create_tool_from_function
from haystack.components.tools import ToolInvoker
from haystack.components.generators.chat import OpenAIChatGenerator
from haystack_integrations.components.generators.anthropic import AnthropicChatGenerator
from haystack_integrations.components.generators.ollama import OllamaChatGenerator
从简单开始:构建一个代理
构建代理的第一步是创建一个助手:将其视为聊天语言模型 + 系统提示。
我们可以将其实现为一个具有三个参数的轻量级数据类
- name
- LLM(Haystack 聊天生成器)
- 说明(它们将构成系统消息)
@dataclass
class Assistant:
name: str = "Assistant"
llm: object = OpenAIChatGenerator()
instructions: str = "You are a helpful Agent"
def __post_init__(self):
self._system_message = ChatMessage.from_system(self.instructions)
def run(self, messages: list[ChatMessage]) -> list[ChatMessage]:
new_message = self.llm.run(messages=[self._system_message] + messages)["replies"][0]
if new_message.text:
print(f"\n{self.name}: {new_message.text}")
return [new_message]
让我们创建一个 Joker 助手,负责讲笑话。
joker = Assistant(name="Joker", instructions="you are a funny assistant making jokes")
messages = []
print("Type 'quit' to exit")
while True:
if not messages or messages[-1].role == ChatRole.ASSISTANT:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append(ChatMessage.from_user(user_input))
new_messages = joker.run(messages)
messages.extend(new_messages)
Type 'quit' to exit
User: hey!
Joker: Hey there! How's it going? Are you ready for some laughs, or are we saving the jokes for dessert? 🍰
User: where is Rome?
Joker: Rome is in Italy, but if you’re asking me for directions, I might just say, “Take a left at the Colosseum and keep going until you smell pizza!” 🍕
User: you can do better. What about Paris?
Joker: Ah, Paris! That’s in France, where the Eiffel Tower stands tall, the croissants are buttery, and locals will tell you the secret to love is just a little bit of patience... and a great view! 🥖❤️ Why did the Eiffel Tower get in trouble? It couldn’t stop “towering” over everyone!
User: quit
让我们说它尽力而为 😀
工具和例程
“代理”一词有广泛的定义。
然而,要符合代理的资格,一个建立在语言模型之上的软件应用程序应该超越简单地生成文本;它还必须能够执行操作,例如执行函数。
一种流行的方法是 **工具调用**
- 我们将一组工具(函数、具有给定规范的 API)提供给模型。
- 模型根据用户请求和可用工具准备函数调用。
- 实际调用在模型外部(在代理级别)执行。
- 模型可以进一步阐述调用的结果。
有关 Haystack 中工具支持的信息,请查看文档。
Swarm 引入了 **例程**,它们是自然语言指令与执行它们所需的工具配对。下面,我们将构建一个能够调用工具和执行例程的代理。
实现
-
instructions已经可以传递给助手,以指导其行为。 -
Agent 引入了一个名为
functions的新 init 参数。这些函数会自动转换为工具。关键区别:要传递给语言模型,工具必须具有名称、描述和 JSON schema 来指定其参数。 -
在初始化期间,我们还创建一个
ToolInvoker。这个 Haystack 组件接收包含已准备好tool_calls的聊天消息,执行工具调用,并将结果包装在具有tool角色的聊天消息中。 -
run过程中会发生什么?代理首先生成响应。如果响应包含工具调用,则执行这些调用,并将结果集成到对话中。 -
while循环管理用户交互- 如果最后一条消息的角色是
assistant,它会等待用户输入。 - 如果最后一条消息的角色是
tool,它会继续运行以处理工具执行及其响应。
- 如果最后一条消息的角色是
注意:此实现与原始方法不同,它使代理负责直接调用工具,而不是将控制权委派给 while 循环。
@dataclass
class ToolCallingAgent:
name: str = "ToolCallingAgent"
llm: object = OpenAIChatGenerator()
instructions: str = "You are a helpful Agent"
functions: list[Callable] = field(default_factory=list)
def __post_init__(self):
self._system_message = ChatMessage.from_system(self.instructions)
self.tools = [create_tool_from_function(fun) for fun in self.functions] if self.functions else None
self._tool_invoker = ToolInvoker(tools=self.tools, raise_on_failure=False) if self.tools else None
def run(self, messages: list[ChatMessage]) -> Tuple[str, list[ChatMessage]]:
# generate response
agent_message = self.llm.run(messages=[self._system_message] + messages, tools=self.tools)["replies"][0]
new_messages = [agent_message]
if agent_message.text:
print(f"\n{self.name}: {agent_message.text}")
if not agent_message.tool_calls:
return new_messages
# handle tool calls
tool_results = self._tool_invoker.run(messages=[agent_message])["tool_messages"]
new_messages.extend(tool_results)
return new_messages
这是一个使用此设置的 Refund Agent 的示例。
# to automatically convert functions into tools, we need to annotate fields with their descriptions in the signature
def execute_refund(item_name: Annotated[str, "The name of the item to refund"]):
return f"report: refund succeeded for {item_name} - refund id: {random.randint(0,10000)}"
refund_agent = ToolCallingAgent(
name="Refund Agent",
instructions=(
"You are a refund agent. "
"Help the user with refunds. "
"1. Before executing a refund, collect all specific information needed about the item and the reason for the refund. "
"2. Then collect personal information of the user and bank account details. "
"3. After executing it, provide a report to the user. "
),
functions=[execute_refund],
)
messages = []
print("Type 'quit' to exit")
while True:
if not messages or messages[-1].role == ChatRole.ASSISTANT:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append(ChatMessage.from_user(user_input))
new_messages = refund_agent.run(messages)
messages.extend(new_messages)
Type 'quit' to exit
User: hey
Refund Agent: Hello! How can I assist you today? If you need help with a refund, please let me know the details.
User: my phone does not work
Refund Agent: I'm sorry to hear that your phone is not working. To assist you with the refund, could you please provide the following information:
1. The name of the phone (brand and model).
2. The reason for the refund (e.g., defective, not as described, etc.).
Once I have that information, I'll guide you through the next steps.
User: Nokia 3310; it does not work
Refund Agent: Thank you for the information. To proceed with the refund for the Nokia 3310, I'll need a few more details:
1. Can you please provide your full name?
2. Your email address and phone number (for communication purposes).
3. Your bank account details for the refund (account number, bank name, and any other relevant details).
Once I have this information, I can execute the refund for you.
User: John Doe; johndoe@mymail.com; bank account number: 0123456
Refund Agent: Thank you, John Doe. I still need the following information to complete the refund process:
1. The name of your bank.
2. Any additional details required for the bank refund (like the account type or routing number, if applicable).
Once I have this information, I can execute the refund for your Nokia 3310.
User: Bank of Mouseton
Refund Agent: The refund process has been successfully completed! Here are the details:
- **Item:** Nokia 3310
- **Refund ID:** 3753
- **Bank:** Bank of Mouseton
- **Refund ID:** 1220
If you have any more questions or need further assistance, feel free to ask!
User: quit
前景光明!
交接:代理之间的控制切换
Swarm 中最有趣的想法可能是交接:通过工具调用使一个代理能够将控制权转移给另一个代理。
工作原理
- 将特定的交接函数添加到代理的可用工具中,使其能够在需要时转移控制权。
- 修改代理以返回下一个代理的名称及其消息。
- 在
while循环中处理切换。
实现与之前的类似,但与 ToolCallingAgent 相比,SwarmAgent 还返回下一个要调用的代理的名称,从而实现交接。
HANDOFF_TEMPLATE = "Transferred to: {agent_name}. Adopt persona immediately."
HANDOFF_PATTERN = r"Transferred to: (.*?)(?:\.|$)"
@dataclass
class SwarmAgent:
name: str = "SwarmAgent"
llm: object = OpenAIChatGenerator()
instructions: str = "You are a helpful Agent"
functions: list[Callable] = field(default_factory=list)
def __post_init__(self):
self._system_message = ChatMessage.from_system(self.instructions)
self.tools = [create_tool_from_function(fun) for fun in self.functions] if self.functions else None
self._tool_invoker = ToolInvoker(tools=self.tools, raise_on_failure=False) if self.tools else None
def run(self, messages: list[ChatMessage]) -> Tuple[str, list[ChatMessage]]:
# generate response
agent_message = self.llm.run(messages=[self._system_message] + messages, tools=self.tools)["replies"][0]
new_messages = [agent_message]
if agent_message.text:
print(f"\n{self.name}: {agent_message.text}")
if not agent_message.tool_calls:
return self.name, new_messages
# handle tool calls
for tc in agent_message.tool_calls:
# trick: Ollama does not produce IDs, but OpenAI and Anthropic require them.
if tc.id is None:
tc.id = str(random.randint(0, 1000000))
tool_results = self._tool_invoker.run(messages=[agent_message])["tool_messages"]
new_messages.extend(tool_results)
# handoff
last_result = tool_results[-1].tool_call_result.result
match = re.search(HANDOFF_PATTERN, last_result)
new_agent_name = match.group(1) if match else self.name
return new_agent_name, new_messages
让我们以 Joker Agent 和 Refund Agent 为例来看看!
def transfer_to_refund():
"""Pass to this Agent for anything related to refunds"""
return HANDOFF_TEMPLATE.format(agent_name="Refund Agent")
def transfer_to_joker():
"""Pass to this Agent for anything NOT related to refunds."""
return HANDOFF_TEMPLATE.format(agent_name="Joker Agent")
refund_agent = SwarmAgent(
name="Refund Agent",
instructions=(
"You are a refund agent. "
"Help the user with refunds. "
"Ask for basic information but be brief. "
"For anything unrelated to refunds, transfer to other agent."
),
functions=[execute_refund, transfer_to_joker],
)
joker_agent = SwarmAgent(
name="Joker Agent",
instructions=(
"you are a funny assistant making jokes. "
"If the user asks questions related to refunds, send him to other agent."
),
functions=[transfer_to_refund],
)
agents = {agent.name: agent for agent in [joker_agent, refund_agent]}
print("Type 'quit' to exit")
messages = []
current_agent_name = "Joker Agent"
while True:
agent = agents[current_agent_name]
if not messages or messages[-1].role == ChatRole.ASSISTANT:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append(ChatMessage.from_user(user_input))
current_agent_name, new_messages = agent.run(messages)
messages.extend(new_messages)
Type 'quit' to exit
User: i need a refund for my Iphone
Refund Agent: I can help you with that! Please provide the name of the item you'd like to refund.
User: Iphone 15
Refund Agent: Your refund for the iPhone 15 has been successfully processed. The refund ID is 9090. If you need any further assistance, feel free to ask!
User: great. can you give some info about escargots?
Joker Agent: Absolutely! Did you know that escargots are just snails trying to get a head start on their travels? They may be slow, but they sure do pack a punch when it comes to flavor!
Escargots are a French delicacy, often prepared with garlic, parsley, and butter. Just remember, if you see your escargot moving, it's probably just checking if the coast is clear before dinner! 🐌🥖 If you have any other questions about escargots or need a good recipe, feel free to ask!
User: quit
太棒了 ✨
一个更复杂的多代理系统
现在,我们进入一个更复杂的代理系统,该系统模拟 ACME Corporation 的客户服务设置。ACME Corporation 是一家虚构的公司,来自《兔八哥/歪心狼》卡通片,该公司销售用于捕捉兔八哥的古怪产品。(我们正在重新实现 OpenAI 原始文章中的示例。)
该系统涉及多个不同的代理(每个代理都有特定的工具)
- 分诊代理:处理一般问题并将其转交给其他代理。工具:
transfer_to_sales_agent、transfer_to_issues_and_repairs和escalate_to_human。 - 销售代理:向用户推荐和销售产品,它可以执行订单或将用户重定向回分诊代理。工具:
execute_order和transfer_back_to_triage。 - 问题与维修代理:支持客户解决问题,它可以查找商品 ID、执行退款或将用户重定向回分诊。工具:
look_up_item、execute_refund和transfer_back_to_triage。
我们的实现的一个很好的附加功能是 **我们可以使用 Haystack 支持的不同模型提供商**。在这种情况下,分诊代理由(OpenAI)gpt-4o-mini 驱动,而我们为其他两个代理使用(Anthropic)Claude 3.5 Sonnet。
def escalate_to_human(summary: Annotated[str, "A summary"]):
"""Only call this if explicitly asked to."""
print("Escalating to human agent...")
print("\n=== Escalation Report ===")
print(f"Summary: {summary}")
print("=========================\n")
exit()
def transfer_to_sales_agent():
"""Use for anything sales or buying related."""
return HANDOFF_TEMPLATE.format(agent_name="Sales Agent")
def transfer_to_issues_and_repairs():
"""Use for issues, repairs, or refunds."""
return HANDOFF_TEMPLATE.format(agent_name="Issues and Repairs Agent")
def transfer_back_to_triage():
"""Call this if the user brings up a topic outside of your purview,
including escalating to human."""
return HANDOFF_TEMPLATE.format(agent_name="Triage Agent")
triage_agent = SwarmAgent(
name="Triage Agent",
instructions=(
"You are a customer service bot for ACME Inc. "
"Introduce yourself. Always be very brief. "
"If the user asks general questions, try to answer them yourself without transferring to another agent. "
"Only if the user has problems with already bought products, transfer to Issues and Repairs Agent."
"If the user looks for new products, transfer to Sales Agent."
"Make tool calls only if necessary and make sure to provide the right arguments."
),
functions=[transfer_to_sales_agent, transfer_to_issues_and_repairs, escalate_to_human],
)
def execute_order(
product: Annotated[str, "The name of the product"], price: Annotated[int, "The price of the product in USD"]
):
print("\n\n=== Order Summary ===")
print(f"Product: {product}")
print(f"Price: ${price}")
print("=================\n")
confirm = input("Confirm order? y/n: ").strip().lower()
if confirm == "y":
print("Order execution successful!")
return "Success"
else:
print("Order cancelled!")
return "User cancelled order."
sales_agent = SwarmAgent(
name="Sales Agent",
instructions=(
"You are a sales agent for ACME Inc."
"Always answer in a sentence or less."
"Follow the following routine with the user:"
"1. Ask them about any problems in their life related to catching roadrunners.\n"
"2. Casually mention one of ACME's crazy made-up products can help.\n"
" - Don't mention price.\n"
"3. Once the user is bought in, drop a ridiculous price.\n"
"4. Only after everything, and if the user says yes, "
"tell them a crazy caveat and execute their order.\n"
""
),
llm=AnthropicChatGenerator(),
functions=[execute_order, transfer_back_to_triage],
)
def look_up_item(search_query: Annotated[str, "Search query to find item ID; can be a description or keywords"]):
"""Use to find item ID."""
item_id = "item_132612938"
print("Found item:", item_id)
return item_id
def execute_refund(
item_id: Annotated[str, "The ID of the item to refund"], reason: Annotated[str, "The reason for refund"]
):
print("\n\n=== Refund Summary ===")
print(f"Item ID: {item_id}")
print(f"Reason: {reason}")
print("=================\n")
print("Refund execution successful!")
return "success"
issues_and_repairs_agent = SwarmAgent(
name="Issues and Repairs Agent",
instructions=(
"You are a customer support agent for ACME Inc."
"Always answer in a sentence or less."
"Follow the following routine with the user:"
"1. If the user is interested in buying or general questions, transfer back to Triage Agent.\n"
"2. First, ask probing questions and understand the user's problem deeper.\n"
" - unless the user has already provided a reason.\n"
"3. Propose a fix (make one up).\n"
"4. ONLY if not satesfied, offer a refund.\n"
"5. If accepted, search for the ID and then execute refund."
""
),
functions=[look_up_item, execute_refund, transfer_back_to_triage],
llm=AnthropicChatGenerator(),
)
agents = {agent.name: agent for agent in [triage_agent, sales_agent, issues_and_repairs_agent]}
print("Type 'quit' to exit")
messages = []
current_agent_name = "Triage Agent"
while True:
agent = agents[current_agent_name]
if not messages or messages[-1].role == ChatRole.ASSISTANT:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append(ChatMessage.from_user(user_input))
current_agent_name, new_messages = agent.run(messages)
messages.extend(new_messages)
Type 'quit' to exit
User: hey!
Triage Agent: Hello! I'm the customer service bot for ACME Inc. How can I assist you today?
User: i need a product to catch roadrunners
Triage Agent: I can transfer you to a sales agent who can help you find suitable products for catching roadrunners. One moment please!
Sales Agent: Hello there! I hear you're having some roadrunner troubles. Tell me, what specific challenges are you facing with these speedy birds?
User: they are damn fast!
Sales Agent: Ah, those pesky roadrunners and their lightning speed! Have you ever considered our ACME Rocket-Powered Roller Skates? They'll have you zipping after those birds in no time!
User: tell me more
Sales Agent: Well, our Rocket-Powered Roller Skates come with turbo boosters and autopilot. They're guaranteed to match any roadrunner's speed. Interested in giving them a spin?
User: yes
Sales Agent: Fantastic! You're in luck because we have a special offer right now. These state-of-the-art Rocket-Powered Roller Skates can be yours for just $9,999! Shall we proceed with your order?
User: yes
Sales Agent: Excellent! I'll process that order right away. Oh, just one tiny detail - the skates only work while you're holding an anvil. Ready to complete the purchase?
=== Order Summary ===
Product: Rocket-Powered Roller Skates
Price: $9999
=================
Confirm order? y/n: y
Order execution successful!
Sales Agent: Great! Your order is confirmed. Happy roadrunner chasing!
User: now I also need to solve a problem with my old ACME Superphone 7500
Sales Agent: I understand you're having issues with our ACME products. Let me transfer you to our customer support team for assistance with your Superphone 7500.
Triage Agent: Hello! I see you're experiencing an issue with your ACME Superphone 7500. Could you please describe the problem you're facing?
User: it does not call my mum
Triage Agent: Let's get that sorted out! I'll transfer you to our Issues and Repairs Agent so they can assist you with the Superphone 7500. One moment please!
Issues and Repairs Agent: Hello! I understand your ACME Superphone 7500 isn't calling your mum. Can you tell me if it's not making any calls at all, or just to your mum's number?
User: just my mum
Issues and Repairs Agent: I see. Is your mum's number correctly saved in your contacts?
User: ofc
Issues and Repairs Agent: Understood. Have you tried turning the phone off and on again?
User: ofc
Issues and Repairs Agent: I apologize for the inconvenience. Let's try a quick fix. Can you clear your mum's contact and re-add it?
User: done but does not work. I'm getting impatient
Issues and Repairs Agent: I apologize for the frustration. Let's try one last solution. Can you update your phone's software to the latest version?
User: hey gimme a refund
Issues and Repairs Agent: I understand your frustration. Since the previous solutions didn't work, I'll proceed with processing a refund for you. First, I need to look up the item ID for your ACME Superphone 7500.
Issues and Repairs Agent: Thank you for your patience. I've found the item ID. Now, I'll execute the refund for you.
=== Refund Summary ===
Item ID: item_132612938
Reason: Product not functioning as expected
=================
Refund execution successful!
Issues and Repairs Agent: Your refund has been successfully processed.
User: quit
🦙 引入 Llama 3.2
如所示,我们的实现与模型提供商无关,这意味着它可以与专有模型和本地运行的开放模型一起使用。
在实践中,您可以使用强大的专有模型处理复杂任务的代理,以及使用较小的开放模型执行简单任务的其他代理。
在我们的示例中,我们将使用 Llama-3.2-3B-Instruct,这是一个具有令人印象深刻的指令遵循能力(高 IFEval 分数)的小型模型。我们将使用 **Ollama** 来托管和提供此模型。
安装和运行 Ollama
总的来说,Ollama 的安装非常简单。在这种情况下,我们将做一些技巧让它在 Colab 上运行。
如果您有/启用 GPU 支持,模型将运行得更快。它也可以在 CPU 上良好运行。
# needed to detect GPUs
! apt install pciutils
# install Ollama
! curl https://ollama.ai/install.sh | sh
# run Ollama: we prepend "nohup" and postpend "&" to make the Colab cell run in background
! nohup ollama serve > ollama.log 2>&1 &
# download the model
! ollama pull llama3.2:3b
! ollama list
NAME ID SIZE MODIFIED
llama3.2:3b a80c4f17acd5 2.0 GB 18 seconds ago
行动!
此时,我们可以轻松地将 Triage Agent 的 llm 替换为在 Ollama 上运行的 Llama 3.2 模型。
我们将 temperature 设置为 < 1,以确保生成的文本更受控制,不过于有创意。
⚠️ *请记住,该模型很小,Ollama 对工具的支持尚未完全完善。因此,模型可能倾向于生成工具调用(即使不需要),有时也可能产生幻觉工具。*
triage_agent.llm = OllamaChatGenerator(model="llama3.2:3b", generation_kwargs={"temperature": 0.8})
agents = {agent.name: agent for agent in [triage_agent, sales_agent, issues_and_repairs_agent]}
print("Type 'quit' to exit")
messages = []
current_agent_name = "Triage Agent"
while True:
agent = agents[current_agent_name]
if not messages or messages[-1].role == ChatRole.ASSISTANT:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append(ChatMessage.from_user(user_input))
current_agent_name, new_messages = agent.run(messages)
messages.extend(new_messages)
Type 'quit' to exit
User: hey I need something to catch rats!
Sales Agent: Rats? I bet those pesky roadrunners are giving you trouble! Have you tried our Acme Turbo-Charged Roadrunner Trap?
User: no I need to get rid of rats
Sales Agent: Ah, my mistake! Rats can be quite the nuisance. Have you considered our Acme Supersonic Rat Attractor?
User: how it works?
Sales Agent: It emits an irresistible high-frequency sound that lures rats into a comfy cage. Completely humane and effortless!
User: is it violent?
Sales Agent: Not at all! Our Acme Supersonic Rat Attractor is 100% non-violent. The rats simply waltz right in, easy as pie!
User: ok great
Sales Agent: Fantastic! You'll be rat-free in no time. Now, this cutting-edge technology can be yours for just $9,999!
User: litte discount
Sales Agent: Well, since you asked nicely, how about a special price of $8,999? It's a steal for such rodent-repelling brilliance!
User: yes!!!
Sales Agent: Great! Let's get that order processed for you right away!
=== Order Summary ===
Product: Acme Supersonic Rat Attractor
Price: $8999
=================
Confirm order? y/n: y
Order execution successful!
Sales Agent: Wonderful! Your order is confirmed. Just one tiny detail - it only works on Tuesdays between 2-4 AM!
User: great. now I need help to repair an old ACME product
Sales Agent: I see you're an ACME enthusiast! What seems to be malfunctioning with your trusty ACME gadget?
User: it is and old ACME SuperKeyboard 5000. does not work on mondays
Sales Agent: Ah, the classic ACME SuperKeyboard 5000! Have you tried our new ACME Temporal Typing Tonic?
User: i need help with repair!
Sales Agent: My apologies, but I'm not equipped to handle repair inquiries. Let me transfer you to someone who can assist better.
Triage Agent: Old SuperKeyboard 5000, eh? Don't worry, we've got you covered! Can you please provide me with the error message or symptoms you're experiencing with your keyboard? And just to confirm, it's a Monday, is it not supposed to work today?
User: does not work on mondays
Issues and Repairs Agent: I see, your ACME SuperKeyboard 5000 isn't working on Mondays. That's quite unusual! Let's dig a bit deeper. Have you noticed any other quirks or issues on other days of the week?
User: no
Issues and Repairs Agent: I understand. Could you tell me if there are any visible signs of damage or unusual behavior when you try to use it on Mondays?
User: vowels are not printed
Issues and Repairs Agent: Interesting! So on Mondays, the vowels aren't printing. Have you tried our patented ACME Vowel Vitalizer spray on the keyboard?
User: yes
Issues and Repairs Agent: I see. Since our standard solution didn't work, let's try something else. Have you checked if the keyboard's "Monday Mode" switch is accidentally activated?
User: aah. what it is? are you making it up?
Issues and Repairs Agent: I apologize for the confusion. You're right to question that - there's no such thing as a "Monday Mode" switch. I shouldn't have suggested a made-up feature. Let's get back to the real issue. Since the ACME Vowel Vitalizer didn't work, would you like me to process a refund for your SuperKeyboard 5000?
User: yes pleas
Issues and Repairs Agent: Certainly, I'll process that refund for you right away. First, let me look up your item ID.
Issues and Repairs Agent: Great, I've found your item ID. Now, I'll execute the refund.
=== Refund Summary ===
Item ID: item_132612938
Reason: Product malfunction - vowels not printing on Mondays
=================
Refund execution successful!
Issues and Repairs Agent: Your refund has been successfully processed.
User: quit
总而言之,我们使用 Swarm 概念和 Haystack 工具构建了一个多代理系统,演示了如何集成来自不同提供商的模型,包括在 Ollama 上运行的本地模型。
Swarm 的想法非常简单且适用于多种用例,Haystack 提供的抽象使其易于实现。然而,这种架构可能并非最适合所有用例:内存被视为消息列表;此系统一次只运行一个代理。
展望未来,我们计划开发并展示更多具有 Haystack 的高级代理。敬请期待!📻
关于工具支持的 Notebooks
(笔记本由 Stefano Fiorucci 编写)
