mirror of
https://github.com/hwchase17/langchain.git
synced 2025-08-06 03:27:55 +00:00
beta: logger (#307)
This commit is contained in:
parent
36b4c58acf
commit
e02d6b2288
@ -12,7 +12,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"execution_count": 6,
|
||||
"id": "4e272b47",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
@ -32,13 +32,13 @@
|
||||
" )\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"llm = OpenAI(temperature=0)\n",
|
||||
"llm = OpenAI(temperature=0, model_name=\"text-davinci-002\")\n",
|
||||
"react = initialize_agent(tools, llm, agent=\"react-docstore\", verbose=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 7,
|
||||
"id": "8078c8f1",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
@ -46,9 +46,31 @@
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\u001b[1m> Entering new ReActDocstoreAgent chain...\u001b[0m\n",
|
||||
"Author David Chanoff has collaborated with a U.S. Navy admiral who served as the ambassador to the United Kingdom under which President?\n",
|
||||
"Thought 1:"
|
||||
"Thought 1:\u001b[32;1m\u001b[1;3m I need to search David Chanoff and find the U.S. Navy admiral he collaborated\n",
|
||||
"with.\n",
|
||||
"Action 1: Search[David Chanoff]\u001b[0m\n",
|
||||
"Observation 1: \u001b[36;1m\u001b[1;3mDavid Chanoff is a noted author of non-fiction work. His work has typically involved collaborations with the principal protagonist of the work concerned. His collaborators have included; Augustus A. White, Joycelyn Elders, Đoàn Văn Toại, William J. Crowe, Ariel Sharon, Kenneth Good and Felix Zandman. He has also written about a wide range of subjects including literary history, education and foreign for The Washington Post, The New Republic and The New York Times Magazine. He has published more than twelve books.\u001b[0m\n",
|
||||
"Thought 2:\u001b[32;1m\u001b[1;3m The U.S. Navy admiral David Chanoff collaborated with is William J. Crowe.\n",
|
||||
"Action 2: Search[William J. Crowe]\u001b[0m\n",
|
||||
"Observation 2: \u001b[36;1m\u001b[1;3mWilliam James Crowe Jr. (January 2, 1925 – October 18, 2007) was a United States Navy admiral and diplomat who served as the 11th chairman of the Joint Chiefs of Staff under Presidents Ronald Reagan and George H. W. Bush, and as the ambassador to the United Kingdom and Chair of the Intelligence Oversight Board under President Bill Clinton.\u001b[0m\n",
|
||||
"Thought 3:\u001b[32;1m\u001b[1;3m William J. Crowe served as the ambassador to the United Kingdom under President Bill Clinton.\n",
|
||||
"Action 3: Finish[Bill Clinton]\u001b[0m\n",
|
||||
"\u001b[1m> Finished ReActDocstoreAgent chain.\u001b[0m\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"'Bill Clinton'"
|
||||
]
|
||||
},
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
@ -81,7 +103,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.7.6"
|
||||
"version": "3.10.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -15,6 +15,7 @@ from langchain.chains import (
|
||||
)
|
||||
from langchain.docstore import InMemoryDocstore, Wikipedia
|
||||
from langchain.llms import Cohere, HuggingFaceHub, OpenAI
|
||||
from langchain.logger import BaseLogger, StdOutLogger
|
||||
from langchain.prompts import (
|
||||
BasePromptTemplate,
|
||||
FewShotPromptTemplate,
|
||||
@ -25,6 +26,9 @@ from langchain.serpapi import SerpAPIChain, SerpAPIWrapper
|
||||
from langchain.sql_database import SQLDatabase
|
||||
from langchain.vectorstores import FAISS, ElasticVectorSearch
|
||||
|
||||
logger: BaseLogger = StdOutLogger()
|
||||
verbose: bool = False
|
||||
|
||||
__all__ = [
|
||||
"LLMChain",
|
||||
"LLMBashChain",
|
||||
|
@ -2,24 +2,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, ClassVar, Dict, List, NamedTuple, Optional, Tuple
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Tuple
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from langchain.agents.input import ChainedInput
|
||||
from langchain.agents.tools import Tool
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain.input import ChainedInput, get_color_mapping
|
||||
from langchain.input import get_color_mapping
|
||||
from langchain.llms.base import LLM
|
||||
from langchain.prompts.base import BasePromptTemplate
|
||||
|
||||
|
||||
class Action(NamedTuple):
|
||||
"""Action to take."""
|
||||
|
||||
tool: str
|
||||
tool_input: str
|
||||
log: str
|
||||
from langchain.schema import AgentAction
|
||||
|
||||
|
||||
class Agent(Chain, BaseModel, ABC):
|
||||
@ -99,7 +93,7 @@ class Agent(Chain, BaseModel, ABC):
|
||||
llm_chain = LLMChain(llm=llm, prompt=cls.create_prompt(tools))
|
||||
return cls(llm_chain=llm_chain, tools=tools, **kwargs)
|
||||
|
||||
def get_action(self, text: str) -> Action:
|
||||
def get_action(self, text: str) -> AgentAction:
|
||||
"""Given input, decided what to do.
|
||||
|
||||
Args:
|
||||
@ -119,7 +113,7 @@ class Agent(Chain, BaseModel, ABC):
|
||||
full_output += output
|
||||
parsed_output = self._extract_tool_and_input(full_output)
|
||||
tool, tool_input = parsed_output
|
||||
return Action(tool, tool_input, full_output)
|
||||
return AgentAction(tool, tool_input, full_output)
|
||||
|
||||
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
|
||||
"""Run text through and get agent response."""
|
||||
@ -145,7 +139,7 @@ class Agent(Chain, BaseModel, ABC):
|
||||
# Call the LLM to see what to do.
|
||||
output = self.get_action(chained_input.input)
|
||||
# Add the log to the Chained Input.
|
||||
chained_input.add(output.log, color="green")
|
||||
chained_input.add_action(output, color="green")
|
||||
# If the tool chosen is the finishing tool, then we end and return.
|
||||
if output.tool == self.finish_tool_name:
|
||||
return {self.output_key: output.tool_input}
|
||||
@ -159,8 +153,9 @@ class Agent(Chain, BaseModel, ABC):
|
||||
observation = f"{output.tool} is not a valid tool, try another one."
|
||||
color = None
|
||||
# We then log the observation
|
||||
chained_input.add(f"\n{self.observation_prefix}")
|
||||
chained_input.add(observation, color=color)
|
||||
# We then add the LLM prefix into the prompt to get the LLM to start
|
||||
# thinking, and start the loop all over.
|
||||
chained_input.add(f"\n{self.llm_prefix}")
|
||||
chained_input.add_observation(
|
||||
observation,
|
||||
self.observation_prefix,
|
||||
self.llm_prefix,
|
||||
color=color,
|
||||
)
|
||||
|
44
langchain/agents/input.py
Normal file
44
langchain/agents/input.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""Input manager for agents."""
|
||||
from typing import Optional
|
||||
|
||||
import langchain
|
||||
from langchain.schema import AgentAction
|
||||
|
||||
|
||||
class ChainedInput:
|
||||
"""Class for working with input that is the result of chains."""
|
||||
|
||||
def __init__(self, text: str, verbose: bool = False):
|
||||
"""Initialize with verbose flag and initial text."""
|
||||
self._verbose = verbose
|
||||
if self._verbose:
|
||||
langchain.logger.log_agent_start(text)
|
||||
self._input = text
|
||||
|
||||
def add_action(self, action: AgentAction, color: Optional[str] = None) -> None:
|
||||
"""Add text to input, print if in verbose mode."""
|
||||
if self._verbose:
|
||||
langchain.logger.log_agent_action(action, color=color)
|
||||
self._input += action.log
|
||||
|
||||
def add_observation(
|
||||
self,
|
||||
observation: str,
|
||||
observation_prefix: str,
|
||||
llm_prefix: str,
|
||||
color: Optional[str],
|
||||
) -> None:
|
||||
"""Add observation to input, print if in verbose mode."""
|
||||
if self._verbose:
|
||||
langchain.logger.log_agent_observation(
|
||||
observation,
|
||||
color=color,
|
||||
observation_prefix=observation_prefix,
|
||||
llm_prefix=llm_prefix,
|
||||
)
|
||||
self._input += f"\n{observation_prefix}{observation}\n{llm_prefix}"
|
||||
|
||||
@property
|
||||
def input(self) -> str:
|
||||
"""Return the accumulated input."""
|
||||
return self._input
|
@ -2,7 +2,9 @@
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
from pydantic import BaseModel, Extra, Field
|
||||
|
||||
import langchain
|
||||
|
||||
|
||||
class Memory(BaseModel, ABC):
|
||||
@ -28,12 +30,16 @@ class Memory(BaseModel, ABC):
|
||||
"""Save the context of this model run to memory."""
|
||||
|
||||
|
||||
def _get_verbosity() -> bool:
|
||||
return langchain.verbose
|
||||
|
||||
|
||||
class Chain(BaseModel, ABC):
|
||||
"""Base interface that all chains should implement."""
|
||||
|
||||
memory: Optional[Memory] = None
|
||||
|
||||
verbose: bool = False
|
||||
verbose: bool = Field(default_factory=_get_verbosity)
|
||||
"""Whether to print out response text."""
|
||||
|
||||
@property
|
||||
|
@ -3,8 +3,8 @@ from typing import Any, Dict, List, Union
|
||||
|
||||
from pydantic import BaseModel, Extra
|
||||
|
||||
import langchain
|
||||
from langchain.chains.base import Chain
|
||||
from langchain.input import print_text
|
||||
from langchain.llms.base import LLM
|
||||
from langchain.prompts.base import BasePromptTemplate
|
||||
|
||||
@ -55,12 +55,13 @@ class LLMChain(Chain, BaseModel):
|
||||
selected_inputs = {k: inputs[k] for k in self.prompt.input_variables}
|
||||
prompt = self.prompt.format(**selected_inputs)
|
||||
if self.verbose:
|
||||
print("Prompt after formatting:")
|
||||
print_text(prompt, color="green", end="\n")
|
||||
langchain.logger.log_llm_inputs(selected_inputs, prompt)
|
||||
kwargs = {}
|
||||
if "stop" in inputs:
|
||||
kwargs["stop"] = inputs["stop"]
|
||||
response = self.llm(prompt, **kwargs)
|
||||
if self.verbose:
|
||||
langchain.logger.log_llm_response(response)
|
||||
return {self.output_key: response}
|
||||
|
||||
def predict(self, **kwargs: Any) -> str:
|
||||
|
@ -27,25 +27,3 @@ def print_text(text: str, color: Optional[str] = None, end: str = "") -> None:
|
||||
else:
|
||||
color_str = _TEXT_COLOR_MAPPING[color]
|
||||
print(f"\u001b[{color_str}m\033[1;3m{text}\u001b[0m", end=end)
|
||||
|
||||
|
||||
class ChainedInput:
|
||||
"""Class for working with input that is the result of chains."""
|
||||
|
||||
def __init__(self, text: str, verbose: bool = False):
|
||||
"""Initialize with verbose flag and initial text."""
|
||||
self._verbose = verbose
|
||||
if self._verbose:
|
||||
print_text(text, color=None)
|
||||
self._input = text
|
||||
|
||||
def add(self, text: str, color: Optional[str] = None) -> None:
|
||||
"""Add text to input, print if in verbose mode."""
|
||||
if self._verbose:
|
||||
print_text(text, color=color)
|
||||
self._input += text
|
||||
|
||||
@property
|
||||
def input(self) -> str:
|
||||
"""Return the accumulated input."""
|
||||
return self._input
|
||||
|
65
langchain/logger.py
Normal file
65
langchain/logger.py
Normal file
@ -0,0 +1,65 @@
|
||||
"""BETA: everything in here is highly experimental, do not rely on."""
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain.input import print_text
|
||||
from langchain.schema import AgentAction
|
||||
|
||||
|
||||
class BaseLogger:
|
||||
"""Base logging interface."""
|
||||
|
||||
def log_agent_start(self, text: str, **kwargs: Any) -> None:
|
||||
"""Log the start of an agent interaction."""
|
||||
pass
|
||||
|
||||
def log_agent_end(self, text: str, **kwargs: Any) -> None:
|
||||
"""Log the end of an agent interaction."""
|
||||
pass
|
||||
|
||||
def log_agent_action(self, action: AgentAction, **kwargs: Any) -> None:
|
||||
"""Log agent action decision."""
|
||||
pass
|
||||
|
||||
def log_agent_observation(self, observation: str, **kwargs: Any) -> None:
|
||||
"""Log agent observation."""
|
||||
pass
|
||||
|
||||
def log_llm_inputs(self, inputs: dict, prompt: str, **kwargs: Any) -> None:
|
||||
"""Log LLM inputs."""
|
||||
pass
|
||||
|
||||
def log_llm_response(self, output: str, **kwargs: Any) -> None:
|
||||
"""Log LLM response."""
|
||||
pass
|
||||
|
||||
|
||||
class StdOutLogger(BaseLogger):
|
||||
"""Interface for printing things to stdout."""
|
||||
|
||||
def log_agent_start(self, text: str, **kwargs: Any) -> None:
|
||||
"""Print the text to start the agent."""
|
||||
print_text(text)
|
||||
|
||||
def log_agent_action(
|
||||
self, action: AgentAction, color: Optional[str] = None, **kwargs: Any
|
||||
) -> None:
|
||||
"""Print the log of the action in a certain color."""
|
||||
print_text(action.log, color=color)
|
||||
|
||||
def log_agent_observation(
|
||||
self,
|
||||
observation: str,
|
||||
color: Optional[str] = None,
|
||||
observation_prefix: Optional[str] = None,
|
||||
llm_prefix: Optional[str] = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""Print the observation in a special color."""
|
||||
print_text(f"\n{observation_prefix}")
|
||||
print_text(observation, color=color)
|
||||
print_text(f"\n{llm_prefix}")
|
||||
|
||||
def log_llm_inputs(self, inputs: dict, prompt: str, **kwargs: Any) -> None:
|
||||
"""Print the prompt in green."""
|
||||
print("Prompt after formatting:")
|
||||
print_text(prompt, color="green", end="\n")
|
11
langchain/schema.py
Normal file
11
langchain/schema.py
Normal file
@ -0,0 +1,11 @@
|
||||
"""Common schema objects."""
|
||||
|
||||
from typing import NamedTuple
|
||||
|
||||
|
||||
class AgentAction(NamedTuple):
|
||||
"""Agent's action to take."""
|
||||
|
||||
tool: str
|
||||
tool_input: str
|
||||
log: str
|
@ -3,7 +3,8 @@
|
||||
import sys
|
||||
from io import StringIO
|
||||
|
||||
from langchain.input import ChainedInput, get_color_mapping
|
||||
from langchain.agents.input import ChainedInput
|
||||
from langchain.input import get_color_mapping
|
||||
|
||||
|
||||
def test_chained_input_not_verbose() -> None:
|
||||
@ -18,11 +19,11 @@ def test_chained_input_not_verbose() -> None:
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = mystdout = StringIO()
|
||||
chained_input.add("bar")
|
||||
chained_input.add_observation("bar", "1", "2", None)
|
||||
sys.stdout = old_stdout
|
||||
output = mystdout.getvalue()
|
||||
assert output == ""
|
||||
assert chained_input.input == "foobar"
|
||||
assert chained_input.input == "foo\n1bar\n2"
|
||||
|
||||
|
||||
def test_chained_input_verbose() -> None:
|
||||
@ -37,19 +38,19 @@ def test_chained_input_verbose() -> None:
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = mystdout = StringIO()
|
||||
chained_input.add("bar")
|
||||
chained_input.add_observation("bar", "1", "2", None)
|
||||
sys.stdout = old_stdout
|
||||
output = mystdout.getvalue()
|
||||
assert output == "bar"
|
||||
assert chained_input.input == "foobar"
|
||||
assert output == "\n1bar\n2"
|
||||
assert chained_input.input == "foo\n1bar\n2"
|
||||
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = mystdout = StringIO()
|
||||
chained_input.add("baz", color="blue")
|
||||
chained_input.add_observation("baz", "3", "4", "blue")
|
||||
sys.stdout = old_stdout
|
||||
output = mystdout.getvalue()
|
||||
assert output == "\x1b[36;1m\x1b[1;3mbaz\x1b[0m"
|
||||
assert chained_input.input == "foobarbaz"
|
||||
assert output == "\n3\x1b[36;1m\x1b[1;3mbaz\x1b[0m\n4"
|
||||
assert chained_input.input == "foo\n1bar\n2\n3baz\n4"
|
||||
|
||||
|
||||
def test_get_color_mapping() -> None:
|
||||
|
Loading…
Reference in New Issue
Block a user