环境准备与基础工具定义
安装第三方库
bash
pip install openai python-dotenv
配置api密钥
创建.env文件,将任何兼容OpenAI服务的配置记录在该文件中:
text
# .env file
LLM_API_KEY="YOUR-API-KEY"
LLM_MODEL_ID="YOUR-MODEL"
LLM_BASE_URL="YOUR-URL"
封装基础llm调用函数
定义一个专属的LLM客户端类,封装所有与模型服务交互的细节,更易于复用:
Python
import os
import time
from openai import OpenAI,APIConnectionError, RateLimitError
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
class BaseLLM:
"""
为 "Hello Agents" 定制的LLM客户端
用于调用任何兼容OpenAI接口的服务,默认使用流式响应
"""
def __init__(
self,
model: str = None,
api_key: str = None,
base_url: str = None,
timeout: int = None,
):
"""
初始化LLM客户端,优先使用提供的参数,否则从环境变量中获取
"""
self.model = model or os.getenv(LLM_MODEL_ID)
self.api_key = api_key or os.getenv(LLM_API_KEY)
self.base_url = base_url or os.getenv(LLM_API_BASE)
self.timeout = timeout or os.getenv(LLM_API_TIMEOUT)
if not all([self.api_key, self.base_url, self.model]):
missing = [k for k, v in {"API Key": self.api_key, "Base URL": self.base_url,
"Model": self.model}.items() if not v]
raise ValueError(f"缺少以下配置: {', '.join(missing)}")
self.client = OpenAI(
api_key=self.api_key,
base_url=self.base_url,
timeout=self.timeout,
)
@retry(
# 当函数抛出特定异常(APIConnectionError, RateLimitError)时才进行重试
retry=retry_if_exception_type((APIConnectionError, RateLimitError)),
# 最多尝试3次(包括第一次原始调用),如果3次都失败,就不再重试,抛出异常
stop=stop_after_attempt(3),
# 定义重试之间的等待时间,初始3秒,每次增加1倍,最多10秒
wait=wait_exponential(multiplier=1, max=10)
)
def think(
self,
messages: list[dict[str, str]],
temperature: float = 0.7,
) -> str:
"""
调用LLM模型思考,返回思考结果
"""
print(f"正在调用模型 {self.model} 进行思考......")
try:
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
)
# 处理流式响应
collected_content = ""
for chunk in response:
# 防御性编程,确保chunk.choices[0].delta.content存在
content = chunk.choices[0].delta.content or ""
# 只有有内容才打印
if content:
print(content, flush=True, end="")
collected_content += content
# 换行
print("\n思考完成")
return collected_content
except Exception as e:
print(f"调用模型 {self.model} API 发生错误: {str(e)}")
raise e
class DeepSeekLLM(BaseLLM):
"""
为 "DeepSeek" 定制的LLM客户端
用于调用DeepSeek的API,默认使用流式响应
"""
def __init__(
self,
model: str = None,
timeout: int = None,
):
"""
初始化DeepSeekLLM客户端,优先使用提供的参数,否则从环境变量中获取
"""
super().__init__(
model="deepseek-chat", # 默认使用DeepSeek-V3.2非思考模式,思考模式用deepseek-reasoner
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com/v1",
timeout=timeout,
)
# 客户端使用示例
if __name__ == "__main__":
try:
llm_client = DeepSeekLLM(
model="deepseek-chat",
timeout=60,
)
messages = [
{"role": "system", "content": "你是一个专业的python编程助手"},
{"role": "user", "content": "写一个很快速排序算法"},
]
result = llm_client.think(messages)
if result:
print(result)
else:
print("模型返回空结果")
except Exception as e:
print(f"客户端调用发生错误: {str(e)}")

ReAct
ReAct核心思想是模仿人类解决问题的方式,将推理 (Reasoning) 与行动 (Acting) 显式地结合起来,形成一个 "思考-行动-观察" 的循环。
ReAct 工作流程
ReAct范式每一步的输出都遵循一个固定的轨迹:
- Thought (思考): Agent分析当前情况、分解任务、制定下一步计划,或者反思上一步的结果。
- Action (行动): 这是智能体决定采取的具体动作,通常是调用一个外部工具。
- Observation (观察): 这是执行
Action后从外部工具返回的结果。
智能体将不断重复这个 Thought -> Action -> Observation 的循环,将新的观察结果追加到历史记录中,形成一个不断增长的上下文,直到它在Thought中认为已经找到了最终答案,然后输出结果。
<math xmlns="http://www.w3.org/1998/Math/MathML"> t t </math>t表示时间步, <math xmlns="http://www.w3.org/1998/Math/MathML"> π \pi </math>π代表大语言模型, <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q表示初始问题, <math xmlns="http://www.w3.org/1998/Math/MathML"> ( a k , o k ) (a_k, o_k) </math>(ak,ok)表示第 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k轮的行动-观察:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ( t h t , a t ) = π ( q , ( q 1 , o 1 ) , . . . , ( a t − 1 , o t − 1 ) ) (th_t, a_t) = \pi(q, (q_1, o_1), ..., (a_{t-1}, o_{t-1})) </math>(tht,at)=π(q,(q1,o1),...,(at−1,ot−1))
大语言模型 <math xmlns="http://www.w3.org/1998/Math/MathML"> π \pi </math>π或根据初始问题 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q和之前所有步骤的"行动-观察"历史轨迹 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( ( a 1 , o 1 ) , . . . , ( a t − 1 , o t − 1 ) ) ((a_1,o_1),...,(a_{t−1},o_{t−1})) </math>((a1,o1),...,(at−1,ot−1)),生成当前的思考 <math xmlns="http://www.w3.org/1998/Math/MathML"> t h k th_k </math>thk和行动 <math xmlns="http://www.w3.org/1998/Math/MathML"> a t a_t </math>at。
随后,环境中的工具 <math xmlns="http://www.w3.org/1998/Math/MathML"> T T </math>T会执行行动 <math xmlns="http://www.w3.org/1998/Math/MathML"> a t a_t </math>at,并得到一个新的观察结果 <math xmlns="http://www.w3.org/1998/Math/MathML"> o t o_t </math>ot:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> o t = T ( a t ) o_t = T(a_t) </math>ot=T(at)
这个循环不断进行,将新的 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( a t , o t ) (a_t, o_t) </math>(at,ot) 对追加到历史中,直到模型在思考 <math xmlns="http://www.w3.org/1998/Math/MathML"> t h t th_t </math>tht 中判断任务已完成。

