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

This commit is contained in:
2026-06-30 00:35:06 +03:00
parent 865926c001
commit 4be7a21036
4 changed files with 262 additions and 23 deletions
+159
View File
@@ -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<Array<{tool: string, input: string}>>} 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<string>} 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<string>} 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 };
+18
View File
@@ -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);
}
})();