Complete Guide: Integrating OpenAI Agents SDK with Ollama for Local AI Agent Development
A comprehensive guide to integrating the OpenAI Agents SDK with Ollama for building locally-hosted AI agents, including document analysis, custom agent creation, and advanced implementation strategies.
Daniel Kliewer
Author, Sovereign AI


Complete Guide: Integrating OpenAI Agents SDK with Ollama
This comprehensive guide demonstrates how to integrate the official OpenAI Agents SDK with Ollama to create AI agents that run entirely on local infrastructure. By the end, you'll understand both the theoretical foundations and practical implementation of locally-hosted AI agents.
Table of Contents
- Introduction
- Understanding the Components
- Setting Up Your Environment
- Integrating Ollama with OpenAI Agents SDK
- Building a Document Analysis Agent
- Adding Document Memory
- Putting It All Together
- Troubleshooting
- Conclusion
Introduction
The OpenAI Agents SDK is a powerful framework for building agent-based AI systems that can solve complex tasks through planning and tool use. By integrating it with Ollama, we can run these agents locally, improving privacy, reducing latency, and eliminating API costs.
Understanding the Components
What is the OpenAI Agents SDK?
The OpenAI Agents SDK (agents) is a framework that simplifies the development of AI agents. It provides:
- A structured approach for defining agent behaviors
- Built-in support for tool usage and planning
- Session management for multi-turn conversations
- Memory and state persistence
At its core, this SDK formalizes the agent pattern that emerged from the broader LLM community, giving developers a standard way to implement agents that can plan, reason, and execute complex tasks.
What is Ollama?
Ollama is an open-source framework for running large language models (LLMs) locally. Key features include:
- Easy installation and model management
- Compatible API endpoints that mimic OpenAI's API structure
- Support for many open-source models (Llama, Mistral, etc.)
- Custom model creation and fine-tuning
Why Integrate Them?
Integration provides several benefits:
- Data Privacy: All data stays on your local machine
- Cost Efficiency: No pay-per-token API costs
- Customization: Fine-tune models for specific use cases
- Network Independence: Agents function without internet access
- Reduced Latency: Eliminate network roundtrips
Setting Up Your Environment
Step 1: Install Ollama
First, install Ollama following the instructions for your operating system:
For macOS and Linux:
bash1curl -fsSL https://ollama.ai/install.sh | sh
For Windows:
Download the installer from Ollama's website.
Step 2: Download a Model
Pull a capable model that will power your agent. For this guide, we'll use Mistral:
bash1ollama pull mistral
Verify that Ollama is working by running:
bash1ollama run mistral "Hello, are you running correctly?"
You should see a response generated by the model.
Step 3: Install the OpenAI Agents SDK
Clone the repository and install the package:
bash1git clone https://github.com/openai/openai-agents-python.git2cd openai-agents-python3pip install -e .
This installs the package in development mode, allowing you to modify the code if needed.
Step 4: Set Up Required Dependencies
Install additional dependencies:
bash1pip install requests python-dotenv pydantic
Integrating Ollama with OpenAI Agents SDK
The OpenAI Agents SDK uses the OpenAI Python client underneath. We need to create a custom client that directs requests to Ollama instead of OpenAI's servers.
Step 1: Create a Custom Client
Create a file named ollama_client.py:
python1import os2from openai import OpenAI34class OllamaClient(OpenAI):5 """Custom OpenAI client that routes requests to Ollama."""67 def __init__(self, model_name="mistral", **kwargs):8 # Configure to use Ollama's endpoint9 kwargs["base_url"] = "http://localhost:11434/v1"1011 # Ollama doesn't require an API key but the client expects one12 kwargs["api_key"] = "ollama-placeholder-key"1314 super().__init__(**kwargs)15 self.model_name = model_name1617 # Check if the model exists18 print(f"Using Ollama model: {model_name}")1920 def create_completion(self, *args, **kwargs):21 # Override model name if not explicitly provided22 if "model" not in kwargs:23 kwargs["model"] = self.model_name2425 return super().create_completion(*args, **kwargs)2627 def create_chat_completion(self, *args, **kwargs):28 # Override model name if not explicitly provided29 if "model" not in kwargs:30 kwargs["model"] = self.model_name3132 return super().create_chat_completion(*args, **kwargs)3334 # These methods are needed for compatibility with agents library35 def completion(self, prompt, **kwargs):36 if "model" not in kwargs:37 kwargs["model"] = self.model_name38 return self.completions.create(prompt=prompt, **kwargs)3940 def chat_completion(self, messages, **kwargs):41 if "model" not in kwargs:42 kwargs["model"] = self.model_name43 return self.chat.completions.create(messages=messages, **kwargs)
Step 2: Create an Adapter for OpenAI Agents SDK
Now we'll create an adapter that makes the OpenAI Agents SDK compatible with our Ollama client. Create a file named agent_adapter.py:
python1from ollama_client import OllamaClient2from openai.types.chat import ChatCompletion, ChatCompletionMessage3import agents.agent as agent_module4from agents.agent import Agent5from agents.run import Runner, RunConfig6from agents.models import _openai_shared7import json8import logging910# Configure logging11logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')12logger = logging.getLogger(__name__)1314# Set placeholder OpenAI API key to avoid initialization errors15_openai_shared.set_default_openai_key("placeholder-key")1617# Store original init for Agent class18original_init = Agent.__init__1920def patched_init(self, *args, **kwargs):21 """Replace the model with OllamaClient if not provided."""22 if "model" not in kwargs:23 kwargs["model"] = OllamaClient(model_name="mistral")24 original_init(self, *args, **kwargs)2526# Apply the patched init27Agent.__init__ = patched_init282930# Class for a structured tool call31class ToolCall:32 def __init__(self, name, inputs=None):33 self.name = name34 self.inputs = inputs or {}3536# Define a response class that matches what main.py expects37class AgentResponse:38 def __init__(self, result):39 # Extract the message from the final output40 if hasattr(result, 'final_output'):41 if isinstance(result.final_output, str):42 self.message = result.final_output43 else:44 self.message = str(result.final_output)45 else:46 self.message = "I'm sorry, I couldn't process that request."4748 # Get conversation ID if available49 self.conversation_id = getattr(result, 'conversation_id', None)5051 # Initialize tool_calls52 self.tool_calls = []5354 # Extract tool calls from raw_responses55 if hasattr(result, 'raw_responses'):56 for response in result.raw_responses:57 try:58 if hasattr(response, 'output') and hasattr(response.output, 'tool_calls'):59 for tool_call in response.output.tool_calls:60 # Handle the case where tool_call is a dict61 if isinstance(tool_call, dict):62 name = tool_call.get('name', 'unknown_tool')63 inputs = tool_call.get('inputs', {})64 self.tool_calls.append(ToolCall(name, inputs))65 else:66 # Assume it's already an object with name and inputs attributes67 self.tool_calls.append(tool_call)68 except Exception as e:69 logger.error(f"Error extracting tool calls: {str(e)}")707172# Add a run method to the Agent class73def run(self, message, conversation_id=None):74 """Run the agent with the given message.7576 Args:77 message: The user message to process78 conversation_id: Optional conversation ID for continuity7980 Returns:81 A response object with message, conversation_id, and tool_calls attributes82 """83 try:84 # Create a direct prompt for the model85 prompt = f"""86 {self.instructions}8788 User query: {message}89 """9091 # Get a response directly from the model (OllamaClient)92 response = self.model.chat.completions.create(93 model="mistral",94 messages=[{"role": "user", "content": prompt}],95 temperature=0.7,96 )9798 # Extract the text response99 response_text = response.choices[0].message.content100101 # Create a minimal result object with just the response text102 class MinimalResult:103 def __init__(self, text, conv_id):104 self.final_output = text105 self.conversation_id = conv_id106 self.raw_responses = []107108 result = MinimalResult(response_text, conversation_id)109110 # Return a response object111 return AgentResponse(result)112 except Exception as e:113 import traceback114 error_traceback = traceback.format_exc()115 logger.error(f"Error running agent: {str(e)}\n{error_traceback}")116117 # Create a basic response with the error message118 response = AgentResponse(None)119 response.message = f"An error occurred: {str(e)}"120 return response121122123# Make sure the run method is applied to the Agent class124Agent.run = run125126# Debugging statement - log when the adapter is loaded127print("Agent adapter loaded, Agent class patched with run method.")
Building a Document Analysis Agent
Let's build a practical agent that analyzes documents, extracts key information, and answers questions about the content.
Step 1: Create Document Memory
First, let's create a simple document memory system to store and retrieve analyzed documents. Create a file named document_memory.py:
python1import os2import json3import hashlib4from typing import Dict, List, Optional56class DocumentMemory:7 """Simple document storage system for the agent."""89 def __init__(self, storage_dir: str = "./document_memory"):10 self.storage_dir = storage_dir11 os.makedirs(storage_dir, exist_ok=True)1213 self.index_file = os.path.join(storage_dir, "index.json")14 self.document_index = self._load_index()1516 def _load_index(self) -> Dict:17 """Load document index from disk."""18 if os.path.exists(self.index_file):19 with open(self.index_file, 'r') as f:20 return json.load(f)21 return {"documents": {}}2223 def _save_index(self):24 """Save document index to disk."""25 with open(self.index_file, 'w') as f:26 json.dump(self.document_index, f, indent=2)2728 def _generate_doc_id(self, url: str) -> str:29 """Generate a unique ID for a document based on its URL."""30 return hashlib.md5(url.encode()).hexdigest()3132 def store_document(self, url: str, content: str, metadata: Optional[Dict] = None) -> str:33 """Store a document and return its ID."""34 doc_id = self._generate_doc_id(url)35 doc_path = os.path.join(self.storage_dir, f"{doc_id}.txt")3637 # Store document content38 with open(doc_path, 'w') as f:39 f.write(content)4041 # Update index42 self.document_index["documents"][doc_id] = {43 "url": url,44 "path": doc_path,45 "metadata": metadata or {}46 }4748 self._save_index()49 return doc_id5051 def get_document(self, doc_id: str) -> Optional[Dict]:52 """Retrieve a document by ID."""53 if doc_id not in self.document_index["documents"]:54 return None5556 doc_info = self.document_index["documents"][doc_id]5758 try:59 with open(doc_info["path"], 'r') as f:60 content = f.read()61 return {62 "id": doc_id,63 "url": doc_info["url"],64 "content": content,65 "metadata": doc_info["metadata"]66 }67 except Exception as e:68 print(f"Error retrieving document {doc_id}: {e}")69 return None7071 def get_document_by_url(self, url: str) -> Optional[Dict]:72 """Find and retrieve a document by URL."""73 doc_id = self._generate_doc_id(url)74 return self.get_document(doc_id)7576 def list_documents(self) -> List[Dict]:77 """List all stored documents."""78 return [79 {"id": doc_id, "url": info["url"], "metadata": info["metadata"]}80 for doc_id, info in self.document_index["documents"].items()81 ]
Step 2: Define the Agent's Tools
Create a file named document_agent.py to implement the document analysis agent with its tools:
python1import re2import json3import requests4from datetime import datetime5from typing import List, Dict, Any, Optional6from pydantic import BaseModel, Field78# Import the Agent directly from openai_agents9from agents import Agent, function_tool10from ollama_client import OllamaClient11from document_memory import DocumentMemory1213# Import the agent adapter to add the run method to the Agent class14import agent_adapter1516# Initialize document memory17document_memory = DocumentMemory()181920# Define the tool schemas21class FetchDocumentInput(BaseModel):22 url: str = Field(..., description="URL of the document to fetch")232425class FetchDocumentOutput(BaseModel):26 content: str = Field(..., description="Content of the document")272829class ExtractInfoInput(BaseModel):30 text: str = Field(..., description="Text to extract information from")31 info_type: str = Field(32 ..., description="Type of information to extract (e.g., 'dates', 'names', 'key points')"33 )343536class ExtractInfoOutput(BaseModel):37 information: List[str] = Field(..., description="List of extracted information")383940class SearchDocumentInput(BaseModel):41 text: str = Field(..., description="Document text to search within")42 query: str = Field(..., description="Query to search for")434445class SearchDocumentOutput(BaseModel):46 results: List[str] = Field(..., description="List of matching paragraphs or sentences")474849# Implement tool functions50@function_tool51def fetch_document(url: str) -> Dict[str, Any]:52 """Fetches a document from a URL and returns its content.53 Checks document memory first before making a network request."""54 # Check if document already exists in memory55 cached_doc = document_memory.get_document_by_url(url)56 if cached_doc:57 print(f"Retrieved document from memory: {url}")58 return {"content": cached_doc["content"]}5960 # If not in memory, fetch from URL61 try:62 print(f"Fetching document from URL: {url}")63 response = requests.get(url)64 response.raise_for_status()65 content = re.sub(r"<[^>]+>", "", response.text) # Remove HTML tags6667 # Store in document memory68 document_memory.store_document(url, content, {"fetched_at": str(datetime.now())})6970 return {"content": content}71 except Exception as e:72 return {"content": f"Error fetching document: {str(e)}"}737475@function_tool76def extract_info(text: str, info_type: str) -> Dict[str, Any]:77 """Extracts specified type of information from text using Ollama."""78 client = OllamaClient(model_name="mistral")7980 prompt = f"""81 Extract all {info_type} from the following text.82 Return ONLY a JSON array with the items.8384 TEXT:85 {text[:2000]} # Limit text length to prevent context overflow8687 JSON ARRAY OF {info_type.upper()}:88 """8990 try:91 response = client.chat.completions.create(92 model="mistral",93 messages=[{"role": "user", "content": prompt}],94 temperature=0.1, # Lower temperature for more deterministic output95 )9697 result_text = response.choices[0].message.content98 print(f"Extract info response: {result_text[:100]}...")99100 # Try to find JSON array in the response101 try:102 match = re.search(r"\[.*\]", result_text, re.DOTALL)103 if match:104 information = json.loads(match.group(0))105 else:106 # If no JSON array is found, try to parse the entire response as JSON107 try:108 information = json.loads(result_text)109 if not isinstance(information, list):110 information = [result_text.strip()]111 except:112 information = [result_text.strip()]113 except json.JSONDecodeError:114 # Split by commas or newlines if JSON parsing fails115 information = []116 for line in result_text.split('\n'):117 line = line.strip()118 if line and not line.startswith('```') and not line.endswith('```'):119 information.append(line)120 if not information:121 information = [item.strip() for item in result_text.split(",")]122 except Exception as e:123 print(f"Error in extract_info: {str(e)}")124 information = [f"Error extracting information: {str(e)}"]125126 return {"information": information}127128129@function_tool130def search_document(text: str, query: str) -> Dict[str, Any]:131 """Searches for relevant content in the document."""132 paragraphs = [p.strip() for p in re.split(r"\n\s*\n", text) if p.strip()]133134 client = OllamaClient(model_name="mistral")135136 prompt = f"""137 You need to find paragraphs in a document that answer or relate to the query: "{query}"138 Rate each paragraph's relevance to the query on a scale of 0-10.139 Return the 3 most relevant paragraphs with their ratings as JSON.140141 Document sections:142 {json.dumps(paragraphs[:15])} # Limit to first 15 paragraphs for context limits143144 Output format: [{"rating": 8, "text": "paragraph text"}, ...]145 """146147 try:148 response = client.chat.completions.create(149 model="mistral",150 messages=[{"role": "user", "content": prompt}],151 temperature=0.1, # Lower temperature for more deterministic output152 )153154 result_text = response.choices[0].message.content155 print(f"Search document response: {result_text[:100]}...")156157 # Try to find JSON array in the response158 try:159 match = re.search(r"\[.*\]", result_text, re.DOTALL)160 if match:161 parsed = json.loads(match.group(0))162 results = [item["text"] for item in parsed if "text" in item]163 else:164 # Try to parse the entire response as JSON165 try:166 parsed = json.loads(result_text)167 if isinstance(parsed, list):168 results = [item.get("text", str(item)) for item in parsed]169 else:170 results = [str(parsed)]171 except:172 # If JSON parsing fails, extract quoted text173 results = re.findall(r'"([^"]+)"', result_text)174 if not results:175 results = [result_text]176 except json.JSONDecodeError:177 # If JSON parsing fails completely178 results = [result_text]179 except Exception as e:180 print(f"Error in search_document: {str(e)}")181 results = [f"Error searching document: {str(e)}"]182183 return {"results": results}184185186# Define additional tools for document memory management187class ListDocumentsOutput(BaseModel):188 documents: List[Dict] = Field(..., description="List of stored documents")189190class GetDocumentInput(BaseModel):191 url: str = Field(..., description="URL of the document to retrieve")192193class GetDocumentOutput(BaseModel):194 content: str = Field(..., description="Content of the retrieved document")195 metadata: Dict = Field(..., description="Metadata of the document")196197@function_tool198def list_documents() -> Dict[str, Any]:199 """Lists all stored documents in memory."""200 documents = document_memory.list_documents()201 return {"documents": documents}202203@function_tool204def get_document(url: str) -> Dict[str, Any]:205 """Retrieves a document from memory by URL."""206 doc = document_memory.get_document_by_url(url)207 if not doc:208 return {"content": "Document not found", "metadata": {}}209 return {"content": doc["content"], "metadata": doc["metadata"]}210211# Create a Document Analysis Agent212def create_document_agent():213 """Creates and returns an AI agent for document analysis."""214 client = OllamaClient(model_name="mistral")215216 # Collect all the tools decorated with function_tool217 tools = [218 fetch_document,219 extract_info,220 search_document,221 list_documents,222 get_document223 ]224225 agent = Agent(226 name="DocumentAnalysisAgent",227 instructions=(228 "You are a Document Analysis Assistant that helps users extract valuable information from documents.\n\n"229 "When given a task:\n"230 "1. If you need to analyze a document, first use fetch_document to get its content.\n"231 "2. Use extract_info to identify specific information in the document.\n"232 "3. Use search_document to find answers to specific questions.\n"233 "4. Summarize your findings in a clear, organized manner.\n\n"234 "You can manage documents with:\n"235 "- list_documents to see all stored documents\n"236 "- get_document to retrieve a previously fetched document\n\n"237 "Always be thorough and accurate in your analysis. If the document content is too large, "238 "focus on the most relevant sections for the user's query."239 ),240 tools=tools,241 model=client,242 )243244 return agent
Putting It All Together
Let's create a main.py file that will tie everything together and provide a command-line interface for interacting with our document analysis agent:
python1from document_agent import create_document_agent, document_memory2from ollama_client import OllamaClient34def print_banner():5 """Print a welcome banner for the Document Analysis Agent."""6 print("\n" + "="*60)7 print("📚 Document Analysis Agent 📚".center(60))8 print("="*60)9 print("\nThis agent can analyze documents, extract information, and search for content.")10 print("It also has document memory to store and retrieve documents between sessions.")1112 # Check for existing documents13 docs = document_memory.list_documents()14 if docs:15 print(f"\n🗃️ {len(docs)} documents already in memory:")16 for i, doc in enumerate(docs, 1):17 print(f" {i}. {doc['url']}")1819 print("\nCommands:")20 print(" 'exit' - Quit the program")21 print(" 'list' - Show stored documents")22 print(" 'help' - Show this help message")23 print("="*60 + "\n")2425def main():26 print("Initializing Document Analysis Agent...")2728 agent = create_document_agent()2930 print_banner()3132 # Debug: Test agent with a simple query33 try:34 print("\nDEBUG: Testing agent with 'what is war'")35 print("Processing...")36 test_response = agent.run(message="what is war")37 print(f"\nAgent (test): {test_response.message}")3839 # If tools were used, show info about tool usage40 if test_response.tool_calls:41 print("\n🛠️ Tools Used (test):")42 for tool in test_response.tool_calls:43 # Display more info about each tool call44 inputs = getattr(tool, 'inputs', {})45 inputs_str = ', '.join(f"{k}='{v}'" for k, v in inputs.items()) if inputs else ""46 print(f" • {tool.name}({inputs_str})")47 except Exception as e:48 import traceback49 print(f"\nDEBUG ERROR: {str(e)}")50 traceback.print_exc()5152 # Start a conversation session53 conversation_id = None5455 while True:56 try:57 user_input = input("\nYou: ")5859 if user_input.lower() == 'exit':60 break6162 if user_input.lower() == 'help':63 print_banner()64 continue6566 if user_input.lower() == 'list':67 docs = document_memory.list_documents()68 if not docs:69 print("\nNo documents in memory yet.")70 else:71 print(f"\n📚 Documents in memory ({len(docs)}):")72 for i, doc in enumerate(docs, 1):73 metadata = doc.get('metadata', {})74 fetched_at = metadata.get('fetched_at', 'unknown time')75 print(f" {i}. {doc['url']} (fetched: {fetched_at})")76 continue7778 # Get agent response79 print("\nProcessing...")80 response = agent.run(81 message=user_input,82 conversation_id=conversation_id83 )8485 # Store the conversation ID for continuity86 conversation_id = response.conversation_id8788 # Print the response89 print(f"\nAgent: {response.message}")9091 # If tools were used, show info about tool usage92 if response.tool_calls:93 print("\n🛠️ Tools Used:")94 for tool in response.tool_calls:95 # Display more info about each tool call96 inputs = getattr(tool, 'inputs', {})97 inputs_str = ', '.join(f"{k}='{v}'" for k, v in inputs.items()) if inputs else ""98 print(f" • {tool.name}({inputs_str})")99 except KeyboardInterrupt:100 print("\nExiting...")101 break102 except Exception as e:103 import traceback104 print(f"\nERROR: {str(e)}")105 traceback.print_exc()106107if __name__ == "__main__":108 main()
Example Conversation
Here's an example of how a conversation with the document analysis agent might look:
text1==========================================2 📚 Document Analysis Agent 📚3==========================================45This agent can analyze documents, extract information, and search for content.6It also has document memory to store and retrieve documents between sessions.78Commands:9 'exit' - Quit the program10 'list' - Show stored documents11 'help' - Show this help message12==========================================1314You: Please analyze the article at https://en.wikipedia.org/wiki/Artificial_intelligence and tell me when AI was first developed as a concept.1516Processing...1718Agent: Based on my analysis of the Wikipedia article on Artificial Intelligence, the concept of AI has historical roots that go back centuries, but the formal academic field of AI was founded in 1956.1920According to the article, the workshop at Dartmouth College in the summer of 1956 is widely considered the founding event of artificial intelligence as a field. This was when John McCarthy coined the term "artificial intelligence" as the topic of the Dartmouth Conference.2122However, the concept of artificial beings with intelligence appeared much earlier in history:2324- Ancient Greek myths mentioned mechanical men designed to mimic human functions25- Philosophers like Aristotle described mechanical reasoning in syllogisms26- In the 13th century, Ramon Llull developed logical machines27- In the 17th century, René Descartes, Thomas Hobbes, and Gottfried Leibniz explored mechanical reasoning28- In the 19th century, Charles Babbage and Ada Lovelace contemplated programmable mechanical calculating devices2930The article indicates that the modern field of AI research officially began at that 1956 workshop organized by John McCarthy, Marvin Minsky, Claude Shannon, and Nathaniel Rochester.3132🛠️ Tools Used:33 • fetch_document(url='https://en.wikipedia.org/wiki/Artificial_intelligence')34 • search_document(query='when was AI first developed concept history')35 • extract_info(info_type='key dates in AI history')
Troubleshooting
Here are some common issues you might encounter and how to fix them:
1. Model Issues
Problem: The model generates poor responses, hallucinates, or fails to use tools properly.
Solution:
- Try a more capable model like
llama3ormixtral - Check if your prompts are clear and well-formatted
- Reduce the complexity of your tools
- Add more explicit instructions in the agent's system prompt
You can pull a more capable model with:
bash1ollama pull llama3
Then update your client:
python1client = OllamaClient(model_name="llama3")
2. Context Length Issues
Problem: The model returns incomplete responses or fails when processing long documents.
Solution:
- Implement chunking for document text (we've already limited to 2000 characters in our tools)
- Use models with larger context windows if available (like Llama 3 or Mixtral)
- Break down complex tasks into smaller subtasks
3. API Compatibility Issues
Problem: Some OpenAI client functions aren't supported by Ollama.
Solution:
- Our adapted client handles the most common method differences
- If you encounter unsupported features, add similar wrapper methods to OllamaClient class
- Check Ollama's API documentation for compatible endpoints
Conclusion
In this guide, we've explored how to integrate the OpenAI Agents SDK with Ollama to create a powerful document analysis agent that runs entirely on local infrastructure. This approach combines the best of both worlds: the structured agent framework from OpenAI with the privacy and cost benefits of local inference through Ollama.
Key takeaways:
-
Architecture: We've created a layered architecture with:
- Ollama providing the LLM inference capability
- A custom client adapter connecting Ollama to the OpenAI interface
- The OpenAI Agents SDK providing the agent framework
- Custom tools for document analysis and memory
-
Implementation: We've built a complete document analysis agent with:
- Document fetching and parsing
- Information extraction
- Document search
- Persistent document storage
-
Benefits:
- Complete data privacy
- No ongoing API costs
- Customizable to specific use cases
- Works offline
-
Limitations and Mitigations:
- Model quality limitations (mitigated by using more capable models)
- Context length constraints (mitigated with our chunking approach)
- API compatibility gaps (mitigated with our custom client)
This integration demonstrates how organizations can leverage the power of advanced AI agent frameworks while maintaining control over their data and infrastructure. The result is a flexible, extensible system that can be adapted to many different use cases beyond document analysis.
By building on this foundation, you can create specialized agents for various domains while keeping all processing local and secure.

Sovereign AI: Building Local-First Intelligent Systems
by Daniel Kliewer · Paperback · 72 pages
The hands-on guide to building AI that runs on your hardware, keeps your data private, and eliminates cloud dependence. Working code included.