Microsoft Foundry 支持两种不同的上下文模式。 两者都使用 Foundry 托管的资源,但它们以不同的方式附加到代理,并解决不同的问题。
| Pattern | 代理框架机制 | Behavior |
|---|---|---|
| 文件搜索 RAG | 提供程序托管的文件搜索工具 | 搜索应用程序在 Foundry 项目中显式上传和管理的文件和向量存储。 |
| 托管语义内存 |
FoundryMemoryProvider 上下文提供程序 |
从对话中提取事实和摘要,按范围存储它们,并在后续运行中检索相关记忆。 |
有关模型推理和服务托管的 Foundry 代理,请参阅 Microsoft Foundry 模型提供程序和 Microsoft Foundry 代理服务。
使用文件搜索 RAG
当 Foundry 应为特选知识库拥有文档引入和矢量存储生命周期时,请使用此模式。 文件搜索是托管的工具,而不是上下文提供程序;请参阅工具行为的通用 文件搜索 指南。 当应用程序的真相源是Azure AI 搜索索引时,请使用Azure AI 搜索。
创建 Foundry 向量存储和代理
上传知识库文件,创建矢量存储、附加 FileSearchTool和创建版本控制 FoundryAgent。
var endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
var deploymentName = Environment.GetEnvironmentVariable("FOUNDRY_MODEL") ?? "gpt-5.4-mini";
// Create an AI Project client and get an OpenAI client that works with the foundry service.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
AIProjectClient aiProjectClient = new(
new Uri(endpoint),
new DefaultAzureCredential());
OpenAIClient openAIClient = aiProjectClient.GetProjectOpenAIClient();
// Upload the file that contains the data to be used for RAG to the Foundry service.
OpenAIFileClient fileClient = openAIClient.GetOpenAIFileClient();
ClientResult<OpenAIFile> uploadResult = await fileClient.UploadFileAsync(
filePath: "contoso-outdoors-knowledge-base.md",
purpose: FileUploadPurpose.Assistants);
// Create a vector store in the Foundry service using the uploaded file.
VectorStoreClient vectorStoreClient = openAIClient.GetVectorStoreClient();
ClientResult<VectorStore> vectorStoreCreate = await vectorStoreClient.CreateVectorStoreAsync(options: new VectorStoreCreationOptions()
{
Name = "contoso-outdoors-knowledge-base",
FileIds = { uploadResult.Value.Id }
});
// Use the native OpenAI SDK FileSearchTool directly with the vector store ID.
#pragma warning disable OPENAI001
FileSearchTool fileSearchTool = new([vectorStoreCreate.Value.Id]);
#pragma warning restore OPENAI001
ProjectsAgentVersion agentVersion = await aiProjectClient.AgentAdministrationClient.CreateAgentVersionAsync(
"AskContoso",
new ProjectsAgentVersionCreationOptions(
new DeclarativeAgentDefinition(model: deploymentName)
{
Instructions = "You are a helpful support specialist for Contoso Outdoors. Answer questions using the provided context and cite the source document when available.",
Tools = { fileSearchTool }
}));
FoundryAgent agent = aiProjectClient.AsAIAgent(agentVersion);
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(">> Asking about returns\n");
Console.WriteLine(await agent.RunAsync("Hi! I need help understanding the return policy.", session));
Console.WriteLine("\n>> Asking about shipping\n");
Console.WriteLine(await agent.RunAsync("How long does standard shipping usually take?", session));
Console.WriteLine("\n>> Asking about product care\n");
Console.WriteLine(await agent.RunAsync("What is the best way to maintain the TrailRunner tent fabric?", session));
// Cleanup
await fileClient.DeleteFileAsync(uploadResult.Value.Id);
await vectorStoreClient.DeleteVectorStoreAsync(vectorStoreCreate.Value.Id);
await aiProjectClient.AgentAdministrationClient.DeleteAgentAsync(agent.Name);
为生产知识库重复使用永久性矢量存储,而不是为每个进程运行创建它们。
安装软件包
pip install agent-framework-foundry --pre
通过 Foundry 项目 OpenAI 客户端创建文件和矢量存储,然后将生成的文件搜索工具传递给代理。
async def create_vector_store(client: FoundryChatClient) -> tuple[str, str]:
"""Create a vector store with sample documents."""
file = await client.client.files.create(
file=("todays_weather.txt", b"The weather today is sunny with a high of 75F."), purpose="assistants"
)
vector_store = await client.client.vector_stores.create(
name="knowledge_base",
expires_after={"anchor": "last_active_at", "days": 1},
)
result = await client.client.vector_stores.files.create_and_poll(vector_store_id=vector_store.id, file_id=file.id)
if result.last_error is not None:
raise Exception(f"Vector store file processing failed with status: {result.last_error.message}")
return file.id, vector_store.id
async def delete_vector_store(client: FoundryChatClient, file_id: str, vector_store_id: str) -> None:
"""Delete the vector store after using it."""
with contextlib.suppress(Exception):
await client.client.vector_stores.delete(vector_store_id=vector_store_id)
with contextlib.suppress(Exception):
await client.client.files.delete(file_id=file_id)
async def main() -> None:
print("=== Foundry Chat Client with File Search Example ===\n")
# Initialize the Foundry chat client
# Make sure you're logged in via 'az login' before running this sample
client = FoundryChatClient(credential=AzureCliCredential())
file_id, vector_store_id = await create_vector_store(client)
# Create file search tool using instance method
file_search_tool = client.get_file_search_tool(vector_store_ids=[vector_store_id])
agent = Agent(
client=client,
instructions="You are a helpful assistant that can search through files to find information.",
tools=[file_search_tool],
)
query = "What is the weather today? Do a file search to find the answer."
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
await delete_vector_store(client, file_id, vector_store_id)
注释
目前尚未为 Agent Framework Go 记录 Foundry 文件搜索集成。 有关最新的托管工具支持,请参阅 Agent Framework Go 存储库 。
添加托管语义内存
当代理应在会话之间召回持久用户或应用程序上下文时使用 FoundryMemoryProvider 。 Foundry 内存存储提取的事实和摘要与完整的对话脚本分开。
安装软件包
dotnet add package Microsoft.Agents.AI.Foundry --prerelease
使用稳定的作用域创建 FoundryMemoryProvider ,确保内存存储存在,并在依赖新提取的内存之前等待异步更新。
// Create an AIProjectClient for Foundry with Azure Identity authentication.
// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in production.
// In production, consider using a specific credential (e.g., ManagedIdentityCredential) to avoid
// latency issues, unintended credential probing, and potential security risks from fallback mechanisms.
DefaultAzureCredential credential = new();
AIProjectClient projectClient = new(new Uri(foundryEndpoint), credential);
// Get the ChatClient from the AIProjectClient's OpenAI property using the deployment name.
// The stateInitializer can be used to customize the Foundry Memory scope per session and it will be called each time a session
// is encountered by the FoundryMemoryProvider that does not already have state stored on the session.
// If each session should have its own scope, you can create a new id per session via the stateInitializer, e.g.:
// new FoundryMemoryProvider(projectClient, memoryStoreName, stateInitializer: _ => new(new FoundryMemoryProviderScope(Guid.NewGuid().ToString())), ...)
// In our case we are storing memories scoped by user so that memories are retained across sessions.
FoundryMemoryProvider memoryProvider = new(
projectClient,
memoryStoreName,
stateInitializer: _ => new(new FoundryMemoryProviderScope("sample-user-123")));
ChatClientAgent agent = projectClient.AsAIAgent(
new ChatClientAgentOptions()
{
Name = "TravelAssistantWithFoundryMemory",
ChatOptions = new()
{
ModelId = deploymentName,
Instructions = "You are a friendly travel assistant. Use known memories about the user when responding, and do not invent details."
},
AIContextProviders = [memoryProvider]
});
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine("\n>> Setting up Foundry Memory Store\n");
// Ensure the memory store exists (creates it with the specified models if needed).
await memoryProvider.EnsureMemoryStoreCreatedAsync(deploymentName, embeddingModelName, "Sample memory store for travel assistant");
// Clear any existing memories for this scope to demonstrate fresh behavior.
await memoryProvider.EnsureStoredMemoriesDeletedAsync(session);
Console.WriteLine(await agent.RunAsync("Hi there! My name is Taylor and I'm planning a hiking trip to Patagonia in November.", session));
Console.WriteLine(await agent.RunAsync("I'm travelling with my sister and we love finding scenic viewpoints.", session));
// Memory extraction in Microsoft Foundry is asynchronous and takes time to process.
// WhenUpdatesCompletedAsync polls all pending updates and waits for them to complete.
Console.WriteLine("\nWaiting for Foundry Memory to process updates...");
await memoryProvider.WhenUpdatesCompletedAsync();
Console.WriteLine("Updates completed.\n");
Console.WriteLine(await agent.RunAsync("What do you already know about my upcoming trip?", session));
Console.WriteLine("\n>> Serialize and deserialize the session to demonstrate persisted state\n");
JsonElement serializedSession = await agent.SerializeSessionAsync(session);
AgentSession restoredSession = await agent.DeserializeSessionAsync(serializedSession);
Console.WriteLine(await agent.RunAsync("Can you recap the personal details you remember?", restoredSession));
Console.WriteLine("\n>> Start a new session that shares the same Foundry Memory scope\n");
Console.WriteLine("\nWaiting for Foundry Memory to process updates...");
await memoryProvider.WhenUpdatesCompletedAsync();
AgentSession newSession = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Summarize what you already know about me.", newSession));
安装软件包
pip install agent-framework-foundry --pre
创建内存存储, AIProjectClient然后附加到 FoundryMemoryProvider 代理。
async def main() -> None:
endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
async with (
AzureCliCredential() as credential,
AIProjectClient(endpoint=endpoint, credential=credential) as project_client,
):
# Generate a unique memory store name to avoid conflicts
memory_store_name = f"agent_framework_memory_{datetime.now(timezone.utc).strftime('%Y%m%d')}"
# Specify memory store options
options = MemoryStoreDefaultOptions(
chat_summary_enabled=False,
user_profile_enabled=True,
user_profile_details="Avoid irrelevant or sensitive data, such as age, financials, precise location, and credentials",
)
memory_store_definition = MemoryStoreDefaultDefinition(
chat_model=os.environ["FOUNDRY_MODEL"],
embedding_model=os.environ["AZURE_OPENAI_EMBEDDING_MODEL"],
options=options,
)
print(f"Creating memory store '{memory_store_name}'...")
try:
# Create a memory store
memory_store = await project_client.beta.memory_stores.create(
name=memory_store_name,
description="Memory store for Agent Framework with FoundryMemoryProvider",
definition=memory_store_definition,
)
except Exception as e:
print(f"Failed to create memory store: {e}")
return
print(f"Created memory store: {memory_store.name} ({memory_store.id})")
print(f"Description: {memory_store.description}\n")
print("==========================================")
# Create the chat client
client = FoundryChatClient(project_client=project_client)
# Create the Foundry Memory context provider
memory_provider = FoundryMemoryProvider(
project_client=project_client,
memory_store_name=memory_store.name,
scope="user_123", # Scope memories to a specific user, if not set, the session_id
# will be used as scope, which means memories are only shared within the same session
update_delay=0, # Do not wait to update memories after each interaction (for demo purposes)
# In production, consider setting a delay to batch updates and reduce costs
)
# Create an agent with the memory context provider
async with Agent(
name="MemoryAgent",
client=client,
instructions="""You are a helpful assistant that remembers past conversations.
The memories from previous interactions are automatically provided to you.""",
context_providers=[memory_provider, InMemoryHistoryProvider(load_messages=False)],
default_options={"store": False},
) as agent:
try:
# note that we will use the service side storage, nor load messsages from the history provider,
# but we include it to demonstrate that it can be used alongside the Foundry provider for other use cases.
session = agent.create_session()
# First interaction - establish some preferences
print("=== First conversation ===")
query1 = "I prefer dark roast coffee and I'm allergic to nuts"
print(f"User: {query1}")
result1 = await agent.run(query1, session=session)
print(f"Agent: {result1}\n")
# Wait for memories to be processed
print("Waiting for memories to be stored...")
await asyncio.sleep(8)
# Second interaction - test memory recall
print("=== Second conversation ===")
query2 = "Can you recommend a coffee and snack for me?"
print(f"User: {query2}")
result2 = await agent.run(query2, session=session)
print(f"Agent: {result2}\n")
# Third interaction - continue the conversation
print("=== Third conversation ===")
query3 = "What do you remember about my preferences?"
print(f"User: {query3}")
result3 = await agent.run(query3, session=session)
print(f"Agent: {result3}\n")
print(f"Stored memories from: {memory_store.name} ({memory_store.id})")
res = await project_client.beta.memory_stores.search_memories(name=memory_store.name, scope="user_123")
for memory in res.memories:
print(f"Memory: {memory.memory_item.content}")
except Exception as e:
print(f"An error occurred: {e}")
finally:
await project_client.beta.memory_stores.delete(memory_store_name)
该示例禁用服务端和本地脚本加载,以便后面的响应演示语义内存,而不是聊天历史记录重播。
注释
Microsoft Foundry 内存集成目前不适用于 Agent Framework Go。 有关最新状态,请参阅 Agent Framework Go 存储库 。
生产注意事项
- 为生产知识库重复使用永久性矢量存储。
- 在选择范围之前,请使用应用程序拥有的稳定内存范围标识符并授权访问。
- 在后续操作依赖于新写入的内存时等待异步提取。
- 需要完整的对话记录时,请将确切的脚本保留在历史记录提供程序中。
- 配置保留、区域和模型部署,以满足合规性要求。
后续步骤
更深入: