news 2026/9/10 17:24:52

LangChain+Playwright实战:构建能自主浏览网页的Agent

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LangChain+Playwright实战:构建能自主浏览网页的Agent

用 LangChain + Playwright 搭建一个能自己打开网页、读取信息、再做判断的 Agent,实际难度比想象中低,但坑也不少。我最近把这一套组合完整跑了一遍,覆盖了环境准备、代码实现、开发测试和报错排查,最大的感受是:LangChain 负责把 Agent 的思维流程组织起来,Playwright 负责把 Agent 的“手”接到浏览器上。两者配合好了,很多以前需要手动写死脚本的网页任务,都能交给 Agent 动态完成。

如果你正准备做 Agent 开发,或者想让大模型不只是在聊天框里回答问题,而是能真正访问网页、拿页面内容回来分析,这篇文章值得看完。下面讲的是我实际跑下来的过程,不是理论,所有配置和代码都会拆开说明。

1. 为什么偏偏是 LangChain + Playwright 这个组合

1.1 LangChain 负责 Agent 的“大脑”和“调度”

在 Agent 开发里,LangChain 解决的不是“大模型能做什么”,而是“大模型怎么把任务拆开、怎么调用外部能力、怎么根据结果继续执行”。它提供了工具抽象、Prompt 模板、Agent 执行器、记忆和输出解析。

这里的核心概念很简单:你把若干个函数包装成 Tool,让大模型根据用户输入决定调用哪个工具,然后拿到工具返回结果,再决定下一步动作。这个“决定—调用—观察—再决定”的循环,就是 Agent 的基本工作方式。

很多人会把 LangChain 和 LangGraph 放在一起比较。站在开发测试的角度,我的理解是:LangChain 更偏基础工具集合和 Agent 快速实现;LangGraph 更偏状态图,适合需要精细控制流程分支、状态流转和人工介入的场景。刚开始搭 Agent,用 LangChain 的 AgentExecutor 就能跑通,不用一上来就上 LangGraph。

1.2 Playwright 负责 Agent 的“手”

Playwright 是一个浏览器自动化框架,支持 Chromium、Firefox、WebKit,能打开浏览器、点击按钮、输入文本、翻页、提取页面内容。它比 requests 这类简单 HTTP 库强在一点:它用真实浏览器内核运行页面,JavaScript 动态渲染出来的内容也能拿到。

很多页面用 requests 抓下来,只剩一堆脚本标签,实际数据都在浏览器加载后才出现。Playwright 会等页面执行完动态请求,再给你渲染后的 DOM,所以它特别适合做 Agent 的信息获取通道。

使用的时候要记住一条边界:只操作自己有权限访问的公开页面、自己项目里的测试页面或者内部系统。不要用 Playwright 去做绕过访问控制、突破反爬机制的事情,这是合规底线。

1.3 这套组合和“写死脚本”有什么不一样

传统自动化脚本通常流程固定:先打开某 URL,再等某个选择器,再提取数据。页面结构一变,脚本就崩。Agent 不是这样,用户只需要描述目标,比如“打开这个页面,提取正文和标题”,Agent 自己决定先调用哪个工具、用什么参数、要不要再打开一个链接做交叉验证。

但也不能把 Agent 神化。它本质上是把“选择工具和推理步骤”自动化,底层仍然需要你提供稳定、可被调用的工具函数。工具封装得越清晰,Agent 跑得越稳;工具本身就是一把抓,Agent 再聪明也容易绕晕。

2. 搭建之前,先把环境和依赖确认清楚

2.1 Python 环境、LangChain 安装和 Playwright 安装

我建议直接用 Python 3.10 以上版本。太老的 Python 在安装 LangChain 相关依赖时容易出现兼容问题。安装命令不复杂:

pip install langchain langchain-openai playwright

装完 Python 库之后,还差一步容易漏掉:安装浏览器内核。

python -m playwright install chromium

这一步不是可选项。没有浏览器内核,Playwright 运行时会直接报找不到浏览器的错误。哪怕你在本地已经装了 Chrome,Playwright 默认也认不到,它使用的是自己下到缓存目录里的 Chromium 版本。

在 Windows 上有一个很常见的报错:playwright : 无法将“playwright”项识别为 cmdlet、函数、脚本文件或可运行程序。这个问题的根源通常是 Python 的可执行文件目录没有加入 PATH,或者你确实没有安装 playwright 包。可以先执行pip show playwright确认包是否存在,再直接用python -m playwright来调用,绕开 PATH 问题。

2.2 LLM 服务怎么接

Agent 需要一个能支持工具调用(Function Calling)的大模型。OpenAI 的格式是事实上的标准,很多兼容服务也支持,你可以根据实际情况选模型。调试阶段建议用小模型,比如 gpt-4o-mini 这类,成本低,输出简洁,跑测试时不会因为回答太长刷屏。

需要准备好 API Key,并在代码里正确配置。如果你是本地开发,Key 可以放在环境变量里,避免硬编码到代码里:

export OPENAI_API_KEY="你的key"

如果你使用的是私有化部署或者公司内网模型,要确认它能支持 OpenAI 格式的 Function Calling 接口,否则 Agent 的工具调用没法正常触发。

2.3 硬件要求和目录结构

本地跑这套组合并不需要太高配置,但也不能太低。Chromium 在无头模式下运行会占用几百 MB 到 1GB 内存,加上 LangChain、LLM 服务调用,建议内存至少 8GB。如果后续要并发开多个浏览器上下文,内存会成倍上涨,这点要提前有预期。

磁盘方面,Chromium 内核加依赖包预留 1GB 以上比较稳。项目目录建议这样组织:

agent-project/ ├── tools/ # 自定义工具函数 ├── config.py # 模型、超时、路径等配置 ├── agent_runner.py # Agent 组装和运行入口 ├── logs/ # 运行日志 ├── tasks/ # 任务输入,可以是 txt、json、csv └── outputs/ # 结果输出

目录分开的最大好处是排错方便。任务卡住时,你不需要翻代码找日志,任务输入、输出、日志三块独立,能快速定位问题出在哪个环节。

2.4 网络和权限边界

确保目标站点可以从你当前网络访问。如果站点需要登录,你要在代码里处理登录态,比如用 Playwright 的storage_state保存和复用 cookie。但这只适用于你有合法账号、有权限访问的站点。

这里多说一句:网页自动化不是不能做,而是要守住边界。公开信息的获取和测试页面的自动化是正常开发场景;未授权访问、绕过验证、破解反爬,这些不属于技术教程该覆盖的范围,也不应该写进开发流程里。

3. 核心架构设计:浏览器操作怎么变成 Agent 的工具

3.1 不要把“打开页面”直接扔给 Agent

最开始我踩过一个坑:只给 Agent 一个“打开任意 URL”的空工具,然后让它自由发挥。结果 Agent 经常把 URL 拼错、反复打开同一个页面、在一个问题上绕很多轮。

更稳的做法是:把常见浏览器能力拆成几个职责明确的工具。比如“打开页面并返回标题”“提取页面可见文本”“等待某个元素出现”“点击页面内按钮”。每个工具只做一件事,输入参数和返回值都描述清楚,Agent 才知道什么时候该调它。

3.2 封装 Playwright 工具的通用思路

封装 Playwright 工具时,有几个关键点:

第一,尽量复用同一个浏览器实例,不要每个工具都启动一个新浏览器。启动 Chromium 的开销很大,频繁启动会让整个流程变慢。

第二,每个工具函数要写好异常处理。页面加载失败、元素找不到、超时,这些都是常态。工具函数应该把异常信息返回给 Agent,而不是直接崩溃。Agent 拿到错误信息后,有可能自己修正策略,比如换个 URL,或者换个方式重试。

第三,超时等待要写清楚。元素没出现时不要硬等固定秒数,最好用locator.wait_for(timeout=5000)这种带超时的等待。页面加载可以分层次,比如先等 DOM 加载完成,再根据业务需要等待关键元素。

3.3 Prompt 设计:把任务边界说清楚

Agent 的 Prompt 不只是聊天开场白,它是整套行为的规则。我的建议是在 system 消息里明确写这几条:

  • 你是网页调研助手,只能通过给定工具获取页面信息。
  • 工具返回的内容是唯一信息源,不要自己编造页面内容。
  • 如果页面加载失败,尝试换一种方式,但最多重试一次。
  • 如果没有获取到有效内容,直接返回“未获取到有效内容”,不编造结果。

这些规则看着简单,实测定能减少很多“幻觉”问题。没有边界的大模型很容易在工具调用失败后随手编一段文字,看起来像结果,实际全是假的。

3.4 执行循环是怎么串起来的

