"""
**Agent** 是一个使用LLM来选择一系列动作的类。
在Chains中,一系列动作是硬编码的。在Agents中,语言模型被用作推理引擎,确定要采取哪些动作以及顺序。
Agents选择并使用 **Tools** 和 **Toolkits** 进行动作。
**类层次结构:**
.. code-block::
BaseSingleActionAgent --> LLMSingleActionAgent
OpenAIFunctionsAgent
XMLAgent
Agent --> <name>Agent # 例如: ZeroShotAgent, ChatAgent
BaseMultiActionAgent --> OpenAIMultiFunctionsAgent
**主要辅助功能:**
.. code-block::
AgentType, AgentExecutor, AgentOutputParser, AgentExecutorIterator,
AgentAction, AgentFinish, AgentStep
""" # noqa: E501
from __future__ import annotations
import json
from typing import Any, List, Literal, Sequence, Union
from langchain_core.load.serializable import Serializable
from langchain_core.messages import (
AIMessage,
BaseMessage,
FunctionMessage,
HumanMessage,
)
[docs]class AgentAction(Serializable):
"""一个ActionAgent执行的动作的完整描述。"""
tool: str
"""要执行的工具的名称。"""
tool_input: Union[str, dict]
"""工具的输入。"""
log: str
"""关于该操作的额外日志信息。
这个日志可以用几种方式。首先,它可以用于审计LLM预测导致这个(工具,工具输入)的确切情况。其次,它可以在未来的迭代中显示LLM之前的想法。当(工具,工具输入)不包含关于LLM预测的完整信息时,这是有用的(例如,在工具/工具输入之前的任何“想法”)。"""
type: Literal["AgentAction"] = "AgentAction"
def __init__(
self, tool: str, tool_input: Union[str, dict], log: str, **kwargs: Any
):
"""覆盖init以支持通过位置进行实例化,以实现向后兼容。"""
super().__init__(tool=tool, tool_input=tool_input, log=log, **kwargs)
[docs] @classmethod
def is_lc_serializable(cls) -> bool:
"""返回类是否可序列化。"""
return True
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "schema", "agent"]
@property
def messages(self) -> Sequence[BaseMessage]:
"""返回与此操作对应的消息。"""
return _convert_agent_action_to_messages(self)
[docs]class AgentActionMessageLog(AgentAction):
message_log: Sequence[BaseMessage]
"""类似于日志,这可以用来传递关于LLM预测的确切消息的额外信息,在解析出(tool, tool_input)之前。如果(tool, tool_input)不能完全重现LLM预测,并且您需要该LLM预测(用于未来的代理迭代),则这是非常有用的。与`log`相比,当基础LLM是ChatModel(因此返回消息而不是字符串)时,这是有用的。"""
# Ignoring type because we're overriding the type from AgentAction.
# And this is the correct thing to do in this case.
# The type literal is used for serialization purposes.
type: Literal["AgentActionMessageLog"] = "AgentActionMessageLog" # type: ignore
[docs]class AgentStep(Serializable):
"""运行AgentAction的结果。"""
action: AgentAction
"""执行的AgentAction。"""
observation: Any
"""AgentAction的结果。"""
@property
def messages(self) -> Sequence[BaseMessage]:
"""返回与此观察相对应的消息。"""
return _convert_agent_observation_to_messages(self.action, self.observation)
[docs]class AgentFinish(Serializable):
"""ActionAgent的最终返回值。"""
return_values: dict
"""返回值的字典。"""
log: str
"""关于返回值的额外信息记录。
这用于传递完整的LLM预测,而不仅仅是解析出来的返回值。例如,如果完整的LLM预测是`Final Answer: 2`,您可能只想返回`2`作为返回值,但是将完整字符串作为`log`传递(用于调试或可观察性目的)。"""
type: Literal["AgentFinish"] = "AgentFinish"
def __init__(self, return_values: dict, log: str, **kwargs: Any):
"""覆盖init以支持通过位置进行实例化,以实现向后兼容。"""
super().__init__(return_values=return_values, log=log, **kwargs)
[docs] @classmethod
def is_lc_serializable(cls) -> bool:
"""返回类是否可序列化。"""
return True
[docs] @classmethod
def get_lc_namespace(cls) -> List[str]:
"""获取langchain对象的命名空间。"""
return ["langchain", "schema", "agent"]
@property
def messages(self) -> Sequence[BaseMessage]:
"""返回与此观察相对应的消息。"""
return [AIMessage(content=self.log)]
def _convert_agent_action_to_messages(
agent_action: AgentAction,
) -> Sequence[BaseMessage]:
"""将代理动作转换为消息。
此代码用于从代理动作重构原始的AI消息。
参数:
agent_action:要转换的代理动作。
返回:
对应于原始工具调用的AIMessage。
"""
if isinstance(agent_action, AgentActionMessageLog):
return agent_action.message_log
else:
return [AIMessage(content=agent_action.log)]
def _convert_agent_observation_to_messages(
agent_action: AgentAction, observation: Any
) -> Sequence[BaseMessage]:
"""将代理动作转换为消息。
此代码用于从代理动作重构原始的AI消息。
参数:
agent_action:要转换的代理动作。
返回:
对应于原始工具调用的AIMessage。
"""
if isinstance(agent_action, AgentActionMessageLog):
return [_create_function_message(agent_action, observation)]
else:
return [HumanMessage(content=observation)]
def _create_function_message(
agent_action: AgentAction, observation: Any
) -> FunctionMessage:
"""将代理动作和观察转换为函数消息。
参数:
agent_action:代理发送的工具调用请求
observation:工具调用的结果
返回:
对应于原始工具调用的FunctionMessage。
"""
if not isinstance(observation, str):
try:
content = json.dumps(observation, ensure_ascii=False)
except Exception:
content = str(observation)
else:
content = observation
return FunctionMessage(
name=agent_action.tool,
content=content,
)