news 2026/10/21 9:44:12

Playwright Web自动化测试实战:元素定位与跨浏览器技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Playwright Web自动化测试实战:元素定位与跨浏览器技巧

1. 开场:别再从Selenium开始了

如果你是一个刚入行或转岗测试开发的工程师,大概率已经听过不少关于Playwright的声音。如果你还在纠结"要不要试试这个新的自动化工具",我建议你直接看完这篇文章:Playwright是一个由开源社区和商业团队合力打磨的自动化测试框架,主打跨浏览器、跨平台、高稳定性的Web端到端测试,同时也支持移动端Web模拟、接口Mock、文件下载、多标签页管理等一系列操作。

我做Web自动化测试做了七八年,早年用Selenium比较多,中间也折腾过Cypress,后来逐步把核心回归测试迁移到Playwright上。我对这套工具最大的感受是:它把很多以前需要自己封装、自己踩坑的能力,直接内置到了框架里。换句话说,它用极其清爽的API设计,解决了过去Web自动化测试里最常见的三大痛点——元素定位不稳定、等待难管理、调试太费劲。如果你现在正被脚本跑着跑着就超时、定位器经常换一次页面结构就要大面积改代码、或者维护一套跨浏览器的测试代码需要三份逻辑,那Playwright大概率会让你觉得解脱。

这篇文章里,我会直入主题,从环境安装开始,带你把这个工具跑起来,然后完完整整走一遍元素定位的实战过程,最后把我在真实项目里踩过的一些坑也一并分享出来。不论你是完全没写过自动化脚本的小白,还是从其他工具迁移过来的老手,跟着这套思路走,都能比较快地进入状态。

2. 为什么我建议你认真考虑Playwright

2.1 好几个框架摆在面前,Playwright赢在哪

自动化测试框架现在并不少,Selenium依然是老牌大哥,Cypress在纯前端圈子里也很流行。但如果你接触过的业务场景足够多,就会意识到一个事实:Selenium的上手成本虽然低,但稳定性和维护成本偏高;Cypress虽然开发体验好,但它在浏览器支持和多标签页的处理上相对受限;而Playwright虽然出道时间晚一些,却在架构设计上借鉴了前两者的经验,把很多常见麻烦直接做进了框架内核。

我用一个实际的例子来解释这种差距。以前我们用Selenium写登录流程测试,最让人头疼的就是等待元素出现,大概率得自己封装各种Wait方法,比如显式等待轮询、处理StaleElement异常,代码写起来长且容易出错。而Playwright内置了一套自动等待机制,执行click或fill这类操作之前,它会自动判断元素是否存在、可见、可点击,然后等到条件满足时才真正操作。这个机制听起来只是少写几行代码,但它减少了大量的时序类偶发问题,在实际跑测试时稳定性提升非常明显。

另一个Playwright赢得很明显的点是多浏览器支持。同一套代码几乎不用改动,就可以跑Chromium、Firefox和WebKit(Safari的底层内核)。这一点在以前简直是奢望,Selenium时代我们要维护三套兼容逻辑,测试时间也长。Playwright底层采用了一套协议来驱动浏览器,执行速度反而很快,日常维护成本也低。如果你是做前端组件库或者公共业务系统的测试,跨浏览器验证几乎是刚需,这里选型基本没有太多纠结余地。

2.2 它解决了哪些让人头疼的痛点

如果你问我以前做Web自动化最痛苦的时刻是什么,我一定会说是定位器写得太脆。页面一个按钮的class属性稍微调整,脚本就歇菜;动态加载的内容晚了几毫秒,脚本就报NoSuchElement;弹出iframe之后,所有外层选择器全部失效。这些问题在Playwright里大多有原生的解决方案。