ReAct机制特别适用于以下场景:
- 需要外部知识的任务:如查询实时信息(天气、新闻、股价)、搜索专业领域的知识等。
- 需要精确计算的任务:将数学问题交给计算器工具,避免LLM的计算错误。
- 需要与API交互的任务:如操作数据库、调用某个服务的API来完成特定功能。
工具的定义与实现
工具 (Tools) 就是大语言模型与外部世界交互的"手和脚"。为了让ReAct范式能够真正解决我们设定的问题,智能体需要具备调用外部工具的能力。
本节设定的目标------回答关于"华为最新手机"的问题,我们需要为智能体提供一个网页搜索工具。
安装和配置
SerpApi通过API提供结构化的Google搜索结果。
安装:
Python
pip install google-search-results
在SerpApi官网 注册一个免费账户,获取API密钥,并存储在.env文件中。
text#
SERPAPI_API_KEY="YOUR_SERPAPI_API_KEY"
搜索工具的核心逻辑
一个良好定义的工具应包含以下三个核心要素:
- 名称 (Name) : 一个简洁、唯一的标识符,供智能体在
Action中调用,例如Search。 - 描述 (Description) : 一段清晰的自然语言描述,说明这个工具的用途。这是整个机制中最关键的部分,因为大语言模型会依赖这段描述来判断何时使用哪个工具。
- 执行逻辑 (Execution Logic) : 真正执行任务的函数或方法。
首先定义一个search函数作为工具,它接收一个字符串,返回搜索结果:
Python
import os
from serpapi import SerpApiClient
from dotenv import load_dotenv
load_dotenv()
def search(query: str) -> str:
"""
使用 SerpApi 执行 Google 实时搜索,并返回智能解析后的最佳答案。
需要获取训练数据之外的最新知识、实时新闻、具体数据或核实事实时,请调用此工具。
该函数会优先提取结构化数据(如答案框、知识图谱),若没有直接答案,
则返回前三个自然搜索结果的摘要。适合作为 AI Agent 的网页检索工具。
Args:
query (str): 用户想要搜索的关键词或自然语言问题,为了获得最佳结果,请将其构造为简洁明确的搜索词组或自然语言问题。例如 "Python 3.13 新特性"。
Returns:
str: 搜索结果的文本描述。成功时返回答案或摘要;失败时返回以 "错误:" 开头的说明。
Raises:
本函数内部捕获所有异常并转化为字符串返回,不会向调用方抛出异常。
"""
print(f"正在执行 SerpApi 搜索: {query}")
try:
api_key = os.getenv("SERPAPI_API_KEY")
if not api_key:
return "错误:SerpApi API Key 未配置"
params = {
"api_key": api_key,
"q": query,
"engine": "google",
"gl": "cn", # 国家代码
"hl": "zh-CN", # 语言代码
"num": 10,
"start": 0,
"safe": "off",
}
client = SerpApiClient(params)
results = client.get_dict()
from pprint import pprint
pprint(results.get("answer_box"))
pprint(results.get("knowledge_graph"))
# 智能解析:优先寻找最直接的答案
if "answer_box_list" in results:
return "\n".join(results["answer_box_list"])
if "answer_box" in results and "answer" in results["answer_box"]:
return results["answer_box"]["answer"]
if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
return results["knowledge_graph"]["description"]
if "organic_results" in results and results["organic_results"]:
# 如果没有直接答案,则返回前三个有机结果的摘要
snippets = [
f"[{i+1}] {res.get('title', '')}\n{res.get('snippet', '')}"
for i, res in enumerate(results["organic_results"][:3])
]
return "\n\n".join(snippets)
return f"对不起,没有找到关于 '{query}' 的信息。"
except Exception as e:
return f"错误:搜索时发生问题 {str(e)}"
通用的工具执行器
创建一个 ToolExecutor 类,统一的管理器来注册和调度Agent需要使用的多种工具。
Python
from tools.search_tool import search
from typing import Any, Callable
class ToolExecutor:
"""
一个执行工具的类。
用于执行指定的工具函数,返回执行结果。
"""
def __init__(self):
self.tools: dict[str, dict[str, Any]] = {}
def register_tool(
self,
tool_name: str,
tool_description: str,
func: Callable,
):
"""
注册一个新的工具
"""
if tool_name in self.tools:
print(f"警告:工具{tool_name}已存在,将被覆盖。")
self.tools[tool_name] = {
"description": tool_description,
"func": func
}
def get_available_tools(self) -> str:
"""
注册一个新工具。若工具名已存在,覆盖旧工具并给出警告。
"""
return "\n".join([f"- {name}: {tool['description']}" for name, tool in self.tools.items()])
def get_tool(
self,
tool_name: str,
) -> Callable:
"""
根据工具名称获取一个工具的执行函数。
"""
tool_info = self.tools.get(tool_name)
if tool_info is None:
raise ValueError(f"工具 {tool_name} 不存在")
func = tool_info.get("func")
if func is None:
raise ValueError(f"工具 {tool_name} 缺少可执行的函数")
return func
进行测试:
python
if __name__ == "__main__":
# 初始化工具执行器
tool_executor = ToolExecutor()
# 注册搜索工具
tool_description = "一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。"
tool_executor.register_tool(tool_name="Search", tool_description=tool_description, func=search)
# 输出可用的工具
print("\n-------已注册的工具列表-------")
print(tool_executor.get_available_tools())
# 使用搜索工具回答一个实时性问题
print("\n------执行 Action:Search['英伟达最新的GPU型号是什么']--------")
tool_name = "Search"
query = "英伟达最新的GPU型号是什么"
tool_func = tool_executor.get_tool(tool_name=tool_name)
if tool_func:
observation = tool_func(query)
print("\n--------观察(Observation)---------")
print(observation)
else:
print(f"错误:未找到名为{tool_name}的工具")
测试结果:

