Here are two solid approaches, from the most robust to a more direct workaround.
This is the most professional and scalable solution. Instead of letting the agent use its default, generic file-saving tool, you create your own custom tool and give it to the agent. This gives you complete control over the process.
Your custom tool will do two things:
Save the DataFrame to a CSV in a known, web-accessible directory on your server.
Return the publicly accessible URL for that file as its final output.
Here’s how that would look in a FastAPI context:
1. Create a custom tool for your agent:
import pandas as pd
import uuid
from langchain.tools import tool
# Assume you have a '/static/downloads' directory that FastAPI can serve files from.
DOWNLOAD_DIR = "/app/static/downloads/"
BASE_URL = "https://your-azure-app-service.com" # Or your server's base URL
@tool
def save_dataframe_and_get_link(df: pd.DataFrame, filename_prefix: str = "export") -> str:
"""
Saves a pandas DataFrame to a CSV file in a web-accessible directory
and returns a public download link. Use this tool whenever you need to
provide a file for the user to download.
"""
try:
# Generate a unique filename to avoid conflicts
unique_id = uuid.uuid4()
filename = f"{filename_prefix}_{unique_id}.csv"
full_path = f"{DOWNLOAD_DIR}{filename}"
# Save the dataframe
df.to_csv(full_path, index=False)
# Generate the public URL
download_url = f"{BASE_URL}/downloads/{filename}"
print(f"DataFrame saved. Download link: {download_url}")
return f"Successfully saved the data. The user can download it from this link: {download_url}"
except Exception as e:
return f"Error saving file: {str(e)}"
# When you initialize your agent, you pass this tool in the `tools` list.
# agent_executor = create_pandas_dataframe_agent(..., tools=[save_dataframe_and_get_link])
2. Update your FastAPI to serve these static files:
from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles
app = FastAPI()
# This tells FastAPI to make the 'static' directory available to the public
app.mount("/static", StaticFiles(directory="static"), name="static")
# Your existing agent endpoint...
@app.post("/chat")
def handle_chat(...):
# ... your agent runs and uses the custom tool ...
result = agent.run(...)
# The 'result' will now contain the download URL!
return {"response": result}
I would strongly recommend Approach 1 for any production application. It gives you much more control and reliability