const { Agent } = require('langchain/agents'); const { LLMChain } = require('langchain/chains'); const { FunctionChatMessage, AIChatMessage } = require('langchain/schema'); const { ChatPromptTemplate, MessagesPlaceholder, SystemMessagePromptTemplate, HumanMessagePromptTemplate } = require('langchain/prompts'); const PREFIX = `You are a helpful AI assistant. Objective: Resolve the user's query with provided functions. The user is demanding a function response to the query; if any part of the query requires a function, respond with the function call first since you won't get to later`; function parseOutput(message) { if (message.additional_kwargs.function_call) { const function_call = message.additional_kwargs.function_call; return { tool: function_call.name, toolInput: function_call.arguments ? JSON.parse(function_call.arguments) : {}, log: message.text }; } else { return { returnValues: { output: message.text }, log: message.text }; } } class FunctionsAgent extends Agent { constructor(input) { super({ ...input, outputParser: undefined }); this.tools = input.tools; } lc_namespace = ['langchain', 'agents', 'openai']; _agentType() { return 'openai-functions'; } observationPrefix() { return 'Observation: '; } llmPrefix() { return 'Thought:'; } _stop() { return ['Observation:']; } static createPrompt(_tools, fields) { const { prefix = PREFIX, currentDateString } = fields || {}; return ChatPromptTemplate.fromPromptMessages([ SystemMessagePromptTemplate.fromTemplate(`Date: ${currentDateString}\n${prefix}`), HumanMessagePromptTemplate.fromTemplate(`{chat_history} Query: {input} {agent_scratchpad}`), new MessagesPlaceholder('agent_scratchpad') ]); } static fromLLMAndTools(llm, tools, args) { FunctionsAgent.validateTools(tools); const prompt = FunctionsAgent.createPrompt(tools, args); const chain = new LLMChain({ prompt, llm, callbacks: args?.callbacks }); return new FunctionsAgent({ llmChain: chain, allowedTools: tools.map((t) => t.name), tools }); } async constructScratchPad(steps) { return steps.flatMap(({ action, observation }) => [ new AIChatMessage('', { function_call: { name: action.tool, arguments: JSON.stringify(action.toolInput) } }), new FunctionChatMessage(observation, action.tool) ]); } async plan(steps, inputs, callbackManager) { // Add scratchpad and stop to inputs const thoughts = await this.constructScratchPad(steps); const newInputs = Object.assign({}, inputs, { agent_scratchpad: thoughts }); if (this._stop().length !== 0) { newInputs.stop = this._stop(); } // Split inputs between prompt and llm const llm = this.llmChain.llm; const valuesForPrompt = Object.assign({}, newInputs); const valuesForLLM = { tools: this.tools }; for (let i = 0; i < this.llmChain.llm.callKeys.length; i++) { const key = this.llmChain.llm.callKeys[i]; if (key in inputs) { valuesForLLM[key] = inputs[key]; delete valuesForPrompt[key]; } } const promptValue = await this.llmChain.prompt.formatPromptValue(valuesForPrompt); const message = await llm.predictMessages( promptValue.toChatMessages(), valuesForLLM, callbackManager ); console.log('message', message); return parseOutput(message); } } module.exports = FunctionsAgent;