-
Notifications
You must be signed in to change notification settings - Fork 15.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding a Character Chat Agent #4411
Changes from all commits
dd411ad
9f66a3e
d746b9a
27a5556
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""An agent designed to hold a conversation as a given character in addition to using tools.""" |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,133 @@ | ||||||
"""An agent designed to hold a conversation as a given character in addition to using tools.""" | ||||||
from __future__ import annotations | ||||||
|
||||||
from typing import Any, List, Optional, Sequence, Tuple | ||||||
|
||||||
from pydantic import Field | ||||||
|
||||||
from langchain.agents.agent import Agent, AgentOutputParser | ||||||
from langchain.agents.character_chat.output_parser import ConvoOutputParser | ||||||
from langchain.agents.character_chat.prompt import (CHARACTER_SUMMARY, PREFIX, | ||||||
SUFFIX, | ||||||
TEMPLATE_TOOL_RESPONSE) | ||||||
from langchain.agents.utils import validate_tools_single_input | ||||||
from langchain.base_language import BaseLanguageModel | ||||||
from langchain.callbacks.base import BaseCallbackManager | ||||||
from langchain.chains import LLMChain | ||||||
from langchain.prompts.base import BasePromptTemplate | ||||||
from langchain.prompts.chat import (ChatPromptTemplate, | ||||||
HumanMessagePromptTemplate, | ||||||
MessagesPlaceholder, | ||||||
SystemMessagePromptTemplate) | ||||||
from langchain.schema import (AgentAction, AIMessage, BaseMessage, | ||||||
BaseOutputParser, SystemMessage) | ||||||
from langchain.tools.base import BaseTool | ||||||
|
||||||
|
||||||
class CharacterChatAgent(Agent): | ||||||
"""An agent designed to hold a conversation in addition to using tools.""" | ||||||
|
||||||
output_parser: AgentOutputParser = Field(default_factory=ConvoOutputParser) | ||||||
|
||||||
@classmethod | ||||||
def _get_default_output_parser(cls, **kwargs: Any) -> AgentOutputParser: | ||||||
return ConvoOutputParser() | ||||||
|
||||||
@property | ||||||
def _agent_type(self) -> str: | ||||||
raise NotImplementedError | ||||||
|
||||||
@property | ||||||
def observation_prefix(self) -> str: | ||||||
"""Prefix to append the observation with.""" | ||||||
return "Observation: " | ||||||
|
||||||
@property | ||||||
def llm_prefix(self) -> str: | ||||||
"""Prefix to append the llm call with.""" | ||||||
return "Thought:" | ||||||
|
||||||
@classmethod | ||||||
def _validate_tools(cls, tools: Sequence[BaseTool]) -> None: | ||||||
super()._validate_tools(tools) | ||||||
validate_tools_single_input(cls.__name__, tools) | ||||||
|
||||||
@classmethod | ||||||
def create_prompt( | ||||||
cls, | ||||||
tools: Sequence[BaseTool], | ||||||
system_message: str = PREFIX, | ||||||
human_message: str = SUFFIX, | ||||||
character_summary: str = CHARACTER_SUMMARY, | ||||||
input_variables: Optional[List[str]] = None, | ||||||
output_parser: Optional[BaseOutputParser] = None, | ||||||
) -> BasePromptTemplate: | ||||||
character_message = system_message.format(character_summary=character_summary) | ||||||
tool_strings = "\n".join( | ||||||
[f"> {tool.name}: {tool.description}" for tool in tools] | ||||||
) | ||||||
tool_names = ", ".join([tool.name for tool in tools]) | ||||||
_output_parser = output_parser or cls._get_default_output_parser() | ||||||
format_instructions = character_message.format( | ||||||
format_instructions=_output_parser.get_format_instructions() | ||||||
) | ||||||
final_prompt = format_instructions.format( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
how about naming it to |
||||||
tool_names=tool_names, tools=tool_strings | ||||||
) | ||||||
if input_variables is None: | ||||||
input_variables = ["input", "chat_history", "agent_scratchpad"] | ||||||
messages = [ | ||||||
SystemMessagePromptTemplate.from_template(final_prompt), | ||||||
MessagesPlaceholder(variable_name="chat_history"), | ||||||
HumanMessagePromptTemplate.from_template(human_message), | ||||||
MessagesPlaceholder(variable_name="agent_scratchpad"), | ||||||
] | ||||||
return ChatPromptTemplate(input_variables=input_variables, messages=messages) | ||||||
|
||||||
def _construct_scratchpad( | ||||||
self, intermediate_steps: List[Tuple[AgentAction, str]] | ||||||
) -> List[BaseMessage]: | ||||||
"""Construct the scratchpad that lets the agent continue its thought process.""" | ||||||
thoughts: List[BaseMessage] = [] | ||||||
for action, observation in intermediate_steps: | ||||||
thoughts.append(AIMessage(content=action.log)) | ||||||
system_message = SystemMessage( | ||||||
content=TEMPLATE_TOOL_RESPONSE.format(observation=observation) | ||||||
) | ||||||
thoughts.append(system_message) | ||||||
return thoughts | ||||||
|
||||||
@classmethod | ||||||
def from_llm_and_tools( | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I saw this piece of code in several places, ever considered inheriting |
||||||
cls, | ||||||
llm: BaseLanguageModel, | ||||||
tools: Sequence[BaseTool], | ||||||
callback_manager: Optional[BaseCallbackManager] = None, | ||||||
output_parser: Optional[AgentOutputParser] = None, | ||||||
system_message: str = PREFIX, | ||||||
human_message: str = SUFFIX, | ||||||
input_variables: Optional[List[str]] = None, | ||||||
**kwargs: Any, | ||||||
) -> Agent: | ||||||
"""Construct an agent from an LLM and tools.""" | ||||||
cls._validate_tools(tools) | ||||||
_output_parser = output_parser or cls._get_default_output_parser() | ||||||
prompt = cls.create_prompt( | ||||||
tools, | ||||||
system_message=system_message, | ||||||
human_message=human_message, | ||||||
input_variables=input_variables, | ||||||
output_parser=_output_parser, | ||||||
) | ||||||
llm_chain = LLMChain( | ||||||
llm=llm, | ||||||
prompt=prompt, | ||||||
callback_manager=callback_manager, | ||||||
) | ||||||
tool_names = [tool.name for tool in tools] | ||||||
return cls( | ||||||
llm_chain=llm_chain, | ||||||
allowed_tools=tool_names, | ||||||
output_parser=_output_parser, | ||||||
**kwargs, | ||||||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be more accurate to rephrase this as "An agent designed to hold a conversation as a given character in addition to using tools."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as above. Incorporating.