LangChain中的代理(Agent)是一种能够根据工具调用和对话历史自主决策的智能体。它通过以下核心机制实现智能交互:
from langchain.agents import Agent
from langchain.schema import AgentAction, AgentFinish
from langchain.tools import Tool
from typing import List, Union, Dict, Any
class CustomAgent(Agent):
"""自定义代理基类,实现基本决策逻辑"""
tools: List[Tool] # 可调用的工具列表
@property
def _agent_type(self) -> str:
return "custom_agent" # 代理类型标识符
def _get_tools(self) -> List[Tool]:
return self.tools # 返回可调用工具列表
def _validate_tools(self) -> None:
if not self.tools:
raise ValueError("代理必须至少包含一个工具")
def _construct_prompt(self) -> str:
"""构建LLM提示,包含工具描述和对话历史"""
tool_descriptions = "\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
history = "\n".join([f"User: {msg}\nAgent: {resp}" for msg, resp in self._get_history()])
return f"可用工具:\n{tool_descriptions}\n对话历史:\n{history}\n当前问题: {{input}}\n请决策:"
def _get_history(self) -> List[Dict[str, str]]:
"""获取对话历史,需子类实现"""
raise NotImplementedError
def _get_next_action(self, input: str) -> Union[AgentAction, AgentFinish]:
"""生成下一步动作,需子类实现"""
raise NotImplementedError
工具通过Tool
类封装,包含以下核心属性:
name
:工具唯一标识func
:工具执行函数description
:工具功能描述return_direct
:是否直接返回结果工具调用流程:
AgentAction
包含工具名称和参数AgentActionResult
class Agent(ABC):
"""代理抽象基类,定义核心接口"""
@property
@abstractmethod
def input_keys(self) -> List[str]:
"""代理输入键,通常为['input']"""
pass
@property
@abstractmethod
def output_keys(self) -> List[str]:
"""代理输出键,通常为['output']"""
pass
@abstractmethod
def _get_next_action(self, input: str) -> Union[AgentAction, AgentFinish]:
"""生成下一步动作或完成标志"""
pass
@abstractmethod
def _run(self, input: str) -> str:
"""执行代理主逻辑"""
pass
async def arun(self, input: str) -> str:
"""异步执行代理"""
raise NotImplementedError("异步方法需子类实现")
def _construct_scratchpad(
self, intermediate_steps: List[Tuple[AgentAction, str]]
) -> str:
"""构建中间步骤记录,供LLM参考"""
return "\n".join([f"Action: {action.tool}\nAction Input: {action.tool_input}\nObservation: {obs}"
for action, obs in intermediate_steps])
def _format_final_answer(self, answer: str) -> str:
"""格式化最终回答"""
return f"最终回答: {answer}"
AgentExecutor
负责协调代理执行流程:
class AgentExecutor:
"""代理执行器,管理代理运行流程"""
agent: Agent # 代理实例
tools: List[Tool] # 工具列表
verbose: bool = False # 是否打印详细日志
def __init__(self, agent: Agent, tools: List[Tool], verbose: bool = False):
self.agent = agent
self.tools = tools
self.verbose = verbose
def run(self, input: str) -> str:
"""同步执行代理"""
intermediate_steps: List[Tuple[AgentAction, str]] = []
while True:
# 生成下一步动作
action = self.agent._get_next_action(input, intermediate_steps)
if isinstance(action, AgentFinish):
# 代理决定结束,返回最终回答
if self.verbose:
print(f"代理结束: {action.log}")
return action.return_values["output"]
# 调用工具
tool = next((t for t in self.tools if t.name == action.tool), None)
if not tool:
raise ValueError(f"未知工具: {action.tool}")
# 执行工具并记录结果
observation = tool.func(action.tool_input)
intermediate_steps.append((action, observation))
if self.verbose:
print(f"工具调用: {action.tool}({action.tool_input}) -> {observation}")
async def arun(self, input: str) -> str:
"""异步执行代理"""
# 异步版本实现类似,使用asyncio处理工具调用
pass
提示生成是代理决策的关键环节:
class AgentPromptTemplate:
"""代理提示模板,生成LLM输入提示"""
template: str # 提示模板
tools: List[Tool] # 工具列表
input_variables: List[str] = ["input", "intermediate_steps"] # 输入变量
def format(self, **kwargs) -> str:
"""格式化提示模板"""
intermediate_steps = kwargs.pop("intermediate_steps", [])
scratchpad = self._format_scratchpad(intermediate_steps)
kwargs["scratchpad"] = scratchpad
return self.template.format(** kwargs)
def _format_scratchpad(self, intermediate_steps: List[Tuple[AgentAction, str]]) -> str:
"""格式化中间步骤"""
if not intermediate_steps:
return ""
return "\n".join([f"步骤 {i+1}:\nAction: {action.tool}\nAction Input: {action.tool_input}\nObservation: {obs}"
for i, (action, obs) in enumerate(intermediate_steps)])
反应式代理(Reactive Agent)是最基础的代理类型,其特点:
from langchain.agents import ReactiveAgent
class CustomReactiveAgent(ReactiveAgent):
"""自定义反应式代理"""
def _get_next_action(self, input: str, intermediate_steps: List[Tuple[AgentAction, str]]) -> Union[AgentAction, AgentFinish]:
"""生成下一步动作"""
# 构建提示
prompt = self.agent_llm_chain.prompt.format(
input=input,
intermediate_steps=intermediate_steps
)
# 调用LLM生成决策
llm_output = self.agent_llm_chain.llm(prompt)
# 解析LLM输出为动作或完成
return self._parse_llm_output(llm_output)
def _parse_llm_output(self, text: str) -> Union[AgentAction, AgentFinish]:
"""解析LLM输出为动作"""
if "Final Answer:" in text:
# 解析最终回答
answer = text.split("Final Answer:")[-1].strip()
return AgentFinish(
return_values={"output": answer},
log=text
)
# 解析工具调用
try:
action_match = re.search(r"Action: (.*?)[\n]*Action Input:[\s]*(.*)", text, re.DOTALL)
if not action_match:
raise ValueError(f"无法解析动作: {text}")
action = action_match.group(1).strip()
action_input = action_match.group(2).strip()
return AgentAction(
tool=action,
tool_input=action_input,
log=text
)
except Exception as e:
raise ValueError(f"解析动作失败: {e}") from e
规划式代理(Planner Agent)具备多步规划能力:
class CustomPlannerAgent(Agent):
"""自定义规划式代理"""
planner: Planner # 规划器实例
executor: Executor # 执行器实例
def __init__(self, planner: Planner, executor: Executor, tools: List[Tool], **kwargs):
super().__init__(tools=tools, **kwargs)
self.planner = planner
self.executor = executor
def _get_next_action(self, input: str, intermediate_steps: List[Tuple[AgentAction, str]]) -> Union[AgentAction, AgentFinish]:
"""生成下一步动作"""
# 1. 生成任务规划
plan = self.planner.plan(input, intermediate_steps)
# 2. 检查规划是否完成
if plan.is_complete():
return AgentFinish(
return_values={"output": plan.get_final_answer()},
log=str(plan)
)
# 3. 获取下一步执行步骤
next_step = plan.get_next_step()
# 4. 转换为工具调用动作
return AgentAction(
tool=next_step.tool,
tool_input=next_step.input,
log=str(next_step)
)
def _run(self, input: str) -> str:
"""执行代理主逻辑"""
intermediate_steps: List[Tuple[AgentAction, str]] = []
plan = self.planner.plan(input, intermediate_steps)
while not plan.is_complete():
next_step = plan.get_next_step()
action = AgentAction(
tool=next_step.tool,
tool_input=next_step.input,
log=str(next_step)
)
# 执行工具调用
observation = self.executor.execute(action)
intermediate_steps.append((action, observation))
# 更新规划
plan.update(observation)
return plan.get_final_answer()
代理状态管理包括:
class AgentStateStore:
"""代理状态存储"""
def __init__(self, capacity: int = 10):
self.history: List[Dict[str, str]] = [] # 对话历史
self.capacity = capacity # 存储容量
def add_message(self, role: str, content: str) -> None:
"""添加对话消息"""
self.history.append({"role": role, "content": content})
# 保持历史长度不超过容量
if len(self.history) > self.capacity:
self.history = self.history[-self.capacity:]
def get_history(self) -> List[Dict[str, str]]:
"""获取对话历史"""
return self.history
def clear(self) -> None:
"""清除状态"""
self.history = []
def add_tool_call(self, tool: str, input: str, output: str) -> None:
"""添加工具调用记录"""
self.add_message(
"tool",
f"Tool: {tool}, Input: {input}, Output: {output}"
)
class SerializableAgent(Agent):
"""可序列化代理,支持状态保存与恢复"""
def save_state(self) -> Dict[str, Any]:
"""保存代理状态"""
return {
"history": self.state_store.get_history(),
"tools": [t.name for t in self.tools],
"parameters": self._get_parameters()
}
def _get_parameters(self) -> Dict[str, Any]:
"""获取代理参数"""
return {
"verbose": self.verbose,
# 其他参数...
}
@classmethod
def load_state(cls, state: Dict[str, Any], tools: List[Tool]) -> "SerializableAgent":
"""从状态恢复代理"""
agent = cls(tools=tools, **state.get("parameters", {}))
agent.state_store = AgentStateStore()
for msg in state.get("history", []):
agent.state_store.add_message(msg["role"], msg["content"])
return agent
自定义工具需实现Tool
类或继承BaseTool
:
from langchain.tools import BaseTool
class CustomTool(BaseTool):
"""自定义工具示例"""
name: str = "custom_tool" # 工具名称
description: str = "用于执行自定义任务的工具" # 工具描述
def _run(self, input: str) -> str:
"""同步执行工具"""
# 自定义工具逻辑
return f"自定义工具处理结果: {input}"
async def _arun(self, input: str) -> str:
"""异步执行工具"""
# 异步工具逻辑
return f"异步自定义工具处理结果: {input}"
def is_single_input(self) -> bool:
"""是否支持单输入"""
return True
工具注册表实现:
class ToolRegistry:
"""工具注册表"""
registry: Dict[str, Type[BaseTool]] = {} # 工具注册表
@classmethod
def register_tool(cls, name: str, tool_cls: Type[BaseTool]) -> None:
"""注册工具"""
if name in cls.registry:
raise ValueError(f"工具 {name} 已注册")
cls.registry[name] = tool_cls
@classmethod
def get_tool(cls, name: str, **kwargs) -> BaseTool:
"""获取工具实例"""
if name not in cls.registry:
raise ValueError(f"未知工具: {name}")
return cls.registry[name](** kwargs)
@classmethod
def list_tools(cls) -> List[str]:
"""列出所有注册工具"""
return list(cls.registry.keys())
工具调用链用于组合多个工具:
class ToolChain:
"""工具调用链"""
tools: List[BaseTool] # 工具列表
def __init__(self, tools: List[BaseTool]):
self.tools = tools
def run(self, input: str) -> str:
"""按顺序执行工具链"""
result = input
for tool in self.tools:
result = tool.run(result)
return result
async def arun(self, input: str) -> str:
"""异步执行工具链"""
result = input
for tool in self.tools:
result = await tool.arun(result)
return result
代理性能瓶颈主要存在于:
LLM调用优化:
工具调用优化:
状态管理优化:
class AsyncOptimizedAgent(Agent):
"""异步优化代理"""
async def arun(self, input: str) -> str:
"""异步执行代理"""
intermediate_steps: List[Tuple[AgentAction, str]] = []
while True:
# 异步生成下一步动作
action = await self._a_get_next_action(input, intermediate_steps)
if isinstance(action, AgentFinish):
return action.return_values["output"]
# 异步调用工具
tool = next((t for t in self.tools if t.name == action.tool), None)
if not tool:
raise ValueError(f"未知工具: {action.tool}")
# 异步执行工具
observation = await tool.arun(action.tool_input)
intermediate_steps.append((action, observation))
async def _a_get_next_action(self, input: str, intermediate_steps: List[Tuple[AgentAction, str]]) -> Union[AgentAction, AgentFinish]:
"""异步生成下一步动作"""
# 异步构建提示
prompt = self._construct_prompt()
# 异步调用LLM
llm_output = await self.llm.arun(prompt)
# 解析结果
return self._parse_llm_output(llm_output)
代理可能面临的安全风险:
class SecureToolWrapper(BaseTool):
"""安全工具包装器,添加权限控制"""
tool: BaseTool # 被包装的工具
allowed_users: Set[str] = set() # 允许使用的用户
def _run(self, input: str, user: str) -> str:
"""带用户验证的工具执行"""
if user not in self.allowed_users:
raise PermissionError(f"用户 {user} 无权限使用工具 {self.name}")
return self.tool.run(input)
def _arun(self, input: str, user: str) -> str:
"""带用户验证的异步工具执行"""
if user not in self.allowed_users:
raise PermissionError(f"用户 {user} 无权限使用工具 {self.name}")
return self.tool.arun(input)
class ValidatingAgent(Agent):
"""带输入验证的代理"""
input_validator: Callable[[str], bool] # 输入验证函数
def _run(self, input: str) -> str:
"""验证输入后执行代理"""
if not self.input_validator(input):
raise ValueError("输入验证失败")
return super()._run(input)
class FilteringAgent(Agent):
"""带提示过滤的代理"""
bad_words: List[str] = ["attack", "hack", "steal"] # 禁止的关键词
def _construct_prompt(self) -> str:
"""过滤提示中的不良词汇"""
prompt = super()._construct_prompt()
for word in self.bad_words:
prompt = prompt.replace(word, "[filtered]")
return prompt
class SanitizingAgent(Agent):
"""带输出净化的代理"""
def _format_final_answer(self, answer: str) -> str:
"""净化最终回答"""
# 移除敏感信息
answer = re.sub(r"\d{11,}", "[敏感信息已隐藏]", answer)
return super()._format_final_answer(answer)
代理与链的集成方式:
class ChainIntegratedAgent(Agent):
"""集成链的代理"""
chain: Chain # 集成的链
def _get_next_action(self, input: str, intermediate_steps: List[Tuple[AgentAction, str]]) -> Union[AgentAction, AgentFinish]:
"""使用链处理输入"""
# 先通过链处理输入
chain_output = self.chain.run(input)
# 再生成动作
return super()._get_next_action(chain_output, intermediate_steps)
代理与内存的集成:
from langchain.memory import ConversationBufferMemory
class MemoryEnabledAgent(Agent):
"""带内存的代理"""
memory: ConversationBufferMemory # 对话内存
def _get_history(self) -> List[Dict[str, str]]:
"""从内存获取对话历史"""
return self.memory.load_memory_variables({})["history"]
def _save_to_memory(self, input: str, output: str) -> None:
"""保存到内存"""
self.memory.save_context({"input": input}, {"output": output})
def _run(self, input: str) -> str:
"""执行代理并保存到内存"""
result = super()._run(input)
self._save_to_memory(input, result)
return result
代理与索引的集成:
from langchain.indexes import VectorstoreIndexCreator
class IndexAwareAgent(Agent):
"""感知索引的代理"""
index: VectorstoreIndex # 文档索引
def __init__(self, index: VectorstoreIndex, tools: List[Tool], **kwargs):
super().__init__(tools=tools, **kwargs)
self.index = index
def _get_relevant_documents(self, query: str) -> List[Document]:
"""获取相关文档"""
return self.index.similarity_search(query)
def _construct_prompt(self) -> str:
"""构建包含相关文档的提示"""
prompt = super()._construct_prompt()
documents = self._get_relevant_documents(self.input)
if documents:
doc_text = "\n".join([d.page_content for d in documents])
prompt += f"\n相关文档:\n{doc_text}"
return prompt
多代理协作架构:
class MultiAgentSystem:
"""多代理协作系统"""
agents: List[Agent] # 代理列表
communication_channel: Channel # 通信通道
def __init__(self, agents: List[Agent], channel: Channel):
self.agents = agents
self.communication_channel = channel
def run(self, input: str) -> str:
"""多代理协作处理输入"""
# 分配任务给合适的代理
assigned_agent = self._assign_agent(input)
if not assigned_agent:
raise ValueError("无法分配代理")
# 代理间通信
self.communication_channel.clear()
result = assigned_agent.run(input)
# 收集代理间通信记录
return f"最终结果: {result}\n通信记录: {self.communication_channel.get_records()}"
def _assign_agent(self, input: str) -> Optional[Agent]:
"""为输入分配合适的代理"""
# 简单实现:按工具集匹配
for agent in self.agents:
if any(tool.name in input for tool in agent.tools):
return agent
return None
动态工具加载机制:
class DynamicToolAgent(Agent):
"""支持动态加载工具的代理"""
tool_registry: ToolRegistry # 工具注册表
loaded_tools: Dict[str, BaseTool] = {} # 已加载工具
def add_tool(self, tool_name: str, **kwargs) -> None:
"""动态加载工具"""
if tool_name in self.loaded_tools:
return
tool_cls = self.tool_registry.get_tool_class(tool_name)
self.loaded_tools[tool_name] = tool_cls(** kwargs)
self.tools.append(self.loaded_tools[tool_name])
def remove_tool(self, tool_name: str) -> None:
"""卸载工具"""
if tool_name in self.loaded_tools:
self.tools.remove(self.loaded_tools[tool_name])
del self.loaded_tools[tool_name]
def _get_tools(self) -> List[BaseTool]:
"""获取所有工具,包括动态加载的"""
return list(self.loaded_tools.values())
元学习代理实现:
class MetaLearningAgent(Agent):
"""元学习代理,可从历史中学习"""
learning_rate: float = 0.1 # 学习率
model: MetaModel # 元学习模型
def __init__(self, model: MetaModel, tools: List[Tool], **kwargs):
super().__init__(tools=tools, **kwargs)
self.model = model
def _run(self, input: str) -> str:
"""执行代理并更新元模型"""
result = super()._run(input)
# 从历史中学习
self.model.update(self.history, result)
return result
def _get_next_action(self, input: str, intermediate_steps: List[Tuple[AgentAction, str]]) -> Union[AgentAction, AgentFinish]:
"""使用元模型生成动作"""
# 元模型预测最佳动作
predicted_action = self.model.predict(input, intermediate_steps)
if predicted_action:
return predicted_action
return super()._get_next_action(input, intermediate_steps)