ReAct Agent 的编码实现
将工具执行器和LLM客户端组装起来,构建一个 ReActAgent 类封装 ReAct 智能体的核心逻辑。
我们将这个类的实现过程拆分为以下几个关键部分:
系统提示词设计
提示词为大语言模型提供了行动的操作指令,将动态地插入可用工具、用户问题以及中间步骤的交互历史。
Python
REACT_PROMPT_TEMPLATE =
"""
你是一个自动化代理,负责用工具回答问题。你只能按照指定格式输出,禁止输出任何其他内容。
可用工具:
{tools}
示例:
Question: 今天天气如何?
Thought: 需要搜索天气信息。
Action: Search[今天天气]
Observation: 晴,25°C
Thought: 已获得天气数据,可以回答。
Action: Finish[今天天气晴,气温25°C。]
---真实任务---
Question: {question}
历史记录:
{history}
现在按照示例格式输出。禁止输出 Thought: 和 Action: 之外的任何内容。
"""
提示词模板定义了智能体与llm之间的交互规划:
- 角色定义: "你是一个xxxxx",设定了LLM的角色。
- 工具清单 (
{tools}) : 告知LLM它有哪些可用的工具。 - 格式规约 (
Thought/Action) : 这是最重要的部分,它强制LLM的输出具有结构性,使我们能通过代码精确解析其意图。 - 动态上下文 (
{question}/{history}) : 将用户的原始问题和不断累积的交互历史注入,让LLM基于完整的上下文进行决策。
增强提示词的格式约束
想要让提示词起到更强的约束作用,要遵循以下策略:
- 使用few-shot:给模型看几个完整的示例,比纯文字效果要好。
- 把要求放在结尾:模型对提示词开头和结尾内容的权重更高。
- 使用命令式语气:直接给指令,去掉客套话。
核心循环的实现
ReActAgent的核心是一个"格式化提示词 -> 调用llm -> 执行动作 -> 整合结果"的循环,直到任务完成,或者达到最大的步数限制。
Python
@dataclass
class AgentAction:
tool_name: str | None = None
tool_input: str | None = None
finish: str | None = None
@dataclass
class LLMOutput:
thought : str | None = None
action : str | None = None
class Operation(Enum):
ACTION = "Action"
OBSERVATION = "Observation"
@dataclass
class AgentMessage:
operation: Operation
content: str
class ReActAgent:
def __init__(
self,
llm_client: DeepSeekLLM,
tool_executor: ToolExecutor,
max_steps: int = 5,
):
self.llm_client = llm_client
self.tool_executor = tool_executor
self.max_steps = max_steps
self.history = []
def reset(self):
self.history = []
def _parse_output(self, text: str) -> LLMOutput:
"""
解析LLM的输出,提取Thought和Action。
"""
# Thought: 匹配到 Action: 或文本末尾
thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|$)", text, re.DOTALL)
# Action: 匹配到文本末尾
action_match = re.search(r"Action:\s*(.*?)$", text, re.DOTALL)
thought = thought_match.group(1).strip() if thought_match else None
action = action_match.group(1).strip() if action_match else None
return LLMOutput(thought, action)
def _parse_action(self, action_text: str) -> AgentAction:
"""解析Action字符串,返回AgentAction实例。"""
match = re.match(r"(\w+)\[(.*)\]$", action_text, re.DOTALL)
if not match:
return AgentAction() # 三个字段都是 None,调用方据此判断解析失败
name = match.group(1)
content = match.group(2)
if name == "Finish":
return AgentAction(finish=content)
else:
return AgentAction(tool_name=name, tool_input=content)
def get_prompt(self, question: str) -> str:
tools_desc = self.tool_executor.get_available_tools()
history_str = "\n".join([f"{agent_message.operation}: {agent_message.content}" for agent_message in self.history])
prompt = REACT_PROMPT_TEMPLATE.format(
tools=tools_desc,
question=question,
history=history_str,
)
return prompt
def think(self, prompt: str) -> str:
messages = [
{
"role": "user",
"content": prompt,
}
]
return self.llm_client.think(messages=messages)
def run(self, question: str) -> str | None:
"""
运行一个ReAct智能体来回答问题
"""
# 每次运行都重置历史记录
self.reset()
# 使用for...else语法,else 在 for 自然结束之后执行
for step in range(self.max_steps):
logger.info(f"-----------------第{step+1}步-----------------")
# 1. 格式化提示词
prompt = self.get_prompt(question)
# 2. 调用llm进行思考
response = self.think(prompt)
if not response:
logger.error(f"错误:LLM未能返回有效响应")
break
# 3. 解析LLM输出
llm_output = self._parse_output(response)
if llm_output.thought is not None:
logger.info(f"思考:{llm_output.thought}")
if not llm_output.action:
logger.error(f"错误:未能解析出有效的Action。流程终止。")
break
# 4. 执行Action
parsed_action = self._parse_action(llm_output.action)
if parsed_action.finish:
logger.info(f"最终答案:{parsed_action.finish}")
return parsed_action.finish
if not parsed_action.tool_name or not parsed_action.tool_input:
logger.error(f"错误:工具名称{parsed_action.tool_name}或工具输入{parsed_action.tool_input}为空")
break
logger.info(f"行动:{parsed_action.tool_name}[{parsed_action.tool_input}]")
tool_func = self.tool_executor.get_tool(parsed_action.tool_name)
if not tool_func:
observation = f"错误:未找到名为{parsed_action.tool_name}的工具"
else:
# 调用工具
observation = tool_func(parsed_action.tool_input)
# 5. 整合观察结果
logger.info(f"观察:{observation}")
# 将本轮的action和observation添加到历史记录中
self.history.append(AgentMessage(operation=Operation.ACTION, content=f"{parsed_action.tool_name}[{parsed_action.tool_input}]"))
self.history.append(AgentMessage(operation=Operation.OBSERVATION, content=observation))
else:
logger.error("错误:达到最大步数。终止流程。")
return None
测试ReActAgent
Python
from utils.logger import setup_logging
from agent.react_agent import ReActAgent
from tools.tool_executor import ToolExecutor
from tools.search_tool import search
from client.client import DeepSeekLLM
setup_logging()
llm_client = DeepSeekLLM(
model="deepseek-chat",
timeout=60,
)
tool_executor = ToolExecutor()
tool_executor.register_tool(
tool_name="Search",
tool_description="一个网页搜索引擎。当你需要回答关于时事、事实以及在你的知识库中找不到的信息时,应使用此工具。",
func=search
)
react_agent = ReActAgent(llm_client, tool_executor)
question = "介绍华为最新发布的手机"
try:
answer = react_agent.run(question)
print(f"最终结论:{answer}")
except Exception as e:
print(f"运行智能体时发生错误: {str(e)}")
运行结果:
Text
INFO - agent.react_agent - -----------------第1步-----------------
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.react_agent - 思考:需要搜索华为最新发布的手机信息。
INFO - agent.react_agent - 行动:Search[华为最新发布的手机]
INFO - tools.search_tool - 正在执行 SerpApi 搜索: 华为最新发布的手机
INFO - agent.react_agent - 观察:[1] 华为手机- 华为官网
探索并选购华为最新手机,了解Mate 系列、Pura 系列、Pocket 系列、nova 系列、畅享系列及相关配件,体验鸿蒙AI 、影像、通信等功能。
[2] 华为发布会- 华为官网
2025.11.25华为Mate 80 系列| Mate X7 及全场景新品发布会华为全新发布Mate 80 系列、Mate X7、MatePad Edge、WATCH Ultimate 2、华为智慧屏MateTV Max、华为路由X3 Pro ...
[3] 华为新品发布会
Pura 80 Ultra. 一英寸超高动态主摄. 超大底双长焦 · ¥7999. ¥9999 ; Pura 80 Pro+. 一英寸高动态主摄. 个性色卡AI 辅助构图 · ¥6499. ¥7999 ; Pura 80. 丝绒直屏. 红枫原色 ...
INFO - agent.react_agent - -----------------第2步-----------------
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.react_agent - 思考:根据搜索结果,华为最新发布的手机包括Mate 80系列、Mate X7折叠屏以及Pura 80系列(如Pura 80 Ultra、Pura 80 Pro+、Pura 80),具备鸿蒙AI、影像升级等特点。
INFO - agent.react_agent - 最终答案:华为最新发布的手机涵盖多个系列:Mate 80系列主打旗舰性能与AI;Mate X7是折叠屏旗舰;Pura 80系列包括Pura 80 Ultra(一英寸超高动态主摄,¥7999起)、Pura 80 Pro+(一英寸高动态主摄,¥6499起)和Pura 80(丝绒直屏,红枫原色),均支持鸿蒙AI与影像创新。
最终结论:华为最新发布的手机涵盖多个系列:Mate 80系列主打旗舰性能与AI;Mate X7是折叠屏旗舰;Pura 80系列包括Pura 80 Ultra(一英寸超高动态主摄,¥7999起)、Pura 80 Pro+(一英寸高动态主摄,¥6499起)和Pura 80(丝绒直屏,红枫原色),均支持鸿蒙AI与影像创新。
Plan-and-Solve
Plan-and-solve范式将任务处理明确分为两个阶段:先规划,后执行。
工作原理
Plan-and-solve将工作流程分成两个核心阶段:
1.规划阶段(planning) :Agent接受用户的问题,先调用llm将问题分解,制定一个清晰、分步骤的行动计划。 2. 执行阶段(solving) :在获得完整计划后,Agent严格按照计划中的步骤,逐一执行。每一步执行可能是调用llm,或者对上一步的结果进行处理,直到计划中的步骤都执行完,得到最终答案。
先思考再执行的策略,让Agent在处理需要长远规划的复杂任务时,能保持更高的目标一致性。
形式化表达
首先,规划模型 <math xmlns="http://www.w3.org/1998/Math/MathML"> π p l a n \pi_{plan} </math>πplan根据原始问题 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q生成一个包含 <math xmlns="http://www.w3.org/1998/Math/MathML"> n n </math>n个步骤的计划 <math xmlns="http://www.w3.org/1998/Math/MathML"> P = ( p 1 , p 2 , . . . , p n ) P = (p_1, p_2, ..., p_n) </math>P=(p1,p2,...,pn):
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> P = π p l a n ( q ) P = \pi_{plan}(q) </math>P=πplan(q)
随后,在执行阶段,执行模型 <math xmlns="http://www.w3.org/1998/Math/MathML"> π s o l v e \pi_{solve} </math>πsolve会逐一完成计划中的步骤。对于第 <math xmlns="http://www.w3.org/1998/Math/MathML"> i i </math>i个步骤,其执行结果 <math xmlns="http://www.w3.org/1998/Math/MathML"> s i s_i </math>si的生成会依赖原始问题 <math xmlns="http://www.w3.org/1998/Math/MathML"> q q </math>q、完整计划 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P,以及之前所有步骤的执行结果 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( s 1 , s 2 , . . . , s i − 1 ) (s_1, s_2, ..., s_{i-1}) </math>(s1,s2,...,si−1)。
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> s i = π s o l v e ( q , P , ( s 1 , s 2 , . . . , s i − 1 ) ) s_i = \pi_{solve}(q, P, (s_1, s_2, ..., s_{i-1})) </math>si=πsolve(q,P,(s1,s2,...,si−1))
最终答案就是最后一个步骤的执行结果 <math xmlns="http://www.w3.org/1998/Math/MathML"> s n s_n </math>sn。

