news 2026/10/21 9:56:26

基于Selenium的多语言站点自动化测试实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Selenium的多语言站点自动化测试实战指南

之前做过一个面向海外用户的产品,界面同时支持中、英、日三种语言。测试团队光是每天手工切换语言回归一遍核心流程,就要占用一个人大半天的时间。加上后来需求越加越多,语言版本从三种扩到五种,再靠人去切来切去点来点去,效率低倒是其次,关键是漏测的东西太多了——经常出现中文环境一切正常、切到英文或日文就按钮找不到、文案截断、日期格式错乱之类的问题。

后来我干脆把整套多语言站点的自动化测试用 Selenium 搭了起来,把“切换语言”这个动作彻底参数化,一份用例跑遍所有语言版本。这篇实战指南就是把当时沉淀下来的思路、踩过的坑、以及最终可复用的代码框架完整写出来,给正在跟多语言站点死磕的测试工程师做个参考。

1. 多语言站点测试的痛点和整体破局思路

1.1 为什么多语言站点这么难测

多语言站点跟普通站点最大的差别,不是多了几个翻译文件,而是同一个产品在每一种语言下都变成了一个“半独立”的系统。这带来几个很直接的测试难题。

第一,元素定位不稳定。很多测试脚本习惯用按钮文案写 XPath,比如//button[text()='提交']。这在中文环境跑得好好的,切到英文环境,文案变成Submit,脚本立刻挂掉。如果产品里文案是动态拼接的(比如“共 N 条记录”),就更没法用固定文本来定位了。

第二,语言切换本身的状态管理容易出问题。有的站点用 Cookie 保存语言偏好,有的用 URL 路径(/en、/zh-CN),有的还分“首次访问默认语言”和“用户手动切换语言”两套逻辑。自动化脚本如果没处理好切换机制,经常出现用例之间互相“污染”——上一个用例把语言切成了英文,下一个用例没重置,直接在当前语言环境下继续跑,断言就乱了。

第三,本地化之后不仅仅是文案变了。日期格式、数字千分位、货币符号、时间的时区显示都可能不同。比如英文环境下日期是2025/03/15,中文环境下可能是2025年3月15日,断言的时候如果硬按一种格式去匹配,必然会挂。这些都属于“语言壁垒”的一部分,光靠肉眼手工测试很难每次都注意到。

1.2 为什么选 Selenium 而不是其他工具

做多语言自动化测试,可选的技术栈其实不少,比如 Playwright、Cypress、Appium 等。我最终主力选择 Selenium,主要看中三点。

一是语言生态成熟。Selenium 支持的编程语言非常多,Java、Python、JavaScript、C# 都有官方绑定。团队里无论擅长哪种语言,都能很快上手,不用为了测试工具单独学一门新语法。

二是社区案例多。多语言站点测试遇到的问题,比如动态元素、iframe、下拉框、文件上传,几乎都在社区里被讨论烂了,遇到奇葩问题基本能搜到解决方案。对于测试团队来说,可维护性和问题排查速度非常重要。

三是对浏览器的覆盖稳。Selenium 通过 WebDriver 直接驱动真实浏览器,对 Chrome、Firefox、Safari、Edge 的原生行为支持都比较完整。多语言站点经常要验证不同浏览器下的表现,这一点尤其重要。

当然,Selenium 也有自身短板,比如对前端框架的自动等待支持不如 Playwright 好,脚本写得不严谨就容易偶发失败。但这些问题可以通过合理的等待策略和封装来缓解,后面实操部分会讲到具体做法。

1.3 多语言自动化测试的整体方案设计

整个方案的核心思路,是把“语言”从测试代码中抽离出来,当作一个可配置的参数来驱动脚本。这样一来,同一套测试逻辑只需要维护一份,语言版本再多也不会让脚本代码成倍膨胀。

具体来说,我把它分成三层。

第一层是语言配置层。定义一套语言标识,比如en、zh_CN、ja,然后通过配置文件、环境变量或者测试启动参数传入。测试脚本启动时,根据当前传入的语言标识,自动决定使用哪个语言环境的操作方式、断言文案和 URL 路径。