Playwright提供了一套名为locator(定位器)的API,它不再只是返回一个WebElement对象,而是包含了一整套完整的定位、等待和操作策略。你可以用它定位文本、属性、CSS选择器、XPath,甚至可以根据元素之间的层级关系组合定位。更重要的是,定位器对象本身是可重试的,每次执行动作时会重新查找页面元素,天然规避了旧Selenium脚本里常见的元素失效问题。

此外,Playwright内置了强大的调试工具,比如Trace Viewer(录制操作轨迹)、Codegen(录制脚本)、慢动作模式等。第一次接触这套调试流程的时候我有点惊讶:原来自动化测试的调试体验可以做得跟代码调试一样清晰,能回放每一步操作,能看到每个元素定位时的网络请求和状态快照。这对于定位失败类问题是极大的效率提升。

基于这些体验,我非常建议现在的测试开发同学优先学Playwright。它的API设计理念很现代,表达力强,社区生态也在快速成熟,无论未来你是去大厂面试还是自己写工具脚本,这个技能都会很有竞争力。

3. 从零开始:安装与项目初始化

3.1 准备工作与环境要求

我把Playwright跑通的环境写在这里,方便你对照参考:操作系统是Windows 11(macOS和Linux也完全支持),Node.js用的是18 LTS版本,IDE用的VSCode。Playwright官方对Node.js的版本要求是16以上,实际上18、20版本我都用过,运行稳定。唯一的忠告是:不要用老掉牙的Node 12或14,因为部分API和依赖在老旧环境下会对不上,出错时无从排查。

安装之前你需要先有一个项目文件夹。可以考虑给它起一个清晰的名字,比如web-auto-test,然后在终端里进入该目录,执行:

npm init -y

这个命令会生成一个package.json。如果你对npm比较熟悉,也可以手动建文件,但init -y是最快的路径。

接下来安装Playwright库本体:

npm i -D @playwright/test

注意,这里特意加上了-D,把它作为开发依赖安装。为什么?因为Playwright本身只是一个测试工具,不会跑到生产环境里,放进开发依赖既符合工程习惯,也能避免在生产部署时下载一堆无关包。如果你还计划在项目里使用Playwright库去写非测试类的自动化脚本,比如自己写的爬虫或者页面巡检工具,那还需要额外安装playwright核心库:

npm i -D playwright

不过单纯做端到端测试,装@playwright/test就够了,它内部已经集成了所有需要的核心模块,包括测试运行器。

3.2 安装浏览器内核

安装这一步,很多新手会栽跟头。光装npm包还不行,Playwright需要下载相应的浏览器内核才能驱动浏览器执行。这里走的不是普通浏览器安装路径,而是它自己维护的浏览器副本,以保证兼容性:核心命令是:

npx playwright install

执行这行命令后,它会自动下载当前版本匹配的Chromium、Firefox和WebKit。这个下载体积不算小,主要是浏览器完整包。如果你在公司网络环境受限,安装失败是很常见的事情,解决办法可以是用国内镜像源:

npx playwright install --with-deps

如果你的机器上已经装了Chrome或者Edge,也可以让Playwright直接复用系统浏览器,在启动浏览器时指定通道(channel)。比如:

// playwright.config.js 里配置 use: { channel: 'chrome' }

但这里我想给你一个务实的建议:如果只是学习入门,直接用默认的Chromium就行,没有必要折腾系统浏览器通道。因为Playwright自带浏览器版本和API高度匹配,省去一大部分兼容性排查工作。后面我再讲怎么在正式项目里选择合适的浏览器策略。

3.3 跑通第一个脚本

在项目根目录下新建一个测试文件,比如first.spec.js。Playwright测试文件后缀一般是.spec.js或.spec.ts,这也是它测试运行器默认扫描的文件格式。文件中写一个最简单的用例:

const { test, expect } = require('@playwright/test'); test('首页标题正确', async ({ page }) => { await page.goto('https://example.com'); const title = await page.title(); await expect(page).toHaveTitle(/Example/); });

先不急着写复杂场景,这段脚本的作用是验证环境是否完好。在终端里执行:

npx playwright test first.spec.js

如果一切正常,你应该能看到测试通过的信息,同时终端会打印通过数量和耗时。如果你看到类似"Executable doesn't exist"的报错,就说明浏览器内核没有装好,重新按3.2小节安装即可。

这里我多说一句:在我的经验里,第一次跑通脚本后,一定不要立刻进入写复杂场景的环节,先手动改一下goto地址,改一下标题断言,故意制造几次失败,感受一下Playwright在失败时输出的错误信息长得什么样。这个"刻意出错"的过程能帮你快速理解它的运行机制,后续出了问题你会比别人更淡定。

4. 元素定位实战:核心方法与选择器策略

4.1 认识locator:定位不再是找一个元素

过去用Selenium写页面对象模型的时候,每个人都有自己一套查找元素的方法,常见的By.id、By.xpath等。Playwright虽然也支持page.$这类底层选择器,但它主推的用法是用page.locator()创建一个定位器,这个定位器不是静态的元素引用,而是一个动态的"查找规则"。

我从一个实际业务场景开始讲。还是拿登录页面做例子,假设有一个用户名输入框,它的DOM大致是:

<input id="username" name="username" placeholder="请输入用户名" />

那么你可以这样定位:

const usernameInput = page.locator('#username');

如果ID不存在,就直接用placeholder属性:

const usernameInput = page.locator('input[placeholder="请输入用户名"]');

乍一看,和CSS选择器语法几乎一样,但locator真正强大的地方在于,它可以像方法链一样组合条件、过滤和层级查找。比如页面上有两个类似结构的卡片,你需要定位包含某个文本的那一个:

const targetCard = page.locator('.card', { hasText: '高级会员' });

这个写法在Selenium时代很难用一条表达式写出来,但在Playwright里就是一行。它内部会实时去DOM里匹配条件,然后返回符合要求的元素。

4.2 定位方式的优先级与使用心得

结合我的日常实践,给你一个定位优先级参考表格:

优先级定位方式示例适用场景
1文本定位page.getByText('登录')按钮、链接、标题等带可见文本的元素
2角色定位page.getByRole('button', { name: '提交' })适合表单按钮、可访问性友好的页面
3标签+属性page.locator('input[name="email"]')表单控件,稳定且语义清晰
4测试专属属性page.locator('[data-testid="login-btn"]')前端团队约定维护测试标识的场景
5CSS / XPathpage.locator('div.container > ul > li:nth-child(2)')结构相对固定的复杂DOM

我个人的习惯是优先用getByRole和getByText这一类贴近用户感知的方式,因为文本和可访问性角色通常不会频繁变动,即使前端重构DOM结构,只要文案不变,测试依然能跑;而CSS层级结构只要多加了一个容器,可能整个选择器就断了。

在真实项目里,我见过很多测试同学喜欢在浏览器里右键复制XPath直接粘进代码,结果下次改版脚本大面积飘红。原因是复制的XPath往往又长又脆,里面充满了index下标依赖。Playwright官方文档不推荐直接使用Copy XPath,我强烈建议你也不要依赖它,学会自己看懂DOM结构,写短小精悍的定位表达式。

4.3 文本定位、属性定位与组合定位

文本定位是Playwright很有特色的能力。比如页面上有几个按钮,其中一个写的是"立即购买",你可以直接:

await page.getByText('立即购买').click();

如果担心部分匹配导致命中多个元素,可以改成一个更精确的限定:

await page.getByText('立即购买', { exact: true }).click();

或者使用角色定位,在语义上会更清晰:

await page.getByRole('button', { name: '立即购买' }).click();

getByRole的底层借助了ARIA规范,它对页面可访问性要求高的项目特别友好。如果一个按钮还会附带图标或角标,name字段会自动匹配元素的完整可访问名称,你不需要在意内部嵌套的DOM长什么样。