Agent 的执行流程如图(文字描述版):

  1. 用户输入目标。
  2. 大模型分析任务,决定调用哪个工具。
  3. 系统执行工具函数,拿到页面数据或错误信息。
  4. 大模型根据观察结果判断:任务完成就直接输出答案,没完成就继续调用工具。
  5. 重复直到模型认为任务完成,或者达到最大迭代次数。

在 LangChain 里,AgentExecutor已经封装好了这个循环。你需要配置的只有模型、工具列表、Prompt 和几个关键参数,比如最大迭代次数max_iterations。如果任务比较复杂,建议把最大迭代次数限制在 5 到 10 次,防止模型在死循环里出不来。

4. 完整代码实现:一个最简可运行的 Agent 样例

4.1 代码结构和安装注意事项

下面这段代码是结构示例,不是某个仓库的完整源码复制。LangChain 的 API 在版本演进中变化比较快,不同版本的函数导入位置可能不一样。运行前先确认你的langchainlangchain-openaiplaywright版本,再根据实际情况调整导入。

4.2 定义浏览器工具

import json from playwright.sync_api import sync_playwright # 全局浏览器实例,避免每个工具都重新启动 playwright_obj = None browser = None def init_browser(headless: bool = True): global playwright_obj, browser playwright_obj = sync_playwright().start() browser = playwright_obj.chromium.launch(headless=headless) def open_page_and_get_text(url: str) -> str: """ 打开指定网页,返回页面标题和可见文本。 输入必须是完整的 URL,例如 https://example.com。 """ if browser is None: return "错误:浏览器未初始化,请先调用 init_browser()" try: page = browser.new_page() page.goto(url, timeout=15000, wait_until="domcontentloaded") title = page.title() text = page.locator("body").inner_text(timeout=5000) page.close() return f"标题: {title}\n正文前2000字:\n{text[:2000]}" except Exception as e: return f"页面打开失败: {str(e)}"

这个工具只做一件事:输入 URL,返回标题和正文前 2000 字。返回值设计成纯文本,是因为大模型读文本比读结构化 HTML 更稳定,也不会把大量无用标签混进上下文。

4.3 创建 Agent 并配置模型

from langchain.agents import AgentExecutor, create_openai_functions_agent from langchain.tools import Tool from langchain_openai import ChatOpenAI from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder tools = [ Tool( name="open_page_and_get_text", func=open_page_and_get_text, description="输入一个完整的网页URL,返回该页面的标题和可见正文。适用于需要获取网页信息的场景。", ) ] llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) prompt = ChatPromptTemplate.from_messages([ ("system", "你是网页调研助手。请调用可用工具获取网页信息,不要自己编造页面内容。" "目标完成后,用中文总结结论和来源URL。如果工具返回错误,尝试换一种方式,但不要虚构结果。"), ("human", "{input}"), MessagesPlaceholder(variable_name="agent_scratchpad"), ]) agent = create_openai_functions_agent(llm, tools, prompt) agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, max_iterations=3, handle_parsing_errors=True, )

temperature=0是故意设置的。Agent 执行任务时不需要创意,它需要稳定、可重复的输出。温度调高之后,同一个页面它可能每次总结都不一样,排错时很难判断是代码问题还是随机性问题。

4.4 运行入口

if __name__ == "__main__": init_browser(headless=True) try: result = agent_executor.invoke({ "input": "打开 https://example.com 并返回页面标题" }) print(result["output"]) finally: if browser: browser.close() if playwright_obj: playwright_obj.stop()

finally里的关闭逻辑很重要。Chromium 如果长期不释放,即使程序退出,后台也可能残留浏览器进程,占用内存越来越大。我在测试时遇到过几次任务卡住,最后发现是浏览器实例没关干净。

运行成功后,终端会输出类似这样的信息:Agent 先调用open_page_and_get_text,拿到标题,然后生成最终答案。如果verbose=True,你能看到完整的工具调用过程,这是排查 Agent 行为最直接的线索。

5. 开发测试全过程:先单条,再批量,再看边界

5.1 第一轮测试:先跑通一个 URL 的完整链路

第一次测试不要设计复杂任务。就一个最核心的目标:让 Agent 调用工具打开一个网页,拿回标题,再输出结果。

我一般会选一个结构简单、加载快的公开页面。判断标准有四个:

  • Agent 没有自己编造标题,而是确实通过工具拿到。
  • 工具调用次数不超过 2 次。
  • 最终输出包含标题和来源 URL。
  • 全程没有超时和脚本报错。

如果这一步都跑不通,问题大概率出在环境或模型配置,而不是 Agent 逻辑。先不要急着改 Prompt,先看依赖和网络。

