从零构建ReAct智能体:让AI学会边想边做

作者:树獭非懒日期:2026/7/2

前言

大语言模型已经能写诗、编程、做数学题,但它有一个根本性的短板:无法与外部世界交互。你问它"今天深圳天气怎么样",它只能诚恳地告诉你"我的知识截止到某某日期"——不是它不想回答,是它根本没有"看天气"这个动作能力。

这就是智能体(Agent)要解决的核心问题:让 LLM 不仅能,还能

业界现有的智能体框架(LangChain、LlamaIndex 等)已经将这套机制封装得非常完善,但它们的高度抽象也让很多人停留在"调包侠"阶段,对背后的运行机制一知半解。本文的目标,是带你从零构建一个 ReAct 智能体——从设计理念到代码实现,从提示词模板到工具管理,一步步拆解清楚。

读完你会发现,ReAct 的核心理念出奇地简洁,而那份让你真正理解和掌控它的"源代码级自信",只有亲手写过一遍才能获得。


一、智能体的"最后一公里":从思考到行动

1.1 两种极端,各缺一半

在 ReAct 提出之前,让 LLM 解决复杂问题的主流思路大致可以分为两派。

第一派:纯思考型。 以思维链(Chain-of-Thought, CoT)为代表——引导模型在输出最终答案前,先"自言自语"地把推理过程写出来。这种做法显著提升了模型的推理能力,但它有一个致命缺陷:模型完全活在自己的世界里。它无法查阅外部信息,所有推理都基于训练时记住的知识。当遇到它不知道的事实(比如实时股价、今天的新闻),模型就会"幻觉"一个答案——它并非故意说谎,而是它唯一能做的事就是根据概率生成文本。

第二派:纯行动型。 模型直接输出要执行的动作(调用搜索 API、执行计算、操作数据库),然后拿结果说话。这种做法让模型能够触及外部世界,但缺少了一个关键环节——规划。它不知道什么时候该搜索、搜什么关键词、搜到结果之后该如何修正方向。就像一个只会执行命令但没有判断力的助手。

1.2 ReAct 的洞见:思考与行动是硬币的两面

ReAct(Reasoning + Acting)的命名已经揭示了它的核心主张:思考指导行动,行动反过来修正思考。二者不是先后关系,而是不断交替、相互强化的关系。

当你面对一个陌生问题时——比如"帮我查一下英伟达最新的 GPU 型号,并和上一代做对比"——你不会一次性地想清楚所有步骤再行动。你更可能的做法是:先搜"英伟达最新 GPU",看看返回了什么;如果信息不够,再根据结果调整搜索词;搜到足够信息后,整理成对比结果。在每一步之间,你的思考都在被外部反馈持续修正

ReAct 就是把这种人类解决问题的自然模式,通过提示工程"移植"到了 LLM 身上。


二、ReAct 的核心循环:Think → Act → Observe

2.1 三段式节奏

ReAct 将智能体的每一步决策拆解为三个标准化的阶段:

阶段英文含义示例
思考Thought智能体的"内心独白",分析当前状态、拆解子任务、反思上一步结果"用户想了解英伟达最新GPU,我需要先搜索产品发布信息"
行动Action智能体决定执行的具体操作,通常是调用一个外部工具Search['英伟达最新GPU型号 2025']
观察Observation执行 Action 后从外部环境获得的反馈"NVIDIA GeForce RTX 5090 于 2025 年 1 月发布..."

这三个阶段形成一个闭环:Thought 决定 Action,Action 产生 Observation,Observation 又成为下一个 Thought 的输入。历史记录不断累积,智能体的"视野"也随之扩大,直到它在某个 Thought 中判断"我已经有足够信息回答用户了",然后输出最终结果。

2.2 为什么这个循环如此有效?

关键在于外部反馈的注入。一个纯 CoT 模型可能推理了十步,但这十步都基于同一个可能错误的前提——因为没有外部验证。而 ReAct 每执行一步 Action,就会得到一个来自真实世界的 Observation。这个 Observation 可以:

  • 纠正错误前提:如果智能体假设了一个错误的搜索关键词,Observation 的失败会促使它换一种方式提问
  • 提供新线索:搜索结果可能揭示用户问题中隐含的歧义,智能体可以据此进一步追问或细化搜索
  • 积累事实证据:每一步获取的真实信息逐步替换模型内部的"猜测",最终答案建立在可追溯的事实基础上