组合定位是处理复杂页面的杀手锏。比如一个表格里既有"编辑"按钮也有"删除"按钮,你如果想定位"第一行数据对应的编辑按钮",可以这么做:

const firstRow = page.locator('tr').filter({ hasText: '张三' }); await firstRow.getByRole('button', { name: '编辑' }).click();

这行代码的逻辑是:先在所有表格行里过滤出包含"张三"文本的那一行,再在这一行的范围内找"编辑"按钮。链路清晰、维护性好,前端即使调整了按钮的class也不会影响脚本。

4.4 XPath与CSS选择器的正确打开方式

虽然我建议你在大多数情况下优先用文本和角色定位,但有些场景确实离不开结构化选择器。例如翻页控件、树形菜单、自定义组件内部的选择器,这些组件往往没有合适的语义化文本。此时CSS和XPath仍然是兜底方案。

CSS选择器的写法大家都熟悉,比如:

await page.locator('.pagination .next').click();

XPath的写法也是支持的:

await page.locator('xpath=//div[@class="pagination"]//button[contains(text(),"下一页")]').click();

注意这里要用xpath=前缀,或者直接不写前缀但传入的字符串以//开头,Playwright一般也能识别。还有一个我经常用的小技巧:在XPath中使用relative位置,比如定位某个元素之后的兄弟节点:

await page.locator('xpath=//input[@id="username"]/following-sibling::button[1]').click();

很多新手在实际项目里都会遇到一个困扰:页面里存在多个同name或同class的元素,不想用index下标,觉得不稳。这时更推荐使用and连接多个属性条件,缩小范围:

await page.locator('input[type="text"][name="username"][data-scope="login"]');

而不是写一个漫长的//*[@id="..."]/div[3]/div[2]/div/input。

4.5 iframe、Shadow DOM与动态元素定位

真实业务里难免遇到iframe嵌套页面,比如一些老旧的富文本编辑器、客服聊天框、第三方支付组件。Playwright处理iframe的方式相当优雅,它可以通过frameLocator直接进入iframe内部:

const editorFrame = page.frameLocator('#rich-text-editor'); await editorFrame.locator('.ql-editor').fill('这里是内容');

框架会自动处理iframe的切换和内部元素等待,不需要像Selenium那样先switchTo再操作,写完忘记切回还会踩坑。

Shadow DOM也在越来越多的组件库中出现。Playwright的定位器默认支持穿透Shadow DOM,比如你有一个自定义组件,内部结构是这样:

<my-button> #shadow-root <button class="inner-btn">确定</button> </my-button>

你可以直接用:

await page.locator('my-button .inner-btn').click();

它会从外部定位进去,不需要额外写任何特殊API。这一点在做基于Web Components的组件测试时会省下不少力气。

对于动态元素,比如接口返回后异步渲染的列表,Playwright的自动等待很大程度上已经帮你规避了"找太早找不到"的问题。你需要做的是合理利用断言来等待,而不是用固定sleep。例如:

await expect(page.locator('.user-name')).toHaveCount(3);

这样的断言会带着轮询机制去等待条件达成,最多等到设置的timeout。比你手动加await page.waitForTimeout(3000)要可靠得多。实际上,固定等待是我在代码评审里最反感的一种写法,它消耗时间且不能真正解决时序问题。

5. 实战中遇到的高频问题与排查技巧

5.1 元素定位失败:从超时信息反推原因

当你遇到TimeoutError,Playwright会输出一段非常具体的错误信息,包括:

  • 目标页面的完整URL
  • 走了哪个定位器(locator)表达式
  • 等待了多久(默认30秒)
  • 当前页面上有哪些元素与定位器部分匹配

我先说一个最常踩的坑:定位器表达式部分匹配成功,但不是完全匹配。比如你写了page.locator('button', { hasText: '提交' }),但页面上有多个带"提交"字样的按钮,就会导致校验失败。解决办法很简单,增加一个更精准的过滤条件,或者改用getByRole并指定name。

