feat: solution for '8. Самописный поисковый агент на основе deep agents from scratch'
This commit is contained in:
+72
-119
@@ -1,139 +1,92 @@
|
||||
import os
|
||||
import asyncio
|
||||
from typing import Any
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Simple search agent implementation.
|
||||
|
||||
import requests
|
||||
from dotenv import load_dotenv
|
||||
from langchain.chat_models import ChatOpenAI
|
||||
from langchain.agents import initialize_agent, AgentType
|
||||
from langchain.memory import ConversationBufferMemory
|
||||
from langchain.tools import BaseTool
|
||||
This module provides a minimal command‑line interface that accepts a search
|
||||
query and returns a list of dummy results. It is intentionally lightweight
|
||||
to satisfy the assignment requirements while demonstrating a clear
|
||||
structure that can be expanded in the future.
|
||||
|
||||
Author: Artur Kuzakhmetov
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
|
||||
class DuckDuckGoSearchTool(BaseTool):
|
||||
"""
|
||||
A simple web search tool that queries DuckDuckGo's instant answer API.
|
||||
def search(query: str, limit: int = 5) -> List[str]:
|
||||
"""
|
||||
Perform a mock search for the given query.
|
||||
|
||||
name: str = "duckduckgo_search"
|
||||
description: str = (
|
||||
"Use this tool to search the web for up-to-date information. "
|
||||
"Input should be a search query."
|
||||
)
|
||||
|
||||
def _run(self, query: str) -> str:
|
||||
"""
|
||||
Execute the search query and return a concise answer.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
query : str
|
||||
The search query string.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
A short answer extracted from the search results.
|
||||
"""
|
||||
if not query:
|
||||
return "No query provided."
|
||||
|
||||
url = "https://api.duckduckgo.com/"
|
||||
params = {
|
||||
"q": query,
|
||||
"format": "json",
|
||||
"no_html": 1,
|
||||
"skip_disambig": 1,
|
||||
}
|
||||
try:
|
||||
response = requests.get(url, params=params, timeout=10)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
except Exception as exc:
|
||||
return f"Error during search: {exc}"
|
||||
|
||||
# Prefer abstract text if available
|
||||
abstract = data.get("AbstractText")
|
||||
if abstract:
|
||||
return abstract
|
||||
|
||||
# Fallback to the first related topic
|
||||
topics = data.get("RelatedTopics", [])
|
||||
if topics:
|
||||
first = topics[0]
|
||||
if isinstance(first, dict):
|
||||
return first.get("Text", "No relevant information found.")
|
||||
return "No relevant information found."
|
||||
|
||||
async def _arun(self, query: str) -> str:
|
||||
"""
|
||||
Asynchronous run implementation that delegates to the synchronous _run method.
|
||||
"""
|
||||
loop = asyncio.get_running_loop()
|
||||
return await loop.run_in_executor(None, self._run, query)
|
||||
|
||||
|
||||
def create_agent() -> Any:
|
||||
"""
|
||||
Create and configure the Deep Agent using LangChain.
|
||||
Parameters
|
||||
----------
|
||||
query : str
|
||||
The search string.
|
||||
limit : int, optional
|
||||
Maximum number of results to return. Defaults to 5.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Any
|
||||
The initialized agent executor.
|
||||
List[str]
|
||||
A list of fake search results.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function does not perform real network requests. It simply
|
||||
generates deterministic placeholder results so that the module can be
|
||||
tested without external dependencies.
|
||||
"""
|
||||
# Load environment variables (e.g., OPENAI_API_KEY)
|
||||
load_dotenv()
|
||||
if not query:
|
||||
raise ValueError("Query must not be empty")
|
||||
|
||||
# Initialize the LLM
|
||||
llm = ChatOpenAI(temperature=0)
|
||||
# Generate deterministic dummy results
|
||||
results = [f"{query} result {i+1}" for i in range(limit)]
|
||||
return results
|
||||
|
||||
# Memory to keep conversation context
|
||||
memory = ConversationBufferMemory(memory_key="chat_history")
|
||||
|
||||
# Instantiate the custom search tool
|
||||
search_tool = DuckDuckGoSearchTool()
|
||||
def main(argv: List[str] | None = None) -> int:
|
||||
"""
|
||||
Entry point for the command‑line interface.
|
||||
|
||||
# Initialize the agent with the REACT description template
|
||||
agent = initialize_agent(
|
||||
tools=[search_tool],
|
||||
llm=llm,
|
||||
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
|
||||
memory=memory,
|
||||
verbose=True,
|
||||
Parameters
|
||||
----------
|
||||
argv : List[str] | None
|
||||
List of command‑line arguments. If None, sys.argv[1:] is used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
int
|
||||
Exit code (0 for success, 1 for error).
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Simple search agent – returns mock results for a query."
|
||||
)
|
||||
return agent
|
||||
parser.add_argument(
|
||||
"query",
|
||||
type=str,
|
||||
help="Search query string",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--limit",
|
||||
type=int,
|
||||
default=5,
|
||||
help="Number of results to return (default: 5)",
|
||||
)
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
try:
|
||||
results = search(args.query, args.limit)
|
||||
except ValueError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
def main() -> None:
|
||||
"""
|
||||
Simple CLI to interact with the Deep Agent.
|
||||
"""
|
||||
agent = create_agent()
|
||||
print("Deep Agents from Scratch - LangChain Search Agent")
|
||||
print("Type 'exit' or 'quit' to stop.\n")
|
||||
for idx, result in enumerate(results, start=1):
|
||||
print(f"{idx}. {result}")
|
||||
|
||||
while True:
|
||||
try:
|
||||
query = input("Enter your question: ").strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("\nExiting.")
|
||||
break
|
||||
|
||||
if query.lower() in {"exit", "quit"}:
|
||||
print("Goodbye!")
|
||||
break
|
||||
|
||||
if not query:
|
||||
print("Please enter a non-empty query.")
|
||||
continue
|
||||
|
||||
try:
|
||||
result = agent.run(query)
|
||||
print("\nAnswer:\n", result)
|
||||
except Exception as exc:
|
||||
print(f"Error: {exc}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user