@@ -3,12 +3,23 @@ import { ChainValues } from '@langchain/core/utils/types'
3
3
import { AgentStep , AgentAction } from '@langchain/core/agents'
4
4
import { BaseMessage , FunctionMessage , AIMessage } from '@langchain/core/messages'
5
5
import { OutputParserException } from '@langchain/core/output_parsers'
6
+ import { BaseLanguageModel } from '@langchain/core/language_models/base'
6
7
import { CallbackManager , CallbackManagerForChainRun , Callbacks } from '@langchain/core/callbacks/manager'
7
- import { ToolInputParsingException , Tool } from '@langchain/core/tools'
8
- import { Runnable } from '@langchain/core/runnables'
8
+ import { ToolInputParsingException , Tool , StructuredToolInterface } from '@langchain/core/tools'
9
+ import { Runnable , RunnableSequence , RunnablePassthrough } from '@langchain/core/runnables'
9
10
import { Serializable } from '@langchain/core/load/serializable'
11
+ import { renderTemplate } from '@langchain/core/prompts'
10
12
import { BaseChain , SerializedLLMChain } from 'langchain/chains'
11
- import { AgentExecutorInput , BaseSingleActionAgent , BaseMultiActionAgent , RunnableAgent , StoppingMethod } from 'langchain/agents'
13
+ import {
14
+ CreateReactAgentParams ,
15
+ AgentExecutorInput ,
16
+ AgentActionOutputParser ,
17
+ BaseSingleActionAgent ,
18
+ BaseMultiActionAgent ,
19
+ RunnableAgent ,
20
+ StoppingMethod
21
+ } from 'langchain/agents'
22
+ import { formatLogToString } from 'langchain/agents/format_scratchpad/log'
12
23
13
24
export const SOURCE_DOCUMENTS_PREFIX = '\n\n----FLOWISE_SOURCE_DOCUMENTS----\n\n'
14
25
type AgentFinish = {
@@ -647,3 +658,110 @@ export const formatAgentSteps = (steps: AgentStep[]): BaseMessage[] =>
647
658
return [ new AIMessage ( action . log ) ]
648
659
}
649
660
} )
661
+
662
+ const renderTextDescription = ( tools : StructuredToolInterface [ ] ) : string => {
663
+ return tools . map ( ( tool ) => `${ tool . name } : ${ tool . description } ` ) . join ( '\n' )
664
+ }
665
+
666
+ export const createReactAgent = async ( { llm, tools, prompt } : CreateReactAgentParams ) => {
667
+ const missingVariables = [ 'tools' , 'tool_names' , 'agent_scratchpad' ] . filter ( ( v ) => ! prompt . inputVariables . includes ( v ) )
668
+ if ( missingVariables . length > 0 ) {
669
+ throw new Error ( `Provided prompt is missing required input variables: ${ JSON . stringify ( missingVariables ) } ` )
670
+ }
671
+ const toolNames = tools . map ( ( tool ) => tool . name )
672
+ const partialedPrompt = await prompt . partial ( {
673
+ tools : renderTextDescription ( tools ) ,
674
+ tool_names : toolNames . join ( ', ' )
675
+ } )
676
+ // TODO: Add .bind to core runnable interface.
677
+ const llmWithStop = ( llm as BaseLanguageModel ) . bind ( {
678
+ stop : [ '\nObservation:' ]
679
+ } )
680
+ const agent = RunnableSequence . from ( [
681
+ RunnablePassthrough . assign ( {
682
+ //@ts -ignore
683
+ agent_scratchpad : ( input : { steps : AgentStep [ ] } ) => formatLogToString ( input . steps )
684
+ } ) ,
685
+ partialedPrompt ,
686
+ llmWithStop ,
687
+ new ReActSingleInputOutputParser ( {
688
+ toolNames
689
+ } )
690
+ ] )
691
+ return agent
692
+ }
693
+
694
+ class ReActSingleInputOutputParser extends AgentActionOutputParser {
695
+ lc_namespace = [ 'langchain' , 'agents' , 'react' ]
696
+
697
+ private toolNames : string [ ]
698
+ private FINAL_ANSWER_ACTION = 'Final Answer:'
699
+ private FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE = 'Parsing LLM output produced both a final answer and a parse-able action:'
700
+ private FORMAT_INSTRUCTIONS = `Use the following format:
701
+
702
+ Question: the input question you must answer
703
+ Thought: you should always think about what to do
704
+ Action: the action to take, should be one of [{tool_names}]
705
+ Action Input: the input to the action
706
+ Observation: the result of the action
707
+ ... (this Thought/Action/Action Input/Observation can repeat N times)
708
+ Thought: I now know the final answer
709
+ Final Answer: the final answer to the original input question`
710
+
711
+ constructor ( fields : { toolNames : string [ ] } ) {
712
+ super ( ...arguments )
713
+ this . toolNames = fields . toolNames
714
+ }
715
+
716
+ /**
717
+ * Parses the given text into an AgentAction or AgentFinish object. If an
718
+ * output fixing parser is defined, uses it to parse the text.
719
+ * @param text Text to parse.
720
+ * @returns Promise that resolves to an AgentAction or AgentFinish object.
721
+ */
722
+ async parse ( text : string ) : Promise < AgentAction | AgentFinish > {
723
+ const includesAnswer = text . includes ( this . FINAL_ANSWER_ACTION )
724
+ const regex = / A c t i o n \s * \d * \s * : [ \s ] * ( .* ?) [ \s ] * A c t i o n \s * \d * \s * I n p u t \s * \d * \s * : [ \s ] * ( .* ) /
725
+ const actionMatch = text . match ( regex )
726
+ if ( actionMatch ) {
727
+ if ( includesAnswer ) {
728
+ throw new Error ( `${ this . FINAL_ANSWER_AND_PARSABLE_ACTION_ERROR_MESSAGE } : ${ text } ` )
729
+ }
730
+
731
+ const action = actionMatch [ 1 ]
732
+ const actionInput = actionMatch [ 2 ]
733
+ const toolInput = actionInput . trim ( ) . replace ( / " / g, '' )
734
+
735
+ return {
736
+ tool : action ,
737
+ toolInput,
738
+ log : text
739
+ }
740
+ }
741
+
742
+ if ( includesAnswer ) {
743
+ const finalAnswerText = text . split ( this . FINAL_ANSWER_ACTION ) [ 1 ] . trim ( )
744
+ return {
745
+ returnValues : {
746
+ output : finalAnswerText
747
+ } ,
748
+ log : text
749
+ }
750
+ }
751
+
752
+ // Instead of throwing Error, we return a AgentFinish object
753
+ return { returnValues : { output : text } , log : text }
754
+ }
755
+
756
+ /**
757
+ * Returns the format instructions as a string. If the 'raw' option is
758
+ * true, returns the raw FORMAT_INSTRUCTIONS.
759
+ * @param options Options for getting the format instructions.
760
+ * @returns Format instructions as a string.
761
+ */
762
+ getFormatInstructions ( ) : string {
763
+ return renderTemplate ( this . FORMAT_INSTRUCTIONS , 'f-string' , {
764
+ tool_names : this . toolNames . join ( ', ' )
765
+ } )
766
+ }
767
+ }
0 commit comments