另外,很多定位失败其实是因为页面处在"加载中"状态,比如按钮此时处于disabled状态。你可以用expect的toHaveAttribute来等它变成可点击状态,再继续操作:

await expect(page.locator('#submit-btn')).toBeEnabled(); await page.locator('#submit-btn').click();

这样脚本充分尊重前端状态,不会因为点太快而失败。

排查建议:先看报错信息里面locator解析后的实际表达式,然后在浏览器开发者工具里手动执行document.querySelector来判断这个定位器在当下DOM中是否命中。如果手动命中,说明问题大概率是时序;如果手动也命中不了,说明选择器确实有问题,需要重写。

5.2 定位到多个元素时怎么办

页面出现重复结构是很正常的,比如商品卡片、列表行、tab标签。如果你的定位器一下匹配到多个元素,但你还去执行click,Playwright会直接抛错:strict mode violation。因为框架默认是严格模式,不允许在多个结果上执行动作,避免你无意识操作到错误的元素。

解决办法通常有三种:

  1. 通过hasText缩小范围,只保留包含某段文本的项;
  2. 通过nth(index)选中第几项,locator('.card').nth(0);
  3. 用first()或last()快速选择首尾项。

使用nth或first前,建议想清楚业务意图。如果页面顺序是后端接口动态返回,那么位置并不稳定,还是优先用文本或数据属性来定位更稳妥。

5.3 动态内容延迟加载怎么办

服务端渲染转客户端渲染之后,"元素最终会出现"这个事不再必然。很多页面初始只显示骨架屏,数据回来之后才渲染真实内容。此时如果你只是简单等元素可点击,可能等到的是假元素。

我通常会在这类场景里加一层带业务含义的断言,例如等列表中的具体数据出现:

await expect(page.locator('.list-item').filter({ hasText: '目标课程' })).toHaveCount(1);

这个断言会在数据真正渲染完成后才通过。如果你还要继续操作列表中的某个按钮,再在断言之后重新定位即可。

另外提醒一下,不要在调试时靠无限增大timeout来掩盖问题。把timeout调到90秒甚至更久,只会让失败时等待更煎熬,它不会让元素真的出现。正确的方向是搞清楚数据为什么加载慢、异步链路为何没完成,去优化前端或调整测试介入时机。

5.4 调试利器:Trace Viewer与慢动作

Playwright提供的调试工具是它区别于老牌框架的明显优势。一旦用例失败,我们可以先生成trace文件,然后在浏览器里逐步查看执行过程:

npx playwright test --trace on

它会在测试运行过程中记录页面快照、网络请求、控制台输出以及每个定位器的解析结果。测试结束后执行以下命令调出查看器:

npx playwright show-trace test-results/xxx.zip

界面里能看到每一步操作的时间线和DOM快照,失败的最后一步直接标红,还显示定位器在那一刻匹配了几个元素。我遇到过不少棘手问题,都是靠trace一眼看出来的,比如"我定位的元素在那一刻还没渲染出来"或者"输入框被其他元素遮挡,点击落在了别的位置"。

如果你在开发调试阶段想观察操作过程,还可以启动慢动作模式:

npx playwright test --headed --slow-mo=500

每步操作之前停顿500毫秒,肉眼能看清脚本都做了什么。对于选择器本身写得太慢或路径太绕的问题,这种方式比直接跑全量测试更直观。

6. 从入门到落地:组织测试代码与持续集成

6.1 fixture与Page Object的取舍

当你不再是只写一两个脚本,而是维护一整套端到端用例时,代码组织就变得很重要。Playwright原生支持fixture机制,它可以在每个测试执行前后进行环境初始化和清理。

最简单的fixture用法是在配置文件中定义baseURL:

// playwright.config.js module.exports = { use: { baseURL: 'https://example.com', viewport: { width: 1280, height: 720 }, trace: 'retain-on-failure' } };