这种机制使得 ReAct 特别适用于三类场景:

  1. 需要外部知识的任务:查询实时信息(天气、新闻、股价)、搜索专业领域知识
  2. 需要精确计算的任务:将数学问题交给计算器工具,避免 LLM 的推理计算误差
  3. 需要与 API 交互的任务:操作数据库、调用业务系统的 API 完成特定功能

三、基础设施:给智能体装上"大脑"

在开始写 ReAct 逻辑之前,我们需要一个能与 LLM 交互的客户端。这里不是简单地调用 OpenAI API——我们需要一个统一的抽象层,让后续的 ReActAgent 不需要关心底层是哪个模型、哪个服务商、流式还是非流式。

3.1 环境配置

项目使用 Python 3.10+,核心依赖只有两个:

1pip install openai python-dotenv
2

API 密钥通过 .env 文件管理:

1LLM_MODEL_ID=Qwen/Qwen3.5-35B-A3B
2LLM_API_KEY=your-api-key
3LLM_BASE_URL=https://api-inference.modelscope.cn/v1/
4

3.2 AgentLLM:一个极简的 LLM 客户端

1class AgentLLM:
2    def __init__(self, model=None, apiKey=None, baseUrl=None, timeout=None):
3        self.model = model or os.getenv("LLM_MODEL_ID")
4        apiKey = apiKey or os.getenv("LLM_API_KEY")
5        baseUrl = baseUrl or os.getenv("LLM_BASE_URL")
6        timeout = timeout or int(os.getenv("LLM_TIMEOUT", 60))
7
8        self.client = OpenAI(api_key=apiKey, base_url=baseUrl, timeout=timeout)
9
10    def think(self, messages: list[dict], temperature: float = 0) -> str:
11        """调用 LLM,流式输出并返回完整回复"""
12        response = self.client.chat.completions.create(
13            model=self.model,
14            messages=messages,
15            temperature=temperature,
16            stream=True
17        )
18        collected = []
19        for chunk in response:
20            content = chunk.choices[0].delta.content or ""
21            if content:
22                print(content, end="", flush=True)
23                collected.append(content)
24        return "".join(collected)
25

两个设计决策值得说明:

  • 流式输出 (stream=True) :对于 ReAct 这种多步推理场景,实时看到模型的思考过程比等待几十秒后一次性出现结果更有利于调试和建立信任感。你能看到它"在想什么",而不是面对一个黑箱。
  • temperature=0 作为默认值:在多步推理中,我们更需要确定性而非创造性。每一步的输出会被解析为结构化的指令,temperature 过高会导致格式不稳定,正则解析容易失败。

四、打造"手脚":工具的定义与管理

如果说 LLM 是智能体的大脑,那么工具(Tools)就是它与外部世界交互的手和脚。工具系统的设计质量,直接决定了智能体的能力上限。

4.1 工具的三要素

一个良好定义的工具应具备三个核心属性:

要素说明重要性
名称 (Name)简洁唯一的标识符,供智能体在 Action 中引用,如 Search中等
描述 (Description)自然语言说明工具的用途和使用场景极高
执行逻辑 (Func)真正执行任务的函数,接收输入、返回结果

这里有一个容易忽视的关键点:Description 是整个工具系统中最关键的部分,因为 LLM 正是依赖这段自然语言描述来判断"什么时候该用哪个工具"。如果你的搜索工具描述写的是"一个搜索工具",模型可能不知道何时触发它;但如果你写的是"当你需要查询实时信息、事实或训练数据中不存在的知识时使用此工具",模型的决策准确率会显著提升。

换句话说,你写工具描述时不是在写给代码看,而是在写给 LLM 看——这是提示工程的一部分。

4.2 实现搜索工具

我们选择 SerpApi 作为搜索引擎——它通过 API 返回结构化的 Google 搜索结果,能直接提取答案摘要框、知识图谱等信息,比原始 HTML 解析更可靠。

1def search(query: str) -> str:
2    """基于 SerpApi 的网页搜索引擎"""
3    params = {
4        "engine": "google",
5        "q": query,
6        "api_key": os.getenv("SEARCH_API_KEY"),
7    }
8    client = serpapi.Client(api_key=params["api_key"])
9    results = client.search(params)
10
11    # 智能解析:按优先级回退
12    if "answer_box" in results and "answer" in results["answer_box"]:
13        return results["answer_box"]["answer"]          # 直接答案(如"珠穆朗玛峰有多高")
14    if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
15        return results["knowledge_graph"]["description"] # 知识图谱
16    if "organic_results" in results:
17        snippets = [
18            f"[{i+1}] {r.get('title', '')}\n{r.get('snippet', '')}"
19            for i, r in enumerate(results["organic_results"][:3])
20        ]
21        return "\n\n".join(snippets)                    # 兜底:前3条自然结果
22    return f"未找到关于 '{query}' 的信息。"
23

