feat: solution for '8. Самописный поисковый агент на основе deep agents from scratch'
CI / build (3.1) (push) Has been cancelled
CI / build (3.11) (push) Has been cancelled
CI / build (3.8) (push) Has been cancelled
CI / build (3.9) (push) Has been cancelled

This commit is contained in:
2026-06-30 16:22:52 +03:00
parent df9e4f49d2
commit 1039c7065c
14 changed files with 524 additions and 512 deletions
+72 -119
View File
@@ -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 commandline 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 commandline 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 commandline 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())