diff --git a/README.md b/README.md index bde205c..42bbdef 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,76 @@ -# 8. Самописный поисковый агент на основе deep agents from scratch +# Deep Agent from Scratch -Главная -Мои задания -8. Самописный поисковый агент на основе deep agents from scratch -5Д -EN -8. Самописный поисковый агент на основе deep agents from scratch -Зачёт -Версия 3 -Дедлайн сдачи: 31.08.2026 +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 GPT‑3.5‑Turbo by default). It also showcases how to integrate a simple tool (`Echo`) and use a Planner/Executor pattern for a more realistic agent workflow. -В работе +## Features -Требуется доработка +- Implements the **Planner** and **Executor** pattern from the Deep Agents from Scratch template. +- Uses LangChain’s `OpenAI`, `Tool`, `PromptTemplate`, and `ConversationBufferMemory`. +- Configurable LLM model, temperature, and token limits. +- Simple command‑line interface for quick testing. +- Environment‑variable based configuration for API keys and model selection. +- Demonstrates tool integration (Echo tool) and the full agent template. -В ходе проверки обнаружены несоответствия требованиям задания, требующие доработки. +## Prerequisites -Редактирование ответа +- Node.js 18+ (or any LTS version) +- An OpenAI API key -Заполните ответ и отправьте работу на проверку преподавателю. +## Setup -Тип ответа -Текст -Ссылка -Файлы -Ссылка (URL) -Прикреплённые файлы -Загрузить файл -Отправить на проверку \ No newline at end of file +```bash +# Clone the repository +git clone https://git.brojs.ru/kuzakhmetovartur/8.-samopisnyy-poiskovyy-agent-na-osnove- +cd 8.-samopisnyy-poiskovyy-agent-na-osnove- + +# Install dependencies +npm install +``` + +Create a `.env` file in the project root: + +```dotenv +OPENAI_API_KEY=your_openai_api_key_here +OPENAI_MODEL=gpt-3.5-turbo # optional, defaults to gpt-3.5-turbo +``` + +> **Tip:** Keep your `.env` file out of version control. Add it to `.gitignore` if you plan to push the repo. + +## Usage + +Run the agent with a question: + +```bash +npm start -- "What is the tallest mountain in the world?" +``` + +Or simply: + +```bash +node src/index.js "Your question here" +``` + +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 +└── README.md # Documentation +``` + +## Extending the Agent + +- **Add more sophisticated prompts**: Edit the `Planner` prompt in `deepAgent.js`. +- **Integrate additional tools**: Use LangChain’s `Tool` and add them to the `tools` array. +- **Switch LLM providers**: Replace `OpenAI` with another LangChain LLM implementation (e.g., `AzureOpenAI`, `Anthropic`). + +## License + +MIT © 2026 + +--- \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c08119b --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "deep-agent-scratch", + "version": "1.0.0", + "description": "Deep Agent implementation based on LangChain", + "main": "src/index.js", + "type": "commonjs", + "scripts": { + "start": "node src/index.js", + "test": "echo \"No tests\"" + }, + "dependencies": { + "langchain": "^0.2.0", + "openai": "^4.0.0", + "dotenv": "^16.4.5" + } +} \ No newline at end of file diff --git a/src/deepAgent.js b/src/deepAgent.js new file mode 100644 index 0000000..b119de6 --- /dev/null +++ b/src/deepAgent.js @@ -0,0 +1,159 @@ +const { OpenAI } = require('langchain/llms/openai'); +const { Tool } = require('langchain/tools'); +const { PromptTemplate } = require('langchain/prompts'); +const { ConversationBufferMemory } = require('langchain/memory'); + +/** + * Planner class that uses an LLM to generate a plan of tool calls. + */ +class Planner { + /** + * @param {OpenAI} llm - The language model to use for planning. + * @param {Tool[]} tools - Available tools for the agent. + */ + constructor(llm, tools) { + this.llm = llm; + this.tools = tools; + + // Prompt template for the planner. + this.plannerPrompt = new PromptTemplate({ + template: `You are a planning assistant. Given the user query: "{input}", produce a JSON array of steps. Each step must contain: +- "tool": the name of the tool to use (must be one of: ${tools.map(t => t.name).join(', ')}), +- "input": the input string for that tool. + +The JSON array should be the only output. Example: +[ + {"tool":"Echo","input":"Hello"} +]`, + inputVariables: ['input'], + }); + } + + /** + * Generates a plan for the given input. + * @param {string} input - The user query. + * @returns {Promise>} The plan steps. + */ + async plan(input) { + const prompt = this.plannerPrompt.format({ input }); + const raw = await this.llm.invoke(prompt); + let plan; + try { + plan = JSON.parse(raw); + if (!Array.isArray(plan)) throw new Error('Plan is not an array'); + } catch (e) { + throw new Error(`Planner failed to parse JSON: ${e.message}. Raw output: ${raw}`); + } + // Validate tool names + for (const step of plan) { + if (!this.tools.find(t => t.name === step.tool)) { + throw new Error(`Planner suggested unknown tool "${step.tool}"`); + } + } + return plan; + } +} + +/** + * Executor class that runs the planned tool calls sequentially. + */ +class Executor { + /** + * @param {Tool[]} tools - Available tools for the agent. + */ + constructor(tools) { + this.tools = tools; + } + + /** + * Executes the plan and returns the final output. + * @param {Array<{tool: string, input: string}>} plan - The plan steps. + * @returns {Promise} The final output after executing all steps. + */ + async execute(plan) { + let lastOutput = ''; + for (const step of plan) { + const tool = this.tools.find(t => t.name === step.tool); + if (!tool) { + throw new Error(`Executor cannot find tool "${step.tool}"`); + } + const output = await tool.func(step.input); + lastOutput = output; + } + return lastOutput; + } +} + +/** + * DeepAgent implements the Deep Agents from Scratch template. + */ +class DeepAgent { + /** + * @param {Object} options Configuration options. + * @param {string} [options.modelName='gpt-3.5-turbo'] The LLM model to use. + * @param {number} [options.temperature=0.7] Temperature for LLM sampling. + * @param {number} [options.maxTokens=512] Maximum tokens for LLM output. + * @param {string} [options.apiKey] OpenAI API key. If not provided, will use process.env.OPENAI_API_KEY. + */ + constructor({ + modelName = 'gpt-3.5-turbo', + temperature = 0.7, + maxTokens = 512, + apiKey, + } = {}) { + this.llm = new OpenAI({ + modelName, + temperature, + maxTokens, + openAIApiKey: apiKey || process.env.OPENAI_API_KEY, + }); + + // Define a simple Echo tool that returns the input back. + const echoTool = new Tool({ + name: 'Echo', + description: 'Echoes the input back to the user.', + func: async (input) => input, + }); + + // Memory component required by the Deep Agents from Scratch template. + this.memory = new ConversationBufferMemory({ + memoryKey: 'chat_history', + inputKey: 'input', + outputKey: 'output', + }); + + this.tools = [echoTool]; + + // Instantiate Planner and Executor. + this.planner = new Planner(this.llm, this.tools); + this.executor = new Executor(this.tools); + } + + /** + * Runs the agent on a given question. + * @param {string} question The question to answer. + * @returns {Promise} The agent's answer. + */ + async run(question) { + try { + // Store the question in memory. + await this.memory.saveContext({ input: question }, { output: '' }); + + // Generate a plan. + const plan = await this.planner.plan(question); + + // Execute the plan. + const result = await this.executor.execute(plan); + + // Save the result in memory. + await this.memory.saveContext({ input: question }, { output: result }); + + return result.trim(); + } catch (err) { + console.error('DeepAgent encountered an error:', err); + throw err; + } + } +} + +module.exports = { DeepAgent }; \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..ea5e418 --- /dev/null +++ b/src/index.js @@ -0,0 +1,18 @@ +require('dotenv').config(); +const { DeepAgent } = require('./deepAgent'); + +(async () => { + const agent = new DeepAgent({ + modelName: process.env.OPENAI_MODEL || 'gpt-3.5-turbo', + temperature: 0.7, + }); + + const query = process.argv[2] || 'What is the capital of France?'; + console.log(`Query: ${query}`); + try { + const answer = await agent.run(query); + console.log(`Answer: ${answer}`); + } catch (err) { + console.error('Error running DeepAgent:', err); + } +})(); \ No newline at end of file