这个函数的智能解析策略值得留意:它不是一个"把 Google 的原始 HTML 抛给 LLM"的敷衍方案,而是在工具层面就对信息做了质量筛选——优先返回答案框(高置信度的直接答案),其次知识图谱,最次才用自然搜索结果。这层预处理极大提升了后续 LLM 推理的效率和准确率。信息质量的上游优化,比下游提示词调优更有效。

4.3 工具管理器:注册与调度

当智能体需要同时使用搜索、计算器、数据库查询等多个工具时,我们需要一个统一的注册和调度中心:

1class ToolExecutor:
2    def __init__(self):
3        self.tools: dict[str, dict] = {}
4
5    def registerTool(self, name: str, description: str, func: Callable):
6        self.tools[name] = {"description": description, "func": func}
7
8    def getTool(self, name: str) -> Callable:
9        return self.tools.get(name, {}).get("func")
10
11    def getAvailableTools(self) -> str:
12        return "\n".join([
13            f"- {name}: {info.get('description', '')}"
14            for name, info in self.tools.items()
15        ])
16

getAvailableTools() 是连接工具层和 LLM 的桥梁——它的输出会被直接嵌入提示词模板的 {tools} 占位符中,成为 LLM 决定调用哪个工具的唯一信息来源。


五、组装 ReActAgent:提示词、循环与解析

有了大脑(AgentLLM)和手脚(ToolExecutor),现在进入最关键的部分:将二者组合成一个完整的 ReAct 智能体。

5.1 提示词:整个机制的"宪法"

提示词是 ReAct 的灵魂。它不只告诉 LLM "你是谁",还定义了一套强制性的交互协议——LLM 必须按照约定格式输出,否则后续的代码解析会失败。

1REACT_PROMPT_TEMPLATE = """
2请注意,你是一个有能力调用外部工具的智能助手。
3
4可用工具如下:
5{tools}
6
7请严格按照以下格式进行回应:
8
9Thought: 你的思考过程,用于分析问题、拆解任务和规划下一步行动。
10Action: 你决定采取的行动,必须是以下格式之一:
11- `{tool_name}[{tool_input}]`: 调用一个可用工具。
12- `Finish[最终答案]`: 当你认为已经获得最终答案时。
13
14现在,请开始解决以下问题:
15Question: {question}
16History: {history}
17"""
18

这个模板看似简单,但每一部分都在承担结构性职能:

  • 角色定义:"有能力调用外部工具"——这句话激活了 LLM 的"工具使用模式"
  • 工具清单 {tools} :让 LLM 知道它有哪些可用的能力,这是它做决策的信息基础
  • 格式规约 Thought/Action:这是最关键的设计——它强制 LLM 的输出具有可解析的结构。每一个 Thought 和 Action 都是确定的字符串模式,让代码能精确提取
  • 动态上下文 {question}/{history} :每一轮历史都被完整保留并注入,形成不断增长的推理链

5.2 核心循环:永不停歇的 Think-Act-Observe

ReActAgent 的 run 方法是整个智能体的心脏:

1class ReActAgent:
2    def __init__(self, llm_client, tool_executor, max_steps=5):
3        self.llm_client = llm_client
4        self.tool_executor = tool_executor
5        self.max_steps = max_steps
6        self.history = []
7
8    def run(self, question: str):
9        self.history = []
10        current_step = 0
11
12        while current_step < self.max_steps:
13            current_step += 1
14
15            # 1. 格式化提示词 —— 把完整上下文喂给 LLM
16            tools_desc = self.tool_executor.getAvailableTools()
17            history_str = "\n".join(self.history)
18            prompt = REACT_PROMPT_TEMPLATE.format(
19                tools=tools_desc, question=question, history=history_str
20            )
21
22            # 2. 调用 LLM 思考
23            response = self.llm_client.think([{"role": "user", "content": prompt}])
24
25            # 3. 解析输出
26            thought, action = self._parse_output(response)
27
28            # 4. 判断终止条件
29            if action.startswith("Finish"):
30                return self._extract_final_answer(action)
31
32            # 5. 执行工具调用
33            tool_name, tool_input = self._parse_action(action)
34            tool_func = self.tool_executor.getTool(tool_name)
35            observation = tool_func(tool_input)
36
37            # 6. 记录历史,进入下一轮
38            self.history.append(f"Action: {action}")
39            self.history.append(f"Observation: {observation}")
40
41        return None  # 超出最大步数
42

