LangChain代理的规划与执行循环深度剖析
一、LangChain代理的核心架构概述
1.1 代理的基础组件
LangChain代理的核心功能实现依赖于多个基础组件协同工作,这些组件共同构成了代理运行的基础架构。其核心组件包括大语言模型(LLM)、工具集(Tools)、提示模板(Prompt Template)、规划器(Planner)和执行器(Executor)。
从源码层面来看,LangChain库中代理相关的核心类定义在langchain.agents包下。大语言模型作为代理的“大脑”,负责生成文本内容,其接口定义如下:
from abc import ABC, abstractmethod
class LLM(ABC):
"""大语言模型接口"""
@abstractmethod
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
"""执行模型调用,返回生成的文本"""
pass
def __call__(self, prompt: str, stop: Optional[List[str]] = None) -> str:
return self._call(prompt, stop)
工具集则是代理与外部世界交互的桥梁,每个工具都定义了具体的功能和调用方式。例如,文件读取工具的定义:
class FileReaderTool:
def __init__(self, file_path):
self.file_path = file_path
def run(self):
try:
with open(self.file_path, 'r') as f:
return f.read()
except FileNotFoundError:
return f"文件 {self.file_path} 未找到"
提示模板用于构建向大语言模型发送的提示,通过填充变量生成完整的提示内容:
from langchain import PromptTemplate
prompt = PromptTemplate(
input_variables=["question"],
template="请回答这个问题:{question}"
)
1.2 代理的整体运行流程
LangChain代理的运行可以概括为一个不断循环的规划 - 执行过程。在接收到用户输入后,代理首先进入规划阶段,通过大语言模型分析输入内容,结合工具集的功能描述,制定出解决问题的步骤和方案。随后进入执行阶段,根据规划结果调用相应工具获取信息,并将工具返回的结果作为新的输入,再次进入规划阶段,重复上述过程,直到问题得到解决或达到预设的终止条件。
整个流程的核心控制逻辑在Agent类中实现:
class Agent:
def __init__(self, llm, tools, prompt_template):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
def run(self, input):
while True:
# 规划阶段
plan = self._plan(input)
if self._is_finished(plan):
return self._extract_final_answer(plan)
# 执行阶段
action_result = self._execute(plan.action)
input = self._update_input(input, action_result)
在这个框架下,代理通过不断迭代规划和执行步骤,逐步逼近问题的解决方案 。
二、规划阶段:从输入到行动方案的生成
2.1 输入解析与问题理解
代理接收到用户输入后,首要任务是解析输入内容,理解用户意图和问题本质。这一过程通常依赖大语言模型的自然语言理解能力,通过向模型发送精心设计的提示,引导模型分析输入并提取关键信息。
在源码中,输入解析逻辑通常嵌入在提示构建过程中。例如,一个简单的输入解析提示模板如下:
input_parsing_prompt = PromptTemplate(
input_variables=["user_input"],
template="请分析以下用户输入,提取关键问题和需求:{user_input}"
)
parsed_input = llm(input_parsing_prompt.format(user_input=input))
通过这种方式,大语言模型可以将用户的自然语言输入转化为结构化的问题描述,为后续的规划提供清晰的基础 。
2.2 工具选择与方案制定
在理解问题后,代理需要根据问题类型和需求,从工具集中选择合适的工具,并制定使用这些工具的具体方案。这一过程同样依赖大语言模型的推理能力,模型会根据工具的功能描述和问题特点,生成包含工具调用顺序和参数的行动方案。
工具选择提示模板示例:
tool_selection_prompt = PromptTemplate(
input_variables=["parsed_input", "tool_descriptions"],
template="根据以下问题描述和工具列表,选择解决问题所需的工具,并说明使用顺序:\n问题描述:{parsed_input}\n工具列表:{tool_descriptions}"
)
tool_selection_result = llm(tool_selection_prompt.format(
parsed_input=parsed_input,
tool_descriptions="\n".join([f"{tool.name}: {tool.description}" for tool in tools])
))
根据模型生成的工具选择结果,代理进一步构建详细的行动方案,明确每个工具的调用时机和输入参数。
2.3 规划阶段的关键源码逻辑
规划阶段的核心逻辑在_plan方法中实现,该方法整合了输入解析、工具选择和方案制定等步骤:
class Agent:
def _plan(self, input):
# 解析输入
parsed_input = self._parse_input(input)
# 选择工具
tool_selection = self._select_tools(parsed_input)
# 制定行动方案
action_plan = self._create_action_plan(parsed_input, tool_selection)
return action_plan
def _parse_input(self, input):
prompt = self.input_parsing_prompt.format(user_input=input)
return self.llm(prompt)
def _select_tools(self, parsed_input):
prompt = self.tool_selection_prompt.format(
parsed_input=parsed_input,
tool_descriptions="\n".join([f"{tool.name}: {tool.description}" for tool in self.tools])
)
return self.llm(prompt)
def _create_action_plan(self, parsed_input, tool_selection):
prompt = self.action_plan_prompt.format(
parsed_input=parsed_input,
tool_selection=tool_selection
)
return self.llm(prompt)
在这个过程中,大语言模型通过多次调用,逐步细化和完善行动方案,为执行阶段提供明确的指导 。
三、执行阶段:工具调用与结果处理
3.1 工具调用与执行
进入执行阶段后,代理按照规划阶段生成的行动方案,依次调用选定的工具。每个工具都有其特定的run方法,负责执行具体的功能并返回结果。
以搜索引擎工具为例,其调用过程如下:
class SearchTool:
def __init__(self, api_key):
self.api_key = api_key
def run(self, query):
# 调用搜索引擎API,传入查询参数
response = requests.get(f"https://api.search.com/search?query={query}&api_key={self.api_key}")
if response.status_code == 200:
return response.json()
return f"搜索失败,状态码:{response.status_code}"
# 代理执行工具调用
action_result = search_tool.run(action_plan.action.args["query"])
在工具调用过程中,代理需要处理各种可能的异常情况,如API调用失败、参数错误等,并根据情况调整后续的执行策略 。
3.2 执行结果的处理与整合
工具执行完成后,代理需要对返回的结果进行处理和整合。这包括解析结果数据、提取有用信息,并将其与原始输入结合,形成新的输入,为下一轮规划提供更丰富的信息。
结果处理逻辑示例:
def process_result(result):
if isinstance(result, dict):
# 假设结果是JSON格式,提取关键信息
return result.get("key_info", "未找到关键信息")
return result
new_input = f"{original_input}\n工具执行结果:{process_result(action_result)}"
通过这种方式,代理能够不断积累信息,逐步完善对问题的理解和解决方案 。
3.3 执行阶段的源码实现细节
执行阶段的主要逻辑在_execute和_update_input方法中实现:
class Agent:
def _execute(self, action):
tool = self._get_tool(action.tool_name)
return tool.run(**action.args)
def _get_tool(self, tool_name):
for tool in self.tools:
if tool.name == tool_name:
return tool
raise ValueError(f"工具 {tool_name} 未找到")
def _update_input(self, input, result):
processed_result = self._process_result(result)
return f"{input}\n工具执行结果:{processed_result}"
def _process_result(self, result):
if isinstance(result, dict):
return result.get("key_info", "未找到关键信息")
return result
这些方法确保工具能够正确调用,并将结果有效地整合到代理的运行流程中,推动问题解决的进程 。
四、规划与执行循环的控制机制
4.1 循环终止条件的判断
规划与执行循环需要明确的终止条件,以避免无限循环。常见的终止条件包括:问题得到满意的解答、达到预设的最大循环次数、工具执行结果无法提供更多有效信息等。
在源码中,终止条件的判断逻辑通常在_is_finished方法中实现:
class Agent:
def _is_finished(self, plan):
# 判断是否包含最终答案标识
if "final_answer" in plan:
return True
# 判断是否达到最大循环次数
if self.cycle_count >= self.max_cycles:
return True
# 判断工具执行结果是否有效
if not self._is_result_useful(plan.last_result):
return True
return False
def _is_result_useful(self, result):
# 简单示例:结果为空或包含错误信息则认为无效
if not result or "失败" in result or "错误" in result:
return False
return True
通过这些条件的综合判断,代理能够在合适的时机停止循环,输出最终结果 。
4.2 循环过程中的状态管理
在循环过程中,代理需要管理各种状态信息,包括当前循环次数、已执行的工具列表、中间结果等。这些状态信息有助于代理跟踪执行进度,做出合理的决策。
状态管理相关的属性和方法示例:
class Agent:
def __init__(self, llm, tools, prompt_template, max_cycles=10):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
self.max_cycles = max_cycles
self.cycle_count = 0
self.executed_tools = []
self.intermediate_results = []
def run(self, input):
while True:
plan = self._plan(input)
if self._is_finished(plan):
return self._extract_final_answer(plan)
action_result = self._execute(plan.action)
self.cycle_count += 1
self.executed_tools.append(plan.action.tool_name)
self.intermediate_results.append(action_result)
input = self._update_input(input, action_result)
def _extract_final_answer(self, plan):
# 从规划结果中提取最终答案
return plan.final_answer
通过维护这些状态信息,代理能够在复杂的问题解决过程中保持清晰的执行脉络 。
4.3 异常处理与流程回滚
在循环执行过程中,可能会遇到各种异常情况,如工具调用失败、大语言模型响应错误等。代理需要具备有效的异常处理机制,能够在发生异常时采取适当的措施,如重新规划、切换工具或终止循环。
异常处理逻辑示例:
class Agent:
def _execute(self, action):
try:
tool = self._get_tool(action.tool_name)
return tool.run(**action.args)
except Exception as e:
self.logger.error(f"工具 {action.tool_name} 执行失败:{e}")
# 尝试重新规划
return self._replan(action)
def _replan(self, action):
# 构建重新规划提示
replan_prompt = PromptTemplate(
input_variables=["original_input", "failed_action", "error_message"],
template="工具 {failed_action} 执行失败,错误信息:{error_message}。请根据原始输入 {original_input} 重新规划解决方案"
)
new_plan = self.llm(replan_prompt.format(
original_input=self.original_input,
failed_action=action,
error_message=str(e)
))
return self._execute(new_plan.action)
通过这种异常处理和回滚机制,代理能够在面对不确定性时保持鲁棒性 。
五、提示工程在代理循环中的作用
5.1 提示模板的设计原则
提示模板是引导大语言模型生成正确输出的关键。在代理的规划与执行循环中,不同阶段需要设计不同的提示模板,其设计需遵循清晰性、引导性和约束性原则。
清晰性要求提示内容简洁明了,避免歧义;引导性需要通过提问、示例等方式,引导模型生成符合预期的结果;约束性则通过设定条件和格式要求,限制模型输出的范围。
例如,工具选择提示模板的设计:
tool_selection_prompt = PromptTemplate(
input_variables=["problem_description", "tool_list"],
template="请根据以下问题描述和工具列表,选择解决问题最合适的工具,并按使用顺序列出。问题描述:{problem_description}。工具列表:{tool_list}。要求:仅输出工具名称,以逗号分隔。"
)
该模板通过明确的指令和格式要求,引导模型生成规范的工具选择结果 。
5.2 不同阶段的提示策略
在规划阶段,提示模板侧重于引导模型理解问题、分析需求并制定方案;在执行阶段,提示则用于处理工具结果,引导模型整合信息、优化方案。
规划阶段提示示例:
planning_prompt = PromptTemplate(
input_variables=["user_input"],
template="用户输入:{user_input}。请制定解决该问题的详细步骤,每个步骤可以使用以下工具:{tool_descriptions}。步骤格式:步骤1:使用[工具名称]执行[具体操作]。"
)
执行阶段提示示例:
execution_prompt = PromptTemplate(
input_variables=["previous_plan", "tool_result"],
template="之前的规划:{previous_plan}。工具执行结果:{tool_result}。请根据结果更新规划,补充或调整步骤。"
)
通过针对性的提示策略,代理能够更好地利用大语言模型的能力,提高问题解决的效率和准确性 。
5.3 提示优化与迭代
在实际应用中,提示模板需要不断优化和迭代。通过分析模型输出结果与预期的差异,调整提示内容和结构,逐步提升代理的性能。
提示优化流程示例:
def optimize_prompt(prompt, input, expected_output):
output = llm(prompt.format(input=input))
if output != expected_output:
# 分析差异,调整提示
new_prompt = modify_prompt(prompt, output, expected_output)
return optimize_prompt(new_prompt, input, expected_output)
return prompt
通过这种迭代优化过程,代理能够不断适应不同的问题场景,提高通用性和可靠性 。
六、工具集的管理与扩展
6.1 工具的注册与发现
代理需要对工具集进行有效的管理,包括工具的注册、发现和调用。在源码中,通常通过一个工具注册表来实现这一功能,记录每个工具的名称、功能描述和调用方法。
工具注册与发现逻辑示例:
class ToolRegistry:
def __init__(self):
self.tools = {}
def register_tool(self, tool):
self.tools[tool.name] = tool
def get_tool(self, tool_name):
return self.tools.get(tool_name)
def list_tools(self):
return list(self.tools.values())
# 使用示例
registry = ToolRegistry()
registry.register_tool(SearchTool("api_key"))
registry.register_tool(FileReaderTool("file_path"))
selected_tool = registry.get_tool("SearchTool")
通过工具注册表,代理能够方便地管理和调用各种工具,实现功能的灵活扩展 。
6.2 工具的动态扩展
为了适应不同的应用场景,代理需要支持工具的动态扩展。用户可以在运行时添加新的工具,而无需修改代理的核心代码。
动态添加工具的实现方式:
class DynamicAgent(Agent):
def add_tool(self, tool):
self.tools.append(tool)
# 使用示例
agent = DynamicAgent(llm, [], prompt_template)
new_tool = CustomTool()
agent.add_tool(new_tool)
这种动态扩展机制使得代理能够根据实际需求快速集成新的功能,提高了系统的灵活性和适应性 。
6.3 工具的协同与组合
在复杂问题的解决过程中,单一工具往往无法满足需求,需要多个工具协同工作。代理需要能够合理地组合工具,制定出多步骤的解决方案。
工具协同示例:
# 假设需要先搜索信息,再根据信息读取相关文件
search_result = search_tool.run("相关资料关键词")
file_path = extract_file_path(search_result)
file_content = file_reader_tool.run(file_path)
通过这种方式,代理能够将不同工具的功能有机结合
七、代理的多轮对话与上下文管理
7.1 多轮对话的实现机制
在实际应用场景中,LangChain代理往往需要处理多轮对话,持续理解用户意图并逐步完善解决方案。多轮对话的实现依赖于对每轮交互信息的记录与整合,其核心在于维护对话上下文。从源码层面看,Agent类通常会包含一个用于存储对话历史的属性,例如:
class Agent:
def __init__(self, llm, tools, prompt_template):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
self.conversation_history = [] # 存储对话历史
def run(self, input):
self.conversation_history.append(("user", input)) # 添加用户输入到历史记录
while True:
plan = self._plan(input)
if self._is_finished(plan):
self.conversation_history.append(("agent", plan.final_answer)) # 添加最终答案到历史记录
return plan.final_answer
action_result = self._execute(plan.action)
self.conversation_history.append(("tool", action_result)) # 添加工具执行结果到历史记录
input = self._update_input(input, action_result)
在每一轮交互中,代理将用户输入、工具执行结果以及自身生成的中间内容依次记录到conversation_history中。当构建提示传递给大语言模型时,会将对话历史作为上下文信息一同传入,帮助模型理解对话的前因后果,例如:
def _plan(self, input):
# 构建包含对话历史的提示
context = "\n".join([f"{role}: {msg}" for role, msg in self.conversation_history])
prompt = self.prompt_template.format(context=context, input=input)
return self.llm(prompt)
这种机制使得模型能够基于多轮对话的积累,更准确地把握用户需求,生成连贯、合理的回复 。
7.2 上下文的筛选与精简
随着对话轮数的增加,对话历史会不断增长,若将全部历史信息直接传入模型,可能会超出模型的上下文长度限制,同时引入冗余信息影响效率和效果。因此,需要对上下文进行筛选与精简。常见的策略包括基于时间的筛选,即保留最近的若干轮对话;以及基于相关性的筛选,提取与当前问题最相关的历史内容。
基于时间筛选的实现代码如下:
class Agent:
def __init__(self, llm, tools, prompt_template, max_history_length=10):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
self.conversation_history = []
self.max_history_length = max_history_length
def _filter_history(self):
if len(self.conversation_history) > self.max_history_length:
self.conversation_history = self.conversation_history[-self.max_history_length:]
在每次构建提示前调用_filter_history方法,即可确保对话历史保持在合理长度。基于相关性的筛选则更为复杂,需要通过语义匹配算法计算每条历史记录与当前问题的相关度,例如使用嵌入向量计算余弦相似度,保留相关度高的记录 :
from sentence_transformers import SentenceTransformer
import numpy as np
class Agent:
def __init__(self, llm, tools, prompt_template):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
self.conversation_history = []
self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2') # 初始化嵌入模型
def _filter_history_by_relevance(self, current_input):
current_embedding = self.embedding_model.encode([current_input])[0]
history_embeddings = self.embedding_model.encode([msg for _, msg in self.conversation_history])
cos_scores = np.dot(history_embeddings, current_embedding) / (
np.linalg.norm(history_embeddings, axis=1) * np.linalg.norm(current_embedding)
)
sorted_indices = np.argsort(cos_scores)[::-1] # 按相关度降序排列
filtered_history = [self.conversation_history[i] for i in sorted_indices[:self.max_history_length]]
self.conversation_history = filtered_history
7.3 上下文对规划与执行的影响
上下文信息在代理的规划与执行循环中起着关键作用。在规划阶段,丰富且相关的上下文能帮助大语言模型更准确地理解用户需求,制定出更贴合实际的行动方案。例如,当用户在多轮对话中逐步明确需求细节时,模型可以基于历史对话调整工具选择和步骤规划。
在执行阶段,上下文同样不可或缺。工具执行结果的处理需要结合上下文进行分析,以判断结果是否满足需求,是否需要进一步操作。例如,若用户在之前对话中提到了特定的格式要求,代理在处理工具输出时,就能依据这一上下文信息对结果进行格式转换或内容筛选。同时,上下文还能辅助代理在异常处理时做出更合理的决策,比如当工具执行失败时,根据之前的对话信息判断是重新规划,还是向用户请求更多信息 。
八、代理的错误处理与鲁棒性增强
8.1 常见错误类型与来源
LangChain代理在运行过程中可能遭遇多种错误,其来源主要包括三个方面:大语言模型相关错误、工具调用错误以及代理自身逻辑错误。
大语言模型相关错误可能是由于输入提示格式不正确、超出模型能力范围,或者模型本身的不稳定导致输出异常。例如,当提示中包含模型无法理解的专业术语,或请求的内容违反模型的使用规则时,模型可能返回错误信息或不相关的内容 。
工具调用错误常见于API密钥失效、网络连接问题、参数传递错误等情况。以调用外部API工具为例,如果API服务临时宕机,或者传入的参数类型与API要求不匹配,都会导致工具执行失败 。
代理自身逻辑错误则可能源于规划阶段的错误推理、执行阶段的结果处理不当,或是循环控制条件设置不合理,例如错误地判断循环终止条件,导致代理提前结束或陷入无限循环 。
8.2 错误处理的源码实现
针对不同类型的错误,代理在源码中通过多种机制进行处理。对于大语言模型返回的异常,通常在调用模型的方法中添加异常捕获逻辑,例如:
class Agent:
def _plan(self, input):
try:
context = "\n".join([f"{role}: {msg}" for role, msg in self.conversation_history])
prompt = self.prompt_template.format(context=context, input=input)
return self.llm(prompt)
except Exception as e:
self.logger.error(f"大语言模型调用错误: {e}")
# 尝试使用备用提示或调整输入后重新调用模型
fallback_prompt = self._generate_fallback_prompt(prompt, e)
return self.llm(fallback_prompt)
def _generate_fallback_prompt(self, original_prompt, error):
# 简单示例:在原提示中添加错误信息说明
return f"{original_prompt}\n注意:之前调用出现错误 {error},请重新生成合理方案"
对于工具调用错误,在_execute方法中进行捕获和处理:
class Agent:
def _execute(self, action):
try:
tool = self._get_tool(action.tool_name)
return tool.run(**action.args)
except Exception as e:
self.logger.error(f"工具 {action.tool_name} 执行失败:{e}")
# 尝试重新规划
if self._can_replan(action):
replan_prompt = self._generate_replan_prompt(action, e)
new_plan = self.llm(replan_prompt)
return self._execute(new_plan.action)
else:
# 无法重新规划,向用户返回错误信息
return f"工具执行失败,无法继续处理:{e}"
def _can_replan(self, action):
# 根据具体情况判断是否可以重新规划,例如已执行步骤数、工具类型等
return self.cycle_count < self.max_cycles and action.tool_name in self.replannable_tools
def _generate_replan_prompt(self, action, error):
return f"工具 {action.tool_name} 执行时出错:{error}。请根据当前情况重新规划后续步骤"
8.3 鲁棒性增强策略
为增强代理的鲁棒性,除了基本的错误处理外,还可采用多种策略。引入重试机制是常见手段,当工具调用因网络波动等临时性问题失败时,自动进行多次重试:
from tenacity import retry, stop_after_attempt, wait_exponential
class RetryableTool:
def __init__(self, tool):
self.tool = tool
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def run(self, *args, **kwargs):
return self.tool.run(*args, **kwargs)
# 在代理中使用可重试工具
class RobustAgent(Agent):
def __init__(self, llm, tools, prompt_template):
super().__init__(llm, tools, prompt_template)
self.tools = [RetryableTool(tool) for tool in tools]
此外,建立错误预案库,针对常见错误预先制定处理方案。当错误发生时,代理可根据错误类型从预案库中匹配对应的解决措施,快速恢复执行 。还可以通过强化学习等方式,让代理在大量的错误处理实践中学习优化策略,逐步提升应对复杂错误情况的能力 。
九、代理与外部系统的交互优化
9.1 与API服务的高效交互
LangChain代理常常需要与各类API服务进行交互,如搜索引擎API、翻译API、数据分析API等。为实现高效交互,首先要对API请求进行优化。在源码层面,会对API调用进行封装,添加缓存机制以减少重复请求。例如,对于频繁使用且结果变化较小的API调用,可采用内存缓存:
import functools
def api_cache(func):
cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
key = str((args, kwargs))
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return wrapper
class APITool:
@api_cache
def run(self, api_endpoint, params):
response = requests.get(api_endpoint, params=params)
response.raise_for_status()
return response.json()
同时,合理设置API请求的超时时间和重试策略也至关重要。超时时间过短可能导致正常请求被误判为失败,过长则会影响代理的响应速度;重试策略需综合考虑错误类型和服务特性,避免过度重试对API服务造成压力 。
9.2 数据格式的转换与适配
不同的外部系统和工具返回的数据格式各不相同,代理需要具备数据格式转换与适配的能力。在接收外部数据后,首先要进行格式解析,将其转换为代理内部统一的数据结构。例如,若工具返回的是JSON格式数据,而代理后续处理需要Python字典,可通过以下代码进行转换:
class Agent:
def _process_result(self, result):
if isinstance(result, str):
try:
return json.loads(result) # 将JSON字符串转换为字典
except json.JSONDecodeError:
return result
return result
当需要将代理内部数据传递给外部系统时,同样要进行格式适配,确保数据符合目标系统的要求。比如,将Python列表转换为CSV格式以便导入数据分析工具:
import csv
import io
def list_to_csv(data):
output = io.StringIO()
writer = csv.writer(output)
writer.writerows(data)
return output.getvalue()
通过这些数据格式处理操作,代理能够在不同系统间实现顺畅的数据交互 。
9.3 交互安全性保障
与外部系统交互时,安全性是不容忽视的问题。在API调用中,需妥善管理API密钥等敏感信息,避免泄露。常见的做法是将密钥存储在环境变量中,在代码中通过读取环境变量获取密钥,而不是直接硬编码:
import os
api_key = os.environ.get("API_KEY")
if not api_key:
raise ValueError("API_KEY环境变量未设置")
class APITool:
def run(self, api_endpoint, params):
headers = {"Authorization": f"Bearer {api_key}"}
response = requests.get(api_endpoint, params=params, headers=headers)
response.raise_for_status()
return response.json()
此外,对外部输入数据进行严格的验证和过滤,防止恶意数据注入攻击。例如,在处理用户输入作为API请求参数时,对特殊字符进行转义或限制输入长度:
import re
def sanitize_input(input_str, max_length=100):
input_str = input_str[:max_length] # 限制长度
input_str = re.sub(r'[^\w\s-]', '', input_str) # 去除特殊字符
return input_str
同时,建立安全审计机制,对与外部系统的交互行为进行记录和监控,及时发现异常操作并采取应对措施 。
十、代理的性能优化与调优
10.1 减少大语言模型调用次数
大语言模型的调用通常存在成本和延迟,减少调用次数是提升代理性能的关键。在规划阶段,可以通过合并相似的提示请求,将多个小提示整合为一个大提示进行批量调用。例如,当代理需要多次询问模型关于同一主题的不同细节时,可先收集所有问题,然后构建包含多个子问题的提示:
class Agent:
def _batch_plan(self, inputs):
combined_prompt = "\n".join([self.prompt_template.format(input=input) for input in inputs])
responses = self.llm(combined_prompt)
# 解析批量响应,拆分为单个结果
split_responses = self._split_responses(responses, len(inputs))
return split_responses
def _split_responses(self, combined_response, num_inputs):
# 根据响应格式和输入数量进行拆分,这里假设响应按行分隔
lines = combined_response.strip().split("\n")
return [lines[i:i + 1][0] for i in range(0, len(lines), num_inputs)]
在执行阶段,合理利用工具执行结果,避免不必要的模型调用。例如,当工具返回的结果已经包含足够信息时,直接使用该结果,不再向模型询问重复问题 。
10.2 并行处理与异步操作
引入并行处理和异步操作可以显著提高代理的执行效率。对于相互独立的工具调用或模型请求,可采用并行执行方式。在Python中,利用concurrent.futures库实现线程池或进程池并行:
import concurrent.futures
class Agent:
def _parallel_execute(self, actions):
with concurrent.futures.ThreadPoolExecutor() as executor:
results = list(executor.map(self._execute_single_action, actions))
return results
def _execute_single_action(self, action):
return self._execute(action)
对于涉及I/O操作的任务,如API调用、文件读写,使用异步编程能避免线程阻塞。基于asyncio库实现异步工具调用:
import asyncio
import aiohttp
class AsyncAPITool:
def __init__(self, api_endpoint):
self.api_endpoint = api_endpoint
async def run(self, params):
async with aiohttp.ClientSession() as session:
async with session.get(self.api_endpoint, params=params) as response:
return await response.json()
class AsyncAgent:
async def _async_execute(self, actions):
tasks = [action.tool.run(**action.args) for action in actions]
return await asyncio.gather(*tasks)
通过并行和异步技术,代理能够充分利用系统资源,缩短整体执行时间 。
10.3 性能监控与调优策略
建立性能监控体系是持续优化代理的基础。通过记录大语言模型调用耗时、工具执行时间、循环迭代次数等指标,分析性能瓶颈。在源码中添加监控代码,例如:
import time
class Agent:
def __init__(self, llm, tools, prompt_template):
self.llm = llm
self.tools = tools
self.prompt_template = prompt_template
self.performance_log = [] # 记录性能数据
def run(self, input):
start_time = time.time()
result = super().run(input)
end_time = time.time()
self.performance_log.append({
"input": input,
"total_time": end_time - start_time,
"llm_calls": self.llm_call_count,
"tool_calls": self.tool_call_count
})
return result
根据监控数据,针对性地调整代理参数和策略。如发现大语言模型调用时间过长,可尝试更换模型、优化提示;若工具执行效率低,则检查工具实现或更换更高效的工具 。还可以通过A/B测试等方法,对比不同配置下代理的性能表现,逐步找到最优设置 。
十一、代理的可解释性与透明度提升
11.1 决策过程的记录与展示
为提升代理的可解释性,需要完整记录其
599

被折叠的 条评论
为什么被折叠?



