UNPKG

@jackhua/mini-langchain

Version:

A lightweight TypeScript implementation of LangChain with cost optimization features

135 lines (130 loc) 4.54 kB
"use strict"; /** * ReAct (Reasoning + Acting) Agent implementation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ReActAgent = void 0; exports.createReActAgent = createReActAgent; const base_1 = require("./base"); const prompt_1 = require("../prompts/prompt"); /** * ReAct agent prompt template */ const REACT_PROMPT = `You are an AI assistant that uses tools to help answer questions and solve problems. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of the available tools Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {input} {agent_scratchpad}`; /** * ReAct Agent - Combines reasoning with acting */ class ReActAgent extends base_1.BaseAgent { constructor(config) { super(config); this.promptTemplate = new prompt_1.PromptTemplate({ template: REACT_PROMPT, inputVariables: ['tools', 'input', 'agent_scratchpad'] }); } /** * Parse LLM output to extract action or finish */ parseOutput(output) { // Check for Final Answer const finalAnswerMatch = output.match(/Final Answer:\s*(.*)/s); if (finalAnswerMatch) { return { output: finalAnswerMatch[1].trim(), log: output }; } // Parse Action and Action Input const actionMatch = output.match(/Action:\s*(.*)/); const actionInputMatch = output.match(/Action Input:\s*(.*)/s); if (actionMatch && actionInputMatch) { const action = actionMatch[1].trim(); let actionInput = actionInputMatch[1].trim(); // Handle multi-line input by taking everything until the next section const nextSectionIndex = actionInput.search(/\n(Observation|Thought|Action|Final Answer):/); if (nextSectionIndex > -1) { actionInput = actionInput.substring(0, nextSectionIndex).trim(); } return { tool: action, toolInput: actionInput, log: output }; } // If we can't parse, treat as a finish with the output return { output: output, log: output }; } /** * Plan the next action based on current context */ async plan(context) { // Format the agent scratchpad (history) const agentScratchpad = this.formatHistory(context); // Add "Thought:" if we have history to continue the chain const scratchpadWithPrompt = agentScratchpad ? agentScratchpad + '\nThought:' : 'Thought:'; // Create prompt const prompt = await this.promptTemplate.format({ tools: this.getToolsDescription(), input: context.input, agent_scratchpad: scratchpadWithPrompt }); // Get LLM response const response = await this.llm.call(prompt); if (this.verbose) { console.log('LLM Response:', response); } // Parse and return the next step return this.parseOutput(response); } /** * Format history specifically for ReAct format */ formatHistory(context) { return context.steps .map(step => { const parts = []; if (step.action) { // Extract thought from log if available const thoughtMatch = step.action.log?.match(/Thought:\s*(.*?)(?=\nAction:|$)/s); if (thoughtMatch) { parts.push(`Thought: ${thoughtMatch[1].trim()}`); } parts.push(`Action: ${step.action.tool}`); parts.push(`Action Input: ${step.action.toolInput}`); } if (step.observation !== undefined) { parts.push(`Observation: ${step.observation}`); } return parts.join('\n'); }) .filter(s => s) .join('\n'); } } exports.ReActAgent = ReActAgent; /** * Create a ReAct agent with tools */ function createReActAgent(config) { return new ReActAgent(config); } //# sourceMappingURL=react.js.map