feat: solution for '8. Самописный поисковый агент на основе deep agents from scratch'

This commit is contained in:
2026-06-30 14:52:37 +03:00
parent 4be7a21036
commit b5a9604f58
4 changed files with 244 additions and 47 deletions
+80 -42
View File
@@ -1,76 +1,114 @@
# Deep Agent from Scratch
# Deep Agents from Scratch LangChain Search Agent
This repository demonstrates a **Deep Agent** implementation using the **LangChain** library.
The agent follows the “Deep Agents from Scratch” template and can answer arbitrary questions by leveraging an LLM (OpenAI GPT3.5Turbo by default). It also showcases how to integrate a simple tool (`Echo`) and use a Planner/Executor pattern for a more realistic agent workflow.
This project demonstrates a **Deep Agent** built from scratch using **LangChain**.
The agent can answer user questions by searching the web with DuckDuckGo and
providing concise, uptodate responses.
> **Author**: Artur Kuzakhmetov
> **Course**: Deep Agents from Scratch (Lecture: Perplexity, 09.04.2026)
> **Deadline**: 31.08.2026
---
## Features
- Implements the **Planner** and **Executor** pattern from the Deep Agents from Scratch template.
- Uses LangChains `OpenAI`, `Tool`, `PromptTemplate`, and `ConversationBufferMemory`.
- Configurable LLM model, temperature, and token limits.
- Simple commandline interface for quick testing.
- Environmentvariable based configuration for API keys and model selection.
- Demonstrates tool integration (Echo tool) and the full agent template.
- **Custom Search Tool** queries DuckDuckGos instant answer API.
- **Conversation Memory** keeps context across turns.
- **REACT Agent** follows the “Reason → Act → Think” pattern.
- **CLI** simple commandline interface for interactive use.
- **Unit Tests** basic tests for the search tool.
## Prerequisites
- Node.js 18+ (or any LTS version)
- An OpenAI API key
---
## Setup
```bash
# Clone the repository
git clone https://git.brojs.ru/kuzakhmetovartur/8.-samopisnyy-poiskovyy-agent-na-osnove-
cd 8.-samopisnyy-poiskovyy-agent-na-osnove-
1. **Clone the repository**
# Install dependencies
npm install
```
```bash
git clone https://git.brojs.ru/kuzakhmetovartur/8.-samopisnyy-poiskovyy-agent-na-osnove-.git
cd 8.-samopisnyy-poiskovyy-agent-na-osnove-
```
Create a `.env` file in the project root:
2. **Create a virtual environment**
```dotenv
OPENAI_API_KEY=your_openai_api_key_here
OPENAI_MODEL=gpt-3.5-turbo # optional, defaults to gpt-3.5-turbo
```
```bash
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
```
> **Tip:** Keep your `.env` file out of version control. Add it to `.gitignore` if you plan to push the repo.
3. **Install dependencies**
```bash
pip install -r requirements.txt
```
4. **Set up OpenAI API key**
Create a `.env` file in the project root:
```dotenv
OPENAI_API_KEY=sk-...
```
Replace `sk-...` with your actual key.
---
## Usage
Run the agent with a question:
Run the agent:
```bash
npm start -- "What is the tallest mountain in the world?"
python -m src.index
```
Or simply:
You will see:
```
Deep Agents from Scratch - LangChain Search Agent
Type 'exit' or 'quit' to stop.
Enter your question:
```
Type a question, e.g.:
```
What is the capital of France?
```
The agent will search the web and return an answer.
---
## Running Tests
```bash
node src/index.js "Your question here"
python -m unittest discover -s tests
```
The agent will output the answer to the console.
---
## Project Structure
```
├── package.json # Project metadata and dependencies
├── src/
│ ├── deepAgent.js # Core DeepAgent implementation (Planner/Executor)
│ └── index.js # CLI entry point
├── src
│ └── index.py # Main agent implementation
├── tests
│ └── test_search_tool.py # Unit tests for the search tool
├── requirements.txt # Project dependencies
└── README.md # Documentation
```
## Extending the Agent
---
- **Add more sophisticated prompts**: Edit the `Planner` prompt in `deepAgent.js`.
- **Integrate additional tools**: Use LangChains `Tool` and add them to the `tools` array.
- **Switch LLM providers**: Replace `OpenAI` with another LangChain LLM implementation (e.g., `AzureOpenAI`, `Anthropic`).
## Contributing
Feel free to fork the repository, create a feature branch, and submit a pull request.
Please ensure tests pass before merging.
---
## License
MIT © 2026
---
MIT License.
+4 -5
View File
@@ -1,5 +1,4 @@
transformers
torch
requests
click
pytest
langchain==0.1.0
openai==1.3.0
python-dotenv==1.0.0
requests==2.31.0
+139
View File
@@ -0,0 +1,139 @@
import os
import asyncio
from typing import Any
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
class DuckDuckGoSearchTool(BaseTool):
"""
A simple web search tool that queries DuckDuckGo's instant answer API.
"""
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.
Returns
-------
Any
The initialized agent executor.
"""
# Load environment variables (e.g., OPENAI_API_KEY)
load_dotenv()
# Initialize the LLM
llm = ChatOpenAI(temperature=0)
# Memory to keep conversation context
memory = ConversationBufferMemory(memory_key="chat_history")
# Instantiate the custom search tool
search_tool = DuckDuckGoSearchTool()
# 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,
)
return agent
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")
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}")
if __name__ == "__main__":
main()
+21
View File
@@ -0,0 +1,21 @@
import unittest
from src.index import DuckDuckGoSearchTool
class TestSearchTool(unittest.TestCase):
def setUp(self):
self.tool = DuckDuckGoSearchTool()
def test_run_returns_string(self):
result = self.tool.run("Python programming language")
self.assertIsInstance(result, str)
self.assertTrue(len(result) > 0)
def test_run_handles_empty_query(self):
result = self.tool.run("")
self.assertIsInstance(result, str)
self.assertTrue(len(result) > 0)
if __name__ == "__main__":
unittest.main()