这里有一个关键的架构选择值得展开:每一步都重新格式化完整提示词

为什么不维持一个对话 session 而是每次从头构建?因为 ReAct 的每一步都需要 LLM 看到"截至目前为止发生的所有事"——工具列表、用户原始问题、每一轮 Thought-Action-Observation 的历史。构建完整 Prompt 是确保 LLM 拥有全部上下文的最可靠方式。

max_steps=5 则是防止无限循环的安全阀——在实际使用中,你可以根据任务复杂度调整这个值。

5.3 输出解析:正则的艺术

LLM 返回的文本是自然语言,而我们的代码需要从中提取结构化的指令。这里用正则表达式来完成:

1def _parse_output(self, text: str):
2    """提取 Thought  Action"""
3    thought_match = re.search(r"Thought:\s*(.*?)(?=\nAction:|$)", text, re.DOTALL)
4    action_match = re.search(r"Action:\s*(.*?)$", text, re.DOTALL)
5
6    thought = thought_match.group(1).strip() if thought_match else None
7    action = action_match.group(1).strip() if action_match else None
8    return thought, action
9
10def _parse_action(self, action_text: str):
11    """从 Action 字符串中提取工具名和参数"""
12    match = re.match(r"(\w+)[(.*)]", action_text, re.DOTALL)
13    if match:
14        return match.group(1), match.group(2)
15    return None, None
16

_parse_output 负责拆分 LLM 输出的两个核心部分,_parse_action 负责从 Search['英伟达最新 GPU'] 这样的字符串中提取出工具名 Search 和输入参数 英伟达最新 GPU。正则虽然看起来笨拙,但在这种强格式约束的场景下反而是最可靠的选择——比任何"智能解析"都更可预测。


六、客观评价:ReAct 的优势与暗面

任何技术方案都是取舍的结果。在亲手实现过 ReAct 之后,你会对它的优点和局限有更切身的体会。

6.1 三大优势

高可解释性。这是 ReAct 最大的杀手锏。通过 Thought 链,你可以逐字逐句地看到智能体的"心路历程"——它为什么选择这个工具、下一步打算做什么、上一步的结果如何影响它的判断。对于调试、审计和建立用户信任,这种透明度无可替代。

动态规划与纠错。ReAct 是"走一步看一步"的,而非一次性生成完整计划。如果上一步搜索结果不理想,智能体可以在下一步修正搜索词;如果发现走错了方向,它可以根据 Observation 调头。这种灵活性让它对真实世界的噪音和不确定性有更好的适应力。

工具协同。LLM 负责"运筹帷幄"(规划和推理),工具负责"解决具体问题"(搜索、计算、API 调用)。二者的能力边界清晰互补,突破了单一 LLM 在知识时效性、计算准确性方面的固有局限。

6.2 四点局限

对 LLM 能力的强依赖。ReAct 是"强模型依赖"的——如果底层 LLM 的逻辑推理能力、指令遵循能力或格式化输出能力不足,整个流程随时可能中断。在能力较弱的小模型上,Thought 可能逻辑不通,Action 格式可能不规则。

执行效率问题。完成一个任务通常需要多次 LLM 调用,每次调用都伴随着网络延迟和计算成本。对于需要很多步骤的复杂任务,这种串行的"思考-行动"循环可能导致较高的总耗时和费用。

提示词的脆弱性。整个机制的稳定运行建立在一个精心设计的提示词模板之上。模板中任何微小变动都可能影响 LLM 的行为。而且并非所有模型都能持续稳定地遵循预设格式——你在 GPT-4 上调好的 Prompt,换到另一个模型上可能完全不工作。

可能陷入局部最优。步进式决策意味着智能体缺乏全局的、长远的规划。它可能因为眼前的 Observation 而选择一个看似正确但长远来看并非最优的路径,甚至在某些情况下陷入"原地打转"的循环——不断搜索、不满意、换个词再搜、再不满意……

6.3 调试之道

