Skip to content

Commit 53ac219

Browse files
authored
Merge pull request #60 from ks6088ts-labs/feature/issue-43_ai-search
add ai search tool
2 parents 19bb9bd + 88a107d commit 53ac219

File tree

7 files changed

+245
-0
lines changed

7 files changed

+245
-0
lines changed

.env.template

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ COSMOSDB_PARTITION_KEY="/id"
4545
SQL_DATABASE_URI="sqlite:///template_langgraph.db"
4646
# SQL_DATABASE_URI="postgresql://user:password@localhost:5432/db"
4747

48+
# Azure AI Search Settings
49+
AI_SEARCH_ENDPOINT="https://xxx.search.windows.net/"
50+
AI_SEARCH_KEY="xxx"
51+
AI_SEARCH_INDEX_NAME="kabuto"
52+
4853
# ---------
4954
# Utilities
5055
# ---------

docs/references.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [CSVLoader](https://python.langchain.com/docs/how_to/document_loader_csv/)
2222
- [Qdrant](https://github.com/qdrant/qdrant)
2323
- [Azure Cosmos DB No SQL](https://python.langchain.com/docs/integrations/vectorstores/azure_cosmos_db_no_sql/)
24+
- [Azure AI Search](https://python.langchain.com/docs/integrations/vectorstores/azuresearch/)
2425

2526
### Services
2627

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ readme = "README.md"
66
requires-python = ">=3.10"
77
dependencies = [
88
"azure-cosmos>=4.9.0",
9+
"azure-identity>=1.23.1",
10+
"azure-search-documents>=11.5.3",
911
"elasticsearch>=9.1.0",
1012
"fastapi[standard]>=0.116.1",
1113
"httpx>=0.28.1",

scripts/ai_search_operator.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import logging
2+
3+
import typer
4+
from dotenv import load_dotenv
5+
6+
from template_langgraph.loggers import get_logger
7+
from template_langgraph.tools.ai_search_tool import AiSearchClientWrapper
8+
from template_langgraph.utilities.csv_loaders import CsvLoaderWrapper
9+
from template_langgraph.utilities.pdf_loaders import PdfLoaderWrapper
10+
11+
# Initialize the Typer application
12+
app = typer.Typer(
13+
add_completion=False,
14+
help="AI Search operator CLI",
15+
)
16+
17+
# Set up logging
18+
logger = get_logger(__name__)
19+
20+
21+
@app.command()
22+
def add_documents(
23+
verbose: bool = typer.Option(
24+
False,
25+
"--verbose",
26+
"-v",
27+
help="Enable verbose output",
28+
),
29+
):
30+
# Set up logging
31+
if verbose:
32+
logger.setLevel(logging.DEBUG)
33+
34+
# Load documents from PDF files
35+
pdf_documents = PdfLoaderWrapper().load_pdf_docs()
36+
logger.info(f"Loaded {len(pdf_documents)} documents from PDF.")
37+
38+
# Load documents from CSV files
39+
csv_documents = CsvLoaderWrapper().load_csv_docs()
40+
logger.info(f"Loaded {len(csv_documents)} documents from CSV.")
41+
42+
# Combine all documents
43+
documents = pdf_documents + csv_documents
44+
logger.info(f"Total documents to add: {len(documents)}")
45+
46+
# Add documents to AI Search
47+
ai_search_client = AiSearchClientWrapper()
48+
ids = ai_search_client.add_documents(
49+
documents=documents,
50+
)
51+
logger.info(f"Added {len(ids)} documents to AI Search.")
52+
for id in ids:
53+
logger.debug(f"Added document ID: {id}")
54+
55+
56+
@app.command()
57+
def similarity_search(
58+
query: str = typer.Option(
59+
"禅モード",
60+
"--query",
61+
"-q",
62+
help="Query to search in the AI Search index",
63+
),
64+
k: int = typer.Option(
65+
5,
66+
"--k",
67+
"-k",
68+
help="Number of results to return from the similarity search",
69+
),
70+
verbose: bool = typer.Option(
71+
False,
72+
"--verbose",
73+
"-v",
74+
help="Enable verbose output",
75+
),
76+
):
77+
# Set up logging
78+
if verbose:
79+
logger.setLevel(logging.DEBUG)
80+
81+
logger.info(f"Searching AI Search with query: {query}")
82+
83+
# Perform similarity search
84+
ai_search_client = AiSearchClientWrapper()
85+
documents = ai_search_client.similarity_search(
86+
query=query,
87+
k=k, # Number of results to return
88+
)
89+
logger.info(f"Found {len(documents)} results for query: {query}")
90+
91+
# Log the results
92+
for i, document in enumerate(documents, start=1):
93+
logger.debug("-" * 40)
94+
logger.debug(f"#{i}: {document.model_dump_json(indent=2)}")
95+
96+
97+
if __name__ == "__main__":
98+
load_dotenv(
99+
override=True,
100+
verbose=True,
101+
)
102+
app()
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
from functools import lru_cache
2+
3+
from langchain_community.vectorstores.azuresearch import AzureSearch
4+
from langchain_core.documents import Document
5+
from langchain_core.tools import tool
6+
from pydantic import BaseModel, Field
7+
from pydantic_settings import BaseSettings, SettingsConfigDict
8+
9+
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
10+
11+
12+
class Settings(BaseSettings):
13+
ai_search_key: str = "<your-ai-search-key>"
14+
ai_search_endpoint: str = "<your-ai-search-endpoint>"
15+
ai_search_index_name: str = "<your-ai-index-name>"
16+
17+
model_config = SettingsConfigDict(
18+
env_file=".env",
19+
env_ignore_empty=True,
20+
extra="ignore",
21+
)
22+
23+
24+
@lru_cache
25+
def get_ai_search_settings() -> Settings:
26+
"""Get AI Search settings."""
27+
return Settings()
28+
29+
30+
class AiSearchClientWrapper:
31+
def __init__(
32+
self,
33+
settings: Settings = None,
34+
):
35+
if settings is None:
36+
settings = get_ai_search_settings()
37+
self.vector_store: AzureSearch = AzureSearch(
38+
azure_search_endpoint=settings.ai_search_endpoint,
39+
azure_search_key=settings.ai_search_key,
40+
index_name=settings.ai_search_index_name,
41+
embedding_function=AzureOpenAiWrapper().embedding_model.embed_query,
42+
)
43+
44+
def add_documents(
45+
self,
46+
documents: list[Document],
47+
) -> list[str]:
48+
"""Add documents to a Cosmos DB container."""
49+
return self.vector_store.add_documents(
50+
documents=documents,
51+
)
52+
53+
def similarity_search(
54+
self,
55+
query: str,
56+
k: int = 5,
57+
) -> list[Document]:
58+
"""Perform a similarity search in the Cosmos DB index."""
59+
return self.vector_store.similarity_search(
60+
query=query,
61+
k=k, # Number of results to return
62+
)
63+
64+
65+
class AiSearchInput(BaseModel):
66+
query: str = Field(
67+
default="禅モード",
68+
description="Query to search in the AI Search index",
69+
)
70+
k: int = Field(
71+
default=5,
72+
description="Number of results to return from the similarity search",
73+
)
74+
75+
76+
class AiSearchOutput(BaseModel):
77+
content: str = Field(description="Content of the document")
78+
id: str = Field(description="ID of the document")
79+
80+
81+
@tool(args_schema=AiSearchInput)
82+
def search_ai_search(query: str, k: int = 5) -> list[AiSearchOutput]:
83+
"""Search for similar documents in AI Search index.
84+
85+
Args:
86+
query: The search query string
87+
k: Number of results to return (default: 5)
88+
89+
Returns:
90+
AiSearchOutput: A Pydantic model containing the search results
91+
"""
92+
wrapper = AiSearchClientWrapper()
93+
documents = wrapper.similarity_search(
94+
query=query,
95+
k=k,
96+
)
97+
outputs = []
98+
for document in documents:
99+
outputs.append(
100+
{
101+
"content": document.page_content,
102+
"id": document.id,
103+
}
104+
)
105+
return outputs

template_langgraph/tools/common.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from template_langgraph.llms.azure_openais import AzureOpenAiWrapper
22
from template_langgraph.loggers import get_logger
3+
from template_langgraph.tools.ai_search_tool import search_ai_search
34
from template_langgraph.tools.cosmosdb_tool import search_cosmosdb
45
from template_langgraph.tools.dify_tool import run_dify_workflow
56
from template_langgraph.tools.elasticsearch_tool import search_elasticsearch
@@ -18,6 +19,7 @@ def get_default_tools():
1819
logger.error(f"Error occurred while getting SQL database tools: {e}")
1920
sql_database_tools = []
2021
return [
22+
search_ai_search,
2123
search_cosmosdb,
2224
run_dify_workflow,
2325
search_qdrant,

uv.lock

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)