5.2 第二轮测试:多步骤任务

单条跑通后,可以试一个需要两步以上操作的任务。例如:打开一个列表页,提取页面里的链接,再打开前两个链接做内容摘要。

这种任务能检验 Agent 的规划能力。但也很容易出问题,最常见的是 Agent 反复打开同一个页面。明明第一步已经访问过列表页,第二步可能又调一次工具访问同一个 URL,浪费时间和 token。

解决方案是在 Prompt 里加一句“如果已经打开过某个页面,不要再重复打开”。另外一个办法是给工具加一个简单的缓存,同一个 URL 在短时间内直接返回已缓存结果。这两种方式我都试过,缓存更省资源,但副作用是 Agent 拿到的可能是旧内容;Prompt 约束虽然简单,但效果已经足够。

5.3 第三轮测试:批量输入

批量跑的时候,不要天真地以为“能跑单条就一定能并行跑”。我建议按下面这个顺序做:

  1. 先用单线程循环跑 3 到 5 条任务,记录每条任务的耗时和成功率。
  2. 确认单条稳定后,再开并发。并发数从 2 开始,逐步往上加。
  3. 每一条任务都要有独立的日志和输出保存。

批量任务需要考虑三个额外问题:

失败重试:一条任务失败了,是整个流程暂停,还是跳过继续?我的建议是单条任务内部做一次重试,重试失败就标记为失败,不阻塞整个队列。

输出命名:多条任务的结果不能都写到同一个文件里。可以用任务 ID 或者 URL 的哈希作为文件名,避免互相覆盖。

资源和日志:每条任务的 Agent 日志、工具调用记录、最终输出、耗时都要保存下来。以后排查的时候,没有日志等于没有过程,只看结果根本定位不了问题。

一个简单的批量框架可以这样:

import json import time from concurrent.futures import ThreadPoolExecutor, as_completed def run_one_task(url): start = time.time() try: result = agent_executor.invoke({"input": f"打开 {url} 并返回页面标题和简介"}) output = result["output"] status = "success" except Exception as e: output = str(e) status = "failed" return { "url": url, "status": status, "output": output, "time_cost": round(time.time() - start, 2), } urls = [ "https://example.com", "https://example.org", # 更多输入... ] results = [] with ThreadPoolExecutor(max_workers=2) as pool: futures = [pool.submit(run_one_task, url) for url in urls] for f in as_completed(futures): results.append(f.result()) with open("outputs/result.jsonl", "w", encoding="utf-8") as f: for r in results: f.write(json.dumps(r, ensure_ascii=False) + "\n")

不要一开始就把max_workers调到很大。2 到 3 个并发已经能看出问题。如果并发上去后大量报超时或内存飙升,先降下来,优先保证稳定性。

5.4 第四轮测试:接口化

批量脚本跑通后,如果想让其他人通过 API 调用,可以用 FastAPI 把这个 Agent 包成一个服务。接口接收 URL 列表,返回结构化结果。

接口化和脚本化最大的区别是:脚本是跑完就结束,接口是长期驻留。所以你需要考虑任务队列、超时控制、请求限流和并发管理。

一个简单的设计是:创建一个任务队列,接口收到请求后先把任务放进队列,立刻返回一个任务 ID,后台 Worker 去处理。前端轮询任务状态。这样即使一个任务很慢,也不会阻塞其他请求。

但在你还没有把批量任务跑稳之前,先不要急着接接口。接口化会把很多隐性问题放大,比如线程安全问题、浏览器并发、内存泄漏、日志丢失。我的建议是脚本阶段至少连续跑 20 条任务,确认成功率和稳定性,再进入接口阶段。

5.5 验收标准

不同类型任务的验收标准不完全一样,可以用下面这个表做参考:

阶段判断指标合格线
单条任务工具调用是否正常、输出是否准确单条成功,无幻觉编造
多步骤任务Agent 能否按顺序完成多个工具调用能在 3 次迭代内完成
批量任务成功率、平均耗时、失败重试连续 20 条成功率达到 90% 以上
接口服务请求响应、并发稳定性、日志单请求不超时,并发 3 稳定运行

这个表不是绝对标准,但我在做开发测试时通常会先按这个思路定一个基线,再根据项目需要调整。

6. 常见报错和排查链路

6.1 playwright 命令找不到

报错现象:playwright : 无法将“playwright”项识别为 cmdlet、函数、脚本文件或可运行程序

排查顺序:

  1. pip show playwright看包是否安装。
  2. 如果没安装,执行pip install playwright
  3. 如果安装了但命令还是找不到,用python -m playwright代替命令。
  4. 检查 Python 的 Scripts 目录是否在 PATH 中。

这个问题本质是环境变量问题,不是 Playwright 本身出了问题。换一个终端窗口有时也能解决,因为新的窗口会重新加载 PATH。

6.2 浏览器启动失败,找不到 Chromium

报错里通常会出现类似“Executable doesn't exist”的信息。原因是只安装了 Python 包,没有安装浏览器内核。

解决方式:

python -m playwright install chromium

有时候你之前装过,但浏览器内核被清理软件删了,报错也会出现。重新执行安装命令即可。

6.3 Agent 执行超时:execution provider did not respond in time

如果日志里出现类似“execution provider did not respond in time”的提示,我的排查顺序是先判断是哪一层超时:

  1. 单独调用一次 LLM,看模型响应是否正常。
  2. 单独调用一次工具函数,看工具本身是否卡在网络请求或页面加载上。
  3. 查看模型的 max_tokens 设置,是否因为输出太长导致超时。
  4. 查看 Agent 的最大迭代次数,是否因为反复调用工具导致整体耗时过大。

这个错误的常见原因不是 Agent 逻辑,而是外部依赖太慢:模型服务响应慢、目标页面加载慢、本地网络不稳定。先把每一段反应时间测出来,再决定是调大超时还是减少工具调用次数。

6.4 页面元素找不到、点击没反应

这类问题通常和 Agent 无关,而是页面自动化本身的问题。原因一般是:

  • 页面是动态渲染,元素加载有延迟。
  • 选择器不稳定,class 名是动态生成的。
  • 页面根本没有这个元素,可能 URL 或状态不对。

排查方式:

  1. 先手动打开页面,确认元素是否存在。
  2. 在代码里增加等待逻辑,等待关键选择器出现。
  3. 失败时保存页面截图,人工确认页面实际状态。

选择器方面,我更喜欢用get_by_textget_by_role这类语义化定位方式,而不是复制一长串带哈希的 class 名。语义化定位在页面结构变化时,失败概率要低很多。

6.5 通用排查顺序

现象先查什么再查什么
命令找不到Python 依赖PATH 环境变量
浏览器启动失败内核是否安装缓存目录权限
Agent 输出为空LLM 服务输入格式
工具调用失败函数异常处理页面结构和选择器
批量任务卡死并发数资源占用和日志

遇到报错不要第一反应改 Prompt。先看现象,再看输入和日志,最后才动代码和参数。很多所谓“Agent 不听话”的问题,底层都是数据格式、权限、依赖版本或者网络环境的问题。

7. 这个方案的真实边界:别把 Agent 当“全自动工具”

7.1 适合什么场景

这套方案最适合的场景是:目标明确、页面结构相对稳定、你有权限自动化访问的站点。

我把日常用得上的场景分成三类:

  1. 信息收集和汇总:让 Agent 打开多个页面,提取关键信息,再做对比和总结。
  2. 页面巡检:定时打开业务页面,检查标题、价格、库存等关键信息是否正常。
  3. 自动化测试辅助:用 Agent 生成动态测试步骤,辅助测试人员执行回归验证。

这些场景的共同点是:任务边界清楚,失败成本可控。就算 Agent 某次判断错了,也不会造成严重后果,最多重跑一次。

7.2 不适合什么场景

反过来,有些场景不建议硬用这套方案:

高压反爬环境、需要频繁验证码验证、登录流程极其复杂的站点,不适合。不是因为 Agent 做不到,而是这类页面的自动化本身就有合规和稳定性风险,做得越多越容易失控。

需要很低延迟、对每次操作路径有绝对控制要求的任务,也不适合。Agent 的每一步都是大模型临时决定的,输出存在随机性,不可能像传统脚本那样精确到每一步必然执行什么。

还有一类不适合:涉及敏感数据、未授权信息获取、绕过访问控制的场景。这种不管技术可行性如何,都不应该做。

7.3 生产化之前要补什么

如果你决定把一套 Agent 用到生产环境,至少需要解决这几个问题:

  1. 日志和审计。Agent 每一步调用了什么工具、读了什么页面、做了什么判断,都要留痕。不是管理员不信任,而是出了问题要能回溯。
  2. 资源回收。浏览器实例、Page 对象、临时文件,用完必须释放。不做资源管理的 Agent 跑一天,内存会吓到你。
  3. 队列和限流。不要写一个无限并发的循环开跑。控制好并发数,给每个任务设置超时时间。
  4. 人工审核。Agent 的中间操作和最终结论,在正式流程里建议有人审一遍。目前的大模型 Agent 还没有到“全自动无人值守”的火候,把最终人工审核保留下来,比任何参数调优都管用。
  5. 成本控制。设置单次任务的 token 上限、工具调用次数上限。没有限制的 Agent,可能在一次小任务里反复调用工具,账单涨得比预期快。

7.4 下一步进阶方向

如果单 Agent 跑顺了,准备做更复杂的流程,可以考虑两条路径。

一条是迁到 LangGraph,把浏览器工具拆成多个节点,通过状态图控制分支和人工介入。比如“先打开列表页,再选择符合条件的详情页,最后汇总生成报告”,这种流程用 LangGraph 表达会更清晰。

另一条是关注 MCP 这类协议。MCP 提供了一种统一的方式,把浏览器能力、数据库、文件系统等外部资源暴露给模型。以后做 Agent 工具接入,可能不再需要为每个框架单独写适配,协议层能统一掉一部分工作。

不管选哪条路,我的建议都是先把当前这套简单组合用熟。先跑通最基础的信息获取场景,再逐步加复杂度。工具越多、流程越长,出问题的概率不是线性增长,而是指数级增长。

我个人更建议把单任务跑稳,再考虑批量和接口。这个方案真正落地时,最该盯住的不是它聊得有多智能,而是每一步操作有没有记录、有没有超时、有没有把页面内容弄错。工具只是把流程自动化,判断标准始终要放在:输入是否清晰,环境是否稳定,结果是否可审计。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 15:56:00

电影院票务系统全栈开发:从微信小程序到高并发库存管理

简介:本资源是一套面向计算机专业本科生的微信小程序毕业设计实战项目,专为大作业与毕业设计选题提供完整解决方案,解决学生缺乏可运行、可答辩、可扩展的全栈小程序案例的痛点。压缩包共2000个文件,62.1MB,涵盖301个J…

作者头像 李华
网站建设 2026/9/4 8:23:59

图学习会议 2024 笔记(八)

https://github.com/OpenDocCN/dsai-notes-pt1-zh/raw/master/docs/gph-lrn-con-2024/img/ff9d476a8b35c0eb1dc5acf292f1d5c2_3.png 上一节我们概述了教程内容,本节中我们来看看具体的问题设定。 我们假设有一个包含 n 个相关时间序列的集合 D。每个时间序列可以是…

作者头像 李华
网站建设 2026/9/6 0:36:30

基于机器学习的作物叶绿素含量预测:从RGB图像到农业健康监测

简介:本资源是一项面向农业遥感与植物表型分析领域的本科毕业设计实践项目,聚焦于利用图像特征预测叶片叶绿素含量这一关键生理指标,适用于农学、遥感、计算机视觉及机器学习初学者开展跨学科课题研究。压缩包共8个文件,含2个Pyth…

作者头像 李华
网站建设 2026/9/1 21:39:31

Beamer与LaTeX:告别PPT排版痛点,实现内容样式分离

1. 背景与核心概念1.1 做 PPT 的常见痛点在准备技术分享、项目答辩或者组会汇报时,制作 PPT 往往比准备内容本身更消耗时间。很多人都有类似的体验:内容大纲很快就能列完,但真正花时间的是排版细节——调整文本框位置、统一各级标题字号、处理…

作者头像 李华
网站建设 2026/9/3 1:05:57

Spring AI 2.0构建Java Agent实战:仿ClaudeCode工具调用与任务拆解

这次我们来看一个经常被问到的组合: Spring AI 2.0 Agent Utils Spring AI Alibaba ,目标是用 Java 生态,像 ClaudeCode 那样搭出一个能对话、能调用工具、能拆解任务并逐步执行的 Agent 项目。如果你正在做 Java 大模型 Agent 开发&…

作者头像 李华
网站建设 2026/9/3 7:03:25

零基础学CAD2027:掌握完整绘图流程与高效命令组合

学CAD的人有一个共同的困惑:断断续续学了几十个小时,命令背了不少,真到要独立画一张图纸的时候,还是不知道第一步该点什么。这不是个例。我见过太多初学者,报班、刷视频、记笔记,努力程度没问题&#xff0c…

作者头像 李华