当你构建的 ReAct 智能体出现问题时,以下是四个最有效的排查手段:

  1. 打印完整的格式化提示词。在此之前,你不知道 LLM 到底"看到了什么"。把所有历史记录渲染进 Prompt 后整体检查,是追溯决策源头的最直接方式。
  2. 分析 LLM 的原始输出。当正则解析失败时,先把 LLM 返回的原始文本打出来。问题可能是 LLM 没有遵循格式(比如写成了 Action: 后面跟了多行),也可能是你的正则写得太死。
  3. 在提示词中加入 Few-shot 示例。如果模型频繁输出不规则格式,在 Prompt 中加一两个完整的 "Thought-Action-Observation" 成功案例。示例是最好的格式老师。
  4. 调整模型或参数。换一个指令遵循能力更强的模型,或者将 temperature 设为 0 以保证输出确定性。temperature 的微小差异在多步推理场景下会逐轮放大。

总结与延伸

ReAct 的价值不在于它有多"先进"——事实上它是最基础的智能体范式之一——而在于它用极其简洁的机制解决了 LLM 从"思考"到"行动"的跃迁问题。三个概念(Thought、Action、Observation)、一个循环、一个提示词模板,就构成了一个能与外部世界交互的智能体。

但 ReAct 也开启了更多问题,这些问题指向了后续更高级的范式:

  • ReAct 的"走一步看一步"在复杂任务中效率太低怎么办?Plan-and-Solve:先制定完整计划,再按计划执行——"三思而后行"
  • ReAct 缺乏自我纠错怎么办?Reflection:让智能体对自己的输出进行自我批判和迭代优化
  • 一个工具不够用怎么办? → 工具组合与自动选择:根据任务动态编排多个工具的调用序列
  • 单次对话有状态限制怎么办? → 记忆模块:跨对话保持上下文,让智能体"记住"之前的交互

从使用者到创造者的转变,始于对最基础范式的深刻理解。当你亲手写过 ReActAgent 的那几百行代码之后,再去学习 LangChain 或 LlamaIndex,你不会觉得那些抽象层不可思议——你会在心里默默点头:"原来如此,底层就是这么工作的。"

而这,正是从零构建的意义。


从零构建ReAct智能体:让AI学会边想边做》 是转载文章,点击查看原文


相关推荐


Swift 还让你 Excited 吗?-- 肘子的 Swift 周报 #141
东坡肘子2026/6/23

Swift 还让你 Excited 吗? 上周,Reddit 上一篇题为《Is anyone else excited by Swift progress as the language?》的帖子引发了不少讨论。WWDC 2026 上,Apple 明确表示,Swift 已被用于 WebKit 关键组件、QUIC 网络栈、字体渲染、驱动与固件。发帖者据此认为,Swift 正在向核心操作系统内核迈进,并将这一幕视为 2014 年"高级系统编程语言"承诺的兑现。评论区的讨论则很快从“是否兑现了承诺”,


别再让业务层裸奔 CarPropertyManager 了!谈谈汽车车载核心服务的架构封装
潜龙勿用之化骨龙2026/6/15

前言 Android Automotive 项目中,CarPropertyManager 几乎是访问一切车辆状态的入口。但在真实项目里,如果直接在 ViewModel 或业务层大量使用它,代码会变得难以维护、难以测试,也完全不符合 现代Android 架构 的分层思想。 本文将从零开始,梳理一套基于 Clean Architecture 的 CarPropertyManager 封装方案,涵盖: 为什么不能直接用 如何用抽象基类统一封装 callbackFlow 的正确姿势 Flow 与 su


「装闭」-AI驱动的开源装修闭坑系统
fthux2026/6/8

愿每个业主在装修路上心中有数,于是,「装闭」来了。 装修行业的水有点儿深 很多人装修完回头看,都发现自己花了不少冤枉钱。设计师画的效果图美轮美奂——复杂吊顶、满墙背景墙、开放格书架……装好以后才发现,这些东西费钱不说,日常打理还特别费劲。吊顶积灰、开放格藏污纳垢、花哨灯带一年到头开不了几次。 更别说合同和报价单里的猫腻。增项漏项、模糊工艺描述、虚报材料数量——这些套路装修公司玩得轻车熟路,但普通业主根本无从分辨。随便一个增项,几千块甚至上万块就多出来了。 于是就有了「装闭」(RenoPit)这


译 · Jake Wharton 访谈:Android 圈最熟悉的那个名字
bqliang2026/6/1

