您可以将 MongoDB 用作使用 OpenAI Agents SDK 构建的代理的后端数据库。Agents SDK 是一个 Python 框架,用于通过一小组原语构建代理应用程序:
代理,它们是配置了指令和工具的大型语言模型 (LLMs)。
交接,允许一个代理将任务委托给另一个代理。
安全护栏,用于验证代理的输入和输出。
会话,用于存储跨代理运行的对话历史。
SDK 遵循两个设计原则。它包含足够的功能来构建真实的应用程序,但原始元素很少,可以在短时间内学习。它的默认设置也会产生良好的结果,并且您可以自定义代理运行的每个步骤。
为什么将 MongoDB 与代理 SDK 一起使用
代理 SDK 提供 MongoDBSession,这是一个可在 MongoDB 中持久保存会话历史的会话实现。在 MongoDB 中存储该历史记录,可为您的代理提供以下优势:
水平可扩展的多进程会话存储。会话状态存储在您的集群中,而不是存储在单个进程的内存中。因此,任何连接到同一集群的工作进程、容器或无服务器函数都可以继续会话。每条消息都带有一个单调递增的
seq计数器,可以保持在并发写入器之间的消息顺序。会话和应用程序数据的单一数据库。如果您的应用程序已经使用 MongoDB,您的代理会通过相同的连接和相同的驱动程序读取运行数据,并写入会话历史,无需部署和保护单独的内存服务。
用于代理状态的灵活文档。document model 会随着对话轮次、工具调用和结构化输出的演变而将它们一起存储。因此,您可以扩展您记录的内容,而无需进行模式迁移。
可查询的代理历史记录。会话历史存储在普通集合中,因此,您可以对其进行查询、聚合和索引,以 Atlas 审核代理行为或构建分析。
A path to retrieval. Because your agents already connect to MongoDB, you can add MongoDB Vector Search to the same cluster to give them semantic retrieval over your data. To learn more, see Agentic RAG.
Tutorial
在本教程中,您将构建一个多代理旅行助手。分诊代理通过调用两个专业代理作为工具来回答问题,这两个专业代理都从存储对话会话的同一个集群中读取参考数据。
先决条件
如要完成本教程,您必须具备以下条件:
Python 3.10 或更高版本。
以下MongoDB 集群类型之一:
使用 Python 和 Docker 创建的本地 Atlas 部署。安装
atlas-local-lib-py(pip install atlas-local-lib-py),以编程方式创建和管理本地部署。要了解更多信息,请参阅 atlas-local-lib-py 存储库。A MongoDB Community cluster with Search and Vector Search installed.
OpenAI API密钥。您必须拥有一个具有可用于API请求的积分的 OpenAI 帐户。要学习;了解有关注册 OpenAI 帐户的更多信息,请参阅 OpenAI API网站。
设置环境
设置环境变量。
应用程序从 ATLAS_URI 读取您的 Atlas 连接字符串,并且代理 SDK 从 OPENAI_API_KEY 读取您的密钥:
export ATLAS_URI="<connection-string>" export OPENAI_API_KEY="<api-key>"
将 <connection-string> 占位符值替换为集群的 SRV连接字符串。
连接字符串应使用以下格式:
mongodb+srv://<db_username>:<db_password>@<clusterName>.<hostname>.mongodb.net
构建代理
创建应用程序文件。
创建一个名为 multi_agent_app.py 的文件并将以下代码粘贴到该文件中。内联注释解释了应用程序的每个部分如何使用 MongoDB。
"""Multi-agent travel assistant backed by MongoDB Atlas. A triage agent answers travel questions by calling two specialist agents as tools. Both specialists read reference data from the same Atlas cluster that stores the conversation session, so every agent in the application shares one database. """ import asyncio import os from agents import Agent, Runner, function_tool from agents.extensions.memory import MongoDBSession from pymongo import AsyncMongoClient # The script reads your credentials from the environment so that you # don't commit them to source control. REQUIRED_ENV_VARS = ("ATLAS_URI", "OPENAI_API_KEY") DATABASE_NAME = "travel_assistant" # One client serves both the agent tools and the session store. Because # you create the client yourself, your application owns its lifecycle. # main() assigns both of these after it validates the environment. client: AsyncMongoClient | None = None database = None async def lookup_destination(city: str) -> str: """Look up travel guidance for a destination city.""" # Sub-agent tools query the shared database directly, so the agents # answer from your data instead of from model training data. document = await database.destinations.find_one({"city": city}) if document is None: return f"No destination guide found for {city}." return ( f"{document['city']}: best months are {document['best_months']}. " f"{document['summary']}" ) async def lookup_policy(topic: str) -> str: """Look up the company travel policy for a topic.""" document = await database.policies.find_one({"topic": topic}) if document is None: return f"No policy found for {topic}." return f"{document['topic']}: {document['rule']}" # Each specialist is a full agent with its own instructions and tools. destination_agent = Agent( name="Destination expert", instructions=( "You advise travelers on destinations. Always call " "lookup_destination and answer only from what it returns." ), tools=[lookup_destination], ) policy_agent = Agent( name="Policy expert", instructions=( "You answer questions about the company travel policy. Always " "call lookup_policy and answer only from what it returns." ), tools=[lookup_policy], ) # The as_tool() pattern turns each specialist into a tool that the # triage agent can call. Unlike a handoff, control returns to the # triage agent after each call, so it can combine both answers in one # reply. triage_agent = Agent( name="Travel assistant", instructions=( "You are a travel assistant. Use the destination and policy " "tools to gather facts before you answer, and call both when " "the question needs both. Remember details the traveler shared " "earlier in the conversation." ), tools=[ destination_agent.as_tool( tool_name="ask_destination_expert", tool_description="Get travel guidance about a city.", ), policy_agent.as_tool( tool_name="ask_policy_expert", tool_description="Get the company travel policy for a topic.", ), ], ) async def seed_reference_data() -> None: """Load the sample data that the specialist agents read.""" await database.destinations.delete_many({}) await database.policies.delete_many({}) await database.destinations.insert_many( [ { "city": "Lisbon", "best_months": "March through May", "summary": "Mild spring weather and low hotel rates.", }, { "city": "Reykjavik", "best_months": "June through August", "summary": "Long daylight hours and open highland roads.", }, ] ) await database.policies.insert_many( [ {"topic": "flights", "rule": "Book economy for flights under six hours."}, {"topic": "hotels", "rule": "Nightly rates must stay under 250 USD."}, ] ) async def main() -> None: global client, database # Fail fast with a clear message instead of surfacing a connection # error or an authentication error later in the run. missing = [name for name in REQUIRED_ENV_VARS if not os.environ.get(name)] if missing: raise SystemExit( "Set these environment variables before you run this script: " + ", ".join(missing) ) client = AsyncMongoClient(os.environ["ATLAS_URI"]) database = client[DATABASE_NAME] await seed_reference_data() # The session stores conversation history in Atlas. Pass the # existing client so the session and the agent tools share one # connection pool. session = MongoDBSession( session_id="traveler-123", client=client, database=DATABASE_NAME, ) # Confirm connectivity before the first run. await session.ping() # The Runner loads prior turns from the session and writes the new # turn back, so the second question resolves "there" without you # passing the history yourself. first = await Runner.run( triage_agent, "I'm planning a trip to Lisbon. When should I go?", session=session, ) print(first.final_output) second = await Runner.run( triage_agent, "What's our hotel budget for that trip?", session=session, ) print(second.final_output) # session.close() is a no-op when you supply the client, so close # the client yourself. await client.close() if __name__ == "__main__": asyncio.run(main())
应用程序如何使用 MongoDB
应用程序创建一个 AsyncMongoClient,并在代理和会话之间共享它:
专业代理会从其工具函数中查询
destinations和policies集合,因此,他们会从您的数据中回答。MongoDBSession将每个对话回合写入同一数据库中的agent_sessions和agent_messages集合。两个集合名称都可以配置,并且会话会在首次使用时创建所需的索引。
由于应用程序自行构建客户端,因此客户端生命周期属于应用程序,并且 session.close() 不执行任何操作。为了反而让会话拥有客户端,请使用 MongoDBSession.from_uri() 创建它。
后续步骤
要了解有关使用代理 SDK 协调多个代理的更多信息,请参阅 OpenAI 文档中的以下页面:
编排多个代理,了解通过代码编排与通过 LLM 编排之间的权衡。
Handoffs 用于将对话委托给另一个代理,而不是将其作为工具调用。
Guardrails 用于验证代理输入和输出。
会话 以获取完整的
MongoDBSession参考。