Plan-and-Solve 尤其适用于那些结构性强、可以被清晰分解的复杂任务,例如:
- 多步数学应用题:需要先列出计算步骤,再逐一求解。
- 需要整合多个信息源的报告撰写:需要先规划好报告结构(引言、数据来源A、数据来源B、总结),再逐一填充内容。
- 代码生成任务:需要先构思好函数、类和模块的结构,再逐一实现。
规划阶段
我们的目标问题是: "一个水果店周一卖出了15个苹果。周二卖出的苹果数量是周一的两倍。周三卖出的数量比周二少了5个。请问这三天总共卖出了多少个苹果?"
这类任务的特点是,答案无法通过单次查询或计算得出,必须先将问题分解为一系列逻辑连贯的子步骤,然后按顺序求解。
规划阶段的目标是让大语言模型接收原始问题,并输出一个清晰、分步骤的行动计划。这个计划必须是结构化的,以便我们的代码可以轻松解析并逐一执行。
计划提示词
我们设计的提示词需要明确地告诉模型它的角色和任务,并给出一个输出格式的范例:
text
PLANNER_PROMPT_TEMPLATE = """
你是一个顶级的AI规划专家。你的任务是将用户提出的复杂问题分解成一个由多个简单步骤组成的行动计划。
请确保计划中的每个步骤都是一个独立的、可执行的子任务,并且严格按照逻辑顺序排列。
你的输出必须是一个Python列表,其中每个元素都是一个描述子任务的字符串。
问题: {question}
请严格按照以下格式输出你的计划,```python与```作为前后缀是必要的:
```python["步骤1", "步骤2", "步骤3", ...]```
"""
这个提示词通过以下几点确保了输出的质量和稳定性:
- 角色设定: "顶级的AI规划专家",激发模型的专业能力。
- 任务描述: 清晰地定义了"分解问题"的目标。
- 格式约束: 强制要求输出为一个 Python 列表格式的字符串,这极大地简化了后续代码的解析工作,使其比解析自然语言更稳定、更可靠。
规划器
将这个提示词逻辑封装成一个 Planner 类,这个类也是我们的规划器。
Python
class Planner:
def __init__(
self,
llm_client: BaseLLM,
):
self.llm_client = llm_client
# 解析LLM输出的计划
def _parse_plan(self, response: str) -> list[str]:
if not response:
logger.error("LLM 未返回有效响应")
return []
try:
plan_str = response.split("```python")[1].split("```")[0].strip()
plan = ast.literal_eval(plan_str)
if not isinstance(plan, list):
logger.error(f"计划不是列表格式: {plan}")
return []
logger.info(f"计划生成完成: {plan}")
return plan
except (IndexError, SyntaxError, ValueError) as e:
logger.error(f"计划解析失败: {e}")
return []
def plan(self, question: str) -> list:
"""
根据用户问题生成一个行动计划。
"""
prompt = PLANNER_PROMPT_TEMPLATE.format(question=question)
message = [
{
"role": "user",
"content": prompt
},
]
logger.info("------------正在生成计划------------")
response = self.llm_client.think(messages=message)
return self._parse_plan(response)
执行阶段
在规划器 (Planner) 生成了清晰的行动蓝图后,我们就需要一个执行器 (Executor) 来逐一完成计划中的任务。执行器不仅负责调用大语言模型来解决每个子问题,还承担着一个至关重要的角色:状态管理。它必须记录每一步的执行结果,并将其作为上下文提供给后续步骤,确保信息在整个任务链条中顺畅流动
执行提示词
执行器的提示词,是在已有上下文的基础上,专注解决当前这一个步骤。
Python
EXECUTOR_PROMPT_TEMPLATE = """
你是一位顶级的AI执行专家。你的任务是严格按照给定的计划,一步步地解决问题。
你将收到原始问题、完整的计划、以及到目前为止已经完成的步骤和结果。
请你专注于解决"当前步骤",并仅输出该步骤的最终答案,不要输出任何额外的解释或对话。
# 原始问题:
{question}
# 完整计划:
{plan}
# 历史步骤与结果:
{history}
# 当前步骤:
{current_step}
请仅输出针对"当前步骤"的回答:
"""
提示词需要包含以下关键信息:
- 原始问题: 确保模型始终了解最终目标。
- 完整计划: 让模型了解当前步骤在整个任务中的位置。
- 历史步骤与结果: 提供至今为止已经完成的工作,作为当前步骤的直接输入。
- 当前步骤: 明确指示模型现在需要解决哪一个具体任务。
执行器
将执行逻辑封装到 Executor 类中。这个类将循环遍历计划,调用 LLM,并维护一个历史记录(状态)。
Python
@dataclass
class AgentMessage:
idx: int
step: str
result: str
class Executor:
def __init__(
self,
llm_client: BaseLLM,
):
self.llm_client = llm_client
# 用于储存历史步骤和结果
self.history = []
def reset(self):
self.history = []
def get_prompt(self, question: str, current_step: str, plan: list[str]) -> str:
prompt = EXECUTOR_PROMPT_TEMPLATE.format(
question=question,
plan=plan,
current_step=current_step,
history="\n".join([f"步骤{message.idx}: {message.step}\n结果:{message.result}" for message in self.history])
if self.history else "",
)
return prompt
def think(
self,
prompt: str
) -> str:
messages = [
{
"role": "user",
"content": prompt
}
]
return self.llm_client.think(messages=messages)
def execute(
self,
question: str,
plan: list[str],
) -> str:
"""
根据计划,逐步执行后并解决问题
"""
logger.info("------------正在执行计划------------")
for idx, step in enumerate(plan):
logger.info(f"\n正在执行步骤{idx}")
prompt = self.get_prompt(
question=question,
plan=plan,
current_step=step,
)
result = self.think(prompt)
agent_message = AgentMessage(idx=idx, step=step, result=result)
self.history.append(agent_message)
logger.info(f"步骤{agent_message.idx}已完成,结果:{agent_message.result}")
# 计划最后一步的结果,就是最终的答案
return self.history[-1].result
构建统一的Agent
将规划器和执行器组合在一集,赋予它解决问题的完整能力。
Python
class PlanAndSolveAgent:
def __init__(
self,
llm_client: BaseLLM,
):
"""
初始化智能体,同时创建规划器和执行器实例
"""
self.llm_client = llm_client
self.planner = Planner(self.llm_client)
self.executor = Executor(self.llm_client)
def run(self, question: str):
"""
智能体的完整流程:先规划,后执行
"""
logger.info("--------------开始处理问题--------------")
# 0. 重置执行器历史
self.executor.reset()
# 1. 调用规划器,生成计划
plan = self.planner.plan(question)
if not plan:
logger.error("错误:生成计划失败")
return
# 2. 调用执行器,执行计划
final_answer = self.executor.execute(question, plan)
logger.info(f"问题处理完成,最终结果:{final_answer}")
return final_answer
PlanAndSolveAgent 类的设计体现了"组合优于继承"的原则。它本身不包含复杂的逻辑,而是作为一个协调者 (Orchestrator),清晰地调用其内部组件来完成任务。
测试PlanAndSolveAgent
Python
from utils.logger import setup_logging
from agent.plan_and_solve import PlanAndSolveAgent
from client.client import DeepSeekLLM
setup_logging()
llm_client = DeepSeekLLM(
model="deepseek-chat",
timeout=60,
)
agent = PlanAndSolveAgent(llm_client)
question = "一个水果店周一卖出了15个苹果。周二卖出的苹果数量是周一的两倍。周三卖出的数量比周二少了5个。请问这三天总共卖出了多少个苹果?"
try:
answer = agent.run(question)
print(f"最终结论:{answer}")
except Exception as e:
print(f"运行智能体时发生错误: {str(e)}")
运行结果:
Text
INFO - agent.plan_and_solve - --------------开始处理问题--------------
INFO - agent.plan_and_solve - ------------正在生成计划------------
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.plan_and_solve - 计划生成完成: ['确定周一卖出的苹果数量为15个', '计算周二卖出的数量:周一的两倍,即15 * 2', '计算周三卖出的数量:周二的数量减去5个', '将周一、周二、周三的数量相加,得到三天总销量']
INFO - agent.plan_and_solve - ------------正在执行计划------------
INFO - agent.plan_and_solve -
正在执行步骤0
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.plan_and_solve - 步骤0已完成,结果:15个
INFO - agent.plan_and_solve -
正在执行步骤1
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.plan_and_solve - 步骤1已完成,结果:30个
INFO - agent.plan_and_solve -
正在执行步骤2
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.plan_and_solve - 步骤2已完成,结果:25个
INFO - agent.plan_and_solve -
正在执行步骤3
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
最终结论:70个
INFO - client.client -
llm思考完成
INFO - agent.plan_and_solve - 步骤3已完成,结果:70个
INFO - agent.plan_and_solve - 问题处理完成,最终结果:70个
进程已结束,退出代码为 0
Reflection
在ReAct和Plan-and-Solve模式中,Agent会在完成任务后,结束工作流程。 Reflection模式引入了时候的自我校正循环,可以审视自己工作的不足,进行优化迭代。
Reflection 工作流程
Reflection 核心工作流程可以概括为三个步骤的训话:执行 -> 反思 -> 优化。
执行(Execute)
首先,智能体使用 ReAct或Plan-and-Solve等方法,尝试完成任务。生成一个初步的解决方案
反思(Reflection)
然后,智能机进入反思阶段。他会调用一个独立的、带有特殊提示词的llm,扮演评审员的角色。评审员会从多个维度对初步解决方案进行评估:
- 事实性错误:存在与常识相悖的内容。
- 逻辑漏洞:推理过程存在漏洞
- 效率问题:是否有更直接、高效的处理办法。
- 遗漏信息:是否忽略了关键约束或者其他内容
根据评估,评审员会生成一段结构化的反馈(Feedback),指出问题,并提出改进意见。
优化(Refinement)
最后,智能体将初步解决方案和反馈作为新的上下文,再次调用llm,让它根据反馈内容对初步结局方案进行修改,生成一个更完善的解决方案。
形式化表示
循环可以重复进行很多次,直到反思阶段不再发现新的问题,或者到达预设的步数上限。
假设 <math xmlns="http://www.w3.org/1998/Math/MathML"> O 0 O_0 </math>O0是初始输出, <math xmlns="http://www.w3.org/1998/Math/MathML"> O i O_i </math>Oi是第 <math xmlns="http://www.w3.org/1998/Math/MathML"> i i </math>i次迭代产生的输出,反思模型 <math xmlns="http://www.w3.org/1998/Math/MathML"> π r e f l e c t \pi_{reflect} </math>πreflect会生成针对 <math xmlns="http://www.w3.org/1998/Math/MathML"> O i O_i </math>Oi的反馈 <math xmlns="http://www.w3.org/1998/Math/MathML"> F i F_i </math>Fi:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F i = π r e f l e c t ( T a s k , O i ) F_i = \pi_{reflect}(Task, O_i) </math>Fi=πreflect(Task,Oi)
随后,优化模型 <math xmlns="http://www.w3.org/1998/Math/MathML"> π r e f i n e \pi_{refine} </math>πrefine会结合原始任务、上一轮输出 <math xmlns="http://www.w3.org/1998/Math/MathML"> O i O_i </math>Oi以及反馈 <math xmlns="http://www.w3.org/1998/Math/MathML"> F i F_i </math>Fi,生成新一轮的输出 <math xmlns="http://www.w3.org/1998/Math/MathML"> O i O_i </math>Oi:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> O i + 1 = π r e f i n e ( T a s k , O i , F i ) O_{i+1} = \pi_{refine}(Task, O_i, F_i) </math>Oi+1=πrefine(Task,Oi,Fi)