Jake Wharton 是我的偶像,也是很多 Android 开发者绕不开的名字。Retrofit、OkHttp、Dagger、Ktx... 这些项目或多或少都影响过我们写 Android 的方式。 前几天在网上看到他的这篇访谈,读完觉得很受用。里面既有他早年做 Android、做开源的经历,也有他对职业选择、会议、倦怠、家庭和工作方式的很多真实想法。这里把它翻译成中文,分享给同样喜欢 Android、Kotlin 和开源的朋友们。 ⚡️ 欢迎来到 Effective Interviews


从 textarea 到 AI 输入框:用 Tiptap 实现 / 命令、@ 引用和结构化请求
倾颜2026/5/11

本文对应项目版本:v0.0.12 在一个 AI 应用刚开始做聊天功能时,textarea 往往已经够用了。 用户输入一段自然语言,前端把它发给后端,后端拿到消息数组,模型开始生成回答。这个链路简单、直接,也足够支撑最早期的问答场景。 但当项目里慢慢出现 Skill(任务能力层)、Tool(可执行工具)、Resource(可读取上下文)、Prompt(可注入模型的提示模板)、MCP 能力(通过 MCP 接入的外部能力)之后,我开始遇到一个更具体的问题:用户输入不再只是一段自然语言。 它开始同时表


解决 4 大 AI 编码痛点:Matt Pocock 的 Skill 工作流深度拆解
小碗细面 VIP.5 如鱼得水2026/5/1

解决 4 大 AI 编码痛点:Matt Pocock 的 Skill 工作流深度拆解 当所有人都在用 AI 写更多代码的时候,TypeScript 之神 Matt Pocock 把自己的 .claude 目录直接推到 GitHub 上,告诉我们:先别急着 vibe coding,把工程基本功捡回来。 一、写在前面:一个让人拍大腿的开源项目 2026 年 3 月,TypeScript 社区的"老熟人"Matt Pocock(你大概率在他的 Total TypeScript 课程下面留过言)干了


Android Studio 项目模板完全指南
90后晨仔2026/4/22

本文将详细介绍 Android Studio 中创建项目时的各种模板选项,帮助你快速选择最适合的项目起点。一个小白的自学成长之路 一、设备类型分类 首先,左侧列表显示了不同的设备平台: Phone and Tablet:手机和平板应用(最常用) Wear OS:智能手表应用 Android TV:电视应用 Automotive:车载应用 二、Phone and Tablet 模板详解 1. No Activity 含义:创建一个没有任何活动(Activity)的空项目


【Ollama本地大模型】性能优化思考
盛世隐者2026/4/13

文章目录 1. 硬件配置与模型选择2. 模型参数配置🧠 参数详解:为何这样配置你的10核+8GB显存 3. 本地API调用3.1 模型驻留⚙️ 工作原理🛠️ 配置方式💡 实用技巧💡 最佳实践与注意事项 3.2 流式输出🔌 原理:它是如何工作的?⚖️ 场景与效果对比⚙️ 默认行为与语言SDK差异🛠️ 实际应用示例1. 流式 (`stream: true`)2. 非流式 (`stream: false`) ✨ 高级特性:工具调用与思考过程💡 最佳实践 3.3


JVM学习问题记录(2) jps命令无法识别
Engineer邓祥浩2026/4/5

现象: 命令行输入jps命令,提示"jps不是内部或外部命令,也不是可运行的程序" 背景: 学习JVM,测试jdk自带性能分析工具,需要用到jps工具 思路: 先怀疑自己 可能的原因 JDK安装有问题,无jps.exe环境变量设置问题,找不到jps.exe 动手: 去JAVA_HOME配置的查看,发现jps.exe在,用绝对路径执行是正常的查看环境变量,JAVA_HOME和path配置也是正确的,echo结果也是对的 这里就感觉很奇怪,而且试了下javac命令是正常的,那也不是路径配置问题,这里


微软官方Python网格覆盖与鼠标控制库
鹓于2026/3/28

微软官方:网格覆盖 / 鼠标指针控制 Python 库 微软官方网格覆盖(Grid Overlay)、移动鼠标指针的 Python 库,是: ✅ 官方库:windows-ui-automation / pywin32 + 微软 Mouse 原生 API 微软官方推荐的 Python 鼠标 / 网格覆盖控制方案: pywin32(Windows 系统 API,官方支持)windows.ui.input(UWP 官方鼠标 / 指针 API)Win32 API mouse_event / Se

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读