然后在测试里使用相对路径:

await page.goto('/login');

这样环境切换时你只需改配置文件,所有用例自动跟随变化。

关于Page Object模式,我的建议是按照团队规模来权衡。如果你的项目只有几十个用例,直接按页面模块写函数就好,不用强行抽象出一堆类封装,反而增加理解成本。但当你需要多人协作维护几百个用例时,Page Object的价值就体现出来了:它把页面细节封装在一个地方,用例层面只关注业务动作,后续前端改版时,你只需要修改页面封装文件,而不是翻遍所有用例。

// pages/LoginPage.js class LoginPage { constructor(page) { this.page = page; this.usernameInput = page.locator('#username'); this.passwordInput = page.locator('#password'); this.submitButton = page.getByRole('button', { name: '登录' }); } async login(username, password) { await this.usernameInput.fill(username); await this.passwordInput.fill(password); await this.submitButton.click(); } } module.exports = { LoginPage };

用例里这样调用:

const { test } = require('@playwright/test'); const { LoginPage } = require('../pages/LoginPage'); test('普通用户登录成功', async ({ page }) => { const loginPage = new LoginPage(page); await page.goto('/login'); await loginPage.login('demo', '123456'); await expect(page).toHaveURL(/\/dashboard/); });

这种写法读起来非常像一份需求说明书,谁来看都能很快理解用例的业务场景。

6.2 配置与CI集成细节

写了这么多用例,如果只在你本地跑,价值有限。把它接到持续集成流水线里,才能发挥自动化回归的作用。Playwright为此提供了内置的GitHub Actions和通用CI指导,但核心思路和关键配置是通用的。

CI环境里通常没有显示器,Playwright提供了Headless模式。配置文件中默认是无头模式运行,只需确保CI环境中安装了依赖:

npx playwright install --with-deps

在流水线里执行:

npx playwright test --reporter=html

测试结束后,可以把HTML报告作为流水线产物保留下来,方便回看页面快照。

如果测试体量较大,建议使用workers并行执行,项目里配置方式是这样:

module.exports = { workers: 4, retries: 1 };

并行执行会显著缩短整体运行时间,但要注意你的被测系统是否能承受多浏览器同时操作,别因为测试太猛把对方服务打挂了,那就有点尴尬了。

另外,测试数据污染问题在自动化里特别突出。我建议每次跑用例前都有独立的测试账号或独立的测试数据准备逻辑,不要共用一套登录账号多线程跑,否则大量莫名其妙的冲突会耗费你很多排查时间。

6.3 日常维护与稳定运行建议

自动化测试最怕的问题就是"不稳定"。这里我给你几个降低不稳定性的实用建议:

  • 不依赖固定等待。能用expect、能用自动等待解决,就不要加waitForTimeout。
  • 把环境URL、测试账号等配置统一放到env文件或配置文件的use字段,让用例与环境解耦。
  • 经常重新审视定位器是否有冗余层。不要因为能用就攒着,如果底层选择器稳定,就精简成短表达式,方便后续维护。
  • 在测试报告中开启截图和视频,失败时自动留存证据。配置方式:
use: { screenshot: 'only-on-failure', video: 'retain-on-failure' }

视频在排查偶发问题时尤为重要,你可以回放操作全程,直观看到是什么步骤导致失败。

在做了几年自动化测试之后,我逐渐意识到一个道理:真正影响自动化项目最终效果的因素,往往不是工具本身,而是团队是否愿意把用例当作一份长期维护的工程资产来对待。Playwright给了我们一个很好用的地面,但我们自己依然需要建立干净的测试数据、清晰的页面封装、稳定的执行环境,这些才是自动化价值的护城河。

7. 最后还是想说的几句实在话