- Reflection为智能体提供了一个内部纠错回路,时期不在完全依赖外部工具的反馈,从而能从修正更高层次的逻辑和策略错误
- Reflection将一次性的任务执行,转变为一个持续优化的过程,显著提升了复杂任务的最终成功率和答案质量。
- Reflection为智能体构建了一个临时的 "短期记忆" 。整个"执行-反思-优化"的轨迹形成了一个宝贵的经验记录,智能体不仅知道最终答案,还记得自己是如何从有缺陷的初稿迭代到最终版本的。
记忆系统
Reflection 的核心在于迭代,而迭代的前提是能够记住之前的尝试和获得的反馈。因此,一个"短期记忆"模块是实现该范式的必需品。这个记忆模块将负责存储每一次"执行-反思"循环的完整轨迹。
Python
class Operation(Enum):
EXECUTION = "execution"
REFLECTION = "reflection"
@dataclass
class Record:
operation: Operation
content: str
def __str__(self) -> str:
if self.operation == Operation.EXECUTION:
return f"--- 上一轮尝试 (代码) ---\n{self.content}"
else:
return f"--- 评审员反馈 ---\n{self.content}"
class Memory:
"""
一个简单的短期记忆,用于储存Agent的行动和反思轨迹
"""
def __init__(self):
"""
初始化一个空列表来储存所有记忆
"""
self.records: list[Record] = []
def add_record(self, operation: Operation, content: str):
"""
添加一条记忆
"""
record = Record(operation, content)
self.records.append(record)
logger.info(f"新增一条记忆:{record.operation}, {record.content}")
def get_trajectory(self) -> str:
"""
将全部记忆更是化为一个连贯的字符串
"""
if not self.records:
return ""
else:
return "\n".join([str(record) for record in self.records])
def get_last_execution(self) -> str:
"""
返回最近一次的执行结果
"""
if not self.records:
return ""
if self.records[-1].operation == Operation.EXECUTION:
return self.records[-1].content
else:
return self.records[-2].content
提示词
Reflection 机制需要多个不同角色的提示词来协同工作。
初始执行提示词 (Execution Prompt)
尝试解决问题的提示词,内容相对直接,只要求模型完成指定任务。
ini
INITIAL_PROMPT_TEMPLATE = """
你是一位资深的Python程序员。请根据以下要求,编写一个Python函数。
你的代码必须包含完整的函数签名、文档字符串,并遵循PEP 8编码规范。
要求: {task}
请直接输出代码,不要包含任何额外的解释。
"""
反思提示词 (Reflection Prompt)
反思提示词是 Reflection 机制的灵魂。它指示模型扮演"评审员"的角色,对上一轮生成的内容进行批判性分析,并提供具体的、可操作的反馈。
ini
REFLECT_PROMPT_TEMPLATE = """
你是一位极其严格的代码评审专家和资深算法工程师,对代码的性能有极致的要求。
你的任务是审查以下Python代码,并专注于找出其在<strong>算法效率</strong>上的主要瓶颈。
# 原始任务:
{task}
# 待审查的代码:
```python
{code}
```
请分析该代码的时间复杂度,并思考是否存在一种<strong>算法上更优</strong>的解决方案来显著提升性能。
如果存在,请清晰地指出当前算法的不足,并提出具体的、可行的改进算法建议(例如,使用筛法替代试除法)。
如果代码在算法层面已经达到最优,才能回答"无需改进"。
请直接输出你的反馈,不要包含任何额外的解释。
"""
Agent的编码实现
Python
class ReflectionAgent:
def __init__(self, llm_client: BaseLLM, max_iterations: int = 3):
self.llm_client = llm_client
self.memory = Memory()
self.max_iterations = max_iterations
def think(self, prompt: str) -> str:
messages = [
{
"role": "user",
"content": prompt,
}
]
return self.llm_client.think(messages)
def run(self, task: str):
logger.info("------------------开始处理任务------------------\n")
# 1. 初始执行
logger.info("正在进行初始尝试")
initial_prompt = INITIAL_PROMPT_TEMPLATE.format(task=task)
initial_code = self.think(initial_prompt)
self.memory.add_record(Operation.EXECUTION, initial_code)
# 2. 反思-优化 迭代
for i in range(self.max_iterations):
logger.info(f"正在进行第{i+1}次迭代")
# 反思
logger.info("正在进行反思")
last_code = self.memory.get_last_execution()
reflection_prompt = REFLECT_PROMPT_TEMPLATE.format(task=task, code=last_code)
feedback = self.think(reflection_prompt)
self.memory.add_record(Operation.REFLECTION, feedback)
# 检查是否停止改进
if "无需改进" in feedback:
logger.info(f"迭代结束。")
break
# 优化
logger.info("正在进行优化")
refine_prompt = REFINE_PROMPT_TEMPLATE.format(task=task, code=last_code, feedback=feedback)
refine_code = self.think(refine_prompt)
self.memory.add_record(Operation.EXECUTION, refine_code)
final_code = self.memory.get_last_execution()
logger.info(f"------------------任务完成------------------")
return final_code
输出结果:
python
/Users/bytedance/Project/hello_agent/.venv/bin/python /Users/bytedance/Project/hello_agent/main.py
INFO - agent.Reflection - ------------------开始处理任务------------------
INFO - agent.Reflection - 正在进行初始尝试
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.Reflection - 新增一条记忆:Operation.EXECUTION,
def find_primes(n: int) -> list:
"""
找出1到n之间所有的素数。
使用埃拉托斯特尼筛法(Sieve of Eratosthenes)生成所有素数,
1不被视为素数。
Args:
n (int): 上界(包含),必须为正整数。
Returns:
list: 包含所有小于等于n的素数的列表,按升序排列。
Raises:
TypeError: 如果n不是整数。
ValueError: 如果n小于1。
示例:
>>> find_primes(10)
[2, 3, 5, 7]
>>> find_primes(1)
[]
"""
if not isinstance(n, int):
raise TypeError("n必须为整数")
if n < 1:
raise ValueError("n必须大于或等于1")
if n < 2:
return []
# 初始化布尔数组,True表示可能是素数
is_prime = [True] * (n + 1)
is_prime[0] = is_prime[1] = False # 0和1不是素数
for i in range(2, int(n ** 0.5) + 1):
if is_prime[i]:
# 从i*i开始标记,因为i*i之前的倍数已经被更小的素数标记过了
is_prime[i * i:n + 1:i] = [False] * ((n - i * i) // i + 1)
# 收集所有素数
return [num for num, prime in enumerate(is_prime) if prime]
INFO - agent.Reflection - 正在进行第1次迭代
INFO - agent.Reflection - 正在进行反思
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.Reflection - 新增一条记忆:Operation.REFLECTION, ## 评审反馈
### 算法效率瓶颈分析
当前代码使用了埃拉托斯特尼筛法,时间复杂度为 O(n log log n),空间复杂度 O(n)。虽然这已是经典的高效算法,但仍存在以下可优化的点:
1. **重复标记合数**:埃筛在标记 `i` 的倍数时,对于每个质数 `i`,会标记所有 `i * i` 及更大的倍数。但某些合数(如 30 = 2*15 = 3*10 = 5*6)会被多个质数重复标记,造成不必要的计算。
2. **列表切片赋值开销大**:`is_prime[i*i:n+1:i] = [False] * ...` 会创建临时列表并执行多次内存拷贝,在大 `n` 下 CPU 缓存不友好且耗时。
3. **内存使用**:使用 Python 列表存储布尔值,每个布尔值实际占用约 28 字节(Python 对象开销),内存膨胀严重,限制可处理的最大 `n`。
### 更优的算法方案
建议采用**线性筛(欧拉筛)**,其核心思想是让每个合数只被其**最小质因子**标记一次,时间复杂度严格 O(n),且常数因子更小。同时使用 `bytearray` 代替列表以节省内存并提高缓存命中率。
def find_primes_linear(n: int) -> list:
if n < 2:
return []
is_prime = bytearray(b'\x01') * (n + 1)
is_prime[0] = is_prime[1] = 0
primes = []
for i in range(2, n + 1):
if is_prime[i]:
primes.append(i)
# 用每个已知质数与 i 相乘来标记合数
for p in primes:
if i * p > n:
break
is_prime[i * p] = 0
if i % p == 0: # p 是 i 的最小质因子
break
return primes
- **复杂度**:时间复杂度 O(n),空间复杂度 O(n)(但 `bytearray` 节省约 8 倍内存)。
- **优势**:无重复标记,无切片复制,内存紧凑,适合处理超大规模 n(例如千万级)。
**若对内存有极致要求**,还可采用分段筛(Segmented Sieve),进一步将空间降至 O(√n),但会略微增加时间常数。对于一般场景,线性筛已是最优的算法级改进。
INFO - agent.Reflection - 正在进行优化
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.Reflection - 新增一条记忆:Operation.EXECUTION,
def find_primes(n: int) -> list:
"""
找出1到n之间所有的素数(使用线性筛法,欧拉筛)。
利用线性筛(欧拉筛)生成所有小于等于n的素数,每个合数只被其最小质因子
标记一次,时间复杂度严格O(n)。使用bytearray代替列表存储布尔值,
大幅降低内存占用并提升缓存命中率。
Args:
n (int): 上界(包含),必须为正整数。
Returns:
list: 包含所有小于等于n的素数的列表,按升序排列。
Raises:
TypeError: 如果n不是整数。
ValueError: 如果n小于1。
示例:
>>> find_primes(10)
[2, 3, 5, 7]
>>> find_primes(1)
[]
"""
if not isinstance(n, int):
raise TypeError("n必须为整数")
if n < 1:
raise ValueError("n必须大于或等于1")
if n < 2:
return []
# 使用bytearray存储素数标记,0表示非素数,1表示素数
is_prime = bytearray(b'\x01') * (n + 1)
is_prime[0] = is_prime[1] = 0
primes = [] # 用于收集已发现的素数
for i in range(2, n + 1):
if is_prime[i]:
primes.append(i)
# 用每个已知质数与 i 相乘来标记合数
for p in primes:
if i * p > n:
break
is_prime[i * p] = 0
# 当 p 整除 i 时,p 是 i 的最小质因子,后续标记会由更小的质数完成
if i % p == 0:
break
return primes
INFO - agent.Reflection - 正在进行第2次迭代
INFO - agent.Reflection - 正在进行反思
INFO - client.client - 正在调用模型 deepseek-v4-flash 进行思考......
INFO - httpx - HTTP Request: POST https://api.deepseek.com/v1/chat/completions "HTTP/1.1 200 OK"
INFO - client.client -
llm思考完成
INFO - agent.Reflection - 新增一条记忆:Operation.REFLECTION, 无需改进
INFO - agent.Reflection - 迭代结束。
INFO - agent.Reflection - ------------------任务完成------------------
最终结论:
def find_primes(n: int) -> list:
"""
找出1到n之间所有的素数(使用线性筛法,欧拉筛)。
利用线性筛(欧拉筛)生成所有小于等于n的素数,每个合数只被其最小质因子
标记一次,时间复杂度严格O(n)。使用bytearray代替列表存储布尔值,
大幅降低内存占用并提升缓存命中率。
Args:
n (int): 上界(包含),必须为正整数。
Returns:
list: 包含所有小于等于n的素数的列表,按升序排列。
Raises:
TypeError: 如果n不是整数。
ValueError: 如果n小于1。
示例:
>>> find_primes(10)
[2, 3, 5, 7]
>>> find_primes(1)
[]
"""
if not isinstance(n, int):
raise TypeError("n必须为整数")
if n < 1:
raise ValueError("n必须大于或等于1")
if n < 2:
return []
# 使用bytearray存储素数标记,0表示非素数,1表示素数
is_prime = bytearray(b'\x01') * (n + 1)
is_prime[0] = is_prime[1] = 0
primes = [] # 用于收集已发现的素数
for i in range(2, n + 1):
if is_prime[i]:
primes.append(i)
# 用每个已知质数与 i 相乘来标记合数
for p in primes:
if i * p > n:
break
is_prime[i * p] = 0
# 当 p 整除 i 时,p 是 i 的最小质因子,后续标记会由更小的质数完成
if i % p == 0:
break
return primes
Reflection 机制的成本收益分析
Reflection 机制在提升任务解决质量上表现出色,但这种能力的获得并非没有代价。
主要成本
- 模型调用开销增加:每进行一轮迭代,至少需要额外调用两次大语言模型(一次用于反思,一次用于优化)。如果迭代多轮,API 调用成本和计算资源消耗将成倍增加。
- 任务延迟显著提高:Reflection 是一个串行过程,每一轮的优化都必须等待上一轮的反思完成。这使得任务的总耗时显著延长。
- 提示工程复杂度上升:Reflection 的成功在很大程度上依赖于高质量、有针对性的提示词。为"执行"、"反思"、"优化"等不同阶段设计和调试有效的提示词,需要投入更多的开发精力。
核心收益
- 解决方案质量的跃迁:它能将一个"合格"的初始方案,迭代优化成一个"优秀"的最终方案。
- 可靠性增强:通过内部的自我纠错循环,Agent能够发现并修复初始方案中可能存在的逻辑漏洞、事实性错误或边界情况处理不当等问题,从而大大提高了最终结果的可靠性。