第二层是元素对象层。对每个页面的关键元素,维护一份“多语言定位符映射”。比如登录页的登录按钮,在英文环境下通过id="login-btn"定位,在中文环境下同样用这个 id 定位——这里特别重要,等下我会展开讲为什么不要依赖文案定位,而是优先用稳定属性。如果某些元素在不同语言下的属性有差异,就在这一层做适配。

第三层是断言数据层。维护一份“多语言断言配置文件”,存着每种语言下页面上应该出现的标志性文案、格式化规则、预期 URL 片段等。断言的时候,脚本只从这份配置里取数据,不把具体语言文案写死在代码里。

这套结构的好处是,新增一个语言版本,测试人员只需要往配置里加一组数据,不需要改动测试逻辑代码。如果业务逻辑本身没变,连用例都不需要新增。

2. 核心细节:语言切换机制与元素定位策略

2.1 语言切换的三种主流方式及选型建议

多语言站点的语言切换机制,常见的有三种:URL 路径切换、Cookie/LocalStorage 切换、界面点击切换。自动化脚本在切换语言时,必须先搞清楚目标站点用的是哪种,否则后面全部白搭。

第一种,URL 路径方式。站点通过 URL 前缀区分语言,比如https://example.com/en和https://example.com/zh-CN。这种设计在技术上最简单,自动化脚本只需要用driver.navigate().to()直接访问对应 URL 即可。难点在于,很多站点并不允许直接跳转到某个语言子路径——如果你直接访问/en/login,服务端可能先做重定向,甚至因为当前会话状态非法而跳到错误页。我在项目里遇到过这种情况,所以脚本里加上了一个“跳转后校验”的步骤,确认最终的currentUrl包含预期的语言前缀,否则就抛异常。

第二种,Cookie 或 LocalStorage 方式。站点把语言偏好存在浏览器本地,首次访问时用默认语言,之后根据存储的值渲染。自动化脚本里,推荐的做法是在启动浏览器之后、访问页面之前,先把 Cookie 注入进去。这里有个非常容易踩坑的地方:Cookie 通常有域名作用域,必须先访问一次目标域名创建会话,再通过addCookie写入语言 Cookie,然后刷新页面才能生效。如果顺序反了,Cookie 写不进去,语言自然切不过去。

第三种,界面点击方式。脚本先定位语言切换控件(比如页面底部的下拉框,或者右上角的地球图标菜单),点击之后选择目标语言。这种方式最接近真实用户操作,但也是三种方式里最容易出问题的——语言控件本身要能稳定定位,点击之后页面可能异步加载新文案,必须做好等待。

我的选型经验是:如果站点支持 URL 或 Cookie 方式,优先用这两种,因为它们快、稳定,不受页面加载时机影响。UI 点击方式适合作为补充,专门用来测试“用户手动切换语言”这条用户路径本身是否正常。

2.2 元素定位:永远别用文案,除非万不得已

做多语言自动化测试,定位元素的第一原则是:文案不能作为定位依据。原因很简单,同样一个“提交”按钮,在中文、英文、日文下分别是“提交”“Submit”“送信”,如果脚本里写死了文案,换一种语言就得改一次脚本,这就把参数化完全破坏了。

那用什么定位?按优先级依次排:

第一优先是稳定属性。ID、name、><dependencies> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.14.1</version> </dependency> <dependency> <groupId>io.github.bonigarcia</groupId> <artifactId>webdrivermanager</artifactId> <version>5.6.3</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> </dependencies>

3.2 语言配置类:把语言变成一个人人可改的参数

语言配置的核心作用,是让测试人员在不改代码的前提下,通过环境变量、配置项或命令行参数来指定运行哪一套语言。我先把全局配置读进来:

public class LanguageConfig { private static final Properties properties = new Properties(); private static final String DEFAULT_LANGUAGE = "en"; static { try (InputStream input = LanguageConfig.class .getClassLoader().getResourceAsStream("config/global.properties")) { properties.load(input); } catch (IOException e) { throw new IllegalStateException("Cannot load global.properties"); } } public static String getLanguage() { // 优先级:运行时环境变量 > 系统属性 > 配置文件 > 默认值 String language = System.getenv("TEST_LANG"); if (language == null || language.isEmpty()) { language = System.getProperty("test.lang"); } if (language == null || language.isEmpty()) { language = properties.getProperty("test.lang"); } return (language == null || language.isEmpty()) ? DEFAULT_LANGUAGE : language; } public static String getBaseUrl() { return properties.getProperty("base.url", "https://example.com"); } public static String localizedUrl(String path) { String root = getBaseUrl(); if (root.endsWith("/")) { root = root.substring(0, root.length() - 1); } return root + "/" + getLanguage() + path; } }

这里的设计考虑是,把语言参数的决定权留给“人”。在本地调试时,开发者改一下global.properties就能跑单一语言;在 CI 流水线里,可以通过流水线参数传入TEST_LANG环境变量,同一套代码跑多国语言。语言标识的优先级链也值得说明一下——环境变量的优先级最高,因为 CI 环境里往往需要临时覆盖;配置文件的优先级最低,因为哪怕忘记设置任何参数,也能用默认语言跑起来,不至于脚本初始化直接失败。

3.3 元素定位管理:把多语言文案集中在配置文件里

元素定位这一步,我们的原则是:能不用文案就不用文案;必须用文案时,绝不写死,而是从配置文件中读取。下面用一个“语言选择下拉框”和“登录按钮”来演示这个机制。

先看英文配置messages_en.properties:

login.button.id = login-submit-btn language.selector.id = lang-switcher login.page.welcome = Welcome back login.page.username.label = Username login.page.password.label = Password

中文配置messages_zh_CN.properties:

login.button.id = login-submit-btn language.selector.id = lang-switcher login.page.welcome = 欢迎回来 login.page.username.label = 用户名 login.page.password.label = 密码

然后写一个ElementLocators类,专门从语言配置中读取元素定位信息:

public class ElementLocators { private static final String I18N_BASE = "i18n/messages_"; private static final Properties MESSAGES = new Properties(); static { String language = LanguageConfig.getLanguage(); String resourceName = I18N_BASE + language + ".properties"; try (InputStream input = ElementLocators.class .getClassLoader().getResourceAsStream(resourceName)) { MESSAGES.load(input); } catch (IOException e) { throw new IllegalStateException("Cannot load i18n messages for " + language); } } public static By loginButton() { String id = MESSAGES.getProperty("login.button.id"); return By.id(id); } public static By languageSelector() { return By.id(MESSAGES.getProperty("language.selector.id")); } public static String expectedWelcomeText() { return MESSAGES.getProperty("login.page.welcome"); } }

你可能注意到了,login.button.id这个值在不同语言配置里是一样的,那为什么不直接写死?有两种情况决定了它必须走配置:一是极少数站点前端对不同语言渲染了不同的 DOM,二是配置文件本身是“必须存在”的约束,它保证了新增语言时测试人员一定会去检查这个语言的定位符是否正确,而不至于漏掉。

3.4 页面对象封装:把语言切换封装成一步操作

页面对象模式是 Selenium 项目的标配,好处是把页面细节和测试用例解耦。这里我写了BasePage和LoginPage两个类,让用例代码只需要关心业务动作。

public class BasePage { protected WebDriver driver; protected WebDriverWait wait; public BasePage(WebDriver driver) { this.driver = driver; this.wait = new WebDriverWait(driver, Duration.ofSeconds(10)); } public void switchLanguageByUrl(String language, String path) { String localizedUrl = LanguageConfig.localizedUrl(path); driver.navigate().to(localizedUrl); // 等待 html lang 属性更新为预期的语言 wait.until(ExpectedConditions.attributeToBe( By.tagName("html"), "lang", language.trim())); } public void switchLanguageByClickAndWait(By selector, String optionText, By expectedMarker) { driver.findElement(selector).click(); wait.until(ExpectedConditions.elementToBeClickable( By.xpath("//option[text()='" + optionText + "']"))).click(); // 等待标志性元素出现,确保渲染完成 wait.until(ExpectedConditions.visibilityOfElementLocated(expectedMarker)); } }
public class LoginPage extends BasePage { public LoginPage(WebDriver driver) { super(driver); } public void loginWithDefaultUser(String username, String password) { driver.findElement(By.name("username")).clear(); driver.findElement(By.name("username")).sendKeys(username); driver.findElement(By.name("password")).clear(); driver.findElement(By.name("password")).sendKeys(password); driver.findElement(ElementLocators.loginButton()).click(); wait.until(ExpectedConditions.urlContains("/dashboard")); } public String getWelcomeText() { return driver.findElement(By.cssSelector(".welcome-message")).getText(); } public boolean isLoginFormDisplayed() { return driver.findElement(ElementLocators.languageSelector()).isDisplayed(); } }

这里有个值得一提的细节,switchLanguageByClickAndWait方法中点击下拉框之后,没有立刻点选项,而是先用wait.until(elementToBeClickable(...))等待,因为很多下拉框是异步加载选项的,直接点会扑空。这也是前面说的“等待策略”的代码体现。

3.5 多语言参数化测试用例:一份用例跑多套语言

有了前面的封装,写最终用例就非常清爽了。用 JUnit 5 的参数化测试,可以针对语言参数一次性跑多套数据。

public class LoginI18nTest { private WebDriver driver; @BeforeEach public void setUp() { // WebDriverManager 自动匹配本地浏览器版本 driver = DriverManager.createDriver(); } @AfterEach public void tearDown() { if (driver != null) { driver.quit(); } } @ParameterizedTest @ValueSource(strings = {"en", "zh_CN", "ja"}) public void testLoginAndVerifyDashboard(String language) { // 1. 指定语言,访问登录页 System.setProperty("test.lang", language); LoginPage loginPage = new LoginPage(driver); loginPage.open(ElementLocators.languageSelector()); // 2. 执行登录动作 loginPage.loginWithDefaultUser("demoUser", "demoPass"); // 3. 断言:登录后页面欢迎语必须与当前语言匹配 String actualWelcome = loginPage.getWelcomeText(); assertThat(actualWelcome) .as("Login page welcome text should match language: " + language) .isEqualTo(ElementLocators.expectedWelcomeText()); } }

这里有一个非常关键的点,就是@ValueSource(strings = {"en", "zh_CN", "ja"})这种写法,每一组语言都是一个独立的测试实例。JUnit 5 会为每个参数分别创建实例并执行,所以理论上并行起来也方便。如果 CI 服务器允许,可以对三个语言并行跑,效率更高。

实际在项目里,我们没有在类级别用@TestInstance来做并发,因为 WebDriver 实例本身不是线程安全的,需要为每个线程创建独立的 driver。建议如果要做并行,用 Maven Surefire 的并行配置配合线程专属的ThreadLocal<WebDriver>来管理。

3.6 断言文案的本地化处理:不只是文案,还有格式

多语言测试里,断言内容往往不只是文案本身,还包含文本的格式。比如英文下的“共 12 条记录”,在日文下可能是“全 12 件”,而且数字的位置也可能不同。这种场景用assertEquals直接对比完整字符串,常常会因为前后空格、标点符号的差异而失败。

我的做法是写一个I18nAssertions工具类,提供更宽容的断言方法。核心思路是:断言时先做标准化,把连续空格压缩成单个空格,把全角空格转成半角,再去掉首尾空白,最后进行相等比较。必要时还会支持正则匹配。

public class I18nAssertions { public static void assertNormalizedEquals(String expected, String actual) { String normalizedExpected = normalize(expected); String normalizedActual = normalize(actual); assertThat(normalizedActual).isEqualTo(normalizedExpected); } public static void assertNormalizedContains(String expectedPart, String actual) { String normalizedActual = normalize(actual); assertThat(normalizedActual).contains(expectedPart); } private static String normalize(String text) { if (text == null) { return ""; } return text .replace('\u3000', ' ') .replaceAll("\\s+", " ") .trim(); } }

3.7 运行时的语言切换脚本:一条命令跑完整套语言

到这里,整套框架已经可以用了。本地验证时,可以直接用 Maven 命令指定语言运行,不需要改动任何配置文件。

mvn test -Dtest=LoginI18nTest -Dtest.lang=zh_CN

如果要把所有语言都跑一遍,我建议在 CI 流水线里写成循环,或者用 Maven profile 分多步骤执行。每次构建的日志里,通过 JUnit 的@DisplayName给每个语言用例加上明确的标识,这样测试报告里一眼就能看出是哪一种语言挂了。

在项目里,我们还做了个更进一步的改造:通过 Maven 的build-helper-maven-plugin动态生成测试套件,这样 CI 上可以基于语言参数并行触发多个 Job,每个 Job 跑一种语言。对于 5 种语言、每个语言 200 条用例的场景,整体耗时从原来的 50 分钟降到了 12 分钟左右,效果非常明显。

4. 常见问题与实战踩坑记录

4.1 元素明明存在却始终定位失败

这个坑我印象最深。有一回脚本在英文环境跑得好好的,切到中文环境后,同一个按钮就是定位不到。后来打开浏览器开发者工具一看,发现中文本地化的文案比英文长了不少,前端的 CSS 把原来的按钮文案拆成了两行,渲染出来的 DOM 结构多了一个内部的<span>,原本基于纯文本的 XPath 自然就失效了。

这个问题的根子在于:多语言环境下,文案长度变化会直接影响布局和 DOM 结构。Selenium 的定位是基于 DOM 的,只要结构变了,之前所有结构相关定位都可能崩。

解决办法归纳成三条:

  • 文本框、按钮等交互元素,优先用 ID 和>@BeforeEach public void setUp() { // 确保每个用例都从干净状态开始 CookieManager.deleteAllCookies(driver); driver.manage().deleteAllCookies(); }

    但如果站点用的是 LocalStorage 保存语言,删除 Cookie 还不够,需要在浏览器里执行localStorage.clear()。所以更通用的做法是写一个resetBrowserState()方法,把 Cookie、LocalStorage、SessionStorage 全部清理干净,再重新访问初始页面。

    这里有一个教训:最初的框架里我们只在用例结束后清理,认为“上一个用例结束清理,下一个用例开始就干净了”。结果发现如果某个用例执行到一半挂掉,@AfterEach里的清理步骤没执行,状态直接污染到后面的所有用例。所以正确的做法是在@BeforeEach里重置,而不是依赖清理逻辑“必然执行”。

    4.4 时间日期格式的坑:不要直接比较字符串

    多语言环境下的日期格式差异是断言的重灾区。同样一个注册日期,可能在英文下是2025/03/15,在中文下是2025年3月15日,在日文下是2025年3月15日但月份可能带零头、不带的都有。这时候直接用assertEquals比对完整字符串,神仙也救不了。

    我的经验是,把“时间格式”和“显示文案”分开断言。时间格式部分,统一先解析成标准LocalDate对象,再比较年、月、日是否一致;而文案部分,用assertNormalizedContains检查关键信息(比如年份数字)是否出现即可,不要求整串相等。

    如果必须校验格式本身,建议针对不同语言做格式模板的配置:

    # messages_zh_CN.properties date.format = yyyy年M月d日 datetime.format = yyyy年M月d日 HH:mm

    断言时,用模板做格式化后再比对。这样文案的修改只需要在配置文件里调整,不需要动代码。

    4.5 数据驱动下测试数据维护量大,容易漏

    多语言测试的用例数量是成倍增加的。5 种语言,每种语言 200 条用例,就是 1000 条用例的数据量。如果每条用例的断言数据都手工维护在代码里,后期维护成本极高,而且经常出现“中文改了、英文忘记同步”的情况。

    我在项目里用的是集中式 i18n 资源文件管理(也就是前面配置的方式),并且加了一个脚本,定时扫描所有messages_*.properties文件,检查不同语言之间是否存在缺失的 key。如果某个语言文件缺少了对应条目,脚本直接抛警告,测试报告里也会标红非致命错误。

    另外一点建议是:测试数据和断言文案的改动,尽量跟着产品文案的变更一起走,不要等到测试时才发现。产品团队更新文案后,及时同步到资源文件中,能避免大量“测试环境正常、上线后断言失败”的尴尬场景。

    5. 经验沉淀:几个让框架更好用的补充技巧

    到这里框架已经基本能跑起来了,最后再分享几个我实际使用中摸索出来的补充技巧,价值一点不比主体代码小。

    第一个技巧是,UI 自动化的“语言探测”功能。有时候你会面对多个环境(开发环境、测试环境、预发布环境),每个环境的默认语言不一致。我在框架里加了一个“启动时自动识别当前默认语言”的小工具,脚本启动后先读取<html lang="...">属性和页面标题,确定当前环境默认语言是什么,再决定断言数据从哪个语言资源文件加载。这个功能省掉了大量人工确认环境配置的工作,尤其适合测试人员需要频繁切换环境跑用例的场景。

    第二个技巧是,视觉回归测试和多语言功能测试搭配使用。Selenium 加上视觉对比工具(比如基于浏览器截图做像素对比的库),能在功能断言之外,额外检查文案是否溢出按钮、图片是否错位等问题。多语言环境下,尤其是文案长度变化导致布局错位的问题,纯靠 Selenium 的文本断言是发现不了的。我是在跑了三周 Selenium 用例后,才被测试同事提醒“不如加个截图对比”,一试才发现真的能抓到不少排版问题。

    第三个技巧是,把多语言测试纳入 CI 的定时任务,但分层制定策略。核心主流程的多语言用例每次代码提交都跑,覆盖面大、耗时长、容易不稳定;二级流程的多语言用例放到每日夜间定时任务里跑,这样白天开发迭代不受影响,夜间又能保证多语言回归的覆盖。我在实际维护中发现,如果什么用例都塞进提交构建,团队很快会在频繁的失败告警里变得麻木,反而不利于问题发现。

    说到最后想提醒一句:多语言自动化测试说到底只是手段,不是目的。不要把“脚本能在多语言环境下跑通”当成终极目标,更重要的是一套可持续维护的框架、可追溯的测试数据和明确的失败反馈机制。框架刚搭好的头两周,我们几乎每天都会发现“更新了英文文案忘了更新日语资源文件”这类问题,但坚持调整资源和流程后,这类低级问题越来越少,多语言测试逐渐成了整个发布流程里非常稳定的一环。无论你的产品是三种语言还是十种语言,只要把这套设计思路吃透了,后续新增语言版本基本就是往配置文件里加一组数据的事。

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

别只盯着AI率:搞懂检测原理,用AI把论文写扎实

先把话说清楚&#xff1a;这篇文章不会教你怎么把论文的AI率压到10%以下&#xff0c;更不会给你一套“投机的降重话术”。因为一旦目标是“骗过检测器”&#xff0c;问题就已经不是文字层面的技术问题了&#xff0c;而是学术诚信层面的原则问题。我自己也在用DeepSeek这类工具帮…

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

JMeter压测全攻略:从场景设计到瓶颈定位的实战指南

干我们这行的&#xff0c;隔三差五就会看到“全网最全”“史上最强”的标题党文章。老实说&#xff0c;Jmeter压测这个主题&#xff0c;市面上教程多如牛毛&#xff0c;但多数是把用户手册复述一遍&#xff0c;真正能直接拿到项目里用的&#xff0c;少之又少。今天这篇不整虚的…

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

Windows下Python+Appium移动端UI自动化测试环境搭建全攻略

做移动端UI自动化测试&#xff0c;windows下搭建pythonappium环境是新手们走得最多、也最容易绕弯路的一条路。很多人一开始觉得装个软件而已&#xff0c;结果卡在环境变量三天起不来&#xff1b;也有人花了一晚上装完各种组件&#xff0c;跑demo的时候却连不上设备。我自己刚入…

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

汽车嵌入式C语言核心编码规范与常见陷阱解析

干过几年汽车电子&#xff0c;尤其是ECU、BMS这类控制器开发的兄弟应该都清楚&#xff0c;汽车嵌入式C语言和互联网后台写业务逻辑完全是两码事。刚转过来或者刚入行的同学&#xff0c;最容易踩的坑不是语法不会&#xff0c;而是思维还停留在“把功能跑通”的层面。汽车软件讲究…

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

litellm:多模型接入的统一API网关与路由治理实战

1. 为什么需要 litellm&#xff1a;多模型接入的"翻译层"问题 我从去年开始频繁对接各类大模型 API&#xff0c;最头疼的不是某个模型的效果好坏&#xff0c;而是切换模型时的适配成本。OpenAI 用一套请求格式&#xff0c;Anthropic 是另一套&#xff0c;本地部署的模…

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

偏心轮飞剪的Codesys电子凸轮控制与调试

偏心轮飞剪配上Codesys控制的组合&#xff0c;在连续产线的定尺剪切工位里其实非常常见。这个结构看着不复杂&#xff0c;真正要把剪切点掐准、让剪刃和高速运动中的材料不打架&#xff0c;程序里要处理的事比想象中多得多。这篇文章围绕偏心轮加滑块机构的飞剪展开&#xff0c…

作者头像 李华