说实话,Playwright刚出来那阵子,我也是抱着试试看的心态在边角项目里用了两次,并没有立刻全面替换老框架。真正让我下决心迁移的转折点,是有一次线上回归测试里,系统因为某个页面加了一个遮罩层,导致原本的点击操作连续失败,我花了整整一个下午都没定位到是哪个元素被挡住了。后来同事建议我用Playwright的自动等待和trace重跑了一遍,几分钟就看出问题所在——定位器匹配到了外层遮罩层的元素,而不是真正想要操作的那个按钮。那天之后,我开始逐步把核心业务流程的测试都迁移过来,到目前运行状况都比较稳定。

在学习路径上,我的建议是沿着"安装跑通→基础定位→业务场景模拟→复杂组件处理→POM模式→CI集成"这个顺序推进,不要一上来就啃源码或者研究高级网络Mock,那些等有需要时再学也来得及。这套工具足够现代,也足够灵敏,值得你花时间认真学一遍。如果你在实际操作中遇到卡壳的地方,不妨回想一下本文提到的自动等待和locator机制,很多疑问会自己解开。

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

基于K210与MaixCam的人脸识别门禁系统:串口通信协议设计与实现

1. 项目需求与硬件选型拆解 1.1 这个项目到底要解决什么问题 这类门禁考勤Demo的技术链路其实很典型&#xff1a;摄像头采集画面&#xff0c;本地完成人脸检测和识别&#xff0c;把“谁来了”这个结论通过串口发给下位机&#xff0c;下位机根据结果决定开不开锁。整个链路里&a…

作者头像 李华
网站建设 2026/10/21 9:43:46

内存取证实战:从Volatility进程分析到文件提取与隐写解flag

做CTF的Misc方向做了几年&#xff0c;我最大的感受是&#xff1a;内存取证题属于那种“看着不难、一上手就头疼”的类型——丢给你一个几百MB甚至1GB的raw镜像&#xff0c;让你在里面翻出flag&#xff0c;既考验工具熟练度&#xff0c;又考验对操作系统痕迹的敏感度。这次想整理…

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

提示工程架构师:从写提示词到设计人机协作系统

这几年“提示工程”这个词热度一直没降&#xff0c;但大多数人的理解还停留在“会写提示词”这个层面。事实上&#xff0c;真正让AI应用从“演示能用”走到“生产环境稳定跑起来”的&#xff0c;是一类更系统的角色——提示工程架构师。这个角色不是把一句话写得更漂亮&#xf…

作者头像 李华
网站建设 2026/10/21 9:43:08

OCR乱码怎么办?LLM校对+翻译流水线全解析

你接手了一个图片翻译需求&#xff0c;第一批样片刚跑完&#xff0c;OCR 引擎就把“合同编号”识别成了“合同皌号”&#xff0c;“发货日期”变成“发货凵期”。更气人的是&#xff0c;这种乱码不是零散出现的&#xff0c;同一个错字换个阈值就变一个花样。我早几年做文档数字…

作者头像 李华
网站建设 2026/10/21 9:42:42

西门子SMART200与MCGS触摸屏的液压伺服PID闭环控制系统完整方案

1. 项目整体架构与方案选型先把这个项目的底细说清楚。这可不是一个简单的PLC点动启停程序&#xff0c;而是一套完整的液压伺服闭环控制系统。系统里同时有PLC、触摸屏、伺服驱动器和液压执行机构&#xff0c;四个大件协同工作&#xff0c;任何一个环节掉链子&#xff0c;整个项…

作者头像 李华
网站建设 2026/10/21 9:42:41

Java Swing计算器实战:从GUI事件驱动到表达式解析

1. 项目概述&#xff1a;为什么一个“四则运算计算器”值得花两周时间认真做&#xff1f;你可能第一眼看到“基于Java/Swing的四则运算计算器”这个标题&#xff0c;心里会嘀咕&#xff1a;这不就是大一刚学完Swing组件后老师布置的课设吗&#xff1f;网上随便一搜几十个源码&a…

作者头像 李华