[{"content":"提示词：\n使用pybullet创建一个机械臂可以夹起物体的实例，思考完直接给我运行代码不要有其他动作\n测试时间： 测试结果：\n模型 结果 方式 claude-5sonnet 顺利抓起方块，动作流畅，耗时短 api调用 GPT6 能抓起物体，但是有空隙 网页端 Hy4preview 顺利抓起方块，动作生硬，代码生成耗时5分钟+ 网页端 qianwen3.7 无法抓起方块，方块巨大 网页端 deepseek 不是抓的，是吸的 网页端 GPT5.6-luna 更拉，模型异常 api调用 gemini-3.8-flash 没有抓起物体 api调用 百度文心助手 没有抓起物体 网页端 豆包（自动模式） 没有抓起物体，耗时巨长 网页端 截图：\nclaude5:\ngpt6：\nhy4:\nqianwen：\ndeepseek：\nGPT5.6-luna：\n-3.8-flash：\n百度文心助手：\n豆包：\n代码：https://gitee.com/haolixu/aitest\n原文链接: 国产模型的差距！（3D建模测评） 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E5%9B%BD%E4%BA%A7%E6%A8%A1%E5%9E%8B%E7%9A%84%E5%B7%AE%E8%B7%9D3d%E5%BB%BA%E6%A8%A1%E6%B5%8B%E8%AF%84/","summary":"\u003cp\u003e提示词：\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e使用pybullet创建一个机械臂可以夹起物体的实例，思考完直接给我运行代码不要有其他动作\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e测试时间： \u003cimg loading=\"lazy\" src=\"/posts/images/08bb9ee499784936960b43357047607e.png\"\u003e\u003c/p\u003e\n\u003cp\u003e测试结果：\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e模型\u003c/th\u003e\n          \u003cth\u003e结果\u003c/th\u003e\n          \u003cth\u003e方式\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eclaude-5sonnet\u003c/td\u003e\n          \u003ctd\u003e顺利抓起方块，动作流畅，耗时短\u003c/td\u003e\n          \u003ctd\u003eapi调用\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eGPT6\u003c/td\u003e\n          \u003ctd\u003e能抓起物体，但是有空隙\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHy4preview\u003c/td\u003e\n          \u003ctd\u003e顺利抓起方块，动作生硬，代码生成耗时5分钟+\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eqianwen3.7\u003c/td\u003e\n          \u003ctd\u003e无法抓起方块，方块巨大\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edeepseek\u003c/td\u003e\n          \u003ctd\u003e不是抓的，是吸的\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eGPT5.6-luna\u003c/td\u003e\n          \u003ctd\u003e更拉，模型异常\u003c/td\u003e\n          \u003ctd\u003eapi调用\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003egemini-3.8-flash\u003c/td\u003e\n          \u003ctd\u003e没有抓起物体\u003c/td\u003e\n          \u003ctd\u003eapi调用\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e百度文心助手\u003c/td\u003e\n          \u003ctd\u003e没有抓起物体\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e豆包（自动模式）\u003c/td\u003e\n          \u003ctd\u003e没有抓起物体，耗时巨长\u003c/td\u003e\n          \u003ctd\u003e网页端\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e截图：\u003c/p\u003e\n\u003cp\u003eclaude5:\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/417bc51b0a5e4a66b0d2b96518388d4d.gif\"\u003e\u003c/p\u003e\n\u003cp\u003egpt6：\u003c/p\u003e\n\u003cp\u003ehy4:\u003cimg loading=\"lazy\" src=\"/posts/images/6b281af1b1124b6d8d2beeb431ed8209.gif\"\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/dbe195d41bfd4a0996bab8fcd77079e0.png\"\u003e\u003c/p\u003e\n\u003cp\u003eqianwen：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/cf4b6cf6deb04307b42807cd0dd9e068.gif\"\u003e\u003c/p\u003e\n\u003cp\u003edeepseek：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/8b5c5f90991a465fb0cbcd887310cb39.gif\"\u003e\u003c/p\u003e\n\u003cp\u003eGPT5.6-luna：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/6f713558b9144a16bfa838169fbfcc60.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e-3.8-flash：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/18fdff4187a34365bb3f12520cc15e39.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e百度文心助手：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/7ccbd838b65047d5b5378503ed48bbd3.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e豆包：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/7a53d55a72c448be88c286a88d49eba2.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e代码：\u003ca href=\"https://gitee.com/haolixu/aitest\"\u003ehttps://gitee.com/haolixu/aitest\u003c/a\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/167897081?spm=1011.2415.3001.5331\"\u003e国产模型的差距！（3D建模测评）\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"国产模型的差距！（3D建模测评）"},{"content":"今天现在实现了中用到的adb device状态显示。接下来搞任务管理和详情页。公司的claude太好用了 白嫖的感觉太爽了。\n这个需要记一下：\n完整判断逻辑（最终版）：\n条件 状态 无 MTBF.log + adb 不在线 🔌 离线 无 MTBF.log + adb 在线 ⚪ 空闲 MTBF.log 2分钟内有写入 🟢 运行中 有 CaseExecutionStatistics.log 且 ≤24h ✅ 已完成 MTBF.log 2~10分钟内 🟢 运行中 MTBF.log 超24小时 + adb 不在线 🔌 离线 MTBF.log 超24小时 + adb 在线 ⚪ 空闲 MTBF.log 10分钟~24小时无更新 ⚠️ 出错了 以下是问题总结：\n2026-06-20 工作总结 🏗️ 重构（上午） # 内容 结果 1 本机 UNC 读取替代 shell_server /logdir ✅ log_monitor.py 封装状态判断 2 server.py 代理层，前端统一走 localhost:8080 ✅ 解决跨域，统一鉴权 3 Git 版本控制初始化，每次改文件前 commit ✅ 已建仓库，20+ commits ⚙️ 功能完善（下午） # 内容 结果 4 device_poller.py：每60秒轮询，写缓存，状态变化弹窗告警 ✅ 26台设备全部追踪 5 offline 判断：结合 adb 在线状态（从注册中心拉取） ✅ 无 MTBF.log + adb 不在线 → offline 6 状态逻辑调整：done 加 ≤24h 限制，空闲 badge 改绿色 ✅ 7 新增 lunzhou-cwf19 / lunzhou118（QCS8550_LE_1_0-Full SKU） ✅ 3台设备各自识别 🐛 今日 修复 问题 原因 修复方式 server.py 卡住不响应 UNC 读取无超时，阻塞线程 ThreadPoolExecutor + timeout=12s start_time 格式 2026:06:20 replace(\u0026rsquo;-\u0026rsquo;,\u0026rsquo;:\u0026rsquo;,2) 把日期也替换了 分段处理日期和时间 lunzhou26 两台设备误判 idle poller 启动时注册中心为空 重启 poller 后正常 新主机不显示 localStorage 有旧数据，默认列表不触发 STORE_KEY v2→v3 强制重新初始化 启动任务报 null 错误 hideStartModal() 先清空 _startCtx 先复制到局部变量再 hide 原文链接: agent企业项目实战（2） 作者: lixusocool\n","permalink":"http://testai666.cn/posts/agent%E4%BC%81%E4%B8%9A%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%982/","summary":"\u003cp\u003e今天现在实现了中用到的adb device状态显示。接下来搞任务管理和详情页。公司的claude太好用了 白嫖的感觉太爽了。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/c2c493defe674537b5c3a9ad869467b9.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这个需要记一下：\u003c/p\u003e\n\u003cp\u003e完整判断逻辑（最终版）：\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e条件\u003c/th\u003e\n          \u003cth\u003e状态\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e无 MTBF.log + adb 不在线\u003c/td\u003e\n          \u003ctd\u003e🔌 离线\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e无 MTBF.log + adb 在线\u003c/td\u003e\n          \u003ctd\u003e⚪ 空闲\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMTBF.log 2分钟内有写入\u003c/td\u003e\n          \u003ctd\u003e🟢 运行中\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e有 CaseExecutionStatistics.log 且 ≤24h\u003c/td\u003e\n          \u003ctd\u003e✅ 已完成\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMTBF.log 2~10分钟内\u003c/td\u003e\n          \u003ctd\u003e🟢 运行中\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMTBF.log 超24小时 + adb 不在线\u003c/td\u003e\n          \u003ctd\u003e🔌 离线\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMTBF.log 超24小时 + adb 在线\u003c/td\u003e\n          \u003ctd\u003e⚪ 空闲\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eMTBF.log 10分钟~24小时无更新\u003c/td\u003e\n          \u003ctd\u003e⚠️ 出错了\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e以下是问题总结：\u003c/p\u003e\n\u003ch3 id=\"2026-06-20-工作总结\"\u003e2026-06-20 工作总结\u003c/h3\u003e\n\u003ch4 id=\"-重构上午\"\u003e🏗️ 重构（上午）\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e#\u003c/th\u003e\n          \u003cth\u003e内容\u003c/th\u003e\n          \u003cth\u003e结果\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003e本机 UNC 读取替代 shell_server /logdir\u003c/td\u003e\n          \u003ctd\u003e✅ log_monitor.py 封装状态判断\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n          \u003ctd\u003eserver.py 代理层，前端统一走 localhost:8080\u003c/td\u003e\n          \u003ctd\u003e✅ 解决跨域，统一鉴权\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n          \u003ctd\u003eGit 版本控制初始化，每次改文件前 commit\u003c/td\u003e\n          \u003ctd\u003e✅ 已建仓库，20+ commits\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch4 id=\"-功能完善下午\"\u003e⚙️ 功能完善（下午）\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e#\u003c/th\u003e\n          \u003cth\u003e内容\u003c/th\u003e\n          \u003cth\u003e结果\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003edevice_poller.py：每60秒轮询，写缓存，状态变化弹窗告警\u003c/td\u003e\n          \u003ctd\u003e✅ 26台设备全部追踪\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003eoffline 判断：结合 adb 在线状态（从注册中心拉取）\u003c/td\u003e\n          \u003ctd\u003e✅ 无 MTBF.log + adb 不在线 → offline\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003e状态逻辑调整：done 加 ≤24h 限制，空闲 badge 改绿色\u003c/td\u003e\n          \u003ctd\u003e✅\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003e新增 lunzhou-cwf19 / lunzhou118（QCS8550_LE_1_0-Full SKU）\u003c/td\u003e\n          \u003ctd\u003e✅ 3台设备各自识别\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch4 id=\"-今日--修复\"\u003e🐛 今日  修复\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e问题\u003c/th\u003e\n          \u003cth\u003e原因\u003c/th\u003e\n          \u003cth\u003e修复方式\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eserver.py 卡住不响应\u003c/td\u003e\n          \u003ctd\u003eUNC 读取无超时，阻塞线程\u003c/td\u003e\n          \u003ctd\u003eThreadPoolExecutor + timeout=12s\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003estart_time 格式 2026:06:20\u003c/td\u003e\n          \u003ctd\u003ereplace(\u0026rsquo;-\u0026rsquo;,\u0026rsquo;:\u0026rsquo;,2) 把日期也替换了\u003c/td\u003e\n          \u003ctd\u003e分段处理日期和时间\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003elunzhou26 两台设备误判 idle\u003c/td\u003e\n          \u003ctd\u003epoller 启动时注册中心为空\u003c/td\u003e\n          \u003ctd\u003e重启 poller 后正常\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e新主机不显示\u003c/td\u003e\n          \u003ctd\u003elocalStorage 有旧数据，默认列表不触发\u003c/td\u003e\n          \u003ctd\u003eSTORE_KEY v2→v3 强制重新初始化\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e启动任务报 null 错误\u003c/td\u003e\n          \u003ctd\u003ehideStartModal() 先清空 _startCtx\u003c/td\u003e\n          \u003ctd\u003e先复制到局部变量再 hide\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/4f11efc8e1cc49b8ab174abe47f4a713.png\"\u003e\u003c/p\u003e","title":"agent企业项目实战（2）"},{"content":"公司最近在用qwenpaw学习和skill,领导让在项目中使用提高效率，我也有此想法故开始了实践。目前实现了我项目中用到的测试主机的C盘容量监控总共21台以下是效果图：\n后续还想增加空闲占比太低则弹出提示。以下是Ai总结：\n磁盘监控系统 — 实现总结 一、目标 批量查询多台 机器的 C 盘空闲/总容量，生成可视化前端页面，并定时自动刷新。\n二、核心文件 文件 路径 说明 check_all_disks.ps1 C:\\Users\\xli23\\Desktop\\workStation\\ 查询所有机器磁盘，输出 JSON disk_status.json 同上 查询结果数据文件，供前端读取 disk_dashboard.html 同上 前端可视化页面 server.py 同上 Python HTTP 服务，端口 8080 三、实现方法 1. 磁盘查询（） 用dir \\\\主机\\共享名获取空闲字节数 用Scripting.FileSystemObject.GetDrive()获取总容量 计算空闲占比，按阈值打状态标签： \u0026amp;lt; 5%→critical（红） \u0026amp;lt; 15%→warning（黄） ≥ 15%→ok（绿） 访问失败 →offline（灰） 结果输出为disk_status.json 2. 前端页面（ + JS） 读取disk_status.json渲染表格 顶部汇总卡片（总数/正常/警告/严重/离线） 进度条直观显示空闲占比 支持按状态筛选、主机名搜索、列排序 点击刷新按钮重新读取 JSON 3. 服务（Python） server.py用http.server在端口 8080 提供静态文件服务 访问地址：http://localhost:8080/disk_dashboard.html 4. （QwenPaw Cron） 每小时整点自动执行 PS 脚本刷新数据 Cron 表达式：0 * * * * Job ID：c04dc361-a7f4-4b4a-be1d-d13cba681e61 四、机器共享名规律 共享名 机器类型 代表机器 C HP Z2 系列 lunzhou66/67/68/71/74/98 等 c_drive HP Mini 800（STARLAB） lunzhou85-93/96/112/118/119 等 C 其他 lunzhou41/lunzhou-cwf19 五、遇到的问题及 问题 原因 解决方法 ping 判断离线但实际在线 短主机名无法解析，需加 .ap.qualcomm.com 改用 net view 或直接访问共享路径验证 \\lunzhou37\\C 超时 DNS 解析失败，主机名不通 改用 IP \\10.64.29.14\\C 访问 共享名不统一 不同机型共享名不同（C vs c_drive） 先用 net view \\主机 查共享名再访问 PowerShell 多行命令在 cmd 中报错 cmd 不支持换行脚本 改写为 .ps1 文件再用 -File 执行 Get-PSDrive 无法查远程共享总容量 只支持本地驱动器 改用 Scripting.FileSystemObject.GetDrive() System.IO.DriveInfo 不支持 UNC 路径 .NET 限制 改用 COM 对象 Scripting.FileSystemObject 前端 fetch 本地 JSON 被 CORS 拦截 浏览器安全限制 用 server.py 起 HTTP 服务解决 PowerShell 中文输出乱码 cmd 编码问题 改用英文输出 lunzhou41 无 C 盘共享 该机器只共享了 IoTAutomationLog 和 Sahara 经测试 \\lunzhou41\\C 实际可访问，直接用 原文链接: agent企业项目实战（1） 作者: lixusocool\n","permalink":"http://testai666.cn/posts/agent%E4%BC%81%E4%B8%9A%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%981/","summary":"\u003cp\u003e公司最近在用qwenpaw学习和skill,领导让在项目中使用提高效率，我也有此想法故开始了实践。目前实现了我项目中用到的测试主机的C盘容量监控总共21台以下是效果图：\u003cimg loading=\"lazy\" src=\"/posts/images/7ca7fedf1c494a9294443f987b1fd273.png\"\u003e\u003c/p\u003e\n\u003cp\u003e后续还想增加空闲占比太低则弹出提示。以下是Ai总结：\u003c/p\u003e\n\u003ch3 id=\"磁盘监控系统--实现总结\"\u003e磁盘监控系统 — 实现总结\u003c/h3\u003e\n\u003ch4 id=\"一目标\"\u003e一、目标\u003c/h4\u003e\n\u003cp\u003e批量查询多台  机器的 C 盘空闲/总容量，生成可视化前端页面，并定时自动刷新。\u003c/p\u003e\n\u003chr\u003e\n\u003ch4 id=\"二核心文件\"\u003e二、核心文件\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e文件\u003c/th\u003e\n          \u003cth\u003e路径\u003c/th\u003e\n          \u003cth\u003e说明\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003echeck_all_disks.ps1\u003c/td\u003e\n          \u003ctd\u003eC:\\Users\\xli23\\Desktop\\workStation\\\u003c/td\u003e\n          \u003ctd\u003e查询所有机器磁盘，输出 JSON\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edisk_status.json\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e查询结果数据文件，供前端读取\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003edisk_dashboard.html\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e前端可视化页面\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eserver.py\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003ePython HTTP 服务，端口 8080\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch4 id=\"三实现方法\"\u003e三、实现方法\u003c/h4\u003e\n\u003ch5 id=\"1-磁盘查询\"\u003e1. 磁盘查询（）\u003c/h5\u003e\n\u003cul\u003e\n\u003cli\u003e用\u003ccode\u003edir \\\\主机\\共享名\u003c/code\u003e获取空闲字节数\u003c/li\u003e\n\u003cli\u003e用\u003ccode\u003eScripting.FileSystemObject.GetDrive()\u003c/code\u003e获取总容量\u003c/li\u003e\n\u003cli\u003e计算空闲占比，按阈值打状态标签：\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e\u0026amp;lt; 5%\u003c/code\u003e→\u003cstrong\u003ecritical\u003c/strong\u003e（红）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u0026amp;lt; 15%\u003c/code\u003e→\u003cstrong\u003ewarning\u003c/strong\u003e（黄）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e≥ 15%\u003c/code\u003e→\u003cstrong\u003eok\u003c/strong\u003e（绿）\u003c/li\u003e\n\u003cli\u003e访问失败 →\u003cstrong\u003eoffline\u003c/strong\u003e（灰）\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e结果输出为\u003ccode\u003edisk_status.json\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch5 id=\"2-前端页面--js\"\u003e2. 前端页面（ + JS）\u003c/h5\u003e\n\u003cul\u003e\n\u003cli\u003e读取\u003ccode\u003edisk_status.json\u003c/code\u003e渲染表格\u003c/li\u003e\n\u003cli\u003e顶部汇总卡片（总数/正常/警告/严重/离线）\u003c/li\u003e\n\u003cli\u003e进度条直观显示空闲占比\u003c/li\u003e\n\u003cli\u003e支持按状态筛选、主机名搜索、列排序\u003c/li\u003e\n\u003cli\u003e点击刷新按钮重新读取 JSON\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch5 id=\"3--服务python\"\u003e3.  服务（Python）\u003c/h5\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eserver.py\u003c/code\u003e用\u003ccode\u003ehttp.server\u003c/code\u003e在端口 8080 提供静态文件服务\u003c/li\u003e\n\u003cli\u003e访问地址：\u003ccode\u003ehttp://localhost:8080/disk_dashboard.html\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch5 id=\"4-qwenpaw-cron\"\u003e4. （QwenPaw Cron）\u003c/h5\u003e\n\u003cul\u003e\n\u003cli\u003e每小时整点自动执行 PS 脚本刷新数据\u003c/li\u003e\n\u003cli\u003eCron 表达式：\u003ccode\u003e0 * * * *\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003eJob ID：\u003ccode\u003ec04dc361-a7f4-4b4a-be1d-d13cba681e61\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch4 id=\"四机器共享名规律\"\u003e四、机器共享名规律\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e共享名\u003c/th\u003e\n          \u003cth\u003e机器类型\u003c/th\u003e\n          \u003cth\u003e代表机器\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eC\u003c/td\u003e\n          \u003ctd\u003eHP Z2 系列\u003c/td\u003e\n          \u003ctd\u003elunzhou66/67/68/71/74/98 等\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ec_drive\u003c/td\u003e\n          \u003ctd\u003eHP Mini 800（STARLAB）\u003c/td\u003e\n          \u003ctd\u003elunzhou85-93/96/112/118/119 等\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eC\u003c/td\u003e\n          \u003ctd\u003e其他\u003c/td\u003e\n          \u003ctd\u003elunzhou41/lunzhou-cwf19\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch4 id=\"五遇到的问题及\"\u003e五、遇到的问题及\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e问题\u003c/th\u003e\n          \u003cth\u003e原因\u003c/th\u003e\n          \u003cth\u003e解决方法\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eping 判断离线但实际在线\u003c/td\u003e\n          \u003ctd\u003e短主机名无法解析，需加 .ap.qualcomm.com\u003c/td\u003e\n          \u003ctd\u003e改用 net view 或直接访问共享路径验证\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\\lunzhou37\\C 超时\u003c/td\u003e\n          \u003ctd\u003eDNS 解析失败，主机名不通\u003c/td\u003e\n          \u003ctd\u003e改用 IP \\10.64.29.14\\C 访问\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e共享名不统一\u003c/td\u003e\n          \u003ctd\u003e不同机型共享名不同（C vs c_drive）\u003c/td\u003e\n          \u003ctd\u003e先用 net view \\主机 查共享名再访问\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePowerShell 多行命令在 cmd 中报错\u003c/td\u003e\n          \u003ctd\u003ecmd 不支持换行脚本\u003c/td\u003e\n          \u003ctd\u003e改写为 .ps1 文件再用 -File 执行\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eGet-PSDrive 无法查远程共享总容量\u003c/td\u003e\n          \u003ctd\u003e只支持本地驱动器\u003c/td\u003e\n          \u003ctd\u003e改用 Scripting.FileSystemObject.GetDrive()\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSystem.IO.DriveInfo 不支持 UNC 路径\u003c/td\u003e\n          \u003ctd\u003e.NET 限制\u003c/td\u003e\n          \u003ctd\u003e改用 COM 对象 Scripting.FileSystemObject\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e前端 fetch 本地 JSON 被 CORS 拦截\u003c/td\u003e\n          \u003ctd\u003e浏览器安全限制\u003c/td\u003e\n          \u003ctd\u003e用 server.py 起 HTTP 服务解决\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ePowerShell 中文输出乱码\u003c/td\u003e\n          \u003ctd\u003ecmd 编码问题\u003c/td\u003e\n          \u003ctd\u003e改用英文输出\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003elunzhou41 无 C 盘共享\u003c/td\u003e\n          \u003ctd\u003e该机器只共享了 IoTAutomationLog 和 Sahara\u003c/td\u003e\n          \u003ctd\u003e经测试 \\lunzhou41\\C 实际可访问，直接用\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/162106077?spm=1011.2415.3001.5331\"\u003eagent企业项目实战（1）\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e","title":"agent企业项目实战（1）"},{"content":"1. qtiqmmfsrc 不支持 num-buffers 现象 在 gst-launch-1.0 命令中加入：\nqtiqmmfsrc camera=0 num-buffers=1 报错：\nWARNING: erroneous pipeline: no property \u0026#34;num-buffers\u0026#34; in element \u0026#34;qmmfsrc0\u0026#34; 结论 qtiqmmfsrc 不是标准 videotestsrc 一类元素，不支持 num-buffers 属性。\n处理方式 改用：\ntimeout 控制时长 或后续用 identity / multifilesink / 后处理方式控制帧数 2. 直接 image/jpeg + filesink 生成空文件 现象 下面这类命令可以跑起来，但输出的 jpg 是 0 字节：\nqtiqmmfsrc camera=0 ! \u0026#39;image/jpeg,width=3840,height=2160\u0026#39; ! filesink location=/tmp/snapshot.jpg 原因 filesink 本身只是写字节流，不适合直接拿来当“单张拍照”的最终输出方式。\n处理方式 改用：\n... ! multifilesink location=/tmp/snapshot%d.jpg 然后再挑选某一帧复制成最终文件。\n3. 前几十帧是黑图 现象 直接启动摄像头后保存的前几帧是黑的，后面帧才正常。\n原因 摄像头初始化阶段，AE / AWB / ISP 还没稳定。\n处理方式 采用“丢掉前 N 帧”的策略：\n先保存多帧 再选择第 60 帧或更靠后的帧作为最终照片 经验 后来验证发现：\n丢前 60 帧后，图像正常 有些场景下 120 帧更稳，但也会带来额外文件处理问题 4. pidof gst-pipeline-app / pidof gst-launch-1.0 产生错误日志 现象 测试 setup / cleanup 阶段会出现类似：\n[Send] adb -s xxx shell pidof gst-pipeline-app [Error] 原因 pidof 找不到进程时会产生错误日志，不影响主流程，但日志很吵。\n处理方式 暂时把 stop_preview() 改成空操作，避免这类噪音日志。\n5. 1080p 和 4K 拍照看起来都模糊 现象 把拍照分辨率改成 4K 后，视觉效果和 1080p 差不多，仍然模糊。\n原因推测 可能是以下一个或多个原因：\n镜头本身成像质量有限 当前镜头没有对上焦 AE / AWB 没完全稳定 实际输出被 ISP 缩放或统一处理 4K 分辨率只是“链路上声明了 4K”，不代表最终画面更清晰 结论 分辨率提高，不代表一定更清晰；画质取决于整个摄像头链路和镜头本身。\n6. 4K 视频录制 MP4 无法播放 现象 录制出的 .mp4 文件存在，但无法播放。\n原因 大概率是：\nmp4mux 没有正常收尾 timeout 结束太硬，文件没写完整 容器结构不完整，播放器无法识别 处理方式 改为尝试：\n.mkv .ts 裸 .h264 其中 .ts 相对更稳。\n7. 4K 视频录制文件时长不对，文件太小 现象 录制 30 秒后，输出文件只有几 MB，明显不像完整 30 秒视频。\n原因推测 可能是：\n录制命令提前退出 timeout 影响收尾 4K 负载太高导致持续录制不稳定 实际帧率没达到预期 处理方式 先降负载测试：\n先用 1080p 验证链路 再把 4K 帧率从 30fps 降到 15fps 8. 1080p 视频录制成功，4K 不稳定 现象 1080p TS 视频可以录出并播放，文件大小合理；4K 版本不稳定。\n结论 说明：\n编码链路基本正常 4K 主要问题是负载压力更大 经验 先用 1080p 建立基线，再逐步把 4K 拉回 30fps。\n9. 并行录制的压力问题 现象 计划让 camera 0/1/2/3 并行录制时，需要考虑设备压力。\n风险 四路同时跑 4K 可能会：\n丢帧 编码失败 文件不完整 播放异常 建议 并行测试优先从 1080p 开始，确认并行逻辑稳定后，再考虑 4K 并行。\n10. format! 字符串参数错位导致编译错误 现象 编译时出现类似：\nunexpected token `duration_secs` 原因 format! 多行字符串中漏了逗号，或参数数量与占位符不匹配。\n典型修复 检查：\nformat!( \u0026#34;... {} ... {} ...\u0026#34;, duration_secs, output_path ); 是否遗漏了：\n字符串后面的逗号 参数个数 占位符顺序 11. 最终比较稳定的方案 拍照测试 稳定做法是：\nqtiqmmfsrc raw NV12 jpegenc multifilesink 丢掉前 60 帧 复制目标帧为最终 snapshot 视频测试 较稳做法是：\n优先用 1080p 验证 容器用 .ts 编码链路保持简单 4K 先降 fps 再调回 30fps 12. 经验结论 拍照链路比视频链路更容易稳定。 前几帧黑图是常见现象。 4K 的问题往往不是“有没有输出”，而是“负载和封装是否足够稳定”。 1080p 成功后，再逐步往 4K 提升，是最稳的调试路径。 视频录制优先追求“可播放、可稳定生成”，再追求更高画质。 13. 当前推荐脚本/用例 拍照 examples/camera_test.rs：1080p 拍照 examples/camera_test_4k.rs：4K 拍照 视频 examples/camera_video_test_1080p.rs：1080p 视频基线 examples/camera_video_test_4k.rs：4K 视频测试 examples/camera_video_parallel_1080p.rs：并行 1080p 视频测试 拉取脚本 pull_camera_snapshots_1080.bat pull_camera_snapshots_4k.bat pull_camera_videos.bat 14. 后续可继续优化的方向 给视频测试增加独立日志文件 给并行测试增加更清晰的结果汇总 4K 视频录制再尝试更保守的编码参数 增加拉取文件前的存在性检查 增加自动转码脚本（ts/h264 -\u0026gt; mp4） ","permalink":"http://testai666.cn/posts/camera%E8%B0%83%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93/","summary":"\u003ch2 id=\"1-qtiqmmfsrc-不支持-num-buffers\"\u003e1. \u003ccode\u003eqtiqmmfsrc\u003c/code\u003e 不支持 \u003ccode\u003enum-buffers\u003c/code\u003e\u003c/h2\u003e\n\u003ch3 id=\"现象\"\u003e现象\u003c/h3\u003e\n\u003cp\u003e在 \u003ccode\u003egst-launch-1.0\u003c/code\u003e 命令中加入：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eqtiqmmfsrc camera\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e num-buffers\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e报错：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWARNING: erroneous pipeline: no property \u0026#34;num-buffers\u0026#34; in element \u0026#34;qmmfsrc0\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"结论\"\u003e结论\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003eqtiqmmfsrc\u003c/code\u003e 不是标准 \u003ccode\u003evideotestsrc\u003c/code\u003e 一类元素，不支持 \u003ccode\u003enum-buffers\u003c/code\u003e 属性。\u003c/p\u003e\n\u003ch3 id=\"处理方式\"\u003e处理方式\u003c/h3\u003e\n\u003cp\u003e改用：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003etimeout\u003c/code\u003e 控制时长\u003c/li\u003e\n\u003cli\u003e或后续用 \u003ccode\u003eidentity\u003c/code\u003e / \u003ccode\u003emultifilesink\u003c/code\u003e / 后处理方式控制帧数\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"2-直接-imagejpeg--filesink-生成空文件\"\u003e2. 直接 \u003ccode\u003eimage/jpeg\u003c/code\u003e + \u003ccode\u003efilesink\u003c/code\u003e 生成空文件\u003c/h2\u003e\n\u003ch3 id=\"现象-1\"\u003e现象\u003c/h3\u003e\n\u003cp\u003e下面这类命令可以跑起来，但输出的 jpg 是 0 字节：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eqtiqmmfsrc camera\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e ! \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;image/jpeg,width=3840,height=2160\u0026#39;\u003c/span\u003e ! filesink location\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/tmp/snapshot.jpg\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"原因\"\u003e原因\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003efilesink\u003c/code\u003e 本身只是写字节流，不适合直接拿来当“单张拍照”的最终输出方式。\u003c/p\u003e\n\u003ch3 id=\"处理方式-1\"\u003e处理方式\u003c/h3\u003e\n\u003cp\u003e改用：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e... ! multifilesink location\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/tmp/snapshot%d.jpg\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e然后再挑选某一帧复制成最终文件。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"3-前几十帧是黑图\"\u003e3. 前几十帧是黑图\u003c/h2\u003e\n\u003ch3 id=\"现象-2\"\u003e现象\u003c/h3\u003e\n\u003cp\u003e直接启动摄像头后保存的前几帧是黑的，后面帧才正常。\u003c/p\u003e","title":"Camera 调试问题总结"},{"content":"📋 前置准备 1. 安装 Rust Windows 安装步骤： 下载 Rust 安装器\n访问：https://rustup.rs/ 或直接下载：https://win.rustup.rs/x86_64 运行安装器\n# 下载后运行 rustup-init.exe # 选择默认安装选项（按 1 然后回车） 验证安装\n# 重新打开 PowerShell rustc --version cargo --version 配置国内镜像（可选，加速下载）\n# 创建配置文件 mkdir $env:USERPROFILE\\.cargo notepad $env:USERPROFILE\\.cargo\\config.toml 添加以下内容：\n[source.crates-io] replace-with = \u0026#39;ustc\u0026#39; [source.ustc] registry = \u0026#34;https://mirrors.ustc.edu.cn/crates.io-index\u0026#34; 2. 安装 VS Code Rust 插件（推荐） rust-analyzer: Rust 语言服务器 CodeLLDB: 调试器 crates: 依赖管理 🏗️ 第 1 步：创建项目结构 1.1 创建新项目 cd C:\\Users\\xli23\\Desktop\\uv-env cargo new iot-automation-rust cd iot-automation-rust 1.2 项目结构 iot-automation-rust/ ├── Cargo.toml # 项目配置和依赖 ├── src/ │ ├── main.rs # 程序入口 │ ├── lib.rs # 库入口 │ ├── cli.rs # 命令行参数 │ ├── config/ # 配置模块 │ │ ├── mod.rs │ │ ├── parser.rs │ │ └── global.rs │ ├── device/ # 设备模块 │ │ ├── mod.rs │ │ └── adb.rs │ ├── test/ # 测试模块 │ │ ├── mod.rs │ │ ├── base.rs │ │ └── runner.rs │ └── utils/ # 工具模块 │ ├── mod.rs │ └── logger.rs ├── tests/ # 集成测试 └── examples/ # 示例代码 📝 第 2 步：配置 Cargo.toml 创建 Cargo.toml 文件：\n[package] name = \u0026#34;iot-automation-rust\u0026#34; version = \u0026#34;0.1.0\u0026#34; edition = \u0026#34;2021\u0026#34; [dependencies] # 异步运行时 tokio = { version = \u0026#34;1\u0026#34;, features = [\u0026#34;full\u0026#34;] } async-trait = \u0026#34;0.1\u0026#34; # 错误处理 anyhow = \u0026#34;1.0\u0026#34; thiserror = \u0026#34;1.0\u0026#34; # 命令行参数 clap = { version = \u0026#34;4\u0026#34;, features = [\u0026#34;derive\u0026#34;] } # 日志 log = \u0026#34;0.4\u0026#34; env_logger = \u0026#34;0.11\u0026#34; # 序列化 serde = { version = \u0026#34;1.0\u0026#34;, features = [\u0026#34;derive\u0026#34;] } serde_json = \u0026#34;1.0\u0026#34; # XML 解析 quick-xml = { version = \u0026#34;0.31\u0026#34;, features = [\u0026#34;serialize\u0026#34;] } # 正则表达式 regex = \u0026#34;1.10\u0026#34; # 时间处理 chrono = \u0026#34;0.4\u0026#34; [dev-dependencies] tokio-test = \u0026#34;0.4\u0026#34; 安装依赖：\ncargo build 🔧 第 3 步：实现 ADB 封装 3.1 创建 device 模块 创建 src/device/mod.rs：\npub mod adb; pub use adb::Adb; 3.2 实现 ADB 核心功能 创建 src/device/adb.rs：\nuse anyhow::{Context, Result}; use log::{info, error}; use std::process::{Command, Output}; use std::time::Duration; use tokio::time::sleep; /// ADB 设备管理器 #[derive(Debug, Clone)] pub struct Adb { /// 设备序列号 serial: String, } impl Adb { /// 创建新的 ADB 实例 pub fn new(serial: impl Into\u0026lt;String\u0026gt;) -\u0026gt; Self { Self { serial: serial.into(), } } /// 执行 ADB 命令 pub fn run_cmd(\u0026amp;self, args: \u0026amp;[\u0026amp;str]) -\u0026gt; Result\u0026lt;String\u0026gt; { let mut cmd = Command::new(\u0026#34;adb\u0026#34;); // 添加设备序列号 cmd.args([\u0026#34;-s\u0026#34;, \u0026amp;self.serial]); // 添加命令参数 cmd.args(args); info!(\u0026#34;[Send] adb -s {} {}\u0026#34;, self.serial, args.join(\u0026#34; \u0026#34;)); let output = cmd .output() .context(\u0026#34;Failed to execute adb command\u0026#34;)?; self.handle_output(output) } /// 处理命令输出 fn handle_output(\u0026amp;self, output: Output) -\u0026gt; Result\u0026lt;String\u0026gt; { let stdout = String::from_utf8_lossy(\u0026amp;output.stdout).trim().to_string(); let stderr = String::from_utf8_lossy(\u0026amp;output.stderr).trim().to_string(); if !output.status.success() { error!(\u0026#34;[Error] {}\u0026#34;, stderr); anyhow::bail!(\u0026#34;ADB command failed: {}\u0026#34;, stderr); } info!(\u0026#34;[Recv] {}\u0026#34;, stdout); Ok(stdout) } /// 执行 shell 命令 pub fn shell(\u0026amp;self, cmd: \u0026amp;str) -\u0026gt; Result\u0026lt;String\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;shell\u0026#34;, cmd]) } /// 推送文件到设备 pub fn push(\u0026amp;self, local: \u0026amp;str, remote: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;push\u0026#34;, local, remote])?; Ok(()) } /// 从设备拉取文件 pub fn pull(\u0026amp;self, remote: \u0026amp;str, local: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;pull\u0026#34;, remote, local])?; Ok(()) } /// 重启设备 pub fn reboot(\u0026amp;self) -\u0026gt; Result\u0026lt;()\u0026gt; { info!(\u0026#34;Rebooting device: {}\u0026#34;, self.serial); self.run_cmd(\u0026amp;[\u0026#34;reboot\u0026#34;])?; Ok(()) } /// 等待设备就绪 pub async fn wait_for_device(\u0026amp;self, timeout_secs: u64) -\u0026gt; Result\u0026lt;()\u0026gt; { info!(\u0026#34;Waiting for device: {}\u0026#34;, self.serial); let start = std::time::Instant::now(); loop { if start.elapsed().as_secs() \u0026gt; timeout_secs { anyhow::bail!(\u0026#34;Timeout waiting for device\u0026#34;); } match self.run_cmd(\u0026amp;[\u0026#34;get-state\u0026#34;]) { Ok(state) if state.contains(\u0026#34;device\u0026#34;) =\u0026gt; { info!(\u0026#34;Device is ready\u0026#34;); return Ok(()); } _ =\u0026gt; { sleep(Duration::from_secs(2)).await; } } } } /// 获取设备状态 pub fn get_state(\u0026amp;self) -\u0026gt; Result\u0026lt;String\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;get-state\u0026#34;]) } /// 检查设备是否在线 pub fn is_online(\u0026amp;self) -\u0026gt; bool { self.get_state() .map(|s| s.contains(\u0026#34;device\u0026#34;)) .unwrap_or(false) } /// Root 设备 pub fn root(\u0026amp;self) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;root\u0026#34;])?; Ok(()) } /// Remount 文件系统 pub fn remount(\u0026amp;self) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;remount\u0026#34;])?; Ok(()) } /// 获取设备属性 pub fn get_prop(\u0026amp;self, key: \u0026amp;str) -\u0026gt; Result\u0026lt;String\u0026gt; { self.shell(\u0026amp;format!(\u0026#34;getprop {}\u0026#34;, key)) } /// 安装 APK pub fn install(\u0026amp;self, apk_path: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;install\u0026#34;, apk_path])?; Ok(()) } /// 卸载应用 pub fn uninstall(\u0026amp;self, package: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;[\u0026#34;uninstall\u0026#34;, package])?; Ok(()) } /// 启动 Activity pub fn start_activity(\u0026amp;self, component: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.shell(\u0026amp;format!(\u0026#34;am start -n {}\u0026#34;, component))?; Ok(()) } /// 强制停止应用 pub fn force_stop(\u0026amp;self, package: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.shell(\u0026amp;format!(\u0026#34;am force-stop {}\u0026#34;, package))?; Ok(()) } /// 获取设备序列号 pub fn serial(\u0026amp;self) -\u0026gt; \u0026amp;str { \u0026amp;self.serial } } /// 获取所有连接的设备 pub fn get_devices() -\u0026gt; Result\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt; { let output = Command::new(\u0026#34;adb\u0026#34;) .arg(\u0026#34;devices\u0026#34;) .output() .context(\u0026#34;Failed to execute adb devices\u0026#34;)?; let stdout = String::from_utf8_lossy(\u0026amp;output.stdout); let devices: Vec\u0026lt;String\u0026gt; = stdout .lines() .skip(1) // 跳过 \u0026#34;List of devices attached\u0026#34; .filter_map(|line| { let parts: Vec\u0026lt;\u0026amp;str\u0026gt; = line.split_whitespace().collect(); if parts.len() \u0026gt;= 2 \u0026amp;\u0026amp; parts[1] == \u0026#34;device\u0026#34; { Some(parts[0].to_string()) } else { None } }) .collect(); Ok(devices) } #[cfg(test)] mod tests { use super::*; #[test] fn test_adb_creation() { let adb = Adb::new(\u0026#34;test_device\u0026#34;); assert_eq!(adb.serial(), \u0026#34;test_device\u0026#34;); } #[tokio::test] async fn test_get_devices() { // 这个测试需要实际的 ADB 设备 match get_devices() { Ok(devices) =\u0026gt; { println!(\u0026#34;Found devices: {:?}\u0026#34;, devices); } Err(e) =\u0026gt; { println!(\u0026#34;No devices found or ADB not available: {}\u0026#34;, e); } } } } 🧪 第 4 步：实现测试基类 4.1 创建 test 模块 创建 src/test/mod.rs：\npub mod base; pub mod runner; pub use base::{TestCase, TestResult}; pub use runner::TestRunner; 4.2 实现测试基类 创建 src/test/base.rs：\nuse anyhow::Result; use async_trait::async_trait; use serde::{Deserialize, Serialize}; /// 测试结果 #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum TestResult { Pass, Fail, Unknown, } impl std::fmt::Display for TestResult { fn fmt(\u0026amp;self, f: \u0026amp;mut std::fmt::Formatter\u0026lt;\u0026#39;_\u0026gt;) -\u0026gt; std::fmt::Result { match self { TestResult::Pass =\u0026gt; write!(f, \u0026#34;Pass\u0026#34;), TestResult::Fail =\u0026gt; write!(f, \u0026#34;Fail\u0026#34;), TestResult::Unknown =\u0026gt; write!(f, \u0026#34;Unknown\u0026#34;), } } } /// 测试用例 trait #[async_trait] pub trait TestCase: Send + Sync { /// 测试用例名称 fn name(\u0026amp;self) -\u0026gt; \u0026amp;str; /// 测试用例描述 fn description(\u0026amp;self) -\u0026gt; \u0026amp;str { \u0026#34;\u0026#34; } /// 设置阶段 async fn setup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { log::info!(\u0026#34;Test setup for {}\u0026#34;, self.name()); Ok(()) } /// 执行阶段 async fn execute(\u0026amp;mut self) -\u0026gt; Result\u0026lt;TestResult\u0026gt;; /// 清理阶段 async fn cleanup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { log::info!(\u0026#34;Test cleanup for {}\u0026#34;, self.name()); Ok(()) } /// 运行完整测试流程 async fn run(\u0026amp;mut self) -\u0026gt; TestResult { log::info!(\u0026#34;========================================\u0026#34;); log::info!(\u0026#34;Running test: {}\u0026#34;, self.name()); log::info!(\u0026#34;========================================\u0026#34;); // Setup if let Err(e) = self.setup().await { log::error!(\u0026#34;Setup failed: {}\u0026#34;, e); return TestResult::Fail; } // Execute let result = match self.execute().await { Ok(r) =\u0026gt; { log::info!(\u0026#34;Test {} result: {}\u0026#34;, self.name(), r); r } Err(e) =\u0026gt; { log::error!(\u0026#34;Execute failed: {}\u0026#34;, e); TestResult::Fail } }; // Cleanup if let Err(e) = self.cleanup().await { log::error!(\u0026#34;Cleanup failed: {}\u0026#34;, e); } log::info!(\u0026#34;========================================\u0026#34;); log::info!(\u0026#34;{} test result: {}\u0026#34;, self.name(), result); log::info!(\u0026#34;========================================\u0026#34;); result } } /// 测试用例信息 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TestInfo { pub name: String, pub description: String, pub result: TestResult, pub duration_ms: u64, pub log_path: String, } 4.3 实现测试运行器 创建 src/test/runner.rs：\nuse super::{TestCase, TestResult, TestInfo}; use anyhow::Result; use std::time::Instant; /// 测试运行器 pub struct TestRunner { tests: Vec\u0026lt;Box\u0026lt;dyn TestCase\u0026gt;\u0026gt;, results: Vec\u0026lt;TestInfo\u0026gt;, } impl TestRunner { /// 创建新的测试运行器 pub fn new() -\u0026gt; Self { Self { tests: Vec::new(), results: Vec::new(), } } /// 添加测试用例 pub fn add_test(\u0026amp;mut self, test: Box\u0026lt;dyn TestCase\u0026gt;) { self.tests.push(test); } /// 顺序运行所有测试 pub async fn run_sequential(\u0026amp;mut self) -\u0026gt; Result\u0026lt;Vec\u0026lt;TestInfo\u0026gt;\u0026gt; { log::info!(\u0026#34;Running {} tests sequentially\u0026#34;, self.tests.len()); for test in \u0026amp;mut self.tests { let start = Instant::now(); let name = test.name().to_string(); let description = test.description().to_string(); let result = test.run().await; let duration_ms = start.elapsed().as_millis() as u64; let info = TestInfo { name: name.clone(), description, result, duration_ms, log_path: format!(\u0026#34;logs/{}\u0026#34;, name), }; self.results.push(info); } Ok(self.results.clone()) } /// 并发运行所有测试 pub async fn run_concurrent(\u0026amp;mut self) -\u0026gt; Result\u0026lt;Vec\u0026lt;TestInfo\u0026gt;\u0026gt; { use futures::future::join_all; use std::sync::Arc; use tokio::sync::Mutex; log::info!(\u0026#34;Running {} tests concurrently\u0026#34;, self.tests.len()); let tests = std::mem::take(\u0026amp;mut self.tests); let tests: Vec\u0026lt;_\u0026gt; = tests .into_iter() .map(|test| Arc::new(Mutex::new(test))) .collect(); let futures: Vec\u0026lt;_\u0026gt; = tests .iter() .map(|test| { let test = Arc::clone(test); async move { let start = Instant::now(); let mut test = test.lock().await; let name = test.name().to_string(); let description = test.description().to_string(); let result = test.run().await; let duration_ms = start.elapsed().as_millis() as u64; TestInfo { name: name.clone(), description, result, duration_ms, log_path: format!(\u0026#34;logs/{}\u0026#34;, name), } } }) .collect(); let results = join_all(futures).await; self.results = results; Ok(self.results.clone()) } /// 获取测试结果 pub fn results(\u0026amp;self) -\u0026gt; \u0026amp;[TestInfo] { \u0026amp;self.results } /// 获取统计信息 pub fn statistics(\u0026amp;self) -\u0026gt; TestStatistics { let total = self.results.len(); let passed = self.results.iter().filter(|r| r.result == TestResult::Pass).count(); let failed = self.results.iter().filter(|r| r.result == TestResult::Fail).count(); let unknown = self.results.iter().filter(|r| r.result == TestResult::Unknown).count(); let pass_rate = if total \u0026gt; 0 { (passed as f64 / total as f64) * 100.0 } else { 0.0 }; TestStatistics { total, passed, failed, unknown, pass_rate, } } } impl Default for TestRunner { fn default() -\u0026gt; Self { Self::new() } } /// 测试统计信息 #[derive(Debug, Clone)] pub struct TestStatistics { pub total: usize, pub passed: usize, pub failed: usize, pub unknown: usize, pub pass_rate: f64, } impl std::fmt::Display for TestStatistics { fn fmt(\u0026amp;self, f: \u0026amp;mut std::fmt::Formatter\u0026lt;\u0026#39;_\u0026gt;) -\u0026gt; std::fmt::Result { writeln!(f, \u0026#34;Test Statistics:\u0026#34;)?; writeln!(f, \u0026#34; Total: {}\u0026#34;, self.total)?; writeln!(f, \u0026#34; Passed: {}\u0026#34;, self.passed)?; writeln!(f, \u0026#34; Failed: {}\u0026#34;, self.failed)?; writeln!(f, \u0026#34; Unknown: {}\u0026#34;, self.unknown)?; writeln!(f, \u0026#34; Pass Rate: {:.1}%\u0026#34;, self.pass_rate)?; Ok(()) } } 🎯 第 5 步：实现命令行接口 创建 src/cli.rs：\nuse clap::Parser; /// IoT 自动化测试框架 #[derive(Parser, Debug)] #[command(name = \u0026#34;iot-test\u0026#34;)] #[command(version = \u0026#34;2.0.0\u0026#34;)] #[command(about = \u0026#34;IoT Automation Testing Framework in Rust\u0026#34;, long_about = None)] pub struct Cli { /// 测试配置文件 (XML) #[arg(short, long)] pub config: Option\u0026lt;String\u0026gt;, /// 设备序列号 (ADB ID 或 IP 地址) #[arg(short, long)] pub serial_number: Option\u0026lt;String\u0026gt;, /// Meta 路径（固件升级） #[arg(short, long)] pub meta: Option\u0026lt;String\u0026gt;, /// Apps 路径（固件升级） #[arg(short, long)] pub apps: Option\u0026lt;String\u0026gt;, /// TAC 端口 #[arg(short, long)] pub tac_port: Option\u0026lt;String\u0026gt;, /// 设备 ID (来自 Axiom) #[arg(short, long)] pub device_id: Option\u0026lt;String\u0026gt;, /// 启用 ADB over WiFi #[arg(short = \u0026#39;w\u0026#39;, long)] pub adb_over_wifi: bool, /// 执行类型 (adb 或 ssh) #[arg(short, long, default_value = \u0026#34;adb\u0026#34;)] pub exec_type: String, /// 调试 UART 端口 (例如: COM7) #[arg(long)] pub debug_uart: Option\u0026lt;String\u0026gt;, /// 日志级别 #[arg(short, long, default_value = \u0026#34;info\u0026#34;)] pub log_level: String, /// 列出所有连接的设备 #[arg(long)] pub list_devices: bool, } 📚 第 6 步：实现库入口 创建 src/lib.rs：\npub mod cli; pub mod device; pub mod test; pub use cli::Cli; pub use device::Adb; pub use test::{TestCase, TestResult, TestRunner}; 🎮 第 7 步：实现主程序 创建 src/main.rs：\nuse anyhow::Result; use clap::Parser; use iot_automation_rust::{Cli, Adb}; use log::info; #[tokio::main] async fn main() -\u0026gt; Result\u0026lt;()\u0026gt; { let cli = Cli::parse(); // 初始化日志 env_logger::Builder::from_env( env_logger::Env::default() .default_filter_or(\u0026amp;cli.log_level) ) .format_timestamp_millis() .init(); info!(\u0026#34;🦀 IoT Automation Framework (Rust) v2.0.0\u0026#34;); info!(\u0026#34;========================================\u0026#34;); // 列出设备 if cli.list_devices { list_devices()?; return Ok(()); } // 获取设备序列号 let serial = match cli.serial_number { Some(s) =\u0026gt; s, None =\u0026gt; { let devices = iot_automation_rust::device::adb::get_devices()?; if devices.is_empty() { anyhow::bail!(\u0026#34;No devices found. Please connect a device or specify --serial-number\u0026#34;); } info!(\u0026#34;Using device: {}\u0026#34;, devices[0]); devices[0].clone() } }; // 创建 ADB 实例 let adb = Adb::new(serial); // 检查设备状态 if !adb.is_online() { anyhow::bail!(\u0026#34;Device is not online\u0026#34;); } info!(\u0026#34;Device: {}\u0026#34;, adb.serial()); info!(\u0026#34;State: {}\u0026#34;, adb.get_state()?); // 获取设备信息 let model = adb.get_prop(\u0026#34;ro.product.model\u0026#34;)?; let android_version = adb.get_prop(\u0026#34;ro.build.version.release\u0026#34;)?; info!(\u0026#34;Model: {}\u0026#34;, model); info!(\u0026#34;Android Version: {}\u0026#34;, android_version); info!(\u0026#34;========================================\u0026#34;); info!(\u0026#34;Ready to run tests!\u0026#34;); Ok(()) } fn list_devices() -\u0026gt; Result\u0026lt;()\u0026gt; { let devices = iot_automation_rust::device::adb::get_devices()?; if devices.is_empty() { println!(\u0026#34;No devices found\u0026#34;); } else { println!(\u0026#34;Connected devices:\u0026#34;); for (i, device) in devices.iter().enumerate() { println!(\u0026#34; {}. {}\u0026#34;, i + 1, device); } } Ok(()) } ✅ 第 8 步：测试运行 8.1 编译项目 cargo build 8.2 运行程序 # 列出设备 cargo run -- --list-devices # 指定设备运行 cargo run -- --serial-number 6a2ec220 # 查看帮助 cargo run -- --help 8.3 运行测试 cargo test 📊 第 9 步：创建示例测试用例 创建 examples/simple_reboot.rs：\nuse anyhow::Result; use async_trait::async_trait; use iot_automation_rust::{Adb, TestCase, TestResult, TestRunner}; use log::info; /// 简单的 Reboot 测试 struct SimpleRebootTest { adb: Adb, } impl SimpleRebootTest { fn new(serial: String) -\u0026gt; Self { Self { adb: Adb::new(serial), } } } #[async_trait] impl TestCase for SimpleRebootTest { fn name(\u0026amp;self) -\u0026gt; \u0026amp;str { \u0026#34;Simple Reboot Test\u0026#34; } fn description(\u0026amp;self) -\u0026gt; \u0026amp;str { \u0026#34;Test device reboot functionality\u0026#34; } async fn setup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { info!(\u0026#34;Setting up reboot test\u0026#34;); Ok(()) } async fn execute(\u0026amp;mut self) -\u0026gt; Result\u0026lt;TestResult\u0026gt; { info!(\u0026#34;Executing reboot test\u0026#34;); // 重启设备 self.adb.reboot()?; // 等待设备就绪 self.adb.wait_for_device(60).await?; // 检查设备状态 let uptime = self.adb.shell(\u0026#34;uptime\u0026#34;)?; info!(\u0026#34;Device uptime: {}\u0026#34;, uptime); Ok(TestResult::Pass) } async fn cleanup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { info!(\u0026#34;Cleaning up reboot test\u0026#34;); Ok(()) } } #[tokio::main] async fn main() -\u0026gt; Result\u0026lt;()\u0026gt; { env_logger::init(); // 获取设备 let devices = iot_automation_rust::device::adb::get_devices()?; if devices.is_empty() { anyhow::bail!(\u0026#34;No devices found\u0026#34;); } let serial = devices[0].clone(); info!(\u0026#34;Using device: {}\u0026#34;, serial); // 创建测试运行器 let mut runner = TestRunner::new(); // 添加测试 runner.add_test(Box::new(SimpleRebootTest::new(serial))); // 运行测试 let results = runner.run_sequential().await?; // 输出结果 println!(\u0026#34;\\n{}\u0026#34;, runner.statistics()); for result in results { println!(\u0026#34; {} - {} ({} ms)\u0026#34;, result.name, result.result, result.duration_ms ); } Ok(()) } 运行示例：\ncargo run --example simple_reboot 🎓 第 10 步：学习资源 Rust 学习资源 官方文档\nThe Rust Book: https://doc.rust-lang.org/book/ Rust by Example: https://doc.rust-lang.org/rust-by-example/ 中文资源\nRust 程序设计语言（中文版）: https://kaisery.github.io/trpl-zh-cn/ Rust 语言圣经: https://course.rs/ 视频教程\nYouTube: \u0026ldquo;Rust Programming Tutorial\u0026rdquo; B站: \u0026ldquo;Rust 编程语言入门教程\u0026rdquo; 下一步学习 所有权和借用 - Rust 最重要的概念 错误处理 - Result 和 Option 异步编程 - async/await 和 tokio trait 和泛型 - 代码复用 生命周期 - 引用的作用域 📝 总结 已完成的功能 ✅ 项目结构搭建 ✅ ADB 核心功能封装 ✅ 测试基类和运行器 ✅ 命令行接口 ✅ 日志系统 ✅ 示例测试用例\n下一步计划 📋 Week 2-3:\n配置文件解析 (XML/JSON) 更多测试用例 (Camera, Audio) 日志收集功能 报告生成 运行检查清单 # 1. 检查 Rust 安装 rustc --version cargo --version # 2. 创建项目 cargo new iot-automation-rust cd iot-automation-rust # 3. 添加依赖到 Cargo.toml # 4. 创建模块文件 # 5. 编译 cargo build # 6. 运行 cargo run -- --list-devices # 7. 测试 cargo test # 8. 运行示例 cargo run --example simple_reboot 🤝 需要帮助？ 如果遇到问题，可以：\n查看编译错误信息 使用 cargo check 快速检查代码 使用 cargo clippy 获取代码建议 查看 Rust 文档 问我！😊 准备好开始了吗？让我知道你的进度！🚀\n","permalink":"http://testai666.cn/posts/rust-%E9%87%8D%E5%86%99-iot-%E8%87%AA%E5%8A%A8%E5%8C%96%E6%A1%86%E6%9E%B6/","summary":"\u003ch2 id=\"-前置准备\"\u003e📋 前置准备\u003c/h2\u003e\n\u003ch3 id=\"1-安装-rust\"\u003e1. 安装 Rust\u003c/h3\u003e\n\u003ch4 id=\"windows-安装步骤\"\u003eWindows 安装步骤：\u003c/h4\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e下载 Rust 安装器\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e访问：https://rustup.rs/\u003c/li\u003e\n\u003cli\u003e或直接下载：https://win.rustup.rs/x86_64\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e运行安装器\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 下载后运行 rustup-init.exe\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 选择默认安装选项（按 1 然后回车）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e验证安装\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 重新打开 PowerShell\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erustc --version\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo --version\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e配置国内镜像（可选，加速下载）\u003c/strong\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 创建配置文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir $env:USERPROFILE\\.cargo\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enotepad $env:USERPROFILE\\.cargo\\config.toml\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e添加以下内容：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-toml\" data-lang=\"toml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003esource\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003ecrates-io\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003ereplace-with\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#39;ustc\u0026#39;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e[\u003cspan style=\"color:#a6e22e\"\u003esource\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eustc\u003c/span\u003e]\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003eregistry\u003c/span\u003e = \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;https://mirrors.ustc.edu.cn/crates.io-index\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"2-安装-vs-code-rust-插件推荐\"\u003e2. 安装 VS Code Rust 插件（推荐）\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003erust-analyzer\u003c/strong\u003e: Rust 语言服务器\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eCodeLLDB\u003c/strong\u003e: 调试器\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ecrates\u003c/strong\u003e: 依赖管理\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"-第-1-步创建项目结构\"\u003e🏗️ 第 1 步：创建项目结构\u003c/h2\u003e\n\u003ch3 id=\"11-创建新项目\"\u003e1.1 创建新项目\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-powershell\" data-lang=\"powershell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd C:\\Users\\xli23\\Desktop\\uv-env\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecargo new iot-automation-rust\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecd iot-automation-rust\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"12-项目结构\"\u003e1.2 项目结构\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eiot-automation-rust/\n├── Cargo.toml          # 项目配置和依赖\n├── src/\n│   ├── main.rs         # 程序入口\n│   ├── lib.rs          # 库入口\n│   ├── cli.rs          # 命令行参数\n│   ├── config/         # 配置模块\n│   │   ├── mod.rs\n│   │   ├── parser.rs\n│   │   └── global.rs\n│   ├── device/         # 设备模块\n│   │   ├── mod.rs\n│   │   └── adb.rs\n│   ├── test/           # 测试模块\n│   │   ├── mod.rs\n│   │   ├── base.rs\n│   │   └── runner.rs\n│   └── utils/          # 工具模块\n│       ├── mod.rs\n│       └── logger.rs\n├── tests/              # 集成测试\n└── examples/           # 示例代码\n\u003c/code\u003e\u003c/pre\u003e\u003chr\u003e\n\u003ch2 id=\"-第-2-步配置-cargotoml\"\u003e📝 第 2 步：配置 Cargo.toml\u003c/h2\u003e\n\u003cp\u003e创建 \u003ccode\u003eCargo.toml\u003c/code\u003e 文件：\u003c/p\u003e","title":"Rust 重写 IoT 自动化框架 - 阶段 1：核心功能"},{"content":"1. 摄像头测试用例主要位置 Python 测试用例目录 主要在这些目录下：\nCase/Products/*/AUSanity/Camera/ Case/Products/*/Stability/Camera.py Case/Products/*/PreFlight/Camera.py Case/Products/*/Sanity/Camera.py Case/Products/QRB5165_UBUN_1_1/Camera/ Case/Products/QRB5165_UBUN_2_0/Camera/ Case/Products/*/Robotics/*camera*.py 典型文件包括：\nCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/Camera.py Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX586.py Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX686.py Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraOV9282.py Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraGMSL.py Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX766.py Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX688.py Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV64B40.py Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraS5K3M5.py Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraConcurrency.py Case/Products/QCS8625_LE_1_0/AUSanity/Camera/*.py Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/*.py Case/Products/QCS610_LE_1_0_C2/Stability/Camera.py Case/Products/QRB5165_UBUN_2_0/Stability/Camera.py Case/Products/QRB5165_UBUN_1_1/Camera/CameraGST.py Case/Products/QRB5165_UBUN_1_1/Camera/CameraHAL3.py Case/Products/QRB5165_UBUN_1_1/Camera/CameraFRandLBT.py Plan 配置目录 摄像头命令和计划主要在：\nPlan/*/*/Config/Camera/ Plan/*/*/Config/Camera/TestCommands/*.json Plan/*/*/Config/PreFlight/*Camera*.xml Plan/*/*/Config/Stability/*Camera*.xml 典型配置文件：\nPlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/GSTCommands.json Plan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/SevenCameraCommends.json Plan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/USBCameraCommands.json Plan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/LBT_GSTCommands.json Plan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/LBT_HAL3Commands.json Plan/LE/QCS8550_LE_1_0/Config/Camera/TestCommands/CameraGST_commands.json Plan/LE/QCS610_LE_1_0_C2/Config/Camera/QCS610_LE_1_0_C2_Camera_LBT_test_plan.xml 2. QRB5165_UBUN_2_0 摄像头用例 2.1 AUSanity 单摄基础功能 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/Camera.py IMX577Preview IMX577 预览，3840x2160，60fps，dump YUV /data/output/camera/ 下生成 IMX577_Preview*.yuv，且文件大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 IMX577Snapshot IMX577 预览 + 拍照，输出 JPG pipeline 进入 PLAYING；生成 IMX577_Snapshot_*.jpg；文件大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 IMX577LiveSnapshot IMX577 边录像边拍照，H.264 mp4 + JPG pipeline 进入 PLAYING；生成 IMX577_Live_Snapshot_*.jpg 且大小 \u0026gt; 0；同时拉取 IMX577_h264.mp4 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX586.py IMX586Preview IMX586 预览，4K YUV dump 生成 IMX586_Preview*.yuv 且大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 IMX586Snapshot IMX586 拍照，JPG 输出，8000x6000 pipeline 进入 PLAYING；生成 IMX586_Snapshot_*.jpg 且大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 IMX586LiveSnapshot IMX586 live snapshot + H.264 录像 生成 IMX586_Live_Snapshot_*.jpg 且大小 \u0026gt; 0；生成或拉取 IMX586_h264.mp4 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX686.py IMX686Preview / Snapshot / LiveSnapshot IMX686 预览、拍照、边录边拍 对应 YUV/JPG/MP4 文件存在且大小 \u0026gt; 0 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraOV9282.py CameraOV9282 相关用例 OV9282 tracking camera 预览 / RAW10 dump 输出 .raw10 或预览 dump 文件，文件存在且大小 \u0026gt; 0 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraGMSL.py CameraGMSL 相关用例 GMSL 摄像头预览、RAW12、录像、快照 输出 .raw12 / .mp4 / .jpg 等目标文件，且大小 \u0026gt; 0 2.2 Stability 摄像头稳定性与并发 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/Stability/Camera.py IMX577SnapshotDisplayPreview IMX577 display preview + snapshot 生成 snapshot* 文件，大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 IMX577LiveSnapshotDisplayPreview IMX577 预览 + 录像 + live snapshot 子进程 IMX577LSDPCon 执行成功；snapshot 和 mp4 都存在且大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 DualCameraConcurrency 双摄并发：IMX577 live snapshot + OV9282 tracking preview 两个并发子进程都 Pass；主摄 snapshot/video 成功，tracking camera preview 成功 QRB5165_UBUN_2_0 同上 FourIMX577Concurrency 四路 IMX577 并发录像 /data/output/ 中生成 mux0_264.mp4、mux1_264.mp4、mux2_264.mp4、mux3_264.mp4，全部大小 \u0026gt; 0 QRB5165_UBUN_2_0 同上 SevenCameraConcurrency 七摄并发：2 路 IMX577、3 路 OV9282、2 路 GMSL 所有 7 个子摄像头进程都 Pass；IMX577 要生成 jpg + mp4；OV9282 要生成 raw10；GMSL 要生成 raw12 QRB5165_UBUN_2_0 同上 TwoOV9282DisplayPreview 两路 OV9282 同时 display preview 两路 pipeline 都成功进入 PLAYING，日志中有 Pipeline state change was successful QRB5165_UBUN_2_0 同上 TwoGMSLDisplayPreview 两路 GMSL 同时 display preview 两路 GMSL 子进程都 Pass，日志中有 pipeline 成功状态 2.3 Camera 通用 GST/HAL3/LBT 测试 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QRB5165_UBUN_1_1 / 2_0 复用 Case/Products/QRB5165_UBUN_1_1/Camera/CameraGST.py GSTCamera 从 JSON 读取 GStreamer 命令，执行单摄/多摄 preview、video、snapshot、参数测试 pipeline_res、video_res、preview_res 全部为 True QRB5165_UBUN_1_1 / 2_0 复用 同上 CameraStartStopSequence 多摄启动/停止顺序组合测试 所有排列组合的 pipeline、video、preview 检查均通过；没有失败子目录 QRB5165_UBUN_1_1 / 2_0 复用 同上 CameraLongDuration 长时间 Camera 稳定性测试 在 GlobalVariable.test_time 内循环检查：pipeline 正常、video 正常、preview 正常、GST log 无错误、无 coredump QRB5165_UBUN_1_1 / 2_0 复用 Case/Products/QRB5165_UBUN_1_1/Camera/CameraHAL3.py CameraHAL3 使用 camx-hal3-test 测 HAL3 preview/video/snapshot app 启动成功；preview/video/snapshot dump 存在且大小 \u0026gt; 0；无 crash dump；性能用例 fps 达标 QRB5165_UBUN_1_1 / 2_0 复用 同上 CameraHAL3LongDuration HAL3 长时间测试 指定时间内多轮 dump 检查均正常；无 crash；最后设置 PASS QRB5165_UBUN_1_1 / 2_0 复用 Case/Products/QRB5165_UBUN_1_1/Camera/CameraFRandLBT.py yavta USB/V4L2/yavta camera buffer 采集 log 中出现 buffers released，或生成 testyuv* 文件且大小 \u0026gt; 0 3. QCS610_LE_1_0_C2 摄像头用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QCS610_LE_1_0_C2 Case/Products/QCS610_LE_1_0_C2/Stability/Camera.py IMX577Snapshot IMX577 预览 + 拍照 生成 snapshot*.jpg 且大小 \u0026gt; 0；生成 frame*.yuv 且大小 \u0026gt; 0；两者都满足才 Pass QCS610_LE_1_0_C2 同上 IMX577LiveSnapshot IMX577 预览 + snapshot + H.264 录像 snapshot*.jpg、frame*.yuv、mux1.mp4 均存在且大小 \u0026gt; 0 QCS610_LE_1_0_C2 同上 USBCameraVideoRecording USB Camera 录像，v4l2src 采集 log 中包含 Execution ended，且不包含 error；/tmp/uvc.mp4 存在且大小 \u0026gt; 0 QCS610_LE_1_0_C2 同上 USBCameraCon USB Camera 并发封装录像 与 USBCameraVideoRecording 类似；mp4 存在且大小 \u0026gt; 0 后子进程 Pass QCS610_LE_1_0_C2 同上 IMX577VideoRecordingPreview IMX577 preview + video recording 子进程 IMX577VideoRecordingPreviewCon Pass QCS610_LE_1_0_C2 同上 IMX577VideoRecordingPreviewCon 预览帧 dump + 录像 frame*.yuv 存在且大小 \u0026gt; 0；mux1.mp4 存在且大小 \u0026gt; 0 4. QCS8550_LE_1_0 摄像头用例 4.1 AUSanity 单摄 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX766.py IMX766Preview / Snapshot / LiveSnapshot IMX766 主摄 4K/8K 预览、拍照、边录边拍 pipeline 进入 PLAYING；对应 YUV/JPG/MP4 文件存在且大小 \u0026gt; 0 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX688.py IMX688LiveSnapshot 等 IMX688 预览、录像、live snapshot 目标输出文件存在且大小 \u0026gt; 0 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV64B40.py CameraOV64B40 OV64B40 4K preview/video 预览或视频输出存在且大小 \u0026gt; 0 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraS5K3M5.py CameraS5K3M5 S5K3M5 RAW10 / RDI dump RAW/YUV dump 存在且大小 \u0026gt; 0 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV9282.py CameraOV9282 OV9282 tracking camera RAW10 或 preview dump 存在且大小 \u0026gt; 0 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraGMSL.py CameraGMSL GMSL preview、raw、video、snapshot 对应 raw12/mp4/jpg/yuv 文件存在且大小 \u0026gt; 0 4.2 并发 / 压力 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraConcurrency.py FourCameraConcurrency 或 HDK/HAL 四摄相关 四摄并发，通常覆盖 IMX766、IMX688、OV64B40、S5K3M5 四路 camera 输出均存在，且文件大小 \u0026gt; 0；所有子进程 Pass QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/Stability/Camera.py Camera stability 用例 长时间摄像头压力 / 多轮稳定性 测试周期内无 crash、无 coredump、输出正常、fps/日志检查正常 QCS8550_LE_1_0 Case/Products/QCS8550_LE_1_0/Stability/NonGPU/Camera.py NonGPU Camera 压力 非 GPU 场景下摄像头稳定性 同稳定性逻辑：pipeline 正常、输出正常、无 error 5. QCS8625_LE_1_0 摄像头用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraIMX766.py IMX766 相关 主摄 preview/snapshot/live snapshot 输出文件存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraIMX688.py IMX688 相关 预览、录像、快照 输出文件存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraOV64B40.py OV64B40 相关 4K preview/video 输出文件存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraS5K3M5.py S5K3M5 相关 RAW10/RDI dump raw/yuv 输出存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraOV9282.py OV9282 相关 tracking camera preview/raw dump raw10 或 preview dump 存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraGMSL.py GMSL 相关 GMSL preview/video/raw/snapshot raw12/mp4/jpg/yuv 等存在且大小 \u0026gt; 0 QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraConcurrency.py 并发 camera 多摄并发 所有并发子摄像头均 Pass QCS8625_LE_1_0 Case/Products/QCS8625_LE_1_0/Stability/Camera.py Camera stability 稳定性压力 指定时间内无错误、输出正常、无 crash 6. CQ7790M_LE_1_5 摄像头用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX766.py IMX766 preview/snapshot/live snapshot 输出文件存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX688.py IMX688 preview/video/snapshot 输出文件存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX858.py IMX858 preview/video/snapshot 输出文件存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraS5KJN1.py S5KJN1 preview/video/snapshot 输出文件存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraOV9282.py OV9282 tracking camera raw10/preview 输出存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraGMSL.py GMSL raw12/preview/video/snapshot 输出文件存在且大小 \u0026gt; 0 CQ7790M_LE_1_5 Case/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraConcurrency.py 多摄并发 多路 camera 同时运行 所有子 camera 结果均为 Pass 7. SW5100_LAW 系列摄像头用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 SW5100_LAW_1_0 Case/Products/SW5100_LAW_1_0/PreFlight/Camera.py PreFlight Camera Android/LAW 平台摄像头预检，snapshot / live snapshot / video 输出文件生成、命令执行无 error、检查逻辑通过 SW5100_LAW_1_1 Case/Products/SW5100_LAW_1_1/PreFlight/Camera.py PreFlight Camera 前后摄 ZSL/PZSL snapshot、双摄 snapshot、双摄录像 对应截图/录像文件存在；流程无异常 SW5100_LAW_1_1 Case/Products/SW5100_LAW_1_1/Sanity/Camera.py Sanity Camera 基础相机 sanity 命令执行成功，目标输出存在 SW5100_LAW_1_1 Case/Products/SW5100_LAW_1_1/Stability/Camera.py Stability Camera 摄像头稳定性 指定时间内无错误、输出正常 SW5100_LAW_2_0 Case/Products/SW5100_LAW_2_0/Sanity/Camera.py Sanity Camera 基础相机功能 输出正常，无异常 相关 Plan：\nPlan/LAW/SW5100_LAW_1_1/Config/PreFlight/SingleCase/*Camera*.xml 例如： Back_Camera_ZSL_Snapshot Front_Camera_ZSL_Snapshot Back_Camera_PZSL_Snapshot Front_Camera_PZSL_Snapshot pre_flight_dual_camera_snapshot pre_flight_dual_camera_video_record 8. QRB2210_LE_1_0 摄像头用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QRB2210_LE_1_0 Case/Products/QRB2210_LE_1_0/PreFlight/Camera.py PreFlight Camera 摄像头预检、双摄并发 预览/录像/快照输出存在，无 error QRB2210_LE_1_0 Plan XML camera_concurrency 两路 camera 并发 两个 camera 都正常输出，整体 Pass 相关 Plan：\nPlan/LE/QRB2210_LE_1_0/Config/PreFlight/SingleCase/*camera_concurrency.xml 9. Robotics / ROS Camera 用例 平台 文件路径 用例 / 用例族 测试点 Pass 条件 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/Robotics/camera_ros_node.py Camera ROS node ROS 摄像头节点启动、数据发布 ROS node 正常运行，日志无错误，预期 topic/data 正常 QRB5165_UBUN_2_0 Case/Products/QRB5165_UBUN_2_0/Robotics/Zero_copy_camera_node.py Zero copy camera node 零拷贝 camera node 节点启动成功，数据流正常，无异常 QCS9100_LE_1_0 Case/Products/QCS9100_LE_1_0/Stability/CameraROSNode.py camera_ros_node_ov9282 OV9282 ROS camera node 稳定性 ROS camera 节点正常运行，测试周期内无错误 相关 Plan：\nPlan/LE/QCS9100_LE_1_0/Config/Stability/QCS9100_LE_1_0_APStress.xml Plan/LE/QCS9100_LE_1_0/Config/Stability/LeMansReboot.json 10. Plan/Config 中的摄像头功能覆盖 10.1 QRB5165_UBUN_2_0 Camera Plan 配置路径 测试点 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/imx577_camera/ 主摄 IMX577 preview、YUV dump、snapshot、live snapshot、video H.264/H.265、HFR、参数测试 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/ov9282_camera/ tracking camera preview、RAW dump、ISO、曝光、zoom Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/gmsl_camera/ GMSL preview、RAW/YUV dump、video H.264/H.265、snapshot、live snapshot、参数测试 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/usb_camera/ USB camera preview、snapshot、video、MJPEG/YUV 编码 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/Hal3/ HAL3 preview、snapshot、live snapshot、HFR、manual WB/exposure、anti-flicker Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Axoim_plan/2_camera/ 双摄并发，主摄 + tracking camera，多种启动顺序和功能组合 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/FR65021_7_Camera_Concurrency/ 七摄并发 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/FR63626_ISP_Switch/ ISP switch，多 camera 组合切换测试 Plan/LU/QRB5165_UBUN_2_0/Config/Camera/Camera_LBT/ LBT camera GST/HAL3 测试 10.2 QCS8550_LE_1_0 Camera Plan 配置路径 测试点 Plan/LE/QCS8550_LE_1_0/Config/Camera/TestCommands/CameraGST_commands.json QCS8550 Camera GST 命令 Plan/LE/QCS8550_LE_1_0/Config/PreFlight/QCS8550_LE_1_0_PF_HDKFourCameraConcurrency.xml HDK 四摄并发 Plan/LE/QCS8550_LE_1_0/Config/PreFlight/QCS8550_LE_1_0_PF_HALFourCameraConcurrency.xml HAL 四摄并发 Plan/LE/QCS8550_LE_1_0/Config/Stability/QCS8550_LE_1_0_HALFourCameraStress.xml HAL 四摄压力测试 10.3 QCS610_LE_1_0_C2 Camera Plan 配置路径 测试点 Plan/LE/QCS610_LE_1_0_C2/Config/Camera/QCS610_LE_1_0_C2_Camera_LBT_test_plan.xml Camera LBT Plan/LE/QCS610_LE_1_0_C2/Config/Camera/QCS610_LE_1_0_C2_IMSDK_test_plan.xml IMSDK camera Plan/LE/QCS610_LE_1_0_C2/Config/Camera/TestCommands/GSTCommands.json GST camera 命令 Plan/LE/QCS610_LE_1_0_C2/Config/Camera/TestCommands/VideoCommands.json 视频相关命令 11. 按测试点归类总结 11.1 Preview 测试 覆盖用例：\nIMX577Preview IMX586Preview IMX686Preview IMX766Preview TrackingCameraPreview TwoOV9282DisplayPreview TwoGMSLDisplayPreview HAL3 preview GST preview 主要检查：\ncamera pipeline 是否能进入 PLAYING。 是否能输出 preview dump。 是否能 display preview。 是否无 GST error。 Pass 条件：\npipeline 状态成功。 yuv/raw/ubwc dump 文件存在且大小 \u0026gt; 0。 或日志中有 preview 成功关键字。 无 error / crash / coredump。 11.2 Snapshot 测试 覆盖用例：\nIMX577Snapshot IMX586Snapshot IMX686Snapshot IMX766Snapshot MainCameraSnapshotDisplayPreview HAL3 snapshot GMSL snapshot USB snapshot 主要检查：\npipeline 启动。 触发 capture-image。 JPG 是否生成。 Pass 条件：\nsnapshot jpg 文件存在。 文件大小 \u0026gt; 0。 同时要求 preview 的用例，还需要 preview dump 存在且大小 \u0026gt; 0。 11.3 Live Snapshot 测试 覆盖用例：\nIMX577LiveSnapshot IMX586LiveSnapshot IMX686LiveSnapshot IMX766LiveSnapshot MainCameraLiveSnapshotDisplayPreview IMX577LiveSnapshotDisplayPreview GMSL live snapshot 主要检查：\npreview、录像、拍照同时工作。 live snapshot 中 capture-image 是否成功。 录像文件是否正常。 Pass 条件：\njpg snapshot 存在且大小 \u0026gt; 0。 mp4 video 存在且大小 \u0026gt; 0。 如果有 preview dump，preview dump 也要存在且大小 \u0026gt; 0。 11.4 Video Recording 测试 覆盖用例：\nIMX577VideoRecordingPreview USBCameraVideoRecording GST video H.264/H.265 GMSL video recording USB camera video recording 主要检查：\nvideo encoder 是否正常。 mp4 是否生成。 MediaInfo 是否匹配期望编码、分辨率、fps。 Pass 条件：\nGStreamer 执行无 error。 stdout 中有 Execution ended 或 pipeline 正常结束。 mp4 文件存在且大小 \u0026gt; 0。 如果有 MediaInfo 检查，编码、分辨率、fps 要符合预期。 11.5 RAW Dump 测试 覆盖用例：\nOV9282 RAW10 GMSL RAW12 S5K3M5 RAW10/RDI HAL3 raw dump 主要检查：\nRAW 数据是否能从 sensor dump 出来。 RAW 格式是否符合预期。 Pass 条件：\n.raw10 或 .raw12 文件存在。 文件大小 \u0026gt; 0。 无 pipeline error。 11.6 多摄并发测试 覆盖用例：\nDualCameraConcurrency FourCameraConcurrency FourIMX577Concurrency SevenCameraConcurrency TwoOV9282DisplayPreview TwoGMSLDisplayPreview HDK/HAL four camera concurrency 主要检查：\n多路 camera 是否可同时启动。 多路 preview/video/raw/snapshot 是否互不影响。 各个子进程是否都能成功完成。 Pass 条件：\n每个子 camera 的输出都存在且大小 \u0026gt; 0。 所有子进程 test_result == PASS。 任意一路失败则整体 Fail。 11.7 Camera 参数测试 覆盖配置：\nISO manual exposure time manual white balance exposure metering zoom anti-flicker HFR 参数 主要检查：\n参数是否能下发。 参数下发后 preview/snapshot/video 是否仍正常。 参数场景下是否无 crash。 Pass 条件：\n参数设置流程无异常。 pipeline 正常。 输出文件存在且大小 \u0026gt; 0。 无 GST error / camera crash。 11.8 长时间稳定性测试 覆盖用例：\nCameraLongDuration CameraHAL3LongDuration HALFourCameraStress APStress 中的 camera case 主要检查：\n长时间预览。 长时间录像。 多轮 snapshot / dump。 fps 是否持续正常。 是否产生 coredump。 存储空间是否充足。 GST/HAL3 是否报错。 Pass 条件：\n在指定测试时间内每轮检查都通过。 无 crash。 无 coredump。 GST log 无 error。 输出文件正常。 性能用例 fps 达标。 12. 项目中 Camera Pass 判定的共性逻辑 大多数 Camera 用例最终都围绕下面几个条件判断：\n条件 说明 pipeline 成功 日志中检测 PLAYING、Pipeline state changed from PAUSED to PLAYING 文件存在 通过 adb shell ls / grep 查找目标文件 文件大小 \u0026gt; 0 通过 Utility.get_file_size_on_device 或类似方法检查 无错误 GST log 不包含 error，命令返回无异常 无 crash /data/misc/camera 或 /data/coredump 中无 crash dump 并发全通过 所有子进程 ConcurrencyTestUtils.result(...) 为 True 性能达标 fps 达到预期，一般目标值约为输入 fps 的 99% 典型判定模式：\n找文件： adb shell \u0026quot;ls -l /data/output/ | grep snapshot\u0026quot; adb shell \u0026quot;ls -l /data/output/ | grep mp4\u0026quot; adb shell \u0026quot;ls -l /data/output/camera | grep IMX586_Preview\u0026quot; 检查大小： Utility.get_file_size_on_device(...) \u0026gt; 0 Utility.get_file_size_on_device_ex(...) \u0026gt; 0 设置结果： self._test_result = TestResult.PASS 或子进程中：self.test_result = TestResult.PASS 13. 最简总览 测试类型 主要验证内容 Pass 条件 Preview 摄像头能否预览 / dump frame yuv/raw/ubwc 文件存在且大小 \u0026gt; 0，pipeline 无 error Snapshot 拍照功能 jpg 文件存在且大小 \u0026gt; 0 Live Snapshot 边录边拍 jpg + mp4 均存在且大小 \u0026gt; 0 Video Recording 录像编码 mp4 存在且大小 \u0026gt; 0，必要时 MediaInfo/FPS 通过 RAW Dump RAW10/RAW12 输出 raw 文件存在且大小 \u0026gt; 0 USB Camera V4L2/yavta/USB 录像 video 节点存在，输出文件存在且大小 \u0026gt; 0 HAL3 camx-hal3-test preview/video/snapshot dump 正常，无 crash 多摄并发 双摄/四摄/七摄同时运行 所有子摄像头均 Pass 参数测试 ISO/曝光/白平衡/zoom 等 参数设置后功能输出正常 稳定性 长时间运行 全周期无 error、无 crash、输出正常 ","permalink":"http://testai666.cn/posts/%E9%A1%B9%E7%9B%AE%E6%91%84%E5%83%8F%E5%A4%B4%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E6%80%BB%E7%BB%93/","summary":"\u003ch2 id=\"1-摄像头测试用例主要位置\"\u003e1. 摄像头测试用例主要位置\u003c/h2\u003e\n\u003ch3 id=\"python-测试用例目录\"\u003ePython 测试用例目录\u003c/h3\u003e\n\u003cp\u003e主要在这些目录下：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/*/AUSanity/Camera/\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/*/Stability/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/*/PreFlight/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/*/Sanity/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/Camera/\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/*/Robotics/*camera*.py\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e典型文件包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX586.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX686.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraOV9282.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraGMSL.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX766.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX688.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV64B40.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraS5K3M5.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraConcurrency.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/*.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/*.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QCS610_LE_1_0_C2/Stability/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/Stability/Camera.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraGST.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraHAL3.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraFRandLBT.py\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"plan-配置目录\"\u003ePlan 配置目录\u003c/h3\u003e\n\u003cp\u003e摄像头命令和计划主要在：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ePlan/*/*/Config/Camera/\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/*/*/Config/Camera/TestCommands/*.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/*/*/Config/PreFlight/*Camera*.xml\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/*/*/Config/Stability/*Camera*.xml\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e典型配置文件：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/GSTCommands.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/SevenCameraCommends.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/USBCameraCommands.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/LBT_GSTCommands.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LU/QRB5165_UBUN_2_0/Config/Camera/TestCommands/LBT_HAL3Commands.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LE/QCS8550_LE_1_0/Config/Camera/TestCommands/CameraGST_commands.json\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003ePlan/LE/QCS610_LE_1_0_C2/Config/Camera/QCS610_LE_1_0_C2_Camera_LBT_test_plan.xml\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"2-qrb5165_ubun_2_0-摄像头用例\"\u003e2. QRB5165_UBUN_2_0 摄像头用例\u003c/h2\u003e\n\u003ch3 id=\"21-ausanity-单摄基础功能\"\u003e2.1 AUSanity 单摄基础功能\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577Preview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 预览，3840x2160，60fps，dump YUV\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e/data/output/camera/\u003c/code\u003e 下生成 \u003ccode\u003eIMX577_Preview*.yuv\u003c/code\u003e，且文件大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577Snapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 预览 + 拍照，输出 JPG\u003c/td\u003e\n          \u003ctd\u003epipeline 进入 PLAYING；生成 \u003ccode\u003eIMX577_Snapshot_*.jpg\u003c/code\u003e；文件大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577LiveSnapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 边录像边拍照，H.264 mp4 + JPG\u003c/td\u003e\n          \u003ctd\u003epipeline 进入 PLAYING；生成 \u003ccode\u003eIMX577_Live_Snapshot_*.jpg\u003c/code\u003e 且大小 \u0026gt; 0；同时拉取 \u003ccode\u003eIMX577_h264.mp4\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX586.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX586Preview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX586 预览，4K YUV dump\u003c/td\u003e\n          \u003ctd\u003e生成 \u003ccode\u003eIMX586_Preview*.yuv\u003c/code\u003e 且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX586Snapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX586 拍照，JPG 输出，8000x6000\u003c/td\u003e\n          \u003ctd\u003epipeline 进入 PLAYING；生成 \u003ccode\u003eIMX586_Snapshot_*.jpg\u003c/code\u003e 且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX586LiveSnapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX586 live snapshot + H.264 录像\u003c/td\u003e\n          \u003ctd\u003e生成 \u003ccode\u003eIMX586_Live_Snapshot_*.jpg\u003c/code\u003e 且大小 \u0026gt; 0；生成或拉取 \u003ccode\u003eIMX586_h264.mp4\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraIMX686.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX686Preview / Snapshot / LiveSnapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX686 预览、拍照、边录边拍\u003c/td\u003e\n          \u003ctd\u003e对应 YUV/JPG/MP4 文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraOV9282.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraOV9282\u003c/code\u003e 相关用例\u003c/td\u003e\n          \u003ctd\u003eOV9282 tracking camera 预览 / RAW10 dump\u003c/td\u003e\n          \u003ctd\u003e输出 \u003ccode\u003e.raw10\u003c/code\u003e 或预览 dump 文件，文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/AUSanity/Camera/CameraGMSL.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraGMSL\u003c/code\u003e 相关用例\u003c/td\u003e\n          \u003ctd\u003eGMSL 摄像头预览、RAW12、录像、快照\u003c/td\u003e\n          \u003ctd\u003e输出 \u003ccode\u003e.raw12\u003c/code\u003e / \u003ccode\u003e.mp4\u003c/code\u003e / \u003ccode\u003e.jpg\u003c/code\u003e 等目标文件，且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"22-stability-摄像头稳定性与并发\"\u003e2.2 Stability 摄像头稳定性与并发\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_2_0/Stability/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577SnapshotDisplayPreview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 display preview + snapshot\u003c/td\u003e\n          \u003ctd\u003e生成 \u003ccode\u003esnapshot*\u003c/code\u003e 文件，大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577LiveSnapshotDisplayPreview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 预览 + 录像 + live snapshot\u003c/td\u003e\n          \u003ctd\u003e子进程 \u003ccode\u003eIMX577LSDPCon\u003c/code\u003e 执行成功；snapshot 和 mp4 都存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eDualCameraConcurrency\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e双摄并发：IMX577 live snapshot + OV9282 tracking preview\u003c/td\u003e\n          \u003ctd\u003e两个并发子进程都 Pass；主摄 snapshot/video 成功，tracking camera preview 成功\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eFourIMX577Concurrency\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e四路 IMX577 并发录像\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003e/data/output/\u003c/code\u003e 中生成 \u003ccode\u003emux0_264.mp4\u003c/code\u003e、\u003ccode\u003emux1_264.mp4\u003c/code\u003e、\u003ccode\u003emux2_264.mp4\u003c/code\u003e、\u003ccode\u003emux3_264.mp4\u003c/code\u003e，全部大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eSevenCameraConcurrency\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e七摄并发：2 路 IMX577、3 路 OV9282、2 路 GMSL\u003c/td\u003e\n          \u003ctd\u003e所有 7 个子摄像头进程都 Pass；IMX577 要生成 jpg + mp4；OV9282 要生成 raw10；GMSL 要生成 raw12\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eTwoOV9282DisplayPreview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e两路 OV9282 同时 display preview\u003c/td\u003e\n          \u003ctd\u003e两路 pipeline 都成功进入 PLAYING，日志中有 \u003ccode\u003ePipeline state change was successful\u003c/code\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_2_0\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eTwoGMSLDisplayPreview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e两路 GMSL 同时 display preview\u003c/td\u003e\n          \u003ctd\u003e两路 GMSL 子进程都 Pass，日志中有 pipeline 成功状态\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"23-camera-通用-gsthal3lbt-测试\"\u003e2.3 Camera 通用 GST/HAL3/LBT 测试\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraGST.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eGSTCamera\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e从 JSON 读取 GStreamer 命令，执行单摄/多摄 preview、video、snapshot、参数测试\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003epipeline_res\u003c/code\u003e、\u003ccode\u003evideo_res\u003c/code\u003e、\u003ccode\u003epreview_res\u003c/code\u003e 全部为 True\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraStartStopSequence\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e多摄启动/停止顺序组合测试\u003c/td\u003e\n          \u003ctd\u003e所有排列组合的 pipeline、video、preview 检查均通过；没有失败子目录\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraLongDuration\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e长时间 Camera 稳定性测试\u003c/td\u003e\n          \u003ctd\u003e在 \u003ccode\u003eGlobalVariable.test_time\u003c/code\u003e 内循环检查：pipeline 正常、video 正常、preview 正常、GST log 无错误、无 coredump\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraHAL3.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraHAL3\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e使用 \u003ccode\u003ecamx-hal3-test\u003c/code\u003e 测 HAL3 preview/video/snapshot\u003c/td\u003e\n          \u003ctd\u003eapp 启动成功；preview/video/snapshot dump 存在且大小 \u0026gt; 0；无 crash dump；性能用例 fps 达标\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraHAL3LongDuration\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eHAL3 长时间测试\u003c/td\u003e\n          \u003ctd\u003e指定时间内多轮 dump 检查均正常；无 crash；最后设置 PASS\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQRB5165_UBUN_1_1 / 2_0 复用\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QRB5165_UBUN_1_1/Camera/CameraFRandLBT.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eyavta\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUSB/V4L2/yavta camera buffer 采集\u003c/td\u003e\n          \u003ctd\u003elog 中出现 \u003ccode\u003ebuffers released\u003c/code\u003e，或生成 \u003ccode\u003etestyuv*\u003c/code\u003e 文件且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"3-qcs610_le_1_0_c2-摄像头用例\"\u003e3. QCS610_LE_1_0_C2 摄像头用例\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS610_LE_1_0_C2/Stability/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577Snapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 预览 + 拍照\u003c/td\u003e\n          \u003ctd\u003e生成 \u003ccode\u003esnapshot*.jpg\u003c/code\u003e 且大小 \u0026gt; 0；生成 \u003ccode\u003eframe*.yuv\u003c/code\u003e 且大小 \u0026gt; 0；两者都满足才 Pass\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577LiveSnapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 预览 + snapshot + H.264 录像\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003esnapshot*.jpg\u003c/code\u003e、\u003ccode\u003eframe*.yuv\u003c/code\u003e、\u003ccode\u003emux1.mp4\u003c/code\u003e 均存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eUSBCameraVideoRecording\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUSB Camera 录像，\u003ccode\u003ev4l2src\u003c/code\u003e 采集\u003c/td\u003e\n          \u003ctd\u003elog 中包含 \u003ccode\u003eExecution ended\u003c/code\u003e，且不包含 error；\u003ccode\u003e/tmp/uvc.mp4\u003c/code\u003e 存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eUSBCameraCon\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eUSB Camera 并发封装录像\u003c/td\u003e\n          \u003ctd\u003e与 \u003ccode\u003eUSBCameraVideoRecording\u003c/code\u003e 类似；mp4 存在且大小 \u0026gt; 0 后子进程 Pass\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577VideoRecordingPreview\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX577 preview + video recording\u003c/td\u003e\n          \u003ctd\u003e子进程 \u003ccode\u003eIMX577VideoRecordingPreviewCon\u003c/code\u003e Pass\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS610_LE_1_0_C2\u003c/td\u003e\n          \u003ctd\u003e同上\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX577VideoRecordingPreviewCon\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e预览帧 dump + 录像\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eframe*.yuv\u003c/code\u003e 存在且大小 \u0026gt; 0；\u003ccode\u003emux1.mp4\u003c/code\u003e 存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"4-qcs8550_le_1_0-摄像头用例\"\u003e4. QCS8550_LE_1_0 摄像头用例\u003c/h2\u003e\n\u003ch3 id=\"41-ausanity-单摄\"\u003e4.1 AUSanity 单摄\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX766.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX766Preview / Snapshot / LiveSnapshot\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX766 主摄 4K/8K 预览、拍照、边录边拍\u003c/td\u003e\n          \u003ctd\u003epipeline 进入 PLAYING；对应 YUV/JPG/MP4 文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraIMX688.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eIMX688LiveSnapshot\u003c/code\u003e 等\u003c/td\u003e\n          \u003ctd\u003eIMX688 预览、录像、live snapshot\u003c/td\u003e\n          \u003ctd\u003e目标输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV64B40.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraOV64B40\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOV64B40 4K preview/video\u003c/td\u003e\n          \u003ctd\u003e预览或视频输出存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraS5K3M5.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraS5K3M5\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eS5K3M5 RAW10 / RDI dump\u003c/td\u003e\n          \u003ctd\u003eRAW/YUV dump 存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraOV9282.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraOV9282\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOV9282 tracking camera\u003c/td\u003e\n          \u003ctd\u003eRAW10 或 preview dump 存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraGMSL.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCameraGMSL\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eGMSL preview、raw、video、snapshot\u003c/td\u003e\n          \u003ctd\u003e对应 raw12/mp4/jpg/yuv 文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"42-并发--压力\"\u003e4.2 并发 / 压力\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/AUSanity/Camera/CameraConcurrency.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eFourCameraConcurrency\u003c/code\u003e 或 HDK/HAL 四摄相关\u003c/td\u003e\n          \u003ctd\u003e四摄并发，通常覆盖 IMX766、IMX688、OV64B40、S5K3M5\u003c/td\u003e\n          \u003ctd\u003e四路 camera 输出均存在，且文件大小 \u0026gt; 0；所有子进程 Pass\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/Stability/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCamera stability 用例\u003c/td\u003e\n          \u003ctd\u003e长时间摄像头压力 / 多轮稳定性\u003c/td\u003e\n          \u003ctd\u003e测试周期内无 crash、无 coredump、输出正常、fps/日志检查正常\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8550_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8550_LE_1_0/Stability/NonGPU/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eNonGPU Camera 压力\u003c/td\u003e\n          \u003ctd\u003e非 GPU 场景下摄像头稳定性\u003c/td\u003e\n          \u003ctd\u003e同稳定性逻辑：pipeline 正常、输出正常、无 error\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"5-qcs8625_le_1_0-摄像头用例\"\u003e5. QCS8625_LE_1_0 摄像头用例\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraIMX766.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX766 相关\u003c/td\u003e\n          \u003ctd\u003e主摄 preview/snapshot/live snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraIMX688.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX688 相关\u003c/td\u003e\n          \u003ctd\u003e预览、录像、快照\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraOV64B40.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOV64B40 相关\u003c/td\u003e\n          \u003ctd\u003e4K preview/video\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraS5K3M5.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eS5K3M5 相关\u003c/td\u003e\n          \u003ctd\u003eRAW10/RDI dump\u003c/td\u003e\n          \u003ctd\u003eraw/yuv 输出存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraOV9282.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOV9282 相关\u003c/td\u003e\n          \u003ctd\u003etracking camera preview/raw dump\u003c/td\u003e\n          \u003ctd\u003eraw10 或 preview dump 存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraGMSL.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eGMSL 相关\u003c/td\u003e\n          \u003ctd\u003eGMSL preview/video/raw/snapshot\u003c/td\u003e\n          \u003ctd\u003eraw12/mp4/jpg/yuv 等存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/AUSanity/Camera/CameraConcurrency.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e并发 camera\u003c/td\u003e\n          \u003ctd\u003e多摄并发\u003c/td\u003e\n          \u003ctd\u003e所有并发子摄像头均 Pass\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eQCS8625_LE_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/QCS8625_LE_1_0/Stability/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eCamera stability\u003c/td\u003e\n          \u003ctd\u003e稳定性压力\u003c/td\u003e\n          \u003ctd\u003e指定时间内无错误、输出正常、无 crash\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"6-cq7790m_le_1_5-摄像头用例\"\u003e6. CQ7790M_LE_1_5 摄像头用例\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX766.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX766\u003c/td\u003e\n          \u003ctd\u003epreview/snapshot/live snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX688.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX688\u003c/td\u003e\n          \u003ctd\u003epreview/video/snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraIMX858.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eIMX858\u003c/td\u003e\n          \u003ctd\u003epreview/video/snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraS5KJN1.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eS5KJN1\u003c/td\u003e\n          \u003ctd\u003epreview/video/snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraOV9282.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eOV9282\u003c/td\u003e\n          \u003ctd\u003etracking camera\u003c/td\u003e\n          \u003ctd\u003eraw10/preview 输出存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraGMSL.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eGMSL\u003c/td\u003e\n          \u003ctd\u003eraw12/preview/video/snapshot\u003c/td\u003e\n          \u003ctd\u003e输出文件存在且大小 \u0026gt; 0\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eCQ7790M_LE_1_5\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/CQ7790M_LE_1_5/AUSanity/Camera/CameraConcurrency.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e多摄并发\u003c/td\u003e\n          \u003ctd\u003e多路 camera 同时运行\u003c/td\u003e\n          \u003ctd\u003e所有子 camera 结果均为 Pass\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"7-sw5100_law-系列摄像头用例\"\u003e7. SW5100_LAW 系列摄像头用例\u003c/h2\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e平台\u003c/th\u003e\n          \u003cth\u003e文件路径\u003c/th\u003e\n          \u003cth\u003e用例 / 用例族\u003c/th\u003e\n          \u003cth\u003e测试点\u003c/th\u003e\n          \u003cth\u003ePass 条件\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSW5100_LAW_1_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/SW5100_LAW_1_0/PreFlight/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePreFlight Camera\u003c/td\u003e\n          \u003ctd\u003eAndroid/LAW 平台摄像头预检，snapshot / live snapshot / video\u003c/td\u003e\n          \u003ctd\u003e输出文件生成、命令执行无 error、检查逻辑通过\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSW5100_LAW_1_1\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/SW5100_LAW_1_1/PreFlight/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003ePreFlight Camera\u003c/td\u003e\n          \u003ctd\u003e前后摄 ZSL/PZSL snapshot、双摄 snapshot、双摄录像\u003c/td\u003e\n          \u003ctd\u003e对应截图/录像文件存在；流程无异常\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSW5100_LAW_1_1\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/SW5100_LAW_1_1/Sanity/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSanity Camera\u003c/td\u003e\n          \u003ctd\u003e基础相机 sanity\u003c/td\u003e\n          \u003ctd\u003e命令执行成功，目标输出存在\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSW5100_LAW_1_1\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/SW5100_LAW_1_1/Stability/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eStability Camera\u003c/td\u003e\n          \u003ctd\u003e摄像头稳定性\u003c/td\u003e\n          \u003ctd\u003e指定时间内无错误、输出正常\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSW5100_LAW_2_0\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eCase/Products/SW5100_LAW_2_0/Sanity/Camera.py\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003eSanity Camera\u003c/td\u003e\n          \u003ctd\u003e基础相机功能\u003c/td\u003e\n          \u003ctd\u003e输出正常，无异常\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003e相关 Plan：\u003c/p\u003e","title":"项目摄像头测试用例总结"},{"content":"cloudfare现在可以用自带的创建worker，设置，比我自己找代码好用多了。\n现在国内好像访问不了cloudfare的地址了。。。我搞了个。设置ddns很好用。\n原文链接: 提供cloudfare的api（国内版） 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E6%8F%90%E4%BE%9Bcloudfare%E7%9A%84api%E5%9B%BD%E5%86%85%E7%89%88/","summary":"\u003cp\u003ecloudfare现在可以用自带的创建worker，设置，比我自己找代码好用多了。\u003c/p\u003e\n\u003cp\u003e现在国内好像访问不了cloudfare的地址了。。。我搞了个。设置ddns很好用。\u003cimg loading=\"lazy\" src=\"/posts/images/f5b35774d70c4fd9aacde3aff18ee98f.png\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/160907242?spm=1011.2415.3001.5331\"\u003e提供cloudfare的api（国内版）\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"提供cloudfare的api（国内版）"},{"content":"使用，占用1G左右空间\nnpm install -g opencode-a .我用的 flash free，不开科学用不了。。。。但是用了几次就提示我额度超了得开套餐。。。比较坑。\n生成的一般吧能用但是不算精美。\n原文链接: opencode使用记录 作者: lixusocool\n","permalink":"http://testai666.cn/posts/opencode%E4%BD%BF%E7%94%A8%E8%AE%B0%E5%BD%95/","summary":"\u003cp\u003e使用，占用1G左右空间\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003enpm\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003einstall\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eg\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eopencode\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ea\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e.我用的  flash  free，不开科学用不了。。。。但是用了几次就提示我额度超了得开套餐。。。比较坑。\u003c/p\u003e\n\u003cp\u003e生成的一般吧能用但是不算精美。\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/161343294?spm=1011.2415.3001.5331\"\u003eopencode使用记录\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"opencode使用记录"},{"content":"📊 可行性评估：✅ 完全可行 为什么可以用 Rust 重写？ 1. 核心功能都有 Rust 替代方案 Python 组件 Rust 替代方案 成熟度 ADB 命令执行 std::process::Command ✅ 完美 子进程管理 tokio::process ✅ 完美 JSON 解析 serde_json ✅ 完美 XML 解析 quick-xml, roxmltree ✅ 完美 HTTP 请求 reqwest ✅ 完美 日志系统 log, tracing, env_logger ✅ 完美 正则表达式 regex ✅ 完美 文件操作 std::fs, tokio::fs ✅ 完美 串口通信 serialport ✅ 完美 SSH 连接 ssh2, russh ✅ 良好 异步运行时 tokio, async-std ✅ 完美 命令行参数 clap ✅ 完美 配置管理 config, toml ✅ 完美 测试框架 内置 #[test] ✅ 完美 HTML 生成 askama, tera ✅ 完美 2. UIAutomator2 的替代方案 挑战: Python 的 uiautomator2 没有直接的 Rust 等价物\n解决方案:\n✅ 方案 1: 通过 ADB 命令调用 UIAutomator（推荐）\n// 通过 ADB 执行 UI 操作 adb.shell(\u0026#34;input tap 500 500\u0026#34;)?; adb.shell(\u0026#34;input text \u0026#39;Hello\u0026#39;\u0026#34;)?; adb.shell(\u0026#34;input swipe 100 500 100 100\u0026#34;)?; ✅ 方案 2: 使用 pyo3 调用 Python 的 uiautomator2\nuse pyo3::prelude::*; fn connect_device(serial: \u0026amp;str) -\u0026gt; PyResult\u0026lt;()\u0026gt; { Python::with_gil(|py| { let u2 = py.import(\u0026#34;uiautomator2\u0026#34;)?; let device = u2.call_method1(\u0026#34;connect\u0026#34;, (serial,))?; // 使用 device Ok(()) }) } ✅ 方案 3: 直接使用 ADB UI Automator 命令\n// 使用 uiautomator dump 获取 UI 层次 adb.shell(\u0026#34;uiautomator dump /sdcard/ui.xml\u0026#34;)?; // 解析 XML 找到元素 // 使用 input 命令点击 ✅ 方案 4: 实现自己的 UIAutomator 客户端\n通过 ADB forward 连接到 UIAutomator 服务 使用 HTTP/JSON-RPC 协议通信 🎯 Rust 重写的优势 1. 性能优势 🚀 Python: 解释执行，GIL 限制\rRust: 编译执行，零成本抽象，无 GIL\r性能提升预期:\r- 启动速度: 10-50x 更快\r- 内存占用: 2-5x 更少\r- 并发性能: 显著提升（真正的并行） 2. 类型安全 🛡️ // Rust 编译时捕获错误 fn reboot(device: \u0026amp;Device, reboot_type: RebootType) -\u0026gt; Result\u0026lt;()\u0026gt; { match reboot_type { RebootType::Adb =\u0026gt; device.adb_reboot(), RebootType::Shell =\u0026gt; device.shell_reboot(), } } // Python 运行时才发现错误 def reboot(device, reboot_type): if reboot_type == \u0026#34;ADB\u0026#34;: # 拼写错误运行时才发现 device.adb_reboot() 3. 并发优势 ⚡ // Rust: 真正的并行执行 use tokio::task; async fn run_concurrent_tests() { let (camera1, camera2, camera3, camera4) = tokio::join!( task::spawn(test_imx766()), task::spawn(test_imx688()), task::spawn(test_ov64b40()), task::spawn(test_s5k3m5()), ); } // Python: GIL 限制，只能并发不能并行 4. 错误处理 ✅ // Rust: 强制错误处理 fn execute_test() -\u0026gt; Result\u0026lt;TestResult, TestError\u0026gt; { let device = Device::connect(serial)?; // 必须处理错误 let result = device.run_test()?; Ok(result) } // Python: 容易忘记错误处理 def execute_test(): device = Device.connect(serial) # 可能抛异常 result = device.run_test() return result 5. 内存安全 🔒 无空指针异常 无数据竞争 无内存泄漏 编译时保证线程安全 6. 部署优势 📦 Python:\r- 需要 Python 运行时\r- 需要安装依赖包\r- 跨平台需要注意版本\rRust:\r- 单个可执行文件\r- 无运行时依赖\r- 静态链接，开箱即用\r- 交叉编译简单 🏗️ Rust 重写架构设计 项目结构 iot-automation-framework/\r├── Cargo.toml\r├── src/\r│ ├── main.rs # 入口\r│ ├── lib.rs # 库入口\r│ ├── cli.rs # 命令行参数\r│ ├── config/ # 配置管理\r│ │ ├── mod.rs\r│ │ ├── parser.rs # XML/JSON 解析\r│ │ └── global.rs # 全局变量\r│ ├── device/ # 设备管理\r│ │ ├── mod.rs\r│ │ ├── adb.rs # ADB 封装\r│ │ ├── ssh.rs # SSH 封装\r│ │ └── connection.rs # 连接管理\r│ ├── test/ # 测试框架\r│ │ ├── mod.rs\r│ │ ├── base.rs # 基础测试类\r│ │ ├── procedure.rs # 测试流程\r│ │ ├── runner.rs # 测试运行器\r│ │ └── result.rs # 测试结果\r│ ├── cases/ # 测试用例\r│ │ ├── mod.rs\r│ │ ├── camera/ # 相机测试\r│ │ ├── stability/ # 稳定性测试\r│ │ └── concurrency/ # 并发测试\r│ ├── utils/ # 工具类\r│ │ ├── mod.rs\r│ │ ├── logger.rs # 日志\r│ │ ├── time.rs # 时间工具\r│ │ └── file.rs # 文件工具\r│ └── report/ # 报告生成\r│ ├── mod.rs\r│ ├── html.rs # HTML 报告\r│ └── statistics.rs # 统计\r├── tests/ # 集成测试\r├── benches/ # 性能测试\r└── examples/ # 示例代码 核心代码示例 1. ADB 封装 // src/device/adb.rs use std::process::Command; use anyhow::{Context, Result}; pub struct Adb { serial: String, } impl Adb { pub fn new(serial: impl Into\u0026lt;String\u0026gt;) -\u0026gt; Self { Self { serial: serial.into(), } } pub fn run_cmd(\u0026amp;self, cmd: \u0026amp;str) -\u0026gt; Result\u0026lt;String\u0026gt; { let output = Command::new(\u0026#34;adb\u0026#34;) .args([\u0026#34;-s\u0026#34;, \u0026amp;self.serial]) .args(cmd.split_whitespace()) .output() .context(\u0026#34;Failed to execute adb command\u0026#34;)?; if !output.status.success() { anyhow::bail!(\u0026#34;ADB command failed: {}\u0026#34;, String::from_utf8_lossy(\u0026amp;output.stderr)); } Ok(String::from_utf8_lossy(\u0026amp;output.stdout).trim().to_string()) } pub fn shell(\u0026amp;self, cmd: \u0026amp;str) -\u0026gt; Result\u0026lt;String\u0026gt; { self.run_cmd(\u0026amp;format!(\u0026#34;shell {}\u0026#34;, cmd)) } pub fn push(\u0026amp;self, local: \u0026amp;str, remote: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;format!(\u0026#34;push {} {}\u0026#34;, local, remote))?; Ok(()) } pub fn pull(\u0026amp;self, remote: \u0026amp;str, local: \u0026amp;str) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026amp;format!(\u0026#34;pull {} {}\u0026#34;, remote, local))?; Ok(()) } pub fn reboot(\u0026amp;self) -\u0026gt; Result\u0026lt;()\u0026gt; { self.run_cmd(\u0026#34;reboot\u0026#34;)?; Ok(()) } } 2. 测试基类 // src/test/base.rs use anyhow::Result; use async_trait::async_trait; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TestResult { Pass, Fail, Unknown, } #[async_trait] pub trait TestCase: Send + Sync { fn name(\u0026amp;self) -\u0026gt; \u0026amp;str; async fn setup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { Ok(()) } async fn execute(\u0026amp;mut self) -\u0026gt; Result\u0026lt;TestResult\u0026gt;; async fn cleanup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { Ok(()) } async fn run(\u0026amp;mut self) -\u0026gt; TestResult { if let Err(e) = self.setup().await { log::error!(\u0026#34;Setup failed: {}\u0026#34;, e); return TestResult::Fail; } let result = match self.execute().await { Ok(r) =\u0026gt; r, Err(e) =\u0026gt; { log::error!(\u0026#34;Execute failed: {}\u0026#34;, e); TestResult::Fail } }; if let Err(e) = self.cleanup().await { log::error!(\u0026#34;Cleanup failed: {}\u0026#34;, e); } result } } 3. Reboot 测试用例 // src/cases/stability/reboot.rs use crate::device::Adb; use crate::test::{TestCase, TestResult}; use anyhow::Result; use async_trait::async_trait; use tokio::time::{sleep, Duration}; pub struct RebootTest { adb: Adb, reboot_type: RebootType, } pub enum RebootType { Adb, Shell, } impl RebootTest { pub fn new(serial: String, reboot_type: RebootType) -\u0026gt; Self { Self { adb: Adb::new(serial), reboot_type, } } } #[async_trait] impl TestCase for RebootTest { fn name(\u0026amp;self) -\u0026gt; \u0026amp;str { \u0026#34;Reboot Test\u0026#34; } async fn setup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { log::info!(\u0026#34;Setting up reboot test\u0026#34;); // 获取重启前的 systemd PID Ok(()) } async fn execute(\u0026amp;mut self) -\u0026gt; Result\u0026lt;TestResult\u0026gt; { log::info!(\u0026#34;Executing reboot test: {:?}\u0026#34;, self.reboot_type); match self.reboot_type { RebootType::Adb =\u0026gt; { self.adb.reboot()?; } RebootType::Shell =\u0026gt; { self.adb.shell(\u0026#34;reboot\u0026#34;)?; } } // 等待设备重启 sleep(Duration::from_secs(30)).await; self.adb.run_cmd(\u0026#34;wait-for-device\u0026#34;)?; sleep(Duration::from_secs(5)).await; // 检查设备是否成功重启 let uptime = self.adb.shell(\u0026#34;uptime\u0026#34;)?; log::info!(\u0026#34;Device uptime: {}\u0026#34;, uptime); if uptime.contains(\u0026#34;min\u0026#34;) { Ok(TestResult::Pass) } else { Ok(TestResult::Fail) } } async fn cleanup(\u0026amp;mut self) -\u0026gt; Result\u0026lt;()\u0026gt; { log::info!(\u0026#34;Cleaning up reboot test\u0026#34;); Ok(()) } } 4. 异步测试运行器 // src/test/runner.rs use crate::test::{TestCase, TestResult}; use anyhow::Result; use std::sync::Arc; use tokio::sync::Mutex; pub struct TestRunner { tests: Vec\u0026lt;Box\u0026lt;dyn TestCase\u0026gt;\u0026gt;, } impl TestRunner { pub fn new() -\u0026gt; Self { Self { tests: Vec::new() } } pub fn add_test(\u0026amp;mut self, test: Box\u0026lt;dyn TestCase\u0026gt;) { self.tests.push(test); } pub async fn run_sequential(\u0026amp;mut self) -\u0026gt; Vec\u0026lt;(String, TestResult)\u0026gt; { let mut results = Vec::new(); for test in \u0026amp;mut self.tests { let name = test.name().to_string(); log::info!(\u0026#34;Running test: {}\u0026#34;, name); let result = test.run().await; log::info!(\u0026#34;Test {} result: {:?}\u0026#34;, name, result); results.push((name, result)); } results } pub async fn run_concurrent(\u0026amp;mut self) -\u0026gt; Vec\u0026lt;(String, TestResult)\u0026gt; { use futures::future::join_all; let tests = std::mem::take(\u0026amp;mut self.tests); let tests: Vec\u0026lt;_\u0026gt; = tests .into_iter() .map(|test| Arc::new(Mutex::new(test))) .collect(); let futures: Vec\u0026lt;_\u0026gt; = tests .iter() .map(|test| { let test = Arc::clone(test); async move { let mut test = test.lock().await; let name = test.name().to_string(); let result = test.run().await; (name, result) } }) .collect(); join_all(futures).await } } 5. 命令行接口 // src/cli.rs use clap::Parser; #[derive(Parser, Debug)] #[command(name = \u0026#34;iot-test\u0026#34;)] #[command(about = \u0026#34;IoT Automation Testing Framework\u0026#34;, long_about = None)] pub struct Cli { /// Test configuration file (XML) #[arg(short, long)] pub config: String, /// Device serial number (ADB ID or IP address) #[arg(short, long)] pub serial_number: String, /// Meta path for firmware upgrade #[arg(short, long)] pub meta: Option\u0026lt;String\u0026gt;, /// Apps path for firmware upgrade #[arg(short, long)] pub apps: Option\u0026lt;String\u0026gt;, /// TAC port #[arg(short, long)] pub tac_port: Option\u0026lt;String\u0026gt;, /// Device ID from Axiom #[arg(short, long)] pub device_id: Option\u0026lt;String\u0026gt;, /// Enable ADB over WiFi #[arg(short = \u0026#39;w\u0026#39;, long)] pub adb_over_wifi: bool, /// Execution type (adb or ssh) #[arg(short, long, default_value = \u0026#34;adb\u0026#34;)] pub exec_type: String, /// Debug UART port (e.g., COM7) #[arg(long)] pub debug_uart: Option\u0026lt;String\u0026gt;, /// Log level #[arg(short, long, default_value = \u0026#34;info\u0026#34;)] pub log_level: String, } 6. 主程序 // src/main.rs use anyhow::Result; use clap::Parser; use iot_automation_framework::{Cli, TestRunner, RebootTest, RebootType}; #[tokio::main] async fn main() -\u0026gt; Result\u0026lt;()\u0026gt; { let cli = Cli::parse(); // 初始化日志 env_logger::Builder::from_env( env_logger::Env::default().default_filter_or(\u0026amp;cli.log_level) ).init(); log::info!(\u0026#34;IoT Automation Framework starting...\u0026#34;); log::info!(\u0026#34;Device: {}\u0026#34;, cli.serial_number); log::info!(\u0026#34;Config: {}\u0026#34;, cli.config); // 创建测试运行器 let mut runner = TestRunner::new(); // 添加测试用例 runner.add_test(Box::new( RebootTest::new(cli.serial_number.clone(), RebootType::Adb) )); // 运行测试 let results = runner.run_sequential().await; // 输出结果 for (name, result) in results { println!(\u0026#34;{}: {:?}\u0026#34;, name, result); } Ok(()) } 📦 Cargo.toml 依赖 [package] name = \u0026#34;iot-automation-framework\u0026#34; version = \u0026#34;2.0.0\u0026#34; edition = \u0026#34;2021\u0026#34; [dependencies] # 异步运行时 tokio = { version = \u0026#34;1\u0026#34;, features = [\u0026#34;full\u0026#34;] } async-trait = \u0026#34;0.1\u0026#34; futures = \u0026#34;0.3\u0026#34; # 错误处理 anyhow = \u0026#34;1.0\u0026#34; thiserror = \u0026#34;1.0\u0026#34; # 命令行 clap = { version = \u0026#34;4\u0026#34;, features = [\u0026#34;derive\u0026#34;] } # 日志 log = \u0026#34;0.4\u0026#34; env_logger = \u0026#34;0.11\u0026#34; tracing = \u0026#34;0.1\u0026#34; tracing-subscriber = \u0026#34;0.3\u0026#34; # 序列化 serde = { version = \u0026#34;1.0\u0026#34;, features = [\u0026#34;derive\u0026#34;] } serde_json = \u0026#34;1.0\u0026#34; toml = \u0026#34;0.8\u0026#34; # XML 解析 quick-xml = \u0026#34;0.31\u0026#34; roxmltree = \u0026#34;0.19\u0026#34; # HTTP 客户端 reqwest = { version = \u0026#34;0.11\u0026#34;, features = [\u0026#34;json\u0026#34;] } # 正则表达式 regex = \u0026#34;1.10\u0026#34; # 时间处理 chrono = \u0026#34;0.4\u0026#34; # 文件系统 walkdir = \u0026#34;2\u0026#34; # SSH (可选) ssh2 = { version = \u0026#34;0.9\u0026#34;, optional = true } # 串口 (可选) serialport = { version = \u0026#34;4\u0026#34;, optional = true } # Python 互操作 (可选) pyo3 = { version = \u0026#34;0.20\u0026#34;, optional = true, features = [\u0026#34;auto-initialize\u0026#34;] } # HTML 模板 askama = \u0026#34;0.12\u0026#34; [dev-dependencies] criterion = \u0026#34;0.5\u0026#34; [features] default = [] ssh = [\u0026#34;dep:ssh2\u0026#34;] serial = [\u0026#34;dep:serialport\u0026#34;] python-interop = [\u0026#34;dep:pyo3\u0026#34;] [[bench]] name = \u0026#34;benchmarks\u0026#34; harness = false 🚀 迁移策略 阶段 1: 核心功能 (2-3 周) ✅ ADB 封装 ✅ 配置解析 (XML/JSON) ✅ 测试基类和运行器 ✅ 日志系统 ✅ 命令行接口 阶段 2: 基础测试用例 (2-3 周) ✅ Reboot 测试 ✅ Camera 基础测试 ✅ Audio 测试 ✅ Connectivity 测试 阶段 3: 高级功能 (3-4 周) ✅ 并发测试 ✅ SSH 支持 ✅ 报告生成 ✅ 日志收集 阶段 4: UI 自动化 (2-3 周) ✅ UIAutomator 集成 ✅ LAW 平台支持 阶段 5: 优化和测试 (2-3 周) ✅ 性能优化 ✅ 单元测试 ✅ 集成测试 ✅ 文档完善 总计: 11-16 周 (约 3-4 个月)\n⚠️ 挑战和解决方案 挑战 1: UIAutomator2 依赖 解决方案:\n使用 pyo3 调用 Python 库 或实现纯 Rust 的 UIAutomator 客户端 或使用 ADB 命令替代 挑战 2: 现有测试用例迁移 解决方案:\n逐步迁移，保持 Python 版本并行运行 使用代码生成工具辅助转换 先迁移核心功能，再迁移边缘用例 挑战 3: 团队学习曲线 解决方案:\n提供 Rust 培训 编写详细文档和示例 代码审查和最佳实践分享 挑战 4: 生态系统差异 解决方案:\n使用成熟的 Rust crate 必要时使用 FFI 调用 C/Python 库 贡献开源社区 📈 性能对比预期 指标 Python Rust 提升 启动时间 2-5s 0.1-0.2s 10-50x 内存占用 50-100MB 10-20MB 2-5x 并发测试 受 GIL 限制 真正并行 显著 二进制大小 N/A (需运行时) 5-15MB - 跨平台部署 需要 Python 单文件 简单 🎯 建议 推荐方案: 渐进式迁移 保留 Python 版本作为参考\n先用 Rust 重写核心模块:\nADB 封装 配置解析 测试运行器 报告生成 Python 和 Rust 混合使用:\n// 使用 pyo3 调用 Python 的 UIAutomator2 use pyo3::prelude::*; pub fn ui_click(serial: \u0026amp;str, x: i32, y: i32) -\u0026gt; PyResult\u0026lt;()\u0026gt; { Python::with_gil(|py| { let u2 = py.import(\u0026#34;uiautomator2\u0026#34;)?; let d = u2.call_method1(\u0026#34;connect\u0026#34;, (serial,))?; d.call_method1(\u0026#34;click\u0026#34;, (x, y))?; Ok(()) }) } 逐步替换 Python 组件\n最终完全迁移到 Rust\n✅ 结论 完全可以用 Rust 重写！\n优势 ✅ 性能大幅提升 ✅ 类型安全 ✅ 并发性能优秀 ✅ 部署简单 ✅ 内存安全 建议 🎯 渐进式迁移，不要一次性重写 🎯 先重写核心模块，保留 Python 版本作为参考 🎯 使用 pyo3 桥接 Python 库（如 UIAutomator2） 🎯 投入 3-4 个月完成完整迁移 🎯 团队需要学习 Rust，但长期收益巨大 值得投资！ 🚀\n","permalink":"http://testai666.cn/posts/rust-lot/","summary":"\u003ch2 id=\"-可行性评估-完全可行\"\u003e📊 可行性评估：✅ 完全可行\u003c/h2\u003e\n\u003ch3 id=\"为什么可以用-rust-重写\"\u003e为什么可以用 Rust 重写？\u003c/h3\u003e\n\u003ch4 id=\"1-核心功能都有-rust-替代方案\"\u003e1. \u003cstrong\u003e核心功能都有 Rust 替代方案\u003c/strong\u003e\u003c/h4\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003ePython 组件\u003c/th\u003e\n          \u003cth\u003eRust 替代方案\u003c/th\u003e\n          \u003cth\u003e成熟度\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eADB 命令执行\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estd::process::Command\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e子进程管理\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003etokio::process\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eJSON 解析\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eserde_json\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eXML 解析\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003equick-xml\u003c/code\u003e, \u003ccode\u003eroxmltree\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHTTP 请求\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003ereqwest\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e日志系统\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003elog\u003c/code\u003e, \u003ccode\u003etracing\u003c/code\u003e, \u003ccode\u003eenv_logger\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e正则表达式\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eregex\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e文件操作\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003estd::fs\u003c/code\u003e, \u003ccode\u003etokio::fs\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e串口通信\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eserialport\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eSSH 连接\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003essh2\u003c/code\u003e, \u003ccode\u003erussh\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 良好\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e异步运行时\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003etokio\u003c/code\u003e, \u003ccode\u003easync-std\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e命令行参数\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003eclap\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e配置管理\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003econfig\u003c/code\u003e, \u003ccode\u003etoml\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e测试框架\u003c/td\u003e\n          \u003ctd\u003e内置 \u003ccode\u003e#[test]\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eHTML 生成\u003c/td\u003e\n          \u003ctd\u003e\u003ccode\u003easkama\u003c/code\u003e, \u003ccode\u003etera\u003c/code\u003e\u003c/td\u003e\n          \u003ctd\u003e✅ 完美\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch4 id=\"2-uiautomator2-的替代方案\"\u003e2. \u003cstrong\u003eUIAutomator2 的替代方案\u003c/strong\u003e\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e挑战\u003c/strong\u003e: Python 的 \u003ccode\u003euiautomator2\u003c/code\u003e 没有直接的 Rust 等价物\u003c/p\u003e","title":"用 Rust 重写 IoTAutomationFramework 的可行性分析"},{"content":"这个，有没有大佬能用的\n原文链接: freebuff国内用不了 作者: lixusocool\n","permalink":"http://testai666.cn/posts/freebuff%E5%9B%BD%E5%86%85%E7%94%A8%E4%B8%8D%E4%BA%86/","summary":"\u003cp\u003e这个，有没有大佬能用的\u003cimg loading=\"lazy\" src=\"/posts/images/3faf900daf0e4df5a9f327143e30645c.png\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/160940927?spm=1011.2415.3001.5331\"\u003efreebuff国内用不了\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"freebuff国内用不了"},{"content":"📱 核心性能与屏幕参数 机型 芯片 屏幕尺寸与材质 屏幕刷新率 峰值亮度 (HDR/户外) iPhone 11 A13 仿生 6.1英寸 LCD 60Hz 625尼特 iPhone 11 Pro A13 仿生 5.8英寸 OLED 60Hz 800尼特 iPhone 11 Pro Max A13 仿生 6.5英寸 OLED 60Hz 800尼特 iPhone 12 A14 仿生 6.1英寸 OLED 60Hz 1200尼特 iPhone 12 mini A14 仿生 5.4英寸 OLED 60Hz 1200尼特 iPhone 12 Pro A14 仿生 6.1英寸 OLED 60Hz 1200尼特 iPhone 12 Pro Max A14 仿生 6.7英寸 OLED 60Hz 1200尼特 iPhone 13 A15 仿生 6.1英寸 OLED 60Hz 1200尼特 iPhone 13 mini A15 仿生 5.4英寸 OLED 60Hz 1200尼特 iPhone 13 Pro A15 仿生 6.1英寸 OLED 120Hz (ProMotion) 1200尼特 iPhone 13 Pro Max A15 仿生 6.7英寸 OLED 120Hz (ProMotion) 1200尼特 iPhone 14 A15 仿生 6.1英寸 OLED 60Hz 1200尼特 iPhone 14 Plus A15 仿生 6.7英寸 OLED 60Hz 1200尼特 iPhone 14 Pro A16 仿生 6.1英寸 OLED 120Hz (ProMotion) 1600尼特 iPhone 14 Pro Max A16 仿生 6.7英寸 OLED 120Hz (ProMotion) 1600尼特 iPhone 15 A16 仿生 6.1英寸 OLED 60Hz 1600尼特 iPhone 15 Plus A16 仿生 6.7英寸 OLED 60Hz 1600尼特 iPhone 15 Pro A17 Pro 6.1英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 15 Pro Max A17 Pro 6.7英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 16 A17 Pro 6.1英寸 OLED 60Hz 2000尼特 iPhone 16 Plus A17 Pro 6.7英寸 OLED 60Hz 2000尼特 iPhone 16 Pro A18 Pro 6.3英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 16 Pro Max A18 Pro 6.9英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 17 A18 Pro 6.1英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 17 Pro A18 Pro 6.3英寸 OLED 120Hz (ProMotion) 2000尼特 iPhone 17 Pro Max A18 Pro 6.9英寸 OLED 120Hz (ProMotion) 2000尼特 📸 系统参数 机型 后置主摄 (广角) 超广角镜头 长焦镜头 (光变) 前置摄像头 iPhone 11 1200万像素 1200万像素 无 1200万像素 iPhone 11 Pro 1200万像素 1200万像素 1200万 (2倍) 1200万像素 iPhone 11 Pro Max 1200万像素 1200万像素 1200万 (2倍) 1200万像素 iPhone 12 1200万像素 1200万像素 无 1200万像素 iPhone 12 mini 1200万像素 1200万像素 无 1200万像素 iPhone 12 Pro 1200万像素 1200万像素 1200万 (2倍) 1200万像素 iPhone 12 Pro Max 1200万像素 1200万像素 1200万 (2.5倍) 1200万像素 iPhone 13 1200万像素 1200万像素 无 1200万像素 iPhone 13 mini 1200万像素 1200万像素 无 1200万像素 iPhone 13 Pro 1200万像素 1200万像素 1200万 (3倍) 1200万像素 iPhone 13 Pro Max 1200万像素 1200万像素 1200万 (3倍) 1200万像素 iPhone 14 1200万像素 1200万像素 无 1200万像素 iPhone 14 Plus 1200万像素 1200万像素 无 1200万像素 iPhone 14 Pro 4800万像素 1200万像素 1200万 (3倍) 1200万像素 iPhone 14 Pro Max 4800万像素 1200万像素 1200万 (3倍) 1200万像素 iPhone 15 4800万像素 1200万像素 无 (支持2倍无损裁切) 1200万像素 iPhone 15 Plus 4800万像素 1200万像素 无 (支持2倍无损裁切) 1200万像素 iPhone 15 Pro 4800万像素 1200万像素 1200万 (3倍) 1200万像素 iPhone 15 Pro Max 4800万像素 1200万像素 4800万 (5倍) 1200万像素 iPhone 16 4800万像素 1200万像素 无 (支持2倍无损裁切) 1200万像素 iPhone 16 Plus 4800万像素 1200万像素 无 (支持2倍无损裁切) 1200万像素 iPhone 16 Pro 4800万像素 1200万像素 4800万 (5倍) 1200万像素 iPhone 16 Pro Max 4800万像素 1200万像素 4800万 (5倍) 1200万像素 iPhone 17 4800万像素 1200万像素 4800万 (5倍) 1200万像素 iPhone 17 Pro 4800万像素 1200万像素 4800万 (5倍) 1200万像素 iPhone 17 Pro Max 4800万像素 1200万像素 4800万 (5倍) 1200万像素 🔋 续航与充电参数 机型 电池容量 (典型值) 视频播放时长 (小时) 有线充电功率 无线充电 iPhone 11 3110mAh 17小时 18W 支持 iPhone 11 Pro 3046mAh 18小时 18W 支持 iPhone 11 Pro Max 3418mAh 20小时 18W 支持 iPhone 12 2815mAh 17小时 20W 支持 iPhone 12 mini 2227mAh 15小时 20W 支持 iPhone 12 Pro 2815mAh 17小时 20W 支持 iPhone 12 Pro Max 3687mAh 20小时 20W 支持 iPhone 13 3227mAh 19小时 27W 支持 iPhone 13 mini 2406mAh 17小时 27W 支持 iPhone 13 Pro 3095mAh 22小时 27W 支持 iPhone 13 Pro Max 4352mAh 28小时 27W 支持 iPhone 14 3279mAh 20小时 27W 支持 iPhone 14 Plus 4325mAh 26小时 27W 支持 iPhone 14 Pro 3200mAh 23小时 27W 支持 iPhone 14 Pro Max 4323mAh 29小时 27W 支持 iPhone 15 3349mAh 26小时 35W 支持 iPhone 15 Plus 4383mAh 26小时 35W 支持 iPhone 15 Pro 3274mAh 23小时 35W 支持 iPhone 15 Pro Max 4422mAh 29小时 35W 支持 iPhone 16 3558mAh 22小时 35W 支持 iPhone 16 Plus 4674mAh 24小时 35W 支持 iPhone 16 Pro 3582mAh 25小时 35W 支持 iPhone 16 Pro Max 4685mAh 28小时 35W 支持 iPhone 17 3700mAh 24小时 45W 支持 iPhone 17 Pro 3850mAh 26小时 45W 支持 iPhone 17 Pro Max 4800mAh 30小时 45W 支持 📏 外观与接口参数 机型 机身尺寸 (高×宽×厚 mm) 机身重量 (g) 接口类型 特殊设计 iPhone 11 150.9 × 75.7 × 8.3 194 Lightning 铝合金边框 iPhone 11 Pro 144.0 × 71.4 × 8.1 188 Lightning 不锈钢边框 iPhone 11 Pro Max 158.0 × 77.8 × 8.1 226 Lightning 不锈钢边框 iPhone 12 146.7 × 71.5 × 7.4 162 Lightning 直角边框 iPhone 12 mini 131.5 × 64.2 × 7.4 133 Lightning 直角边框 iPhone 12 Pro 146.7 × 71.5 × 7.4 187 Lightning 直角边框 iPhone 12 Pro Max 160.8 × 78.1 × 7.4 226 Lightning 直角边框 iPhone 13 146.7 × 71.5 × 7.65 173 Lightning 刘海缩小 iPhone 13 mini 131.5 × 64.2 × 7.65 140 Lightning 刘海缩小 iPhone 13 Pro 146.7 × 71.5 × 7.65 203 Lightning 刘海缩小 iPhone 13 Pro Max 160.8 × 78.1 × 7.65 238 Lightning 刘海缩小 iPhone 14 146.7 × 71.5 × 7.8 172 Lightning 卫星紧急联络 iPhone 14 Plus 160.8 × 78.1 × 7.8 203 Lightning 卫星紧急联络 iPhone 14 Pro 147.5 × 71.5 × 7.85 206 Lightning 灵动岛设计 iPhone 14 Pro Max 160.7 × 77.6 × 7.85 240 Lightning 灵动岛设计 iPhone 15 147.6 × 71.6 × 7.8 171 USB-C 磨砂玻璃背板 iPhone 15 Plus 160.9 × 77.8 × 7.8 201 USB-C 磨砂玻璃背板 iPhone 15 Pro 146.6 × 70.6 × 8.25 187 USB-C 钛金属边框 iPhone 15 Pro Max 159.9 × 76.7 × 8.25 221 USB-C 钛金属边框 iPhone 16 147.6 × 71.6 × 7.8 170 USB-C 相机控制按键 iPhone 16 Plus 160.9 × 77.8 × 7.8 199 USB-C 相机控制按键 iPhone 16 Pro 149.6 × 71.5 × 8.25 199 USB-C 钛金属，相机控制按键 iPhone 16 Pro Max 163.0 × 77.6 × 8.25 227 USB-C 钛金属，相机控制按键 iPhone 17 149.0 × 71.0 × 8.1 195 USB-C 超薄灵动岛 iPhone 17 Pro 151.0 × 72.0 × 8.1 215 USB-C 超薄灵动岛 iPhone 17 Pro Max 164.0 × 78.0 × 8.1 235 USB-C 超薄灵动岛 🧠 与内存参数 机型 起步存储 最高存储 运行内存 (RAM) iPhone 11 64GB 256GB 4GB iPhone 11 Pro 64GB 512GB 4GB iPhone 11 Pro Max 64GB 512GB 4GB iPhone 12 64GB 256GB 4GB iPhone 12 mini 64GB 256GB 4GB iPhone 12 Pro 128GB 512GB 6GB iPhone 12 Pro Max 128GB 512GB 6GB iPhone 13 128GB 512GB 4GB iPhone 13 mini 128GB 512GB 4GB iPhone 13 Pro 128GB 1TB 6GB iPhone 13 Pro Max 128GB 1TB 6GB iPhone 14 128GB 512GB 6GB iPhone 14 Plus 128GB 512GB 6GB iPhone 14 Pro 128GB 1TB 6GB iPhone 14 Pro Max 128GB 1TB 6GB iPhone 15 128GB 512GB 6GB iPhone 15 Plus 128GB 512GB 6GB iPhone 15 Pro 128GB 1TB 8GB iPhone 15 Pro Max 256GB 1TB 8GB iPhone 16 128GB 512GB 8GB iPhone 16 Plus 128GB 512GB 8GB iPhone 16 Pro 128GB 1TB 8GB iPhone 16 Pro Max 256GB 1TB 8GB iPhone 17 128GB 1TB 8GB iPhone 17 Pro 256GB 1TB 8GB iPhone 17 Pro Max 256GB 1TB 8GB 💡 核心升级节点总结 🖥️ 屏幕与设计的全面进化 iPhone 12：全系升级 OLED 屏幕，回归直角边框设计 iPhone 14 Pro：首次引入\u0026quot;灵动岛\u0026quot;交互设计 iPhone 13 Pro：Pro 系列首次普及 120Hz ProMotion 自适应刷新率 iPhone 17：超薄灵动岛设计，进一步缩小前置摄像头区域 原文链接: iPhone 11至iPhone 17系列全型号详细参数对比 作者: lixusocool\n","permalink":"http://testai666.cn/posts/iphone_11%E8%87%B3iphone_17%E7%B3%BB%E5%88%97%E5%85%A8%E5%9E%8B%E5%8F%B7%E8%AF%A6%E7%BB%86%E5%8F%82%E6%95%B0%E5%AF%B9%E6%AF%94/","summary":"\u003ch3 id=\"-核心性能与屏幕参数\"\u003e📱 核心性能与屏幕参数\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e机型\u003c/th\u003e\n          \u003cth\u003e芯片\u003c/th\u003e\n          \u003cth\u003e屏幕尺寸与材质\u003c/th\u003e\n          \u003cth\u003e屏幕刷新率\u003c/th\u003e\n          \u003cth\u003e峰值亮度 (HDR/户外)\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11\u003c/td\u003e\n          \u003ctd\u003eA13 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 LCD\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e625尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro\u003c/td\u003e\n          \u003ctd\u003eA13 仿生\u003c/td\u003e\n          \u003ctd\u003e5.8英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e800尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA13 仿生\u003c/td\u003e\n          \u003ctd\u003e6.5英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e800尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12\u003c/td\u003e\n          \u003ctd\u003eA14 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 mini\u003c/td\u003e\n          \u003ctd\u003eA14 仿生\u003c/td\u003e\n          \u003ctd\u003e5.4英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro\u003c/td\u003e\n          \u003ctd\u003eA14 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA14 仿生\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 mini\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e5.4英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Plus\u003c/td\u003e\n          \u003ctd\u003eA15 仿生\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1200尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro\u003c/td\u003e\n          \u003ctd\u003eA16 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e1600尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA16 仿生\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e1600尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15\u003c/td\u003e\n          \u003ctd\u003eA16 仿生\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1600尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Plus\u003c/td\u003e\n          \u003ctd\u003eA16 仿生\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e1600尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003eA17 Pro\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA17 Pro\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16\u003c/td\u003e\n          \u003ctd\u003eA17 Pro\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Plus\u003c/td\u003e\n          \u003ctd\u003eA17 Pro\u003c/td\u003e\n          \u003ctd\u003e6.7英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e60Hz\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro\u003c/td\u003e\n          \u003ctd\u003eA18 Pro\u003c/td\u003e\n          \u003ctd\u003e6.3英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA18 Pro\u003c/td\u003e\n          \u003ctd\u003e6.9英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17\u003c/td\u003e\n          \u003ctd\u003eA18 Pro\u003c/td\u003e\n          \u003ctd\u003e6.1英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro\u003c/td\u003e\n          \u003ctd\u003eA18 Pro\u003c/td\u003e\n          \u003ctd\u003e6.3英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro Max\u003c/td\u003e\n          \u003ctd\u003eA18 Pro\u003c/td\u003e\n          \u003ctd\u003e6.9英寸 OLED\u003c/td\u003e\n          \u003ctd\u003e120Hz (ProMotion)\u003c/td\u003e\n          \u003ctd\u003e2000尼特\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"-系统参数\"\u003e📸 系统参数\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e机型\u003c/th\u003e\n          \u003cth\u003e后置主摄 (广角)\u003c/th\u003e\n          \u003cth\u003e超广角镜头\u003c/th\u003e\n          \u003cth\u003e长焦镜头 (光变)\u003c/th\u003e\n          \u003cth\u003e前置摄像头\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (2倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro Max\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (2倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 mini\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (2倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro Max\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (2.5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 mini\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (3倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro Max\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (3倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Plus\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (3倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (3倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无 (支持2倍无损裁切)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Plus\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无 (支持2倍无损裁切)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e1200万 (3倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无 (支持2倍无损裁切)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Plus\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e无 (支持2倍无损裁切)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4800万像素\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n          \u003ctd\u003e4800万 (5倍)\u003c/td\u003e\n          \u003ctd\u003e1200万像素\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"-续航与充电参数\"\u003e🔋 续航与充电参数\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e机型\u003c/th\u003e\n          \u003cth\u003e电池容量 (典型值)\u003c/th\u003e\n          \u003cth\u003e视频播放时长 (小时)\u003c/th\u003e\n          \u003cth\u003e有线充电功率\u003c/th\u003e\n          \u003cth\u003e无线充电\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11\u003c/td\u003e\n          \u003ctd\u003e3110mAh\u003c/td\u003e\n          \u003ctd\u003e17小时\u003c/td\u003e\n          \u003ctd\u003e18W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro\u003c/td\u003e\n          \u003ctd\u003e3046mAh\u003c/td\u003e\n          \u003ctd\u003e18小时\u003c/td\u003e\n          \u003ctd\u003e18W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro Max\u003c/td\u003e\n          \u003ctd\u003e3418mAh\u003c/td\u003e\n          \u003ctd\u003e20小时\u003c/td\u003e\n          \u003ctd\u003e18W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12\u003c/td\u003e\n          \u003ctd\u003e2815mAh\u003c/td\u003e\n          \u003ctd\u003e17小时\u003c/td\u003e\n          \u003ctd\u003e20W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 mini\u003c/td\u003e\n          \u003ctd\u003e2227mAh\u003c/td\u003e\n          \u003ctd\u003e15小时\u003c/td\u003e\n          \u003ctd\u003e20W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro\u003c/td\u003e\n          \u003ctd\u003e2815mAh\u003c/td\u003e\n          \u003ctd\u003e17小时\u003c/td\u003e\n          \u003ctd\u003e20W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro Max\u003c/td\u003e\n          \u003ctd\u003e3687mAh\u003c/td\u003e\n          \u003ctd\u003e20小时\u003c/td\u003e\n          \u003ctd\u003e20W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13\u003c/td\u003e\n          \u003ctd\u003e3227mAh\u003c/td\u003e\n          \u003ctd\u003e19小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 mini\u003c/td\u003e\n          \u003ctd\u003e2406mAh\u003c/td\u003e\n          \u003ctd\u003e17小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro\u003c/td\u003e\n          \u003ctd\u003e3095mAh\u003c/td\u003e\n          \u003ctd\u003e22小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4352mAh\u003c/td\u003e\n          \u003ctd\u003e28小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14\u003c/td\u003e\n          \u003ctd\u003e3279mAh\u003c/td\u003e\n          \u003ctd\u003e20小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Plus\u003c/td\u003e\n          \u003ctd\u003e4325mAh\u003c/td\u003e\n          \u003ctd\u003e26小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro\u003c/td\u003e\n          \u003ctd\u003e3200mAh\u003c/td\u003e\n          \u003ctd\u003e23小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4323mAh\u003c/td\u003e\n          \u003ctd\u003e29小时\u003c/td\u003e\n          \u003ctd\u003e27W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15\u003c/td\u003e\n          \u003ctd\u003e3349mAh\u003c/td\u003e\n          \u003ctd\u003e26小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Plus\u003c/td\u003e\n          \u003ctd\u003e4383mAh\u003c/td\u003e\n          \u003ctd\u003e26小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003e3274mAh\u003c/td\u003e\n          \u003ctd\u003e23小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4422mAh\u003c/td\u003e\n          \u003ctd\u003e29小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16\u003c/td\u003e\n          \u003ctd\u003e3558mAh\u003c/td\u003e\n          \u003ctd\u003e22小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Plus\u003c/td\u003e\n          \u003ctd\u003e4674mAh\u003c/td\u003e\n          \u003ctd\u003e24小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro\u003c/td\u003e\n          \u003ctd\u003e3582mAh\u003c/td\u003e\n          \u003ctd\u003e25小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4685mAh\u003c/td\u003e\n          \u003ctd\u003e28小时\u003c/td\u003e\n          \u003ctd\u003e35W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17\u003c/td\u003e\n          \u003ctd\u003e3700mAh\u003c/td\u003e\n          \u003ctd\u003e24小时\u003c/td\u003e\n          \u003ctd\u003e45W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro\u003c/td\u003e\n          \u003ctd\u003e3850mAh\u003c/td\u003e\n          \u003ctd\u003e26小时\u003c/td\u003e\n          \u003ctd\u003e45W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro Max\u003c/td\u003e\n          \u003ctd\u003e4800mAh\u003c/td\u003e\n          \u003ctd\u003e30小时\u003c/td\u003e\n          \u003ctd\u003e45W\u003c/td\u003e\n          \u003ctd\u003e支持\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"-外观与接口参数\"\u003e📏 外观与接口参数\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e机型\u003c/th\u003e\n          \u003cth\u003e机身尺寸 (高×宽×厚 mm)\u003c/th\u003e\n          \u003cth\u003e机身重量 (g)\u003c/th\u003e\n          \u003cth\u003e接口类型\u003c/th\u003e\n          \u003cth\u003e特殊设计\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11\u003c/td\u003e\n          \u003ctd\u003e150.9 × 75.7 × 8.3\u003c/td\u003e\n          \u003ctd\u003e194\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e铝合金边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro\u003c/td\u003e\n          \u003ctd\u003e144.0 × 71.4 × 8.1\u003c/td\u003e\n          \u003ctd\u003e188\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e不锈钢边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro Max\u003c/td\u003e\n          \u003ctd\u003e158.0 × 77.8 × 8.1\u003c/td\u003e\n          \u003ctd\u003e226\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e不锈钢边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12\u003c/td\u003e\n          \u003ctd\u003e146.7 × 71.5 × 7.4\u003c/td\u003e\n          \u003ctd\u003e162\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e直角边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 mini\u003c/td\u003e\n          \u003ctd\u003e131.5 × 64.2 × 7.4\u003c/td\u003e\n          \u003ctd\u003e133\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e直角边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro\u003c/td\u003e\n          \u003ctd\u003e146.7 × 71.5 × 7.4\u003c/td\u003e\n          \u003ctd\u003e187\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e直角边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro Max\u003c/td\u003e\n          \u003ctd\u003e160.8 × 78.1 × 7.4\u003c/td\u003e\n          \u003ctd\u003e226\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e直角边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13\u003c/td\u003e\n          \u003ctd\u003e146.7 × 71.5 × 7.65\u003c/td\u003e\n          \u003ctd\u003e173\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e刘海缩小\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 mini\u003c/td\u003e\n          \u003ctd\u003e131.5 × 64.2 × 7.65\u003c/td\u003e\n          \u003ctd\u003e140\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e刘海缩小\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro\u003c/td\u003e\n          \u003ctd\u003e146.7 × 71.5 × 7.65\u003c/td\u003e\n          \u003ctd\u003e203\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e刘海缩小\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro Max\u003c/td\u003e\n          \u003ctd\u003e160.8 × 78.1 × 7.65\u003c/td\u003e\n          \u003ctd\u003e238\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e刘海缩小\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14\u003c/td\u003e\n          \u003ctd\u003e146.7 × 71.5 × 7.8\u003c/td\u003e\n          \u003ctd\u003e172\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e卫星紧急联络\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Plus\u003c/td\u003e\n          \u003ctd\u003e160.8 × 78.1 × 7.8\u003c/td\u003e\n          \u003ctd\u003e203\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e卫星紧急联络\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro\u003c/td\u003e\n          \u003ctd\u003e147.5 × 71.5 × 7.85\u003c/td\u003e\n          \u003ctd\u003e206\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e灵动岛设计\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro Max\u003c/td\u003e\n          \u003ctd\u003e160.7 × 77.6 × 7.85\u003c/td\u003e\n          \u003ctd\u003e240\u003c/td\u003e\n          \u003ctd\u003eLightning\u003c/td\u003e\n          \u003ctd\u003e灵动岛设计\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15\u003c/td\u003e\n          \u003ctd\u003e147.6 × 71.6 × 7.8\u003c/td\u003e\n          \u003ctd\u003e171\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e磨砂玻璃背板\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Plus\u003c/td\u003e\n          \u003ctd\u003e160.9 × 77.8 × 7.8\u003c/td\u003e\n          \u003ctd\u003e201\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e磨砂玻璃背板\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003e146.6 × 70.6 × 8.25\u003c/td\u003e\n          \u003ctd\u003e187\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e钛金属边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro Max\u003c/td\u003e\n          \u003ctd\u003e159.9 × 76.7 × 8.25\u003c/td\u003e\n          \u003ctd\u003e221\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e钛金属边框\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16\u003c/td\u003e\n          \u003ctd\u003e147.6 × 71.6 × 7.8\u003c/td\u003e\n          \u003ctd\u003e170\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e相机控制按键\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Plus\u003c/td\u003e\n          \u003ctd\u003e160.9 × 77.8 × 7.8\u003c/td\u003e\n          \u003ctd\u003e199\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e相机控制按键\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro\u003c/td\u003e\n          \u003ctd\u003e149.6 × 71.5 × 8.25\u003c/td\u003e\n          \u003ctd\u003e199\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e钛金属，相机控制按键\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro Max\u003c/td\u003e\n          \u003ctd\u003e163.0 × 77.6 × 8.25\u003c/td\u003e\n          \u003ctd\u003e227\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e钛金属，相机控制按键\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17\u003c/td\u003e\n          \u003ctd\u003e149.0 × 71.0 × 8.1\u003c/td\u003e\n          \u003ctd\u003e195\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e超薄灵动岛\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro\u003c/td\u003e\n          \u003ctd\u003e151.0 × 72.0 × 8.1\u003c/td\u003e\n          \u003ctd\u003e215\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e超薄灵动岛\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro Max\u003c/td\u003e\n          \u003ctd\u003e164.0 × 78.0 × 8.1\u003c/td\u003e\n          \u003ctd\u003e235\u003c/td\u003e\n          \u003ctd\u003eUSB-C\u003c/td\u003e\n          \u003ctd\u003e超薄灵动岛\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"-与内存参数\"\u003e🧠 与内存参数\u003c/h3\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e机型\u003c/th\u003e\n          \u003cth\u003e起步存储\u003c/th\u003e\n          \u003cth\u003e最高存储\u003c/th\u003e\n          \u003cth\u003e运行内存 (RAM)\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11\u003c/td\u003e\n          \u003ctd\u003e64GB\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro\u003c/td\u003e\n          \u003ctd\u003e64GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 11 Pro Max\u003c/td\u003e\n          \u003ctd\u003e64GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12\u003c/td\u003e\n          \u003ctd\u003e64GB\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 mini\u003c/td\u003e\n          \u003ctd\u003e64GB\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 12 Pro Max\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 mini\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e4GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 13 Pro Max\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Plus\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 14 Pro Max\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Plus\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e6GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 15 Pro Max\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Plus\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e512GB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 16 Pro Max\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17\u003c/td\u003e\n          \u003ctd\u003e128GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003eiPhone 17 Pro Max\u003c/td\u003e\n          \u003ctd\u003e256GB\u003c/td\u003e\n          \u003ctd\u003e1TB\u003c/td\u003e\n          \u003ctd\u003e8GB\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"-核心升级节点总结\"\u003e💡 核心升级节点总结\u003c/h3\u003e\n\u003ch4 id=\"-屏幕与设计的全面进化\"\u003e🖥️ \u003cstrong\u003e屏幕与设计的全面进化\u003c/strong\u003e\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eiPhone 12\u003c/strong\u003e：全系升级 OLED 屏幕，回归直角边框设计\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eiPhone 14 Pro\u003c/strong\u003e：首次引入\u0026quot;灵动岛\u0026quot;交互设计\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eiPhone 13 Pro\u003c/strong\u003e：Pro 系列首次普及 120Hz ProMotion 自适应刷新率\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eiPhone 17\u003c/strong\u003e：超薄灵动岛设计，进一步缩小前置摄像头区域\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/160771669?spm=1011.2415.3001.5331\"\u003eiPhone 11至iPhone 17系列全型号详细参数对比\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e","title":"iPhone 11至iPhone 17系列全型号详细参数对比"},{"content":" 概述 这是一个基于 的 IoT 设备自动化测试框架，主要用于测试高通（Qualcomm）芯片平台的设备。\n核心 1. ADB (Android Debug Bridge) ⭐⭐⭐⭐⭐ 主要工具，使用最广泛\n位置:Libs/Utilities/adb.py 用途: 设备连接和通信 执行 shell 命令 文件传输（push/pull） 设备重启 日志收集 使用示例: from Libs.Utilities.adb import adb # 执行命令 adb.run_cmd(\u0026#34;adb shell ls /data\u0026#34;, serial_number) # 推送文件 adb.push(local_path, remote_path, serial_number) # 拉取文件 adb.pull(remote_path, local_path, serial_number) # 重启设备 adb.run_cmd(\u0026#34;adb reboot\u0026#34;, serial_number) 特点: 支持多设备（通过 serial_number 区分） 支持 ADB over WiFi 自动日志记录（发送和接收的命令） 2. UIAutomator2 ⭐⭐⭐⭐ UI 自动化工具\n库:uiautomator2(简称 u2) 用途: Android UI 元素定位和操作 点击、滑动、输入等交互 截图和屏幕录制 应用启动和管理 使用示例: import uiautomator2 as u2 # 连接设备 d = u2.connect(serial_number) # 点击按钮 d(resourceId=\u0026#34;com.example:id/button\u0026#34;).click() # 输入文本 d(resourceId=\u0026#34;com.example:id/edit\u0026#34;).set_text(\u0026#34;Hello\u0026#34;) # 检查元素是否存在 if d(text=\u0026#34;Settings\u0026#34;).exists: d(text=\u0026#34;Settings\u0026#34;).click() # 滑动 d.swipe(100, 500, 100, 100) 应用场景: LAW (Linux Android Wear) 平台测试 UI 交互测试 设置配置 弹窗处理 3. SSH ⭐⭐⭐ 远程连接工具\n位置:Libs/Utilities/SSH/SSH.py 用途: Linux 设备远程连接 执行 shell 命令 文件传输 使用场景: LE (Linux Embedded) 平台 LU (Linux Ubuntu) 平台 需要 SSH 访问的设备 4. Alpaca TAC ⭐⭐ 硬件控制工具\n库:alpaca.tac 用途: 硬件电源控制 设备重启（硬件级别） USB 切换 使用示例: import alpaca.tac # 探测设备 ports = alpaca.tac.ProbeForDevices() # 电源控制 # 用于硬件级别的设备控制 5. GStreamer ⭐⭐⭐⭐ 多媒体处理工具\n用途: 相机预览和录制 视频编码/解码 音频处理 多媒体管道测试 使用示例（通过 ADB 执行）: # 相机预览 gst-launch-1.0 qtiqmmfsrc camera=0 ! video/x-raw,format=NV12,width=1920,height=1080,framerate=30/1 ! waylandsink # 视频录制 gst-launch-1.0 qtiqmmfsrc camera=0 ! video/x-raw(memory:GBM),format=NV12 ! qtic2venc ! h264parse ! mp4mux ! filesink location=/data/video.mp4 配置文件:Plan/*/Config/Camera/TestCommands/*.json 辅助工具和库 6. 其他 Python 库 日志和监控 logging- Python 标准日志库 threading- 多线程支持 subprocess- 进程管理 数据处理 json- JSON 配置文件解析 xml.etree.ElementTree- XML 配置解析 re- 正则表达式 网络和通信 requests- HTTP 请求（结果上传） socket- 网络通信 serial- 串口通信（调试 UART） 可视化 matplotlib- 数据可视化 Chart.js- HTML 报告图表（前端） 硬件接口 TACDev- TAC 设备驱动 win32api/win32con- Windows API（文件属性等） 测试框架架构 IoTAutomationFramework_2.0/\r├── TestEntrance.py # 测试入口\r├── Libs/\r│ ├── Utilities/\r│ │ ├── adb.py # ADB 封装\r│ │ ├── SSH/ # SSH 工具\r│ │ └── Utility.py # 通用工具\r│ ├── Tests/\r│ │ ├── BaseTestCase.py # 测试基类\r│ │ └── TestProcedure.py # 测试流程\r│ └── LogCatcher/ # 日志收集\r├── Case/\r│ └── Products/ # 产品测试用例\r│ ├── QCS8550_LE_1_0/\r│ ├── QRB5165_UBUN_2_0/\r│ └── ...\r└── Plan/ # 测试计划和配置\r├── LE/ # Linux Embedded\r├── LU/ # Linux Ubuntu\r└── LAW/ # Linux Android Wear 支持的平台 1. LE (Linux Embedded) 工具: ADB + SSH + GStreamer 芯片: QCS8550, QCS615, QCM6490 等 特点: 嵌入式 Linux 系统 2. LU (Linux Ubuntu) 工具: ADB + SSH + GStreamer 芯片: QRB5165 等 特点: Ubuntu 系统 3. LAW (Linux Android Wear) 工具: ADB + UIAutomator2 芯片: SW5100, SW6100, SDM429W 等 特点: Android Wear 系统，需要 UI 自动化 4. LA (Linux Android) 工具: ADB + UIAutomator2 芯片: QCS8245, QCS610 等 特点: Android 系统 测试类型 1. Stability 测试 Reboot 测试 Suspend/Resume 测试 长时间运行测试 2. 功能测试 Camera 测试（预览、录制、快照） Audio 测试（播放、录制） Video 测试（编解码） Connectivity 测试（WiFi、BT） Sensor 测试（IMU、GPS） 3. 性能测试 CPU/Memory 压力测试 并发测试 AI/ML 模型测试 4. 并发测试 多摄像头并发 WiFi + Audio + Camera + ML 并发 关键特性 1. 多设备支持 通过 serial_number 区分设备 支持 ADB over WiFi 支持 USB 和网络连接 2. 灵活的测试策略 Random - 随机执行测试用例 Sequence - 顺序执行 Iteration - 迭代执行指定次数 3. 完善的日志系统 自动收集 logcat 自动收集 kernel log (kmsg) 自动收集 tombstone/coredump 生成 HTML 测试报告 4. 后台测试支持 CPU/DDR 压力测试 并发测试 长时间稳定性测试 依赖安装 # 核心依赖 pip install uiautomator2 pip install requests # 可选依赖 pip install matplotlib pip install alpaca-tac # 如果使用 Alpaca 硬件 # ADB 需要单独安装 # 下载 Android Platform Tools # 添加到系统 PATH 总结 这个框架是一个混合型自动化测试框架，主要特点：\n以 ADB 为核心- 所有平台都使用 ADB 作为主要通信工具 UI 自动化- 使用 UIAutomator2 处理 Android UI 交互 多媒体测试- 使用 GStreamer 进行相机和视频测试 硬件控制- 使用 Alpaca TAC 进行硬件级别控制 跨平台支持- 支持 Linux Embedded、Ubuntu、Android Wear 等多个平台 适用场景: IoT 设备、嵌入式系统、 设备的自动化测试，特别是高通芯片平台。\n原文链接: IoTAutomationFramework_2.0 安卓测试自动化工具 作者: lixusocool\n","permalink":"http://testai666.cn/posts/iotautomationframework_2.0_%E5%AE%89%E5%8D%93%E6%B5%8B%E8%AF%95%E8%87%AA%E5%8A%A8%E5%8C%96%E5%B7%A5%E5%85%B7/","summary":"\u003ch2\u003e\u003c/h2\u003e\n\u003ch3 id=\"概述\"\u003e概述\u003c/h3\u003e\n\u003cp\u003e这是一个基于  的 IoT 设备自动化测试框架，主要用于测试高通（Qualcomm）芯片平台的设备。\u003c/p\u003e\n\u003ch3 id=\"核心\"\u003e核心\u003c/h3\u003e\n\u003ch4 id=\"1-adb-android-debug-bridge-\"\u003e1. \u003cstrong\u003eADB (Android Debug Bridge)\u003c/strong\u003e ⭐⭐⭐⭐⭐\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e主要工具，使用最广泛\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e位置\u003c/strong\u003e:\u003ccode\u003eLibs/Utilities/adb.py\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e用途\u003c/strong\u003e:\n\u003cul\u003e\n\u003cli\u003e设备连接和通信\u003c/li\u003e\n\u003cli\u003e执行 shell 命令\u003c/li\u003e\n\u003cli\u003e文件传输（push/pull）\u003c/li\u003e\n\u003cli\u003e设备重启\u003c/li\u003e\n\u003cli\u003e日志收集\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e使用示例\u003c/strong\u003e:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003efrom\u003c/span\u003e Libs.Utilities.adb \u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e adb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 执行命令\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erun_cmd(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;adb shell ls /data\u0026#34;\u003c/span\u003e, serial_number)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 推送文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epush(local_path, remote_path, serial_number)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 拉取文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003epull(remote_path, local_path, serial_number)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 重启设备\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eadb\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003erun_cmd(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;adb reboot\u0026#34;\u003c/span\u003e, serial_number)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e特点\u003c/strong\u003e:\n\u003cul\u003e\n\u003cli\u003e支持多设备（通过 serial_number 区分）\u003c/li\u003e\n\u003cli\u003e支持 ADB over WiFi\u003c/li\u003e\n\u003cli\u003e自动日志记录（发送和接收的命令）\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"2-uiautomator2-\"\u003e2. \u003cstrong\u003eUIAutomator2\u003c/strong\u003e ⭐⭐⭐⭐\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003eUI 自动化工具\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e库\u003c/strong\u003e:\u003ccode\u003euiautomator2\u003c/code\u003e(简称 u2)\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e用途\u003c/strong\u003e:\n\u003cul\u003e\n\u003cli\u003eAndroid UI 元素定位和操作\u003c/li\u003e\n\u003cli\u003e点击、滑动、输入等交互\u003c/li\u003e\n\u003cli\u003e截图和屏幕录制\u003c/li\u003e\n\u003cli\u003e应用启动和管理\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e使用示例\u003c/strong\u003e:\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eimport\u003c/span\u003e uiautomator2 \u003cspan style=\"color:#66d9ef\"\u003eas\u003c/span\u003e u2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 连接设备\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ed \u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e u2\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003econnect(serial_number)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 点击按钮\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ed(resourceId\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;com.example:id/button\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclick()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 输入文本\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ed(resourceId\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;com.example:id/edit\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eset_text(\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 检查元素是否存在\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eif\u003c/span\u003e d(text\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Settings\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eexists:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    d(text\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#e6db74\"\u003e\u0026#34;Settings\u0026#34;\u003c/span\u003e)\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eclick()\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 滑动\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ed\u003cspan style=\"color:#f92672\"\u003e.\u003c/span\u003eswipe(\u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e500\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e, \u003cspan style=\"color:#ae81ff\"\u003e100\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e应用场景\u003c/strong\u003e:\n\u003cul\u003e\n\u003cli\u003eLAW (Linux Android Wear) 平台测试\u003c/li\u003e\n\u003cli\u003eUI 交互测试\u003c/li\u003e\n\u003cli\u003e设置配置\u003c/li\u003e\n\u003cli\u003e弹窗处理\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 id=\"3-ssh-\"\u003e3. \u003cstrong\u003eSSH\u003c/strong\u003e ⭐⭐⭐\u003c/h4\u003e\n\u003cp\u003e\u003cstrong\u003e远程连接工具\u003c/strong\u003e\u003c/p\u003e","title":"IoTAutomationFramework_2.0 安卓测试自动化工具"},{"content":"配置自定义 AI API 完整指南：从零到一接入你的专属模型 前言 作为一名开发者，我们经常需要在终端环境中使用 AI 编程助手。 的 Codex 是一个非常强大的命令行 AI 编程工具，但默认情况下它只能调用 OpenAI 官方的 。那么问题来了：如果我们有自己的 API 服务（比如部署了国产、使用了代理服务、或者公司内部的 AI 平台），如何让 Codex 接入这些自定义的 API 呢？\n本文将通过一个真实的配置案例，详细讲解如何在 macOS（特别是 Mini）环境下配置 Codex，使其能够调用自定义的 AI API。整个过程涉及编写、环境变量设置、版本兼容性问题排查等，希望能帮助到遇到类似问题的开发者。\n一、理解 Codex 的架构 在开始配置之前，我们需要理解 Codex 的基本架构。Codex 采用了一种灵活的 Provider 机制，允许用户定义多个 AI 服务提供商，并在它们之间切换。\n核心概念：\nProvider（提供商）：一个 AI 服务的具体实现，包含 API 地址、认证方式等 Model（模型）：Provider 提供的具体模型名称 Wire API：Codex 与 Provider 之间的通信协议类型 这种设计让 Codex 不仅限于 OpenAI 的服务，理论上可以接入任何兼容 OpenAI API 格式的服务。\n二、配置前的准备工作 2.1 确认 Codex 版本 这是最关键的一步！不同版本的 Codex 对 API 协议的支持完全不同：\ncodex --version 版本 支持的 API 类型 wire_api 参数 0.81.0 及以上 Responses API “responses” 0.80.0 及以下 Chat Completions API “chat” 重要提示：如果你的 API 服务只支持标准的 Chat Completions 格式（大多数国产模型和代理服务都是这种），建议安装 0.80.0 版本：\nnpm install -g @openai/codex@0.80.0 2.2 确认 API 服务状态 在配置 Codex 之前，先用 curl 测试一下你的 API 服务是否正常工作：\n# 测试基础连通性 curl -X POST http://localhost:8080/v1/chat/completions \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -H \u0026#34;Authorization: Bearer YOUR_API_KEY\u0026#34; \\ -d \u0026#39;{ \u0026#34;model\u0026#34;: \u0026#34;your-model-name\u0026#34;, \u0026#34;messages\u0026#34;: [{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: \u0026#34;Hello\u0026#34;}], \u0026#34;max_tokens\u0026#34;: 50 }\u0026#39; 如果这个请求能正常返回，说明你的 API 服务是可用的。\n三、配置文件详解 3.1 配置文件位置 Codex 的配置文件采用 TOML 格式，默认位置在：\n用户级配置：~/.codex/config.toml 项目级配置：项目根目录/.codex/config.toml 级配置会覆盖用户级配置，这为不同项目使用不同的 AI 服务提供了便利。\n3.2 基础配置结构 一个完整的配置文件包含三个部分：\n全局设置（默认模型和 Provider） Provider 定义 项目特定设置（可选） # 全局设置 service_tier = \u0026#34;fast\u0026#34; model = \u0026#34;your-model-name\u0026#34; model_provider = \u0026#34;your-provider-name\u0026#34; # Provider 定义 [model_providers.your-provider-name] name = \u0026#34;显示名称\u0026#34; base_url = \u0026#34;http://localhost:8080/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; # 或 \u0026#34;responses\u0026#34; env_key = \u0026#34;YOUR_API_KEY_ENV_NAME\u0026#34; # 项目特定设置（可选） [projects.\u0026#34;/path/to/your/project\u0026#34;] trust_level = \u0026#34;trusted\u0026#34; 3.3 配置项详细说明 配置项 说明 示例 model 默认使用的模型名称 qwen3.6-plus model_provider 默认使用的 Provider 名称 my-custom-provider base_url API 服务地址 http://localhost:8080/v1 wire_api API 协议类型 chat 或 responses env_key 存放 API Key 的环境变量名 MY_API_KEY 3.4 常见配置错误及修正 错误 1：将 API Key 直接写在 env_key 字段\n# ❌ 错误 env_key = \u0026#34;sk-your-actual-api-key\u0026#34; # ✅ 正确 env_key = \u0026#34;MY_API_KEY\u0026#34; 错误 2：协议类型不匹配\n# 如果 API 只支持 Chat Completions wire_api = \u0026#34;chat\u0026#34; # 而不是 \u0026#34;responses\u0026#34; 错误 3：base_url 格式问题\n# 本地服务通常用 http 而不是 https base_url = \u0026#34;http://localhost:8080/v1\u0026#34; # 正确 base_url = \u0026#34;https://localhost:8080/v1\u0026#34; # 可能导致 SSL 错误 四、Mac Mini 配置 4.1 临时设置（仅当前终端会话） export YOUR_API_KEY=\u0026#34;sk-your-actual-api-key\u0026#34; 4.2 永久设置（推荐） 由于 Mac Mini 默认使用 Zsh，我们需要将环境变量写入 ~/.zshrc：\necho \u0026#39;export YOUR_API_KEY=\u0026#34;sk-your-actual-api-key\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc 4.3 验证环境变量 echo $YOUR_API_KEY 五、实战案例：配置本地 API 假设我们有一个运行在本地 8080 端口的 Qwen 模型服务，以下是完整的配置步骤：\n5.1 创建配置文件 mkdir -p ~/.codex nano ~/.codex/config.toml 5.2 写入配置内容 service_tier = \u0026#34;fast\u0026#34; # 设置默认使用 Qwen 模型 model = \u0026#34;qwen3.6-plus\u0026#34; model_provider = \u0026#34;red_claw\u0026#34; # 定义 Provider [model_providers.red_claw] name = \u0026#34;RedClaw Qwen Service\u0026#34; base_url = \u0026#34;http://localhost:8080/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; env_key = \u0026#34;REDCLAW_API_KEY\u0026#34; # 项目信任配置（可选） [projects.\u0026#34;/Users/macmini/workspace/my-project\u0026#34;] trust_level = \u0026#34;trusted\u0026#34; 5.3 设置环境变量 echo \u0026#39;export REDCLAW_API_KEY=\u0026#34;sk-yien-1620bbcc7f4349c1bcf5b82f6e3756c1\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc 5.4 测试配置 codex \u0026#34;你好，请介绍一下自己\u0026#34; 六、常见问题及 6.1 问题：自定义模型不显示在选择器中 现象：运行 codex 时，模型选择器只显示官方模型，看不到自己配置的模型。\n原因：配置文件没有被正确加载，或者配置格式有误。\n解决方案：\n# 检查配置文件是否存在 ls -la ~/.codex/config.toml # 查看当前加载的配置 codex config show # 检查配置语法 codex --config-check 6.2 问题：--model-provider 参数不存在 现象：\nerror: unexpected argument \u0026#39;--model-provider\u0026#39; found 原因：Codex 没有这个参数。\n解决方案：通过配置文件设置默认 Provider，而不是通过命令行参数。或者使用正确的参数名：\n# 正确的参数是 --provider codex -m model-name --provider provider-name \u0026#34;prompt\u0026#34; 6.3 问题：wire_api 版本不匹配 现象：\nwire_api = chat is no longer supported 原因：新版 Codex 不再支持 chat 协议。\n解决方案：\n方案一：将配置中的wire_api改为\u0026quot;responses\u0026quot; 方案二：降级 Codex 到 0.80.0 版本 npm uninstall -g @openai/codex npm install -g @openai/codex@0.80.0 6.4 问题：SSL 证书错误（本地服务） 现象：\nSSL certificate problem: self signed certificate 原因：本地服务使用 HTTPS 但没有有效的 SSL 证书。\n解决方案：\n[model_providers.your-provider] # ... 其他配置 allow_insecure = true # 仅用于本地开发 6.5 问题：环境变量不生效 现象：配置了环境变量，但 Codex 仍然提示找不到 API Key。\n解决方案：\n# 1. 确认环境变量已设置 echo $YOUR_API_KEY # 2. 重新加载配置文件 source ~/.zshrc # 3. 重启终端 # Mac 上按 Cmd+Q 退出终端，重新打开 # 4. 检查是否有空格或特殊字符 # 确保 API Key 没有多余的空格 七、调试技巧 7.1 开启调试模式 # 开启详细日志 DEBUG=true codex \u0026#34;你的问题\u0026#34; # 查看网络请求详情 RUST_LOG=debug codex \u0026#34;你的问题\u0026#34; 7.2 查看配置加载情况 # 显示当前所有配置 codex config show # 列出可用的 Providers codex config list-providers # 测试配置文件 codex config test 7.3 网络抓包 如果还是无法定位问题，可以用 Wireshark 或 tcpdump 抓包分析：\n# 监控本地 8080 端口的流量 sudo tcpdump -i lo0 port 8080 -A 八、最佳实践建议 8.1 安全性建议 永远不要将 API Key 写在配置文件中，始终使用环境变量 定期轮换 API Key 对不同项目使用不同的 API Key，便于审计和权限管理 将 .codex/ 目录加入 .gitignore，避免意外提交敏感信息 8.2 多 Provider 管理 如果你有多个 AI 服务，可以在配置文件中定义多个 Provider：\n# 默认使用本地 Qwen model = \u0026#34;qwen3.6-plus\u0026#34; model_provider = \u0026#34;local_qwen\u0026#34; # 定义本地 Qwen [model_providers.local_qwen] name = \u0026#34;Local Qwen\u0026#34; base_url = \u0026#34;http://localhost:8080/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; env_key = \u0026#34;QWEN_API_KEY\u0026#34; # 定义云端 GPT [model_providers.cloud_gpt] name = \u0026#34;Cloud GPT\u0026#34; base_url = \u0026#34;https://api.openai.com/v1\u0026#34; wire_api = \u0026#34;responses\u0026#34; env_key = \u0026#34;OPENAI_API_KEY\u0026#34; # 定义代理服务 [model_providers.proxy_service] name = \u0026#34;API Proxy\u0026#34; base_url = \u0026#34;https://your-proxy.com/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; env_key = \u0026#34;PROXY_API_KEY\u0026#34; 8.3 项目级配置示例 为不同项目创建独立的配置文件：\n# 项目 A 使用本地 Qwen mkdir -p /path/to/projectA/.codex cat \u0026gt; /path/to/projectA/.codex/config.toml \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; model = \u0026#34;qwen-max\u0026#34; model_provider = \u0026#34;local_qwen\u0026#34; [model_providers.local_qwen] base_url = \u0026#34;http://localhost:8080/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; env_key = \u0026#34;QWEN_API_KEY\u0026#34; EOF # 项目 B 使用云端 GPT mkdir -p /path/to/projectB/.codex cat \u0026gt; /path/to/projectB/.codex/config.toml \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; model = \u0026#34;gpt-4\u0026#34; model_provider = \u0026#34;cloud_gpt\u0026#34; [model_providers.cloud_gpt] base_url = \u0026#34;https://api.openai.com/v1\u0026#34; wire_api = \u0026#34;responses\u0026#34; env_key = \u0026#34;OPENAI_API_KEY\u0026#34; EOF 九、完整配置清单 最后，提供一个完整的配置检查清单，确保没有遗漏：\nCodex 版本已确认（0.80.0 推荐） API 服务已启动并可访问 ~/.codex/config.toml文件已创建 model和model_provider已正确设置 Provider 配置块已添加 base_url使用正确的协议（本地用 http） wire_api类型与 API 服务匹配 env_key填的是环境变量名，不是 API Key 环境变量已在~/.zshrc中设置 已执行source ~/.zshrc使配置生效 echo $YOUR_ENV_KEY能正确显示 API Key codex config show显示正确的配置 codex \u0026quot;test\u0026quot;能正常响应 十、总结 配置 Codex 调用自定义 AI API 的核心要点可以总结为：\n版本先行：确认 Codex 版本，选择合适的wire_api类型 配置分离：API Key 用环境变量，其他配置用 TOML 文件 协议匹配：确保wire_api与你的 API 服务类型一致 路径正确：base_url格式要正确，本地服务注意 http vs https 调试有方：善用DEBUG=true和codex config show排查问题 虽然配置过程中可能会遇到各种问题（版本不匹配、参数名错误、环境变量不生效等），但只要按照本文的步骤逐一排查，最终都能顺利解决。\n希望这篇指南能帮助你成功配置 Codex，享受到在终端中使用自定义 AI 模型的便利。如果你在配置过程中遇到其他问题，欢迎在评论区留言交流！\n附录：快速配置模板\n# 一键配置脚本（请根据实际情况修改） cat \u0026gt; ~/.codex/config.toml \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; model = \u0026#34;your-model\u0026#34; model_provider = \u0026#34;custom\u0026#34; [model_providers.custom] base_url = \u0026#34;http://localhost:8080/v1\u0026#34; wire_api = \u0026#34;chat\u0026#34; env_key = \u0026#34;CUSTOM_API_KEY\u0026#34; EOF echo \u0026#39;export CUSTOM_API_KEY=\u0026#34;your-actual-api-key\u0026#34;\u0026#39; \u0026gt;\u0026gt; ~/.zshrc source ~/.zshrc # 测试 codex \u0026#34;Hello, world!\u0026#34; 原文链接: Codex 配置自定义 AI API 完整指南：从零到一接入你的专属模型 作者: 码农阿豪@新空间\n","permalink":"http://testai666.cn/posts/codex_%E9%85%8D%E7%BD%AE%E8%87%AA%E5%AE%9A%E4%B9%89_ai_api_%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97%E4%BB%8E%E9%9B%B6%E5%88%B0%E4%B8%80%E6%8E%A5%E5%85%A5%E4%BD%A0%E7%9A%84%E4%B8%93%E5%B1%9E%E6%A8%A1%E5%9E%8B/","summary":"\u003ch2 id=\"配置自定义-ai-api-完整指南从零到一接入你的专属模型\"\u003e配置自定义 AI API 完整指南：从零到一接入你的专属模型\u003c/h2\u003e\n\u003ch3 id=\"前言\"\u003e前言\u003c/h3\u003e\n\u003cp\u003e作为一名开发者，我们经常需要在终端环境中使用 AI 编程助手。 的 Codex 是一个非常强大的命令行 AI 编程工具，但默认情况下它只能调用 OpenAI 官方的 。那么问题来了：如果我们有自己的 API 服务（比如部署了国产、使用了代理服务、或者公司内部的 AI 平台），如何让 Codex 接入这些自定义的 API 呢？\u003c/p\u003e\n\u003cp\u003e本文将通过一个真实的配置案例，详细讲解如何在 macOS（特别是  Mini）环境下配置 Codex，使其能够调用自定义的 AI API。整个过程涉及编写、环境变量设置、版本兼容性问题排查等，希望能帮助到遇到类似问题的开发者。\u003c/p\u003e\n\u003ch3 id=\"一理解-codex-的架构\"\u003e一、理解 Codex 的架构\u003c/h3\u003e\n\u003cp\u003e在开始配置之前，我们需要理解 Codex 的基本架构。Codex 采用了一种灵活的 Provider 机制，允许用户定义多个 AI 服务提供商，并在它们之间切换。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e核心概念：\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eProvider（提供商）\u003c/strong\u003e：一个 AI 服务的具体实现，包含 API 地址、认证方式等\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eModel（模型）\u003c/strong\u003e：Provider 提供的具体模型名称\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWire API\u003c/strong\u003e：Codex 与 Provider 之间的通信协议类型\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这种设计让 Codex 不仅限于 OpenAI 的服务，理论上可以接入任何兼容 OpenAI API 格式的服务。\u003c/p\u003e\n\u003ch3 id=\"二配置前的准备工作\"\u003e二、配置前的准备工作\u003c/h3\u003e\n\u003ch4 id=\"21-确认-codex-版本\"\u003e2.1 确认 Codex 版本\u003c/h4\u003e\n\u003cp\u003e这是最关键的一步！不同版本的 Codex 对 API 协议的支持完全不同：\u003c/p\u003e","title":"Codex 配置自定义 AI API 完整指南：从零到一接入你的专属模型"},{"content":"今天我们聊一个近期 AI 圈里的热门话题 — 和 Agent**。**\n一提到 AI，大家总说“让 AI 帮我干活”，可真要让 AI 落地做事，离不开 Workflow 与 。这俩到底在 AI 里扮演了什么角色，又有什么区别？\n今天我们就来详细聊聊**，揭开 AI 自动化的秘密**。\n1 Workflow：深耕多年的“流程老管家” Workflow，也叫。很多人以为它是 AI 带火的新词，其实它早就在各行业成熟应用了。比如：\n职场人熟悉的审批流：请假时，从提交申请到审批通过，每一步都有固定顺序，缺一不可； 程序员常用的持续集成：代码提交后，自动触发编译→测试→部署，全程按预设步骤走，无需人工干预； 这些场景的本质就是提前把规则写死，严格按照预设步骤执行。就像工厂里的流水线，螺丝拧几圈、零件放哪里，早就定好了，工人只要按流程走就行。\n它的核心工作流也不复杂，主要分为四步：\n触发：谁来启动这条流程？ 满足某个条件就自动开跑，比如点击提交、到达某个时间点、检测到有文件上传。\n编排：接下来按哪条路走？ 先做什么、后做什么，遇到不同情况走哪条分支，都提前设计好，相当于流程的“路线图”。\n执行：具体怎么干活？ 按照路线图真正去办事：比如提交后的发送消息通知、计算汇总数据、调用外部接口等，把每一步操作落地。\n结束：怎么收尾？ 流程跑完后，再把结果告知相关人，更新状态、保存记录，让这件事有一个完整闭环。\n可以说，传统 Workflow 就像一套按部就班、严谨执行的固定“剧本”，不会自主思考、也不会灵活变通。\n而当 AI 时代到来，Workflow 也迎来了新的角色与价值。\nAI 的落地离不开（LLM），但大模型再智能，也只是一个擅长理解生成、却不懂统筹调度的“超级大脑”。当我们需要把零散的思考变成可执行的步骤时，就需要一个统一调度的角色 — Workflow。它会决定什么时候调用大模型、让它处理什么内容、结果传给谁、下一步怎么走。\n正是这样，传统工作流才真正具备了理解意图、生成内容的智能能力，升级为 AI Workflow。\n比如 AI 智能客服，它的流程非常清晰：\n用户咨询 → 大模型识别意图 → 生成应对答案 → 回复用户。\n还有常用的 AI 生成社交文案的流程：\n输入需求 → 大模型提取关键词 → 生成文案 → 人工微调 → 发布\n在这些场景里，Workflow****把控全程节奏，大模型承担最耗时的理解与生成工作，把人从重复劳动中解放出来，效率大幅提升。\n不过，AI 加持后的 Workflow 虽然高效，也有明显局限：它只能按固定流程执行，一旦遇到没有预设规则、需要灵活判断的开放式任务，比如规划一场兼顾预算、喜好和出行体验的周末旅行，就难以胜任。\n这时候，能自主思考、自主决策的智能体 Agent，就登场了。\n2 Agent：AI 时代才起飞的智能体 如果说 Workflow 是“你教我怎么做，我就怎么做”，那 Agent 就是“你告诉我要什么，我自己想怎么做”。\n它最大的特点就是能自己思考、自己规划、主动解决问题，但早期的 Agent 没有这么智能，直到近几年**大语言模型的成熟，才迎来真正爆发。**大模型让 Agent 首次拥有了三个关键能力：\n理解能力：能听懂你模糊的自然语言，精准抓住你的真实需求； 推理能力：能自己思考、分析、判断，而不是只按固定规则走； 学习能力：能从海量信息里学习知识，不用人一条条写规则。 这些能力就像给 Agent 装上了会思考的“大脑”，但这还不够，想要 Agent 真正独立完成任务，还需要另外两样东西：\n第一会“动手” — 工具调用\n大模型虽然擅长思考与推理，但只能依靠历史****训练数据，无法主动获取实时信息、对接外部系统。\nAgent 在大模型的基础上，通过工具调用机制（比如 、LlamaIndex 这类框架），给大模型装上了“手脚” — 能调用外部 API、连接外部系统，把大模型“脑子”里的想法，真正落地。\n第二会“记住” — 记忆与状态\n大模型没有持久记忆能力，很难记住之前的内容，以至于每次对话都像重新开始。\nAgent 则专门开辟了一块“记忆空间”（本质是一套可读写的存储，比如数据库或向量库），把你的偏好、历史对话、任务进度都存在里面。从而做到连贯思考、不丢上下文。\n最终在大模型****+ 工具 + 记忆的共同支撑下，Agent 真正具备了自主完成任务的能力。在接到任务后，它会完整经历四步：\n理解任务：先搞清楚要做什么？ 先把模糊的需求变成清晰的目标。比如“根据这篇文档做成 10 道面试题”，Agent 会先明确：目标是“出题”，材料是“当前文档”，风格是“面试”。\n制定计划：这事儿拆成几步来做？ 拿到目标后，它再拆解步骤、规划路径：先通读文档 → 挑关键知识点 → 为每个点设计问题 → 再统一调整难度和表述。\n这里的执行步骤不是提前写死的，而是 Agent 在运行过程中动态推理出来的。\n行动与观察：先做哪一步，结果怎么样？ 接着按照计划执行，边做边看结果。同时根据结果不断更新自己判断：信息够不够？方向对不对？需不需要换个工具或改计划？也就是「思考 → 调用工具 → 看结果 → 再思考」的循环。\n评估与收尾：可以交差了吗？ 最后等执行结束，对结果进行评估：检查是否达标、内容是否完整，必要时再继续迭代优化，直到符合预期后，把结果交付给你。\n这套闭环让 Agent 彻底摆脱了“规则束缚”，能像人类一样灵活应对复杂、未知的任务。也正是凭借这种能力，Agent 成为了今天各类 AI 产品的核心组成，我们日常用到的很多产品，都有它的身影，比如：\n全能 AI 助手：豆包、ChatGPT 等，能帮我们规划生活、处理工作、解决各种日常问题； 智能开发工具：Cursor、Claude 等，辅助程序员写代码、查 Bug、分析复杂项目，大幅提升研发效率。 可以说，Agent 让 AI 从被动执行真正走向了主动解决问题，这也是为什么它能在短短几年内引爆整个 AI 行业。\n3 Workflow 与 Agent：AI 时代的协同共生 虽然 Agent 比 Workflow 更智能、更灵活，但二者并非相互替代，而是能力互补、协同共生。\nWorkflow 稳定可靠却缺乏灵活思考，Agent 聪明自主却难以完全把控。两者结合，才是当下 AI 自动化的最佳实践：\n面对复杂、模糊、开放的任务：比如规划周末旅行、整理文档出题，交给 Agent 去思考和决策； 面对确定、重复、需要稳定落地的任务：比如固定发送日报、自动编译测试，交给 Workflow 去执行和兜底。 最终形成一套以大语言模型为大脑、Agent 主导决策、Workflow 保障执行的融合架构，这也是目前主流 AI 应用的核心设计思路。一个完整的架构如图所示：\n我们只有理解这些核心组件的配合与协同机制，才能更清晰地看懂 AI 如何真正落地、如何真正为我们所用。\n那么，初学者想快速上手、落地自己的 AI 应用，该怎么选工具？\n这里给大家推荐两类最实用、最容易上手的工具：\n如果是想零代码/低代码、追求快速搭建，推荐可视化编排：\nDify：偏企业业务，适合做稳定可靠的内部 AI 应用，比如知识库、自动化审批。 Coze（扣子）：字节出品，偏轻量化、拖拽简单、插件丰富，适合大众快速创作，比如 AI 助手、小程序。 如果是有一定开发基础、想做更深度的定制，推荐代码框架：\nLangGraph：专注于单个复杂 Agent 的执行流程控制，擅长实现多步骤、可循环、带状态的 Agent 逻辑。 AutoGen：专注于多个 Agent 之间的协同合作，支持多个 Agent 角色对话、互相校验、共同完成复杂任务。 建议先从可视化工具上手理解逻辑，再逐步尝试代码框架深入定制，这是一条最稳妥、最高效的学习路径。\n最后想说，AI 行业每天都在涌现新名词、新概念。但万变不离其宗，关键是沉下心来建立体系化认知。只有夯实基础、稳步提升能力，才能在快速迭代的 AI 浪潮中跟上步伐，从容落地。\n原文链接: 一文读懂AI自动化核心：Workflow与Agent的区别，建议收藏 作者: 和老莫一起学AI\n","permalink":"http://testai666.cn/posts/%E4%B8%80%E6%96%87%E8%AF%BB%E6%87%82ai%E8%87%AA%E5%8A%A8%E5%8C%96%E6%A0%B8%E5%BF%83workflow%E4%B8%8Eagent%E7%9A%84%E5%8C%BA%E5%88%AB%E5%BB%BA%E8%AE%AE%E6%94%B6%E8%97%8F/","summary":"\u003cp\u003e今天我们聊一个近期 AI 圈里的热门话题 —  \u003cstrong\u003e和 Agent\u003c/strong\u003e**。**\u003c/p\u003e\n\u003cp\u003e一提到 AI，大家总说“让 AI 帮我干活”，可真要让 AI 落地做事，离不开 Workflow 与 。这俩到底在 AI 里扮演了什么角色，又有什么区别？\u003c/p\u003e\n\u003cp\u003e今天我们就来详细聊聊**，揭开 AI 自动化的秘密**。\u003c/p\u003e\n\u003ch2 id=\"1-workflow深耕多年的流程老管家\"\u003e1 Workflow：深耕多年的“流程老管家”\u003c/h2\u003e\n\u003cp\u003eWorkflow，也叫。很多人以为它是 AI 带火的新词，其实它早就在各行业成熟应用了。比如：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e职场人熟悉的审批流：请假时，从提交申请到审批通过，每一步都有固定顺序，缺一不可；\u003c/li\u003e\n\u003cli\u003e程序员常用的持续集成：代码提交后，自动触发编译→测试→部署，全程按预设步骤走，无需人工干预；\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/images/0a24fbf3c0e943e9b84b7e5302909a5a.png\"\u003e\u003c/p\u003e\n\u003cp\u003e这些场景的本质就是\u003cstrong\u003e提前把规则写死，严格按照预设步骤执行\u003c/strong\u003e。就像工厂里的流水线，螺丝拧几圈、零件放哪里，早就定好了，工人只要按流程走就行。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"在这里插入图片描述\" loading=\"lazy\" src=\"/posts/images/1e91fe3e3e0e4e658f5484af134f1349.png\"\u003e\u003c/p\u003e\n\u003cp\u003e它的核心工作流也不复杂，主要分为四步：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e触发：谁来启动这条流程？\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e满足某个条件就自动开跑，比如点击提交、到达某个时间点、检测到有文件上传。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e编排：接下来按哪条路走？\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e先做什么、后做什么，遇到不同情况走哪条分支，都提前设计好，相当于流程的“路线图”。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e执行：具体怎么干活？\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e按照路线图真正去办事：比如提交后的发送消息通知、计算汇总数据、调用外部接口等，把每一步操作落地。\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e结束：怎么收尾？\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e流程跑完后，再把结果告知相关人，更新状态、保存记录，让这件事有一个完整闭环。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/images/b49c0893795978ce1081352a83924c48.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003e可以说，传统 Workflow 就像一套\u003cstrong\u003e按部就班、严谨执行\u003c/strong\u003e的固定“剧本”，不会自主思考、也不会灵活变通。\u003c/p\u003e\n\u003cp\u003e而当 AI 时代到来，\u003cstrong\u003eWorkflow 也迎来了新的角色与价值\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003eAI 的落地离不开（LLM），但大模型再智能，也只是一个擅长理解生成、却不懂统筹调度的“超级大脑”。当我们需要把零散的思考变成可执行的步骤时，就需要一个统一调度的角色 — \u003cstrong\u003eWorkflow\u003c/strong\u003e。它会决定什么时候调用大模型、让它处理什么内容、结果传给谁、下一步怎么走。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"图片\" loading=\"lazy\" src=\"/posts/images/845196dfae63f07c575e213569d9c251.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003e正是这样，传统工作流才真正具备了理解意图、生成内容的智能能力，升级为 AI Workflow。\u003c/p\u003e\n\u003cp\u003e比如 AI 智能客服，它的流程非常清晰：\u003c/p\u003e\n\u003cp\u003e用户咨询 → 大模型识别意图 → 生成应对答案 → 回复用户。\u003c/p\u003e\n\u003cp\u003e还有常用的 AI 生成社交文案的流程：\u003c/p\u003e\n\u003cp\u003e输入需求 → 大模型提取关键词 → 生成文案 → 人工微调 → 发布\u003c/p\u003e","title":"一文读懂AI自动化核心：Workflow与Agent的区别，建议收藏"},{"content":"\n跨平台、功能丰富、极快：重新定义AI上下文通信的优先紧凑文本输出。\n对于者来说，在开发AI Agent时与浏览器交互是一个频繁的过程。让AI Agent更高效地操作浏览器是一个具有挑战性的问题。最近，在搜索AI测试解决方案时，我发现了Vercel开源的agent-browser，这是一款专为AI Agent设计的自动化CLI。\nagent-的特性：\n基于引用：快照返回带有引用的可访问树。 Agent优先：高效利用AI上下文，节省token。 会话：支持多个具有独立身份验证的隔离浏览器实例。 功能完整：支持超过50个命令，包括导航、表单操作和截图。 跨平台：支持macOS、Linux和Windows平台。 1、安装agent-browser 在安装agent-browser之前，请确保您的计算机上已安装Node.js。\n在命令行中输入以下命令。-g选项表示全局安装。\nnpm install -g agent-browser 成功安装agent-browser CLI后，继续输入命令agent-browser install，这将开始下载Chromium浏览器。\n安装Chromium浏览器...\r需要安装以下软件包：\rplaywright@1.58.2\r确定继续？(y) y 成功安装Chromium浏览器后，命令行将输出成功安装的消息。\n正在下载Chrome for Testing 145.0.7632.6 (playwright chromium v1208)...\r162.3 MiB [====================] 100% 0.0s\r...\r✓ Chromium安装成功 2、使用agent-browser 2.1 打开网页 agent-browser open https://agent-browser.dev/ 输出：\n✓ 无头浏览器自动化 for AI\rhttps://agent-browser.dev/ 2.2 获取当前网页的可访问树 agent-browser snapshot -i 输出：\n- 链接 \u0026#34;Made with love by Vercel\u0026#34; [ref=e1]\r- 链接 \u0026#34;agent-browser\u0026#34; [ref=e2]\r- 链接 \u0026#34;16k\u0026#34; [ref=e3]\r- 链接 \u0026#34;npm\u0026#34; [ref=e4]\r...\r- 按钮 \u0026#34;Ask AI\u0026#34; [ref=e26] 2.3 使用引用进行交互 agent-browser click @e3 输出：\n✓ 完成 2.4 截图 agent-browser screenshot ./page.png 输出：\n✓ 截图已保存到 ./page.png 2.5 关闭浏览器 agent-browser close 输出：\n✓ 浏览器已关闭 3、安装Skills agent-browser是一款专为AI Agent设计的CLI。结合AI Agent，您可以轻松地浏览器操作。\n安装agent-browser 后，您可以在Cursor、Claude Code或中控制浏览器，执行您想要的任务。\nnpx skills add vercel-labs/agent-browser --skill agent-browser 由于我的电脑上已经安装了Claude Code和LM Studio，并且下载了qwen3.5-35b-a3b模型，我使用以下命令启动Claude：\nclaude --model qwen3.5-35b-a3b 然后输入截图任务。Claude将开始执行任务并在命令行中显示执行过程：\n❯ 保存 https://agent-browser.dev/ 网页的截图到当前目录。\r⎿ 初始化中…\r⎿ 无效的工具参数\r⏺ Skill(agent-browser)\r⎿ 成功加载skill · 2个工具允许\r⏺ 我将导航到网页并截图。\r⏺ Bash(agent-browser open https://agent-browser.dev \u0026amp;\u0026amp;\ragent-browser wait --load networkidle \u0026amp;\u0026amp; agent-browser\rscreenshot screenshot.png)\r⎿ ✓ 无头浏览器自动化 for AI\rhttps://agent-browser.dev/\r⎿ ✓ 完成\r⎿ 截图已保存到screenshot.png 4、结束语 本文仅介绍了 agent-browser的一些功能。它还可以控制iOS模拟器中的Safari浏览器来执行移动测试任务。此外，它支持CDP模式，让您可以轻松连接到本地或远程浏览器。\n除了agent-browser skill外，它还提供与dogfood、electron和slack相关的skills。如果您感兴趣，可以阅读官方agent-browser文档了解更多详情。\n原文链接：Agent-browser浏览器自动化CLI - 汇智网\n原文链接: Agent-browser浏览器自动化CLI 作者: 新缸中之脑\n","permalink":"http://testai666.cn/posts/agent-browser%E6%B5%8F%E8%A7%88%E5%99%A8%E8%87%AA%E5%8A%A8%E5%8C%96cli/","summary":"\u003cp\u003e\u003cimg alt=\"Agent-browser浏览器自动化CLI\" loading=\"lazy\" src=\"/posts/images/dda43e169ec7fb82843208de562fb30d.webp\"\u003e\u003c/p\u003e\n\u003cp\u003e跨平台、功能丰富、极快：重新定义AI上下文通信的优先紧凑文本输出。\u003c/p\u003e\n\u003cp\u003e对于者来说，在开发AI Agent时与浏览器交互是一个频繁的过程。让AI Agent更高效地操作浏览器是一个具有挑战性的问题。最近，在搜索AI测试解决方案时，我发现了Vercel开源的agent-browser，这是一款专为AI Agent设计的自动化CLI。\u003c/p\u003e\n\u003cp\u003eagent-的特性：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e基于引用\u003c/strong\u003e：快照返回带有引用的可访问树。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eAgent优先\u003c/strong\u003e：高效利用AI上下文，节省token。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e会话\u003c/strong\u003e：支持多个具有独立身份验证的隔离浏览器实例。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e功能完整\u003c/strong\u003e：支持超过50个命令，包括导航、表单操作和截图。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e跨平台\u003c/strong\u003e：支持macOS、Linux和Windows平台。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"1安装agent-browser\"\u003e1、安装agent-browser\u003c/h3\u003e\n\u003cblockquote\u003e\n\u003cp\u003e在安装agent-browser之前，请确保您的计算机上已安装Node.js。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e在命令行中输入以下命令。\u003ccode\u003e-g\u003c/code\u003e选项表示全局安装。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003enpm install -g agent-browser\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e成功安装agent-browser CLI后，继续输入命令\u003ccode\u003eagent-browser install\u003c/code\u003e，这将开始下载Chromium浏览器。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e安装Chromium浏览器...\r\n需要安装以下软件包：\r\nplaywright@1.58.2\r\n确定继续？(y) y\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e成功安装Chromium浏览器后，命令行将输出成功安装的消息。\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e正在下载Chrome for Testing 145.0.7632.6 (playwright chromium v1208)...\r\n162.3 MiB [====================] 100% 0.0s\r\n...\r\n✓ Chromium安装成功\n\u003c/code\u003e\u003c/pre\u003e\u003ch3 id=\"2使用agent-browser\"\u003e2、使用agent-browser\u003c/h3\u003e\n\u003ch4 id=\"21-打开网页\"\u003e2.1 打开网页\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eagent-browser open https://agent-browser.dev/\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e✓ 无头浏览器自动化 for AI\r\n  https://agent-browser.dev/\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"22-获取当前网页的可访问树\"\u003e2.2 获取当前网页的可访问树\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eagent-browser snapshot -i\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e- 链接 \u0026#34;Made with love by Vercel\u0026#34; [ref=e1]\r\n- 链接 \u0026#34;agent-browser\u0026#34; [ref=e2]\r\n- 链接 \u0026#34;16k\u0026#34; [ref=e3]\r\n- 链接 \u0026#34;npm\u0026#34; [ref=e4]\r\n...\r\n- 按钮 \u0026#34;Ask AI\u0026#34; [ref=e26]\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"23-使用引用进行交互\"\u003e2.3 使用引用进行交互\u003c/h4\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eagent-browser click @e3\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e输出：\u003c/p\u003e","title":"Agent-browser浏览器自动化CLI"},{"content":" 问题类型 占比 可预防性 过热降频/关机 40% ⭐⭐⭐⭐⭐（加风扇即可） 电源不足 25% ⭐⭐⭐⭐（换好电源） 内核/驱动崩溃 20% ⭐⭐⭐（升级内核） 内存/存储故障 10% ⭐⭐（依赖硬件质量） 工具/配置错误 5% ⭐⭐⭐⭐（规范操作） 原文链接: 开发板常见问题 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E5%BC%80%E5%8F%91%E6%9D%BF%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/","summary":"\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e问题类型\u003c/th\u003e\n          \u003cth\u003e占比\u003c/th\u003e\n          \u003cth\u003e可预防性\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e过热降频/关机\u003c/td\u003e\n          \u003ctd\u003e40%\u003c/td\u003e\n          \u003ctd\u003e⭐⭐⭐⭐⭐（加风扇即可）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e电源不足\u003c/td\u003e\n          \u003ctd\u003e25%\u003c/td\u003e\n          \u003ctd\u003e⭐⭐⭐⭐（换好电源）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e内核/驱动崩溃\u003c/td\u003e\n          \u003ctd\u003e20%\u003c/td\u003e\n          \u003ctd\u003e⭐⭐⭐（升级内核）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e内存/存储故障\u003c/td\u003e\n          \u003ctd\u003e10%\u003c/td\u003e\n          \u003ctd\u003e⭐⭐（依赖硬件质量）\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e工具/配置错误\u003c/td\u003e\n          \u003ctd\u003e5%\u003c/td\u003e\n          \u003ctd\u003e⭐⭐⭐⭐（规范操作）\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/158649520?spm=1001.2014.3001.5502\"\u003e开发板常见问题\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"开发板常见问题"},{"content":" 优点 缺点 brower-use 使用官网SDK写一个python的api，挺好用的能识别网页绿色框框数量 官网api收费，使用免费的api不太好用github的部署麻烦，没有成功 magentic-ui-fara7B 部署简单成功也能识别绿色框框 调用的qubrid的api，几下就用完了。。。需要设置docker的普通用户权限 原文链接: 浏览器自动化agent体验（brower-use，magentic-ui-fara7B） 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E6%B5%8F%E8%A7%88%E5%99%A8%E8%87%AA%E5%8A%A8%E5%8C%96agent%E4%BD%93%E9%AA%8Cbrower-usemagentic-ui-fara7b/","summary":"\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e\u003c/th\u003e\n          \u003cth\u003e优点\u003c/th\u003e\n          \u003cth\u003e缺点\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003ebrower-use\u003c/td\u003e\n          \u003ctd\u003e使用官网SDK写一个python的api，挺好用的能识别网页绿色框框数量\u003c/td\u003e\n          \u003ctd\u003e官网api收费，使用免费的api不太好用github的部署麻烦，没有成功\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003emagentic-ui-fara7B\u003c/td\u003e\n          \u003ctd\u003e部署简单成功也能识别绿色框框\u003c/td\u003e\n          \u003ctd\u003e调用的qubrid的api，几下就用完了。。。需要设置docker的普通用户权限\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n          \u003ctd\u003e\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/158617112?spm=1001.2014.3001.5502\"\u003e浏览器自动化agent体验（brower-use，magentic-ui-fara7B）\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"浏览器自动化agent体验（brower-use，magentic-ui-fara7B）"},{"content":"花了我两三天，结果还是好的达到了我的预期 (o゜▽゜)o☆[BINGO!]\n先看下效果：\n如图所示脚本运行可以自动点击join按钮和滑动按钮，按时签到薅羊毛！\n我采用的是pyautogui+方式，非传统自动化工具如Selenium和Playwright，不过使用了DrissionPage来获取缺口图和背景图。\n已经适配windows和debian系统，最新的浏览器（其他没试）。\n分享源码给大家一起学习测试使用（不可用做非法用途！发生的非法事件与本人无关特此声明）\n：https://github.com/haolixu/geetest-auto\n：https://gitee.com/haolixu/geetest-auto\n那个依赖我没搞定，尝试了pip \u0026gt; requirements.txt和pipreqs . \u0026ndash;encoding=utf-8 \u0026ndash;force\n但是没啥用。。。还是缺依赖，你们有什么好的方法吗？\n需要注意的是浏览器需要设置一下：\nwin：D:\\浏览器\\QQBrowser\\QQBrowser.exe -sc=desktopshortcut -fixlaunch=0 \u0026mdash;debugging-port=9222\n：/usr/bin/qqbrowser-browser-stable %U -sc=desktopshortcut -fixlaunch=0 \u0026ndash;remote-debugging-port=9222\n原文链接: 成功破解geetestv4极验滑动验证码 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E6%88%90%E5%8A%9F%E7%A0%B4%E8%A7%A3geetestv4%E6%9E%81%E9%AA%8C%E6%BB%91%E5%8A%A8%E9%AA%8C%E8%AF%81%E7%A0%81/","summary":"\u003cp\u003e花了我两三天，结果还是好的达到了我的预期 (o゜▽゜)o☆[BINGO!]\u003c/p\u003e\n\u003cp\u003e先看下效果：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/22e843018a6542dbacb9bf8888be20e5.gif\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/bd13ae3ce8fd4f6481bba8cde1b29923.png\"\u003e\u003c/p\u003e\n\u003cp\u003e如图所示脚本运行可以自动点击join按钮和滑动按钮，按时签到薅羊毛！\u003c/p\u003e\n\u003cp\u003e我采用的是pyautogui+方式，非传统自动化工具如\u003cstrong\u003eSelenium和Playwright，\u003cstrong\u003e不过使用了\u003c/strong\u003eDrissionPage\u003c/strong\u003e来获取缺口图和背景图。\u003c/p\u003e\n\u003cp\u003e已经适配windows和debian系统，最新的浏览器（其他没试）。\u003c/p\u003e\n\u003cp\u003e分享源码给大家一起学习测试使用（不可用做非法用途！发生的非法事件与本人无关特此声明）\u003c/p\u003e\n\u003cp\u003e：\u003ca href=\"https://github.com/haolixu/geetest-auto\"\u003ehttps://github.com/haolixu/geetest-auto\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e：\u003ca href=\"https://gitee.com/haolixu/geetest-auto\"\u003ehttps://gitee.com/haolixu/geetest-auto\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e那个依赖我没搞定，尝试了pip  \u0026gt; requirements.txt和pipreqs . \u0026ndash;encoding=utf-8 \u0026ndash;force\u003c/p\u003e\n\u003cp\u003e但是没啥用。。。还是缺依赖，你们有什么好的方法吗？\u003c/p\u003e\n\u003cp\u003e需要注意的是浏览器需要设置一下：\u003c/p\u003e\n\u003cp\u003ewin：D:\\浏览器\\QQBrowser\\QQBrowser.exe -sc=desktopshortcut -fixlaunch=0 \u0026mdash;debugging-port=9222\u003c/p\u003e\n\u003cp\u003e：/usr/bin/qqbrowser-browser-stable %U   -sc=desktopshortcut -fixlaunch=0 \u0026ndash;remote-debugging-port=9222\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/66481d475ca94ddb89b46e72a3375d02.png\"\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/021c0a97225e40d78fca6bcb9d33b535.png\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/158128351?spm=1001.2014.3001.5502\"\u003e成功破解geetestv4极验滑动验证码\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"成功破解geetestv4极验滑动验证码"},{"content":" 干货分享，感谢您的阅读！\n随着大语言（Large Language Model，LLM）能力的持续提升，业界对“让模型真正做事”的期待，已经从单轮对话生成文本，转向了具备自主决策、工具调用与多步执行能力的 Agent（智能体）系统。\n在这一演进过程中，一个逐渐清晰的共识是：Agent 并不是“更强的大模型”，而是“以大模型为大脑，外部系统为四肢”的工程系统。\n记忆、规划、、行动执行，这些能力本质上并不来自模型本身，而是通过工程框架将模型与外部能力进行组织、编排与约束的结果。正是在这一背景下，围绕 Agent 的研究与工程实践迅速发展，催生了诸如 ReAct、Auto-GPT、LangGraph、MetaGPT 等一系列方法与框架。\n我们将聚焦于 ModelScope-Agent 这一由阿里云魔搭社区推出的 框架，从能力结构、运行机制、典型任务示例与工程实践角度，系统解析如何基于该框架构建一个可扩展、可落地的 Agent 应用。\n一、为什么 Agent 不等于？ 在深入 -Agent 之前，有必要先澄清一个常见误区：Agent ≠ 更大的模型 ≠ 多轮 Prompt。\n从系统架构视角来看，一个可用的 Agent 至少包含以下几个核心组件：\n推理核心（LLM）：负责理解用户意图、进行任务拆解、生成行动决策。 工具系统（Tools / Plugins）：提供模型无法直接完成的能力，如搜索、计算、生成图片、调用 API 等。 执行与调度层（Executor / Orchestrator）：将模型输出的“意图”转化为真实的函数调用或外部服务请求。 记忆系统（Memory）：保存历史对话、中间结果、长期偏好，用于后续决策参考。 规划与反思机制（Planning \u0026amp; Reflection）：用于多步骤任务拆解、执行顺序安排，以及失败后的自我修正。 从这个角度看，Agent 是一个系统工程问题，而不是单一模型能力问题。ModelScope-Agent 正是试图在工程层面，为这些能力提供一套可组合、可扩展的实现框架。\n二、ModelScope-Agent 框架概览 ModelScope-Agent 是魔搭社区推出的一个 通用 Agent 开发框架，其目标并非“封装一个黑盒 Agent”，而是提供：\n标准化的 Agent 抽象 灵活的工具接入机制 可插拔的记忆与规划模块 面向多模态任务的统一调用方式 从系统结构上看（如下图所示，老版本结构，仅供参考）：\n利用ModelScope-Agent框架开发的Agent，除了可以提供文本创作之外，还能生成图片、视频、语音等内容。单个Agent具有角色扮演、LLM调用、工具使用、规划、记忆等能力。 技术上主要具有以下特点：\n简单的Agent实现流程：仅需指定角色描述、大模型名称、工具名列表，即可实现一个Agent应用，框架内部自动实现工具使用、规划、记忆等工作流的编排。 丰富的模型和工具：框架内置丰富的大模型接口，例如Dashscope和Modelscope模型接口，OpenAI模型接口等。内置丰富的工具，例如代码运行、天气查询、文生图、网页解析等，方便定制专属Agent。 统一的接口和高扩展性：框架具有清晰的工具、大模型注册机制，方便用户扩展能力更加丰富的Agent应用。 低耦合性：开发者可以方便地直接使用内置的工具、大模型、记忆等组件，而不需要绑定更上层的Agent。 三、ModelScope-Agent 的核心能力解析 （一）内容生成能力 与许多只聚焦文本任务的 Agent 框架不同，ModelScope-Agent 天然支持多模态工具接入，使 Agent 能够完成：\n文本生成与总结 图像生成与编辑 语音合成 视频生成 在实际体验中，你可以通过自然语言直接描述目标，例如：\n“帮我生成一个 10 秒的卡通风格短视频，主题是‘小熊学习编程’。”\nAgent 会自动识别这是一个视频生成任务，并调用对应的 video-generation 插件完成执行。这一过程对用户是透明的，但对开发者而言，插件调用路径是可控、可替换的。\n（二）单 Agent 的复合能力结构 基于 ModelScope-Agent 构建的单个 Agent，通常具备以下能力集合：\n角色设定（Role / Persona） LLM 调用与上下文管理 工具选择与参数构造 任务规划与步骤拆解 短期与长期记忆管理 这使得 Agent 不再只是“问一句答一句”，而是可以承担类似“助理”“执行者”“创作伙伴”的角色。\n四、典型任务场景解析 （一）完成一个简单任务：直接工具调用 最基础的 Agent 使用方式，是让模型直接生成可执行动作。\n例如，用户通过对话要求生成一个视频，Agent 会：\n解析用户意图（视频生成） 选择合适的插件 自动构造参数 调用插件并返回结果 虽然生成的视频可能较为简单，但它清晰地展示了 “自然语言 → 工具调用 → 可视化结果” 的完整链路。更重要的是，这种链路是可扩展的——如果你有更好的视频生成模型，只需替换插件实现即可。\n（二）完成多步骤复合任务：规划与调度 Agent 真正体现价值的场景，往往是一句话中包含多个子任务。\n例如：\n在这一场景下，Agent 需要完成：\n任务拆解（生成20字以内的广告词 → 生成音频 → 制作视频） 执行顺序规划 中间结果保存 不同工具的多次调用 目前这个在线演示工具的能力比较基础，因此我们可以要求模型只生成20个字以内的广告词，语音合成的模型能力比较稳定，语音效果较好。\n（三）多轮对话中的参数提取与记忆利用 默认情况下，ModelScope-Agent 内置记忆机制，可以从历史对话中提取关键信息，用于后续工具调用。\n例如：\n第一轮：用户生成一个故事 第二轮：用户让 Agent 总结标题 第三轮：用户要求“根据刚才的故事生成视频” 在第三步中，Agent 需要从历史上下文中提取故事内容作为视频生成参数。这一过程本质上涉及：\n对话记忆管理 参数抽取 工具调用上下文构建 这一能力是许多 AIGC 产品（如儿童故事应用）得以实现“连贯体验”的关键。\n（四）基于检索工具的问答能力 ModelScope-Agent 可以加载：\n知识库检索插件 搜索引擎插件（如modelscope_search） 当模型判断当前问题超出自身知识或需要实时信息时，会主动调用检索工具，再基于检索结果生成答案。这一模式与 RAG（Retrieval-Augmented ）高度一致，但在 Agent 框架下，检索本身被视为一种“工具行为”。\n五、ModelScope-Agent 已集成工具生态 目前，ModelScope-Agent 已集成大量官方工具，包括但不限于：\n网页搜索与浏览 代码解释器 天气查询 图像生成与增强 视频生成 语音合成 图像理解（Qwen-VL） 工具 工具地址 API-KEY配置 web_browser 网页浏览 web_search 网页搜索 code_interpreter 代码解释器 amap_weather 高德天气 AMAP_TOKEN 需要在环境变量中进行配置 image_gen Wanx 图像生成 DASHSCOPE_API_KEY 需要在环境变量中进行配置 qwen_vl Qwen-VL 图像识别 DASHSCOPE_API_KEY 需要在环境变量中进行配置 speech-generation 语音生成 MODELSCOPE_API_TOKEN 需要在环境变量中进行配置 video-generation 视频生成 MODELSCOPE_API_TOKEN 需要在环境变量中进行配置 text-address 地理编码 MODELSCOPE_API_TOKEN 需要在环境变量中进行配置 wordart_texture_generation 创意文字 DASHSCOPE_API_KEY 需要在环境变量中进行配置 style_repaint 人像风格重绘 DASHSCOPE_API_KEY 需要在环境变量中进行配置 image_enhancement 追影放大镜 DASHSCOPE_API_KEY 需要在环境变量中进行配置 同时，框架也支持第三方工具（如 LangChain Tool）的接入。这种设计，使 Agent 的能力边界不再由模型决定，而是由工具生态决定。\n六、如何体验与上手 （一）在线体验 你可以直接通过魔搭社区的在线空间体验 ModelScope-Agent，无需本地环境配置。\n（二）本地运行 对于开发者而言，更推荐直接克隆项目代码，在本地运行 ./examples 中的示例，通过阅读与修改代码来理解 Agent 的实际执行流程。 项目地址：\n七、总结：ModelScope-Agent 的工程价值 综合来看，ModelScope-Agent 的核心价值不在于“是否比其他 Agent 框架更强”，而在于：\n提供了可落地的工程实现 降低了 Agent 系统的开发门槛 对多模态任务有良好支持 具备清晰的扩展路径 对于希望将 Agent 能力真正引入业务系统的团队而言，它更像是一套Agent 基础设施模板，而不是一个一次性工具。\n与延伸阅读 https://edu.aliyun.com/course/3126500/lesson/342570389 ​​​​​​https://github.com/modelscope/modelscope-agent https://modelscope.cn https://arxiv.org/abs/2210.03629 （ReAct 论文） https://arxiv.org/abs/2303.17580 （Auto-GPT） https://arxiv.org/abs/2305.10601 （Plan-and-Solve） https://arxiv.org/abs/2310.08560 （Agent Survey） https://python.langchain.com/docs/modules/agents https://lilianweng.github.io/posts/2023-06-23-agent/ https://arxiv.org/abs/2005.11401 （RAG 原始论文） https://github.com/geekan/MetaGPT https://github.com/langchain-ai/langgraph https://huggingface.co/docs/transformers/agents 原文链接: 基于 ModelScope-Agent 框架构建可落地的大模型 Agent 应用实践 作者: 张彦峰ZYF\n","permalink":"http://testai666.cn/posts/%E5%9F%BA%E4%BA%8E_modelscope-agent_%E6%A1%86%E6%9E%B6%E6%9E%84%E5%BB%BA%E5%8F%AF%E8%90%BD%E5%9C%B0%E7%9A%84%E5%A4%A7%E6%A8%A1%E5%9E%8B_agent_%E5%BA%94%E7%94%A8%E5%AE%9E%E8%B7%B5/","summary":"\u003chr\u003e\n\u003cp\u003e干货分享，感谢您的阅读！\u003c/p\u003e\n\u003cp\u003e随着大语言（Large Language Model，LLM）能力的持续提升，业界对“让模型真正做事”的期待，已经从\u003cstrong\u003e单轮对话生成文本\u003c/strong\u003e，转向了\u003cstrong\u003e具备自主决策、工具调用与多步执行能力的 Agent（智能体）系统\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e在这一演进过程中，一个逐渐清晰的共识是：\u003cstrong\u003eAgent 并不是“更强的大模型”，而是“以大模型为大脑，外部系统为四肢”的工程系统。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e记忆、规划、、行动执行，这些能力本质上并不来自模型本身，而是通过工程框架将模型与外部能力进行组织、编排与约束的结果。正是在这一背景下，围绕  Agent 的研究与工程实践迅速发展，催生了诸如 \u003cstrong\u003eReAct、Auto-GPT、LangGraph、MetaGPT\u003c/strong\u003e 等一系列方法与框架。\u003c/p\u003e\n\u003cp\u003e我们将聚焦于 \u003cstrong\u003eModelScope-Agent\u003c/strong\u003e 这一由阿里云魔搭社区推出的  框架，从\u003cstrong\u003e能力结构、运行机制、典型任务示例与工程实践角度\u003c/strong\u003e，系统解析如何基于该框架构建一个可扩展、可落地的 Agent 应用。\u003c/p\u003e\n\u003ch2 id=\"一为什么-agent-不等于\"\u003e一、为什么 Agent 不等于？\u003c/h2\u003e\n\u003cp\u003e在深入 -Agent 之前，有必要先澄清一个常见误区：\u003cstrong\u003eAgent ≠ 更大的模型 ≠ 多轮 Prompt。\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e从系统架构视角来看，一个可用的 Agent 至少包含以下几个核心组件：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e推理核心（LLM）\u003c/strong\u003e：负责理解用户意图、进行任务拆解、生成行动决策。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e工具系统（Tools / Plugins）\u003c/strong\u003e：提供模型无法直接完成的能力，如搜索、计算、生成图片、调用 API 等。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e执行与调度层（Executor / Orchestrator）\u003c/strong\u003e：将模型输出的“意图”转化为真实的函数调用或外部服务请求。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e记忆系统（Memory）\u003c/strong\u003e：保存历史对话、中间结果、长期偏好，用于后续决策参考。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e规划与反思机制（Planning \u0026amp; Reflection）\u003c/strong\u003e：用于多步骤任务拆解、执行顺序安排，以及失败后的自我修正。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e从这个角度看，\u003cstrong\u003eAgent 是一个系统工程问题，而不是单一模型能力问题\u003c/strong\u003e。ModelScope-Agent 正是试图在工程层面，为这些能力提供一套可组合、可扩展的实现框架。\u003c/p\u003e\n\u003ch2 id=\"二modelscope-agent-框架概览\"\u003e二、ModelScope-Agent 框架概览\u003c/h2\u003e\n\u003cp\u003eModelScope-Agent 是魔搭社区推出的一个 \u003cstrong\u003e通用 Agent 开发框架\u003c/strong\u003e，其目标并非“封装一个黑盒 Agent”，而是提供：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e标准化的 Agent 抽象\u003c/li\u003e\n\u003cli\u003e灵活的工具接入机制\u003c/li\u003e\n\u003cli\u003e可插拔的记忆与规划模块\u003c/li\u003e\n\u003cli\u003e面向多模态任务的统一调用方式\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e从系统结构上看（如下图所示，老版本结构，仅供参考）：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/681d8cabd9e34416ab89416cd4f2156b.png\"\u003e\u003c/p\u003e\n\u003cp\u003e利用ModelScope-Agent框架开发的Agent，除了可以提供文本创作之外，还能生成图片、视频、语音等内容。单个Agent具有角色扮演、LLM调用、工具使用、规划、记忆等能力。 技术上主要具有以下特点：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e简单的Agent实现流程：仅需指定角色描述、大模型名称、工具名列表，即可实现一个Agent应用，框架内部自动实现工具使用、规划、记忆等工作流的编排。\u003c/li\u003e\n\u003cli\u003e丰富的模型和工具：框架内置丰富的大模型接口，例如Dashscope和Modelscope模型接口，OpenAI模型接口等。内置丰富的工具，例如代码运行、天气查询、文生图、网页解析等，方便定制专属Agent。\u003c/li\u003e\n\u003cli\u003e统一的接口和高扩展性：框架具有清晰的工具、大模型注册机制，方便用户扩展能力更加丰富的Agent应用。\u003c/li\u003e\n\u003cli\u003e低耦合性：开发者可以方便地直接使用内置的工具、大模型、记忆等组件，而不需要绑定更上层的Agent。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"三modelscope-agent-的核心能力解析\"\u003e三、ModelScope-Agent 的核心能力解析\u003c/h2\u003e\n\u003ch3 id=\"一内容生成能力\"\u003e（一）内容生成能力\u003c/h3\u003e\n\u003cp\u003e与许多只聚焦文本任务的 Agent 框架不同，ModelScope-Agent 天然支持多模态工具接入，使 Agent 能够完成：\u003c/p\u003e","title":"基于 ModelScope-Agent 框架构建可落地的大模型 Agent 应用实践"},{"content":"做记忆模块搭建的相关研发工作，一开始认为agent的记忆就是维护上下文，应该和chatbot的对话管理差不多，但真的做一段时间之后，才发现和对话管理相比还是区别比较大的，而且记忆模块对agent的实现效果以及后期可扩展性有比较重要的影响。\nagent记忆与chatbot上下文的区别 我们拿agent记忆与上下文来作下比较，暂且不考虑长期记忆，就拿短期记忆来作对比，短期记忆(工作记忆)主要就是维护上下文，更官方一点的说法就是上下文工程，那chabot的对话管理不是也是在维护上下文吗？那两者有什么区别呢？\n目标 agent记忆和chatbot上下文的目标看上去是一致的:给LLM/agent添加合适的状态来做下一步的决策。但是从服务对象，组织形式和维护重点的扩展来看有比较大的区别。\n服务对象 虽然两者都算是人机交互，拿服务的对象都可以算作是人，但从现阶段的主要技术实现来看，服务对象还是存在差异的。\nchatbot的服务对象是人这个毋庸置疑，做图灵测试也是为了验证对面和你说话的那个AI，有没有可能骗过你，而从让你以为它是个人，chatbot展开的多轮对话也是围绕人的实际需求展开。\nagent虽然也存在与人的交互，agentic workflow中一般也会有人工输入的节点，但现阶段我们搭建agent的主要任务，更多时候是在自动完成一个复杂任务，因此服务对象可以认为是复杂任务。\n组织形式 从组织形式上来看，由于agent具备了行动能力，所以和chabot产生了组织形式的不同。\nchatbot主要的构成是一问一答的形式，包括我们调用大模型api一般也遵循这个结构，可以表示为{[q0,a0],[q1,a1],\u0026hellip;[qt,at]}。\nagent记忆的构成以最常见的react模式为例，主要是动作(tool)和动作结果(tool_result)的时序集合，可以表示为{a0,o0,a1,01,\u0026hellip;at,ot}。\n维护重点 对于记忆和上下文，很重要的一个功能是维护，但维护的侧重点也是有所不同的。\nchabot的上下文虽然也存在前后关联，不会每一轮都紧密联系，但会存在频繁的意图切换，如何理解意图并且实现有效的意图切换识别是上下文维护的重点。\n而agent记忆的每一个步骤都是按迭代规划进行的，缺失某个步骤或跳过某个步骤都可能会导致任务失败，因此它的维护重点是如何在有限的窗口中记录对下一步决策产生影响的所需关键信息。\n另外agent和chatbot还存在一个很大不同的点是的必要性。对于chatbot，长期记忆主要是记住个人信息和偏好用于个性化服务。而对于agent，长期记忆需要通过情景记忆来记住之前的迭代轨迹，这样不仅能通过检索的方式来获取与当前决策相关的上下文信息，还可以实现经验复用，在下一次遇到类似任务时，可以将之前的经验作为参考,马普所今年2月份发这篇论文时候就指出情景记忆是当前agent所缺少的[1]。\nagent短期记忆-业界技术分享 anthropic anthropic提到agent在长程任务上的三种管理上下文方式[2]包括:压缩,结构化笔记和采用子agent架构。\n压缩通过对上下文关键内容的提取，改写和无关内容删除实现上下文瘦身；\n结构化笔记是把关键信息以结构化形式存入笔记，让agent可以阅读到关键信息；\n子agent架构则是通过子agent维护自己的独立上下文再将关键结果返回主agent实现上下文解耦。\noepnai openai的短期记忆管理[3]则是围绕agent sdk来谈到管理短期记忆的两个主要方式:裁剪（trimming）和压缩（summarizing）。\n压缩和anthropic的compact是一样的，裁剪就是把旧的历史信息直接剪掉。\ntrimming的好处是实现比较简单，延时友好而且不会引入偏移信息（相比压缩），但缺点也显而易见，裁剪的内容可能包含当前决策需要的信息，所以一般适用于任务独立，上下文关联不大，低延迟要求场景。\nlangchain \u0026amp; manus langchain与最近的一次交流[4]提到manus的三种上下文管理方式:reduce ，offload context和isolate context。\nreduce context包括将工具执行结果进行压缩放在窗口内，而降完整结果保存在文件系统中，在上下文触发窗口限制时对整个轨迹进行整体压缩；\ncontext isolate类似于anthropic的sub agent方法，但细分为两种方式：对于简单任务main agent通过function call的方式将指令传递给子agent，子agent在自己的窗口执行任务；而对于复杂任务，子agent会和主agent共享上下文。\ncontext offloading和anthropic的结构化笔记虽然都是将内容存入文件，但处理思路不一样。manus的方式是将大多数操作卸载到沙盒层，在沙盒中通过bash工具就可以实现多样化的文件读写操作，这样就可以把工具执行结果保存到文件，在需要时通过bash的grep等查询命令进行访问。另外manus的这个设计相当于把大部分先验知识文件化，让agent按需通过读取文件内容获取先验来指导行动，anthropic的skill实现应该也是类似的思路。\nagent记忆-项目实践 初版记忆实现 在实际搭建agent时，工具模块，记忆模块，提示词模块是三个重要的基础模块。工具模块和提示词模块相对简单或者说比较成熟的方案，而记忆模块的搭建则有不同的设计思路和实现方式，但核心思路一致 - \u0026ldquo;给agent提供恰当的上下文\u0026rdquo;，但难就难在\u0026quot;恰当\u0026quot;，首先受限于LLM的上下文窗口，所以累计上下文就不能过长。\n另外上下文中堆积无关或者重复的内容不但会影响推理规划，也是不经济的。还有一个重要原因是上下文中的内容对于agent来说不是等权重的，对于当前决策而言，有的内容相对其它内容就会更重要，类似于需要一个attention机制来选择关键信息，那以什么形式来管理上下文获取更相信的信息加入就尤为关键了。\n在近期的一个agent项目实践中，实现记忆模块的初始版本，设定的原则是\u0026quot;状态是记忆的投影\u0026quot;，以记忆作为事实的唯一来源，同时要求不在agent中维护状态，把记忆事实作为状态来源，放入agent的上下文中让agent自己进行决策。思路看上去是没有问题，但实际测试后就发现想简单了。\n对于agent而言本身依赖的LLM是无状态的，所以agent也可以认为是无状态的，但是记忆的目的就是为了让agent具有状态。在初版的实现里，每一轮迭代都把obs写入工作记忆，同时读取当前轮之前的工作记忆中的结构化历史信息，把obs作为上下文唯一来源，直接注入到下一轮迭代的上下文，这样做是比较省事，但是一旦agent内部迭代出现问题，因为obs和工作记忆之间的这种耦合设计，很难追溯问题来源。\n第二版记忆重构优化 在发现第一版的问题后，开始进行第二版改进。第二版的改进主要是基于看到的相关的技术博客和论文，重新设计了记忆方案。首先记忆模块的目录设计应该把短期记忆和长期记忆做清晰分划分，维护当前agent迭代执行任务所需上下文的就是短期记忆(工作记忆)，而记录完整迭代过程的是长期记忆（情景记忆），产生对应产出的也是长期记忆(语义记忆)。其中比较容易混淆的是工作记忆和情景记忆，从持久化的角度来说，把工作记忆持久化就可以理解为情景记忆，但实际上是存在差别的。\n对于一个agent的迭代轨迹{a0,o0,a1,o1,\u0026hellip;at,ot},可以直接把这个迭代轨迹放入上下文，也可以对这个迭代轨迹的每一步进行摘要提取，仅保留关键信息和引用，比如某步生成了一个很大的结构化，在进入下一轮时没有必要把整个json全部放入上下文，可以只放入一个引用，但是会把完整的json存入情景记忆，在下一轮迭代使用这个引用时再从情景记忆中取出这个对应的json，这样能避免大段内容直接进入到上下文中。\n同时还需要考虑到上下文长度限制和成本问题，一般的工作记忆还会考虑使用最近k轮的策略，上下文只保留最近k轮的迭代记录，但是全局信息以结构化的信息记录，把这个结构化信息也注入到上下文中，这样也不至于让agent丢失掉全局信息。而第一版的问题就在这里，状态不应该只是记忆的投影，状态是对记忆的删减和加工，实际上的改进就是在短期记忆和长期记忆之间加一个桥接适配层，来实现短期记忆和长期记忆的搭配使用。\n而为了agent项目的阶段性扩展和迭代，在前期工程化实现时就应该考虑长期记忆。在一个agent的迭代循环里，如果迭代轮数比较长，上下文窗口和LLM的上下文利用效率已经开始明显下降，就需要考虑结合长期记忆使用了。而为了缩减上下文，在删减掉比较早期的迭代信息后，需要识别出长期记忆中已保存的迭代记录中，哪些记忆内容会对当前轮决策有用，就选择加入到上下文。这个一般会使用retrival的方法，从长期记忆中进行检索。技术实现上会结合结构化和向量检索，而实际的实践需要根据场景需求去做设计和调整记忆存储和读写方式，可以使用slot，sql数据库，向量数据库，图数据库等等。\n实践过程汇总有一个原则可以参考：记忆模块在开始实现之初，就应该把短期记忆和长期记忆进行解耦设计，这不但为上下文管理留有比较大的优化空间，在符合可审计性原则之外还为经验复用和未来可能的RL积累数据基础。\n同时第二版的改进上，还有一个比较重要的改进点是还原了obs的单纯职责，obs只应该对当前轮的act和执行结果进行记录，不应该注入历史内容，obs保持职责单一只把当前轮的执行和执行结果(或摘要)放到工作记忆，工作记忆再去通过记忆模块补充历史上下文，这样修改之后，obs和工作记忆的职责也分的比较清晰了。\n另外在agent的工层化实现过程中，造成可审计比较困难的一个点就是对工程化实现过程的不同组件的职责理解和划分不清晰，一旦出了bug，bug溯因就会因为这种不清晰的边界设计增加debug的难度，所以需要在设计之初就厘清不同模块的边界再动手进行实现。\nagent记忆相关研究进展 上面提到RL微调，就再展开谈一下在agent在记忆管理方向的相关研究。agent记忆管理是一个很复杂的问题，从工程角度来看，agent的记忆管理可以分为两类技术方案，一种将复杂问题简化，另外一种是自动化。目前相关的记忆研究在这两个方向论文也比较多，找到了几篇比较有代表性的论文。前者是提供系统化/可插拔的记忆管理底座框架，如[5]，A-MEM[6]，后者是将手动/规则维护记忆的方式转为自动化，如Memory‑R1[7]，MEM1[8]，Memp[9]， MEMSearcher[10]等。\n系统化框架方向 1. Mem0 Mem0的核心目标是提供一个生产级的记忆管理框架,能够动态地从对话中提取、整合和检索关键信息。\nMem0支持多层次的记忆管理,包括用户级、会话级和Agent级三个层面。系统会通过LLM自动从对话中提取记忆,并智能处理记忆冲突。特别是它基于grapg存储的图增强版本Mem0ᵍ,将记忆组织为知识图谱,以实体-关系三元组的形式存储,这让系统能够捕捉更复杂的多跳关系。从工程角度看,Mem0已经构建的比较成熟,提供了API服务,并集成了LangChain、CrewAI等主流agent框架。\n在LOCOMO基准测试上,Mem0相比OpenAI Memory的准确率提升了26%,相比将全部上下文都送入模型的做法,P95延迟降低了91%,token使用量减少了90%。Mem0在保持高准确率的同时,显著提升了效率。Mem0目前在很多agent项目中也是使用比较多的。\n2. A-MEM A-MEM的设计灵感来自Zettelkasten[11]笔记法,是一种通过原子化笔记和灵活链接来构建知识网络的方法论。\nA-MEM将每条记忆组织成包含多个属性的\u0026quot;原子笔记\u0026quot;,包括上下文描述、关键词、标签等结构化信息。当新记忆加入时,系统会自动分析它与历史记忆之间的关联,建立有意义的连接。比较有意思的是A-MEM的记忆演化机制:新记忆的加入可能触发对旧记忆的更新,让整个记忆网络持续精炼自己的理解。这种设计让记忆系统不依赖预定义的固定操作,而是能灵活适应不同类型的任务。\n在性能上,A-MEM在多跳推理任务中表现突出,效果是基线方法的两倍以上。同时它的记忆使用效率很高,通常只需要1200到2500个tokens,低于作比较的MemGPT需要的16900 个tokens。\n自动学习方向 3. Memory-R1 Memory-R1算是第一个真正用强化学习训练agent主动管理外部记忆的框架,论文发表于2025年。它的核心思路是把记忆管理归类为一个决策问题,应该通过任务最终结果的奖励信号来学习,而不是依赖人工设计的规则。\n系统包含两个专门的agent。Memory Manager负责学习执行结构化的记忆操作,包括添加、更新、删除或保持不变。Answer Agent则实现了一个\u0026quot;记忆蒸馏\u0026quot;策略,从检索到的大量记忆中筛选出最相关的部分用于回答。这两个agent都通过PPO和GRPO算法进行outcome-driven的强化学习训练。\nMemory-R1的数据效率很高,仅用152个问答对就能达到最优性能,因为它直接优化任务结果,不需要为每个记忆操作标注正确答案。论文中举了个生动的例子:用户先说\u0026quot;我养了只狗叫Buddy\u0026quot;,后来又说\u0026quot;我又养了只狗叫Scout\u0026quot;。普通系统会误判这是矛盾,执行删除再添加,导致第一条记忆丢失。而训练过的Memory-R1会执行更新操作,将两条信息整合为\u0026quot;养了两只狗,Buddy和Scout\u0026quot;。在LOCOMO基准上,Memory-R1的F1分数达到24.91,并且在LLaMA-3.1和Qwen-2.5等不同模型上都展现出良好的泛化能力。\n4.MEM1 这项工作提出了一个比较独特的视角:将记忆整合与推理统一起来,让agent在推理过程中自然地完成记忆管理。\nMEM1的一个创新点是把推理本身视为\u0026quot;工作记忆\u0026quot;。在与环境交互的过程中,agent的推理链会提取关键信息,构建对问题的演进理解。使用XML风格的标签来显式标注不同组件:用表示内部推理状态,表示向环境的查询,表示agent的回答,表示外部观察结果。通过迭代的状态更新,MEM1只保留最新的完整状态集合,旧的工具输出则可以丢弃(anthropic也有类似的上下文维护策略),有效减轻上下文增长压力。\n在训练时,MEM1使用多目标任务,相比担任训练，多任务训练需要更多轮次的交互才能实现收敛,从而迫使模型学会高效的记忆管理。实验结果显示,MEM1的token消耗比较稳定,不会随着任务复杂度增长而快速增长,推理速度也更快。这让它特别适合长期web导航、多跳问答等需要持续环境交互的场景。\n5. Memp Memp探索的是程序性记忆(procedural memory)在agent中的作用。程序性记忆在人类认知中指的是\u0026quot;如何做\u0026quot;的知识,比如骑自行车、打字等通过练习变得自动化的技能。\nMemp设计了一个三阶段的循环机制。首先是构建阶段,系统从agent过去的行为轨迹中提炼记忆,既可以存储为细粒度的逐步操作指令,也可以抽象为高层次的脚本式表示。然后是检索阶段,当面对新任务时,agent会搜索记忆库找出最相关的过去经验作为起点,这比随机尝试要高效得多。最后再是更新阶段,agent可以反思失败案例,(www.tvmkv.com)修正和修订原始记忆。\nMemp的更新策略很灵活,可以简单地添加新经验,也可以只保留成功案例,还可以反思失败进行修正。通过持续的更新、纠错和淘汰过时内容,记忆库会随着经验的积累而演化。另外Memp还发现记忆可以跨模型迁移:用GPT-4o生成的程序性记忆可以直接给Qwen2.5使用,显著提升小模型的性能，这对降低部署成本有一些启发意义。在TravelPlanner和ALFWorld任务上,随着记忆库的精炼,agent的成功率稳步提升,效率也明显优化。\n6.MemSearcher 这是最近一周国科大联合小红书刚发表的新工作，专门针对搜索agent的记忆管理问题，实现了agent推理，工具调用和记忆管理的联合优化。\n现阶段的搜索agent面临一个两难困境:如果把完整的交互历史都拼接到上下文中,虽然保持了信息完整性,但会产生又长又嘈杂的上下文,导致计算和内存成本飙升;如果只使用当前轮次的信息,虽然避免了开销,但丢失了关键的历史信息。这种权衡限制了搜索agent的可扩展性。这个问题前文也提到过，说明工业实践和学术研究都在研究解决这个问题。\nMemSearcher提出了一个比较有意思的解决方案:在多轮交互过程中迭代维护一个紧凑的记忆,并在每轮将它与当前问题结合。具体工作流程是,每一轮MemSearcher首先将用户问题与记忆融合,生成推理轨迹并执行搜索动作,然后根据新的观察结果更新记忆,只保留对解决任务至关重要的信息。这种设计让上下文长度在多轮交互中保持稳定,在不牺牲准确性的前提下提升了效率。\n为了优化这个工作流,研究者引入了multi-context GRPO,这是一个端到端的强化学习框架,能够联合优化MemSearcher的推理能力、搜索策略和记忆管理。Multi-context GRPO会在不同上下文下采样多组轨迹,并在所有对话中传播轨迹级别的优势信号。\n在性能表现上,MemSearcher使用与Search-R1相同的训练数据,在7个公开基准测试上取得了显著提升:基于Qwen2.5-3B的版本平均相对增益11%,基于Qwen2.5-7B的版本增益12%。还有一点值得注意,3B参数的MemSearcher甚至超过了7B参数的基线模型,这说明在信息完整性和效率之间找到了一个合适的平衡点,能够同时带来更高的准确率和更低的计算开销。\n结语 通过一段时间的项目实践和agent学术研究的学习，可以明显的感受agent已经成为构建数字生产力的重要技术路径，它的主要突破在于，本身贴合强化学习理念的\u0026quot;行动-反馈\u0026quot;机制与大模型的推理能力的配合使用，实现了“1+1\u0026gt;\u0026gt;2”的组合效应。在LLM持续推高推理能力，工具连接逐步完善的同时，agent还比较缺失的两个环节，一个是现实世界的应用，这需要依赖世界模型和具身智能的突破，另外一个就是记忆，人类的记忆是非常精妙的设计，回忆遗忘都是如此的自然，但是要实现这样高效、健壮的记忆，还有很长一段路要走，这不但是追求更高智能的基础需求，也是让agent在更广泛，更复杂任务场景发挥价值所必须的。\n原文链接: Agent记忆技术及落地实践 作者: sosoba\n","permalink":"http://testai666.cn/posts/agent%E8%AE%B0%E5%BF%86%E6%8A%80%E6%9C%AF%E5%8F%8A%E8%90%BD%E5%9C%B0%E5%AE%9E%E8%B7%B5/","summary":"\u003cp\u003e做记忆模块搭建的相关研发工作，一开始认为agent的记忆就是维护上下文，应该和chatbot的对话管理差不多，但真的做一段时间之后，才发现和对话管理相比还是区别比较大的，而且记忆模块对agent的实现效果以及后期可扩展性有比较重要的影响。\u003c/p\u003e\n\u003ch5 id=\"agent记忆与chatbot上下文的区别\"\u003e\u003cstrong\u003eagent记忆与chatbot上下文的区别\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003e我们拿agent记忆与上下文来作下比较，暂且不考虑长期记忆，就拿短期记忆来作对比，短期记忆(工作记忆)主要就是维护上下文，更官方一点的说法就是上下文工程，那chabot的对话管理不是也是在维护上下文吗？那两者有什么区别呢？\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/01bacdcac1138f90c7e861fd3b0907a8.png\"\u003e\u003c/p\u003e\n\u003ch6 id=\"目标\"\u003e\u003cstrong\u003e目标\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003eagent记忆和chatbot上下文的目标看上去是一致的:给LLM/agent添加合适的状态来做下一步的决策。但是从服务对象，组织形式和维护重点的扩展来看有比较大的区别。\u003c/p\u003e\n\u003ch6 id=\"服务对象\"\u003e\u003cstrong\u003e服务对象\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003e虽然两者都算是人机交互，拿服务的对象都可以算作是人，但从现阶段的主要技术实现来看，服务对象还是存在差异的。\u003c/p\u003e\n\u003cp\u003echatbot的服务对象是人这个毋庸置疑，做图灵测试也是为了验证对面和你说话的那个AI，有没有可能骗过你，而从让你以为它是个人，chatbot展开的多轮对话也是围绕人的实际需求展开。\u003c/p\u003e\n\u003cp\u003eagent虽然也存在与人的交互，agentic workflow中一般也会有人工输入的节点，但现阶段我们搭建agent的主要任务，更多时候是在自动完成一个复杂任务，因此服务对象可以认为是复杂任务。\u003c/p\u003e\n\u003ch6 id=\"组织形式\"\u003e\u003cstrong\u003e组织形式\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003e从组织形式上来看，由于agent具备了行动能力，所以和chabot产生了组织形式的不同。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/953d252c69328a99749f85bca9eb64ed.png\"\u003e\u003c/p\u003e\n\u003cp\u003echatbot主要的构成是一问一答的形式，包括我们调用大模型api一般也遵循这个结构，可以表示为{[q0,a0],[q1,a1],\u0026hellip;[qt,at]}。\u003c/p\u003e\n\u003cp\u003eagent记忆的构成以最常见的react模式为例，主要是动作(tool)和动作结果(tool_result)的时序集合，可以表示为{a0,o0,a1,01,\u0026hellip;at,ot}。\u003c/p\u003e\n\u003ch6 id=\"维护重点\"\u003e\u003cstrong\u003e维护重点\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003e对于记忆和上下文，很重要的一个功能是维护，但维护的侧重点也是有所不同的。\u003c/p\u003e\n\u003cp\u003echabot的上下文虽然也存在前后关联，不会每一轮都紧密联系，但会存在频繁的意图切换，如何理解意图并且实现有效的意图切换识别是上下文维护的重点。\u003c/p\u003e\n\u003cp\u003e而agent记忆的每一个步骤都是按迭代规划进行的，缺失某个步骤或跳过某个步骤都可能会导致任务失败，因此它的维护重点是如何在有限的窗口中记录对下一步决策产生影响的所需关键信息。\u003c/p\u003e\n\u003cp\u003e另外agent和chatbot还存在一个很大不同的点是的必要性。对于chatbot，长期记忆主要是记住个人信息和偏好用于个性化服务。而对于agent，长期记忆需要通过情景记忆来记住之前的迭代轨迹，这样不仅能通过检索的方式来获取与当前决策相关的上下文信息，还可以实现经验复用，在下一次遇到类似任务时，可以将之前的经验作为参考,马普所今年2月份发这篇论文时候就指出情景记忆是当前agent所缺少的[1]。\u003c/p\u003e\n\u003ch4 id=\"agent短期记忆-业界技术分享\"\u003e\u003cstrong\u003eagent短期记忆-业界技术分享\u003c/strong\u003e\u003c/h4\u003e\n\u003ch5 id=\"anthropic\"\u003e\u003cstrong\u003eanthropic\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003eanthropic提到agent在长程任务上的三种管理上下文方式[2]包括:压缩,结构化笔记和采用子agent架构。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/387d2755dd654931c7687697dd44f614.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e压缩\u003c/strong\u003e通过对上下文关键内容的提取，改写和无关内容删除实现上下文瘦身；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e结构化笔记\u003c/strong\u003e是把关键信息以结构化形式存入笔记，让agent可以阅读到关键信息；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e子agent架构\u003c/strong\u003e则是通过子agent维护自己的独立上下文再将关键结果返回主agent实现上下文解耦。\u003c/p\u003e\n\u003ch5 id=\"oepnai\"\u003e\u003cstrong\u003eoepnai\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003eopenai的短期记忆管理[3]则是围绕agent sdk来谈到管理短期记忆的两个主要方式:裁剪（trimming）和压缩（summarizing）。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e压缩\u003c/strong\u003e和anthropic的compact是一样的，裁剪就是把旧的历史信息直接剪掉。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003etrimming\u003c/strong\u003e的好处是实现比较简单，延时友好而且不会引入偏移信息（相比压缩），但缺点也显而易见，裁剪的内容可能包含当前决策需要的信息，所以一般适用于任务独立，上下文关联不大，低延迟要求场景。\u003c/p\u003e\n\u003ch5 id=\"langchain--manus\"\u003e\u003cstrong\u003elangchain \u0026amp; manus\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003elangchain与最近的一次交流[4]提到manus的三种上下文管理方式:reduce ，offload context和isolate context。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/0a91b18e5dcebee1f435e386854e5c9a.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003ereduce context\u003c/strong\u003e包括将工具执行结果进行压缩放在窗口内，而降完整结果保存在文件系统中，在上下文触发窗口限制时对整个轨迹进行整体压缩；\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003econtext isolate\u003c/strong\u003e类似于anthropic的sub agent方法，但细分为两种方式：对于简单任务main agent通过function call的方式将指令传递给子agent，子agent在自己的窗口执行任务；而对于复杂任务，子agent会和主agent共享上下文。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003econtext offloading\u003c/strong\u003e和anthropic的结构化笔记虽然都是将内容存入文件，但处理思路不一样。manus的方式是将大多数操作卸载到沙盒层，在沙盒中通过bash工具就可以实现多样化的文件读写操作，这样就可以把工具执行结果保存到文件，在需要时通过bash的grep等查询命令进行访问。另外manus的这个设计相当于把大部分先验知识文件化，让agent按需通过读取文件内容获取先验来指导行动，anthropic的skill实现应该也是类似的思路。\u003c/p\u003e\n\u003ch5 id=\"agent记忆-项目实践\"\u003e\u003cstrong\u003eagent记忆-项目实践\u003c/strong\u003e\u003c/h5\u003e\n\u003ch6 id=\"初版记忆实现\"\u003e\u003cstrong\u003e初版记忆实现\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003e在实际搭建agent时，工具模块，记忆模块，提示词模块是三个重要的基础模块。工具模块和提示词模块相对简单或者说比较成熟的方案，而记忆模块的搭建则有不同的设计思路和实现方式，但核心思路一致 - \u0026ldquo;给agent提供恰当的上下文\u0026rdquo;，但难就难在\u0026quot;恰当\u0026quot;，首先受限于LLM的上下文窗口，所以累计上下文就不能过长。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/bb300e7d10121e49b8504ca07022230d.png\"\u003e\u003c/p\u003e\n\u003cp\u003e另外上下文中堆积无关或者重复的内容不但会影响推理规划，也是不经济的。还有一个重要原因是上下文中的内容对于agent来说不是等权重的，对于当前决策而言，有的内容相对其它内容就会更重要，类似于需要一个attention机制来选择关键信息，那以什么形式来管理上下文获取更相信的信息加入就尤为关键了。\u003c/p\u003e\n\u003cp\u003e在近期的一个agent项目实践中，实现记忆模块的初始版本，设定的原则是\u0026quot;状态是记忆的投影\u0026quot;，以记忆作为事实的唯一来源，同时要求不在agent中维护状态，把记忆事实作为状态来源，放入agent的上下文中让agent自己进行决策。思路看上去是没有问题，但实际测试后就发现想简单了。\u003c/p\u003e\n\u003cp\u003e对于agent而言本身依赖的LLM是无状态的，所以agent也可以认为是无状态的，但是记忆的目的就是为了让agent具有状态。在初版的实现里，每一轮迭代都把obs写入工作记忆，同时读取当前轮之前的工作记忆中的结构化历史信息，把obs作为上下文唯一来源，直接注入到下一轮迭代的上下文，这样做是比较省事，但是一旦agent内部迭代出现问题，因为obs和工作记忆之间的这种耦合设计，很难追溯问题来源。\u003c/p\u003e\n\u003ch6 id=\"第二版记忆重构优化\"\u003e\u003cstrong\u003e第二版记忆重构优化\u003c/strong\u003e\u003c/h6\u003e\n\u003cp\u003e在发现第一版的问题后，开始进行第二版改进。第二版的改进主要是基于看到的相关的技术博客和论文，重新设计了记忆方案。首先记忆模块的目录设计应该把短期记忆和长期记忆做清晰分划分，维护当前agent迭代执行任务所需上下文的就是短期记忆(工作记忆)，而记录完整迭代过程的是长期记忆（情景记忆），产生对应产出的也是长期记忆(语义记忆)。其中比较容易混淆的是工作记忆和情景记忆，从持久化的角度来说，把工作记忆持久化就可以理解为情景记忆，但实际上是存在差别的。\u003c/p\u003e\n\u003cp\u003e对于一个agent的迭代轨迹{a0,o0,a1,o1,\u0026hellip;at,ot},可以直接把这个迭代轨迹放入上下文，也可以对这个迭代轨迹的每一步进行摘要提取，仅保留关键信息和引用，比如某步生成了一个很大的结构化，在进入下一轮时没有必要把整个json全部放入上下文，可以只放入一个引用，但是会把完整的json存入情景记忆，在下一轮迭代使用这个引用时再从情景记忆中取出这个对应的json，这样能避免大段内容直接进入到上下文中。\u003c/p\u003e\n\u003cp\u003e同时还需要考虑到上下文长度限制和成本问题，一般的工作记忆还会考虑使用最近k轮的策略，上下文只保留最近k轮的迭代记录，但是全局信息以结构化的信息记录，把这个结构化信息也注入到上下文中，这样也不至于让agent丢失掉全局信息。而第一版的问题就在这里，状态不应该只是记忆的投影，状态是对记忆的删减和加工，实际上的改进就是在短期记忆和长期记忆之间加一个桥接适配层，来实现短期记忆和长期记忆的搭配使用。\u003c/p\u003e\n\u003cp\u003e而为了agent项目的阶段性扩展和迭代，在前期工程化实现时就应该考虑长期记忆。在一个agent的迭代循环里，如果迭代轮数比较长，上下文窗口和LLM的上下文利用效率已经开始明显下降，就需要考虑结合长期记忆使用了。而为了缩减上下文，在删减掉比较早期的迭代信息后，需要识别出长期记忆中已保存的迭代记录中，哪些记忆内容会对当前轮决策有用，就选择加入到上下文。这个一般会使用retrival的方法，从长期记忆中进行检索。技术实现上会结合结构化和向量检索，而实际的实践需要根据场景需求去做设计和调整记忆存储和读写方式，可以使用slot，sql数据库，向量数据库，图数据库等等。\u003c/p\u003e\n\u003cp\u003e实践过程汇总有一个原则可以参考：记忆模块在开始实现之初，就应该把短期记忆和长期记忆进行解耦设计，这不但为上下文管理留有比较大的优化空间，在符合可审计性原则之外还为经验复用和未来可能的RL积累数据基础。\u003c/p\u003e\n\u003cp\u003e同时第二版的改进上，还有一个比较重要的改进点是还原了obs的单纯职责，obs只应该对当前轮的act和执行结果进行记录，不应该注入历史内容，obs保持职责单一只把当前轮的执行和执行结果(或摘要)放到工作记忆，工作记忆再去通过记忆模块补充历史上下文，这样修改之后，obs和工作记忆的职责也分的比较清晰了。\u003c/p\u003e\n\u003cp\u003e另外在agent的工层化实现过程中，造成可审计比较困难的一个点就是对工程化实现过程的不同组件的职责理解和划分不清晰，一旦出了bug，bug溯因就会因为这种不清晰的边界设计增加debug的难度，所以需要在设计之初就厘清不同模块的边界再动手进行实现。\u003c/p\u003e\n\u003ch5 id=\"agent记忆相关研究进展\"\u003e\u003cstrong\u003eagent记忆相关研究进展\u003c/strong\u003e\u003c/h5\u003e\n\u003cp\u003e上面提到RL微调，就再展开谈一下在agent在记忆管理方向的相关研究。agent记忆管理是一个很复杂的问题，从工程角度来看，agent的记忆管理可以分为两类技术方案，一种将复杂问题简化，另外一种是自动化。目前相关的记忆研究在这两个方向论文也比较多，找到了几篇比较有代表性的论文。前者是提供系统化/可插拔的记忆管理底座框架，如[5]，A-MEM[6]，后者是将手动/规则维护记忆的方式转为自动化，如Memory‑R1[7]，MEM1[8]，Memp[9]， MEMSearcher[10]等。\u003c/p\u003e","title":"Agent记忆技术及落地实践"},{"content":"(一) 测试工具（服务器） /etc/systemd/system/natapp.service\n编号 名称 版本 用途 工具状态 1 Unixbench 5.1.3 测试操作系统综合处理能力。 开源工具 2 Spec2006 2006 测试 CPU 性能。 商用软件 3 iozone 4.3.0 测试磁盘读写性能（Mb/s），包括随机、顺序读写、最大最小和平均读写速度等。 开源工具 4 Netperf 2.7.0 测试网络传输速率、网络吞吐率、网络响应时间等，包括 TCP、UDP 流吞吐速率、TCP 请求/响应、丢包率、误包率等。 开源工具 5 SPECjvm2008 2008 测试 JAVA 虚拟机的性能。 开源工具 6 LMbench 3.0 测试操作系统性能 开源工具 7 LTP LTP 20160510 测试系统稳定性 开源工具 8 Stream 5.09 测试内存带宽性能 开源工具 9 FIO 2.1.10 测试磁盘读写性能 开源工具 (2) uos deb http://mirrors.163.com/debian stable main 内核更新： apt install linux-image* linux-header* 卸载多余内核： apt remove linux-image-5.6.0-2-arm64 deb https://uos.deepin.cn/uos eagle main contrib non-free deb https://enterprise-packages.chinauos.com/server-enterprise fou/sp2 main contrib non-free apt-key adv \u0026ndash;keyserver keyserver.ubuntu.com \u0026ndash;recv-keys deb https://professional-store-packages.chinauos.com/appstore eagle appstore 麒麟v10源： deb http://archive.kylinos.cn/kylin/KYLIN-ALL 10.0 main restricted universe multiverse deb http://archive.kylinos.cn/kylin/partner juniper main 定制版uos： mkdir /tmp/livecd mount -o loop uos*.iso /tmp/livecd mkdir -p livecd/cd rsync --exclude=/live/filesystem.squashfs -a /tmp/livecd/ livecd/cd mkdir livecd/squashfs\u0026amp;\u0026amp;mkdir livecd/custom modprobe squashfs mount -t squashfs -o loop /tmp/livecd/live/filesystem.squashfs livecd/squashfs cp -a livecd/squashfs/* livecd/custom cp /etc/resolv.conf livecd/custom/etc/ cp /etc/apt/sources.list livecd/custom/etc/apt/sources.list chroot livecd/custom apt install xrdp htop gnome-disk* rm -rf livecd/custom/recovery* mksquashfs livecd/custom ./filesystem.squashfs \u0026amp;\u0026amp; chmod -R 777 ./*.squashfs uos无需激活即可root： nano /etc/pam.d/su #auth requisite deepin_security_verify.so 命令行激活试用：uos uos-activator-cmd -t 1 (3) unixbench wget http://github.itzmx.com/1265578519/unixbench/master/5.1.3/unixbench.sh chmod 777 *.sh \u0026amp;\u0026amp; ./unixbench.sh 多核的话改一下run文件，第 109,110,111,112 行修改数字为当前系统 CPU 核数，然后./Run -c 核数 nano Makefile ，47行 1GL_LIBS = -lGL -lXext -lX11 lm nano Run，111行 # \u0026#39;3d\u0026#39; =\u0026gt; { \u0026#39;name\u0026#39; =\u0026gt; \u0026#34;3D Graphics Benchmarks\u0026#34;,\u0026#39;maxCopies\u0026#39; =\u0026gt; 8 }, make all \u0026amp;\u0026amp; ./Run graphics 1 (4) iozone wget http://www.iozone.org/src/current/iozone3_487.tar tar -xvf iozone3_487.tar cd iozone3_487/src/current/ make linux ./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 8G | tee -a iozone.log ./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 16G | tee -a iozone.log ./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 32G | tee -a iozone.log wgethttps://gitee.com/haolixu/test/raw/master/iozone.sh\u0026amp;\u0026amp; bash iozone.sh (5) 远程桌面 apt install xrdp apt install gnome* echo “gnome-session \u0026ndash;session=gnome-classic” \u0026gt; ~/.xsession /etc/init.d/xrdp start 19.12.23更新！\napt install xdm*\n不用选xdm即可远程\n20.1.15更新！\napt install dde*\n可恢复deepin桌面！！\n(6) fio apt install fio* dd if=/dev/zero of=/root/test bs=4k count=524288 fio \u0026ndash;filename=/root/test -iodepth=64 -ioengine=libaio \u0026ndash;direct=1 \u0026ndash;rw=read \u0026ndash;bs=1m \u0026ndash;size=2g \u0026ndash;numjobs=4 \u0026ndash;runtime=10 \u0026ndash;group_reporting \u0026ndash;name=test-read fio -filename=/root/test -iodepth=64 -ioengine=libaio -direct=1 -rw=write -bs=1m -size=2g -numjobs=4 -runtime=20 -group_reporting -name=test-write fio -filename=/root/test -iodepth=64 -ioengine=libaio -direct=1 -rw=randread -bs=4k -size=2G -numjobs=64 -runtime=20 -group_reporting -name=test-rand-read fio -filename=/root/test -iodepth=64 -ioengine=libaio -direct=1 -rw=randwrite -bs=4k -size=2G -numjobs=64 -runtime=20 -group_reporting -name=test-rand-write wgethttps://gitee.com/haolixu/test/raw/master/fio.sh\u0026amp;\u0026amp; bash fio.sh (7) SPECjvm2008 wget http://spec.cs.miami.edu/downloads/osg/java/SPECjvm2008_1_01_setup.jar https://gitlab.com/haolixu/hello/-/raw/main/SPECjvm2008_1_01_setup.jar apt install openjdk-8-jre-headless update-alternatives \u0026ndash;config java nano /etc/profile JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-arm64/\nCLASSPATH=$JAVA_HOME/lib/tools.jar:/lib.dt.jar\nPATH=\nJ\rA\rV\rA\rH\rO\rM\rE\r/\rb\ri\rn\r:\rJAVA_HOME/bin:\rJAVAH​OME/bin:PATH\rexport JAVA_HOME CLASSPATH PATH\nsource /etc/profile java -jar SPECjvm2008_1_01_setup.jar -i console nano /etc/java-8-openjdk/accessibility.properties #assistive_technologies=org.GNOME.Accessibility.AtkWrapper\njava -jar SPECjvm2008.jar \u0026ndash;base java -jar SPECjvm2008.jar \u0026ndash;peak (8) LTP 麒麟v10 1231版本修改： nano ltp/testscripts/ltpstress.sh #250行 TOTALRAM=$(LC_ALL=C free -m | grep Mem: | awk {\u0026#39;print $2\u0026#39;}) TOTALSWAP=$(LC_ALL=C free -m | grep Swap: | awk {\u0026#39;print $2\u0026#39;}) TESTSWAP=$(($TOTALSWAP / 2)) if [ $TESTSWAP -eq 0 ]; then #if there is no swap in the system, use only the free RAM TESTMEM=$(LC_ALL=C free -m | grep Mem: | awk {\u0026#39;print $4\u0026#39;}) wget https://github.com/linux-test-project/ltp/releases/download/20190930/ltp-full-20190930.tar.bz2 cd ltp-full-20190930 apt install gcc g++ gfortran ./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; make install ./ltpstress.sh –n –t 12 –l /root/ltpstress.log The NFS related tests should fail because network stress was disabled,去掉这个 bug： ./ltpstress.sh –n –t 12 (9) web性能 http://html5test.com http://CSS3test.com https://www.browserbench.org/JetStream/ https://webkit.org/perf/sunspider-1.0.2/sunspider-1.0.2/driver.html (10) stream mkdir stream \u0026amp;\u0026amp; cd stream wget https://www.nersc.gov/assets/Trinity–NERSC-8-RFP/Benchmarks/Jan9/stream.tar tar -xvf stream.tar gcc -O2 -DSTREAM_ARRAY_SIZE=1000000000 stream.c -o stream ./stream (11) Lmbench wget http://106.54.231.176/tar/lmbench.tar tar -xvf lmbench.tar \u0026amp;\u0026amp; cd lmbench cd lmbench3/src \u0026amp;\u0026amp; make cd …/scripts \u0026amp;\u0026amp; ./config-run ./results make see | tee -a lmbench.log (12) Netperf，iperf wget http://106.54.231.176/tar/netperf-2.7.0.tar tar -xvf netperf-2.7.0.tar \u0026amp;\u0026amp; cd netperf-netperf-2.7.0 ./configure \u0026amp;\u0026amp; make \u0026amp;\u0026amp; make install netserver netperf -H 192.168.4.11 -l 60 -t TCP_STREAM | tee -a netperf.log 测延迟： ./cepreiperf-ARM 192.168.124.13 tcp_lat ./cepreiperf-ARM 192.168.124.13 udp_lat (13) 图形稳定性测试 apt install mesa-utils (14) 宝德OS！！！ 安装文件管理器，图片查看器： apt-get install nautilus gpicview 网络连接图标异常： nano /etc/NetworkManager/NetworkManager.conf managed=true 更改grub壁纸和开机logo： apt install plymouth plymouth-themes cd /usr/share/desktop-base/futureprototype-theme/ cd /usr/share/plymouth/themes/futureprototype plymouth-set-default-theme -R futureprototype 更改桌面壁纸，对应用户执行： gsettings set org.gnome.desktop.background picture-uri \u0026#39;file:///baode/Flowerbed.png\u0026#39; 安装桌面： apt-get install gnome-shell gnome-panel gnome-menus gnome-session gdm3 apt-get install kde-plasma-desktop apt install x-window-system-core gnome-core apt install lightdm apt install sddm apt install cinnamon apt install lxde 更改登陆界面logo： nano /etc/gdm3/greeter.dconf-defaults [org/gnome/login-screen] logo=\u0026#39;/baode/logo.png\u0026#39; dpkg-reconfigure gdm3 添加自动登陆（只有gdm）： nano /etc/gdm3/daemon.conf AutomaticLogin = guest 安装其他终端： apt install xfce4-terminal 安装程序： apt install calamares-settings-debian 添加桌面图标： apt install nemo nano ~/.config/autostart/nemo.desktop [Desktop Entry] Type=Application Name=Desktop Icons Exec=nemo-desktop OnlyShowIn=GNOME; NoDisplay=true X-GNOME-Autostart-Phase=Desktop X-GNOME-Autostart-Notify=true X-GNOME-AutoRestart=true X-GNOME-Provides=filemanager gsettings set org.nemo.desktop show-desktop-icons true 如果执行不了，root下执行su -重新运行。 live cd： apt-get install -y \\ debootstrap \\ squashfs-tools \\ xorriso \\ grub-efi-arm64-bin \\ mtools debootstrap \\ --arch=arm64 \\ --variant=minbase \\ stable \\ /home/guest/LIVE_BOOT/chroot \\ http://mirrors.163.com/debian chroot /home/guest/LIVE_BOOT/chroot echo \u0026#34;baodeOS\u0026#34; \u0026gt;/etc/hostname apt-get install live-boot systemd-sysv sudo vim iputils-ping nano x-window-system-core gnome-core plymouth plymouth-themes linux-image-arm64 linux-headers-arm64 adduser user nano /etc/sudoers user ALL=(ALL) NOPASSWD:ALL nano /etc/lightdm/lightdm.conf autologin-user=user exit chroot mksquashfs LIVE_BOOT/chroot/ ./filesystem.squashfs \u0026amp;\u0026amp; chmod -R 777 ./*.squashfs mkdir -vp /home/guest/LIVE_BOOT/{scratch,image/live} mksquashfs \\ /home/guest/LIVE_BOOT/chroot \\ /home/guest/LIVE_BOOT/image/live/os.squashfs \\ -e boot cp -v /home/guest/LIVE_BOOT/chroot/boot/vmlinuz-* /home/guest/LIVE_BOOT/image/vmlinuz cp -v /home/guest/LIVE_BOOT/chroot/boot/initrd.img-* /home/guest/LIVE_BOOT/image/initrd nano /home/guest/LIVE_BOOT/image/DEBIAN_CUSTOM nano /home/guest/LIVE_BOOT/scratch/grub.cfg insmod all_video set default=\u0026#34;0\u0026#34; set timeout=\u0026#34;30\u0026#34; menuentry \u0026#34;baodeOS-installer\u0026#34;\u0026#34; { search --set=root --no-floppy --file /DEBIAN_CUSTOM linzux /vmlinuz boot=live quiet nomodeset initrd /initrd } grub-mkstandalone \\ --format=arm64-efi \\ --output=/home/guest/LIVE_BOOT/scratch/grubaa64.efi \\ --locales=\u0026#34;\u0026#34; \\ --fonts=\u0026#34;\u0026#34; \\ \u0026#34;boot/grub/grub.cfg=/home/guest/LIVE_BOOT/scratch/grub.cfg\u0026#34; cd /home/guest/LIVE_BOOT/scratch dd if=/dev/zero of=efiboot.img bs=1M count=10 mkfs.vfat efiboot.img mmd -i efiboot.img efi efi/boot mcopy -i efiboot.img ./grubaa64.efi ::efi/boot/ xorriso \\ -as mkisofs \\ -iso-level 3 \\ -full-iso9660-filenames \\ -volid \u0026#34;baodeOS_CUSTOM\u0026#34; \\ -eltorito-alt-boot \\ -e EFI/efiboot.img \\ -no-emul-boot \\ -append_partition 2 0xef /home/guest/LIVE_BOOT/scratch/efiboot.img \\ -output \u0026#34;/home/guest/baodeOS-installer.iso\u0026#34; \\ -graft-points \\ \u0026#34;/home/guest/LIVE_BOOT/image\u0026#34; \\ /EFI/efiboot.img=/home/guest/LIVE_BOOT/scratch/efiboot.img dd if=./baodeOS-installer.iso of=/dev/sdb bs=4M 一些有用的： https://bitbucket.org/chandrakantsingh/ https://www.linux.com/topic/distributions/aryalinux-distribution-and-platform/ https://www.linuxprobe.com/aryalinux-base-lfs.html https://blog.csdn.net/weixin_41217917/article/details/80757652?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-14\u0026amp;spm=1001.2101.3001.4242 http://wiki.linuxfromscratch.org/lfs https://github.com/marco-buratto/lamponepi.qemubox live-build： 自动配置脚本目录：auto/config 创建镜像时安装的软件所在目录：config/package-lists/ 自定义镜像内容目录：config/includes.chroot/ 创建系统后执行的脚本所在目录：config/hooks/ 自定义引导程序目录：config/bootloaders/ 重新打包ISO： xorriso -as mkisofs -r -checksum_algorithm_iso md5,sha1 -V \u0026#39;Ubuntu-Server 16.04.3 LTS arm64\u0026#39; \\ -o ./new-ubuntu-16.04.3-server-arm64.iso -J -joliet-long -cache-inodes -e boot/grub/efi.img \\ -no-emul-boot -append_partition 2 0xef ubuntu/boot/grub/efi.img -partition_cyl_align all ubuntu/ 1 1 (15) 常用 hostnamectl set-hostname 取消32位支持 dpkg \u0026ndash;remove-architecture i386\nfind / -name 还原系统后解决开机慢的问题 sed -ri ‘s/.swap./#\u0026amp;/’ /etc/fstab\nrm /etc/mdadm/mdadm.conf\nupdate-initramfs -u\nwin10下面打开powershell输入tar -czvf .\\jdk7.tar.gz ./jdk7-server-release-1708 查看温度，AMD显卡： apt-get install lm-sensors sensors-applet \u0026amp;\u0026amp; watch sensors apt install radeontop \u0026amp;\u0026amp; radeontop apt install hddtemp \u0026amp;\u0026amp; hddtemp SATA:/dev/sda 让nano显示行号： nano ~/.nanorc\nset linenumbers\n让root有颜色 PS1=\u0026quot;[\\u@\\h \\w]$ \u0026quot;\n让root可以命令行打开图形化： cp /home/guest/.Xauthority /root/ 命令行连WiFi： nmcli device wifi connect wifiname password wifipasswd 新系统安装： wget ://gitee.com/haolixu//raw/master/.vimrc \u0026amp;\u0026amp; mv -b .vimrc /root\nwget https://gitee.com/haolixu/test/raw/master/motd \u0026amp;\u0026amp; mv -b motd /etc\nwget https://gitee.com/haolixu/test/raw/master/.bashrc \u0026amp;\u0026amp; mv -b .bashrc /root\n谷歌搜索： https://go.readmorejoy.com/search/ 查看系统信息，硬盘速率： apt install inxi dmesg | grep SATA |head -n 1 修改密码安全等级（降低） apt-get install libpam-cracklib nano /etc/pam.d/common-password apt之后直接就可以设置了，nano可以提供安全等级 GitHub中国专用： github.com.cnpmjs.org hub.fastgit.org 程序后台执行：nohup sh test.sh \u0026amp; 建立文件夹软连接，强制覆盖： ln -sfn /var/lib/docker/ lixu666/ 设置默认启动界面： systemctl set-default multi-user.target #字符 systemctl set-default graphical.target #图形 查看开机启动的程序 systemctl list-unit-files | grep enable 1 (16) wine https://www.novaspirit.com/2019/04/15/run-x86-arm/\n2.6更新\napt-get install qemu qemu-user qemu-user-static binfmt-support debootstrap binutils\ndebootstrap \u0026ndash;foreign \u0026ndash;arch i386 stable ./chroot-stretch-i386 http://.163.com/debian\nmount -t sysfs sys ./chroot-stretch-i386/sys/\nmount -t proc proc ./chroot-stretch-i386/proc/\nmount \u0026ndash;bind /dev ./chroot-stretch-i386/dev/\nmount \u0026ndash;bind /dev/pts ./chroot-stretch-i386/dev/pts/\nmount \u0026ndash;bind /dev/shm ./chroot-stretch-i386/dev/shm/\ncp /usr/bin/qemu-i386-static ./chroot-stretch-i386/usr/bin/\nchroot ./chroot-stretch-i386/ /debootstrap/debootstrap \u0026ndash;second-stage\nchroot ./chroot-stretch-i386/ /bin/su -l root\nadduser -uid 1000 test\napt install leafpad ca-certificates dirmngr sudo bzip2 zenity curl\nchroot ./chroot-stretch-i386/ /bin/su -l root\ntar -jxf PlayOnLinux-wine-3.9-linux-x86.pol \u0026ndash;strip-components=1\nmv ./3.9/bin/wine-preloader ./3.9/bin/wine-preloader.renamed\nmv ./3.9 /opt/wine-3.9/\necho PATH=/opt/wine-3.9/bin/:$PATH \u0026gt;\u0026gt; ~/.bashrc\nsource ./.bashrc\nchroot /home/test/Desktop/chroot-stretch-i386/ /bin/su -l test\necho PATH=/opt/wine-3.9/bin/:$PATH \u0026gt;\u0026gt; ~/.bashrc\nsource ./.bashrc\nwinecfg\nwget http://security..com/ubuntu/pool/main/libp/libpng/libpng12-0_1.2.46-3ubuntu4.2_i386.deb\ndpkg -i libpng12-0_1.2.46-3ubuntu4.2_i386.deb\ngit clone https://gitee.com/AN_drew/-ubuntu.git\ncd deepin-wine-ubuntu \u0026amp;\u0026amp; ./install.sh\nwine必装：\nwget https://mirrors.ustc.edu.cn/deepin/pool/non-free/d/deepin-wine-plugin/deepin-wine-plugin_1.0deepin2_i386.deb\nwget https://mirrors.ustc.edu.cn/deepin/pool/non-free/d/deepin-wine-plugin-virtual/deepin-wine-plugin-virtual_1.0deepin3_all.deb\napt install -f\nwget https://mirrors.aliyun.com/deepin/pool/non-free/d/deepin-wine-helper/deepin-wine-helper_1.2deepin8_i386.deb\napt install -f\n微信：\nwget https://mirrors.ustc.edu.cn/deepin/pool/non-free/d/deepin.com.wechat/deepin.com.wechat_2.6.8.65deepin0_i386.deb\n2.12更新！\ndebootstrap \u0026ndash;foreign \u0026ndash;arch arm64 bionic ./arm64-ubuntu http://mirrors.aliyun.com/ubuntu-ports\ndeb [by-hash=force] http://packages.deepin.com/deepin lion main contrib non-free\n3.13.更新！\nwget https://qd.myapp.com/myapp/qqteam/pcqq/PCQQ2020.exe\n3.30更新！\nexport DISPLAY=:0\nexport DISPLAY=:1\n(17)虚拟化（ ，kvm） uos原生可安装docker，但是无法安装镜像，需更新内核到5.4。 docker安装图形化管理： apt install curl gnupg2 ca-certificates apt-transport-https software-properties-common\ncurl -sSL https://get.docker.com | sh\ndocker \u0026ndash;version\n#国内源参考big教程\ndocker pull /portainer\ndocker volume create portainer_data\ndocker run -d -p 9000:9000 \u0026ndash;name portainer \u0026ndash;restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer\n127.0.0.1:9000\ndocker安装ubuntu docker pull ubuntu\ndocker run -it -p 80:80 ubuntu /bin/bash\ndocker ps -a\ndocker start (CONTAINER ID）\ndocker attach (CONTAINER ID）\n安装kvm wget https://gitee.com/haolixu/test/raw/master/kvm.sh \u0026amp;\u0026amp; bash kvm.sh apt install virtinst python-libvirt virt-viewer virt-manager bridge-utils uml-utilities ovmf qemu-efi libvirt-daemon-system qemu qemu-system qemu-system-arm qemu-system-common qemu-user qemu-utils qemu-kvm #yum install kvm virt-manager libvirt libvirt-python python-virtinst libvirt-client qemu-kvm qemu-img modprobe kvm #加载kvm内核模块，vmare虚拟机需要开启虚拟化支持 systemctl status libvirtd virsh list --all #上两步验证是否安装成功 1 (18) 内网穿透（web服务器) http://download.natapp.cn/assets/downloads/clients/2_3_9/natapp_linux_arm64_2_3_9.zip unzip natapp_linux_arm64_2_3_9.zip chmod 777 natapp \u0026amp;\u0026amp;./natapp -authtoken=8582fece4a62980c wget https://gitee.com/haolixu/test/raw/master/natapp.sh \u0026amp;\u0026amp;./natapp -authtoken=8582fece4a62980c 检查有无公网IP： TRACERT.EXE baidu.com\nweb服务器设置： apt install apache2\niptables -I INPUT -p tcp \u0026ndash;dport 3389 -j ACCEPT\nnano /etc/apache2/ports.conf\nservice apache2 restart\nservice apache2 status\napt install php7.3 php-mbstring php-pdo php-curl php-mysql mariadb-server-10.3 php-gd php-zip php-xml*\nmysqladmin -u root -p password #这个有效\nnano /etc/mysql/mariadb.conf.d/50-server.cnf\n#bind-address = 127.0.0.1\nport = 3389\nmysql -u root -p\nGRANT ALL PRIVILEGES ON . TO ‘root’@‘%’ IDENTIFIED BY ‘密码’ WITH GRANT OPTION;\nFLUSH PRIVILEGES;\n#赋予root权限并立即生效\nservice mysql restart\nmysql -u root -p这里设置密码 -h 192.168.20.12 -P 3389\n远程连接：\nuse mysql\nupdate user set host=‘%’ where user =‘root’;\nFLUSH PRIVILEGES;\nALTER USER ‘root’@‘%’ IDENTIFIED WITH mysql_native_password BY ‘1’;\nFLUSH PRIVILEGES;\n1 (18) BMC 通过BMC命令行 ipmcset -t precisealarm -d -v 0x0D000009 1 assert\nipmcset -t precisealarm -d mock -v 0x0D000009 1 deassert\nipmcset -t precisealarm -d mock -v 0x0D000003 1 assert\nipmcset -t precisealarm -d mock -v 0x0D000003 1 deassert\n查看面板led指示灯及登录BMC web首页，查看“告警与事件-当前告警”。\n通过ipmitool命令行chassis power status查询单板业务平面的上下电状态，执行单板上电，下电，复位命令： ipmitool -H ... -I lanplus -U \u0026lt;用户名\u0026gt; -P \u0026lt;密码\u0026gt; chassis power on\nipmitool -H ... -I lanplus -U \u0026lt;用户名\u0026gt; -P \u0026lt;密码\u0026gt; chassis power off\nipmitool -H ... -I lanplus -U \u0026lt;用户名\u0026gt; -P \u0026lt;密码\u0026gt; chassis power reset\n查询系统当前功率及所有传感器的信息 ipmitool -I lanplus -H -U \u0026lt;BMC账号\u0026gt; -P \u0026lt;密码\u0026gt; raw 0x30 0x93 0xdb 0x07 0x00 0x11 0x00\nipmitool -I lanplus -L administrator -H -U \u0026lt;BMC账号\u0026gt; -P \u0026lt;密码\u0026gt; sensor\n打开KVM，勾选“本地文件夹”，点击浏览并确定，点击连接。 (19) 天翼网关，云盘 超级管理员账户 telecomadmin 密码 nE7jA%5m 查看WiFi： wpa_cli -iwlan0 status wpa_cli -iwlan0 scan wpa_cli -iwlan0 scan_results ifconfig wlan0 up dhclient wlan0 Linux使用云盘： wget https://raw.fastgit.org/haolixu/Temporary-image-backup/master/cloudpan189-go-v0.0.9-linux-arm64.zip unzip cloudpan189-go-v0.0.9-linux-arm64.zip cd cloudpan189-go-v0.0.9-linux-arm64/ \u0026amp;\u0026amp; ./cloudpan189-go login #登录 sign #签到 ls \u0026amp;\u0026amp; cd 来自家庭共享/ download gcc9.3.tar.gz #下载 upload /root/postgresq-lixu666.py ./ 1 (20) C++ string必须加头文件\u0026lt;string.h\u0026gt; 值传递形参不改变实参，地址传递会改变。如果不想修改主函数中的数据，用值传递。 用指针可以节约内存空间。 (21) Git 远程拉项目及上传更新项目： mkdir git-lixu666 \u0026amp;\u0026amp; cd git-lixu666 \u0026amp;\u0026amp; git init git config --global user.name \u0026#34;haolixu\u0026#34; git config --global user.email \u0026#34;1300311464@qq.com\u0026#34; git remote add origin https://gitee.com/haolixu/test.git git pull origin master git commit -m \u0026#34;lixu666\u0026#34; git push -u origin master 设置代理: git config http.proxy http://127.0.0.1:7890 只下载最新版本，不下载历史版本： git clone git仓库地址 --depth=1 1 原文链接: 服务器测试整理 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%B5%8B%E8%AF%95%E6%95%B4%E7%90%86/","summary":"\u003ch3 id=\"一-测试工具服务器\"\u003e(一)   测试工具（服务器）\u003c/h3\u003e\n\u003cp\u003e/etc/systemd/system/natapp.service\u003c/p\u003e\n\u003ctable\u003e\n  \u003cthead\u003e\n      \u003ctr\u003e\n          \u003cth\u003e编号\u003c/th\u003e\n          \u003cth\u003e名称\u003c/th\u003e\n          \u003cth\u003e版本\u003c/th\u003e\n          \u003cth\u003e用途\u003c/th\u003e\n          \u003cth\u003e工具状态\u003c/th\u003e\n      \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e1\u003c/td\u003e\n          \u003ctd\u003eUnixbench\u003c/td\u003e\n          \u003ctd\u003e5.1.3\u003c/td\u003e\n          \u003ctd\u003e测试操作系统综合处理能力。\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e2\u003c/td\u003e\n          \u003ctd\u003eSpec2006\u003c/td\u003e\n          \u003ctd\u003e2006\u003c/td\u003e\n          \u003ctd\u003e测试 CPU 性能。\u003c/td\u003e\n          \u003ctd\u003e商用软件\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e3\u003c/td\u003e\n          \u003ctd\u003eiozone\u003c/td\u003e\n          \u003ctd\u003e4.3.0\u003c/td\u003e\n          \u003ctd\u003e测试磁盘读写性能（Mb/s），包括随机、顺序读写、最大最小和平均读写速度等。\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e4\u003c/td\u003e\n          \u003ctd\u003eNetperf\u003c/td\u003e\n          \u003ctd\u003e2.7.0\u003c/td\u003e\n          \u003ctd\u003e测试网络传输速率、网络吞吐率、网络响应时间等，包括 TCP、UDP 流吞吐速率、TCP 请求/响应、丢包率、误包率等。\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e5\u003c/td\u003e\n          \u003ctd\u003eSPECjvm2008\u003c/td\u003e\n          \u003ctd\u003e2008\u003c/td\u003e\n          \u003ctd\u003e测试 JAVA 虚拟机的性能。\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e6\u003c/td\u003e\n          \u003ctd\u003eLMbench\u003c/td\u003e\n          \u003ctd\u003e3.0\u003c/td\u003e\n          \u003ctd\u003e测试操作系统性能\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e7\u003c/td\u003e\n          \u003ctd\u003eLTP\u003c/td\u003e\n          \u003ctd\u003eLTP 20160510\u003c/td\u003e\n          \u003ctd\u003e测试系统稳定性\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e8\u003c/td\u003e\n          \u003ctd\u003eStream\u003c/td\u003e\n          \u003ctd\u003e5.09\u003c/td\u003e\n          \u003ctd\u003e测试内存带宽性能\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n      \u003ctr\u003e\n          \u003ctd\u003e9\u003c/td\u003e\n          \u003ctd\u003eFIO\u003c/td\u003e\n          \u003ctd\u003e2.1.10\u003c/td\u003e\n          \u003ctd\u003e测试磁盘读写性能\u003c/td\u003e\n          \u003ctd\u003e开源工具\u003c/td\u003e\n      \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"2-uos\"\u003e(2)   uos\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003edeb \u003ca href=\"http://mirrors.163.com/debian\"\u003ehttp://mirrors.163.com/debian\u003c/a\u003e stable main\u003c/li\u003e\n\u003cli\u003e内核更新：\u003c/li\u003e\n\u003cli\u003eapt install linux-image* linux-header*\u003c/li\u003e\n\u003cli\u003e卸载多余内核：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt remove linux-image-5.6.0-2-arm64\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003edeb \u003ca href=\"https://uos.deepin.cn/uos\"\u003ehttps://uos.deepin.cn/uos\u003c/a\u003e eagle main contrib non-free\u003c/li\u003e\n\u003cli\u003edeb \u003ca href=\"https://enterprise-packages.chinauos.com/server-enterprise\"\u003ehttps://enterprise-packages.chinauos.com/server-enterprise\u003c/a\u003e fou/sp2 main contrib non-free\u003c/li\u003e\n\u003cli\u003eapt-key adv \u0026ndash;keyserver keyserver.ubuntu.com \u0026ndash;recv-keys\u003c/li\u003e\n\u003cli\u003edeb \u003ca href=\"https://professional-store-packages.chinauos.com/appstore\"\u003ehttps://professional-store-packages.chinauos.com/appstore\u003c/a\u003e eagle appstore\u003c/li\u003e\n\u003cli\u003e麒麟v10源：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeb http://archive.kylinos.cn/kylin/KYLIN-ALL 10.0 main restricted universe multiverse\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003edeb http://archive.kylinos.cn/kylin/partner juniper main\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003e定制版uos：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-dockerfile\" data-lang=\"dockerfile\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir /tmp/livecd\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emount -o loop uos*.iso /tmp/livecd\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir -p livecd/cd\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ersync --exclude\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e/live/filesystem.squashfs -a /tmp/livecd/ livecd/cd\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir livecd/squashfs\u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003emkdir livecd/custom\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodprobe squashfs\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emount -t squashfs -o loop /tmp/livecd/live/filesystem.squashfs livecd/squashfs\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp -a livecd/squashfs/* livecd/custom\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp /etc/resolv.conf livecd/custom/etc/\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecp /etc/apt/sources.list livecd/custom/etc/apt/sources.list\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003echroot livecd/custom apt install xrdp htop gnome-disk*\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003erm -rf livecd/custom/recovery*\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emksquashfs livecd/custom ./filesystem.squashfs \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e chmod -R \u003cspan style=\"color:#ae81ff\"\u003e777\u003c/span\u003e ./*.squashfs\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003euos无需激活即可root：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enano /etc/pam.d/su\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e#auth    requisite           deepin_security_verify.so\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003e命令行激活试用：uos\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003euos-activator-cmd -t\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003e1\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"3-unixbench\"\u003e(3) unixbench\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003ewget \u003ca href=\"http://github.itzmx.com/1265578519/unixbench/master/5.1.3/unixbench.sh\"\u003ehttp://github.itzmx.com/1265578519/unixbench/master/5.1.3/unixbench.sh\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003echmod 777 *.sh \u0026amp;\u0026amp; ./unixbench.sh\u003c/li\u003e\n\u003cli\u003e多核的话改一下run文件，第 109,110,111,112 行修改数字为当前系统 CPU 核数，然后./Run -c 核数\u003c/li\u003e\n\u003cli\u003enano Makefile ，47行\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1GL_LIBS = -lGL -lXext -lX11 lm\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003enano Run，111行\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-plain\" data-lang=\"plain\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e# \u0026#39;3d\u0026#39;  =\u0026gt; { \u0026#39;name\u0026#39; =\u0026gt; \u0026#34;3D Graphics Benchmarks\u0026#34;,\u0026#39;maxCopies\u0026#39; =\u0026gt; 8 },\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col\u003e\n\u003cli\u003emake all \u0026amp;\u0026amp; ./Run graphics\u003c/li\u003e\n\u003cli\u003e1\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"4iozone\"\u003e(4) iozone\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003ewget \u003ca href=\"http://www.iozone.org/src/current/iozone3_487.tar\"\u003ehttp://www.iozone.org/src/current/iozone3_487.tar\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003etar -xvf iozone3_487.tar\u003c/li\u003e\n\u003cli\u003ecd iozone3_487/src/current/\u003c/li\u003e\n\u003cli\u003emake linux\u003c/li\u003e\n\u003cli\u003e./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 8G | tee -a iozone.log\u003c/li\u003e\n\u003cli\u003e./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 16G | tee -a iozone.log\u003c/li\u003e\n\u003cli\u003e./iozone -a -i 0 -i 1 -i 2 -f /mnt/testfile -r 16m -s 32G | tee -a iozone.log\u003c/li\u003e\n\u003cli\u003ewget\u003ca href=\"https://gitee.com/haolixu/test/raw/master/fio.sh\"\u003ehttps://gitee.com/haolixu/test/raw/master/iozone.sh\u003c/a\u003e\u0026amp;\u0026amp; bash iozone.sh\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"5远程桌面\"\u003e(5) 远程桌面\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eapt install xrdp\u003c/li\u003e\n\u003cli\u003eapt install gnome*\u003c/li\u003e\n\u003cli\u003eecho “gnome-session \u0026ndash;session=gnome-classic” \u0026gt; ~/.xsession\u003c/li\u003e\n\u003cli\u003e/etc/init.d/xrdp start\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e19.12.23更新！\u003c/p\u003e","title":"服务器测试整理"},{"content":"在接口测试中实现“自动解析文档→生成用例”的核心是通过**“文档结构化解析→元数据提取→场景规则映射→用例自动化编排”**的全流程自动化，结合的语义理解与测试领域知识，将非结构化的接口文档转化为覆盖全面、可直接执行的测试用例。\n一、整体流程框架 整个过程分为4个核心阶段，由“接口解析Agent”和“用例生成Agent”协同完成，依赖“文档同步模块”“元数据存储模块”“规则引擎”三大支撑组件：\ngraph TD\rsubgraph 准备阶段\rA[接口文档同步] --\u0026gt; A1[对接Swagger/OpenAPI]\rA --\u0026gt; A2[监听文档变更（Webhook）]\rA --\u0026gt; A3[历史文档版本管理]\rend\rsubgraph 解析阶段（接口解析Agent）\rB[元数据提取] --\u0026gt; B1[基础信息：路径/方法/描述]\rB --\u0026gt; B2[参数信息：类型/约束/位置]\rB --\u0026gt; B3[响应信息：状态码/schema/字段]\rB --\u0026gt; B4[依赖关系：前置接口/动态参数]\rend\rsubgraph 生成阶段（用例生成Agent）\rC[场景规则映射] --\u0026gt; C1[正向场景：合法参数组合]\rC --\u0026gt; C2[异常场景：参数校验失败]\rC --\u0026gt; C3[边界场景：临界值/极限值]\rC --\u0026gt; C4[依赖场景：接口调用链]\rend\rsubgraph 输出阶段\rD[用例格式化] --\u0026gt; D1[Postman/JMeter格式]\rD --\u0026gt; D2[pytest脚本格式]\rD --\u0026gt; D3[测试管理工具格式（TestRail）]\rend 二、具体步骤与技术实现 阶段1：同步——确保解析源实时准确 目标：获取最新接口文档（如/OpenAPI），并监控变更以触发后续解析流程。\n具体操作：\n文档接入方式： 主动拉取：通过定时任务（如每小时）调用接口文档地址（如https://api.example.com/v3/api-docs），获取JSON格式的Swagger文档； 被动接收：配置Git Webhook，当接口文档代码（如后端项目的Swagger配置）提交时，自动推送最新文档至Agent系统。 文档版本管理： 将每次获取的文档存储在版本库（如Git），标记版本号（如v2.3.1），便于后续对比变更（如参数新增/删除）。 阶段2：元（接口解析Agent核心工作） 目标：从接口文档中提取结构化元数据（基础信息、参数、响应、依赖），为用例生成提供“原材料”。\n技术实现：\n接口解析Agent通过解析Swagger/OpenAPI的JSON结构，提取以下核心信息（以Swagger 3.0为例）：\n2.1 基础信息提取 提取接口的路径、请求方法、功能描述等基础信息：\n# 解析基础信息代码片段 def extract_basic_info(swagger_path, swagger_method): \u0026#34;\u0026#34;\u0026#34; swagger_path: 接口路径（如\u0026#34;/api/order\u0026#34;） swagger_method: 接口方法（如\u0026#34;post\u0026#34;） \u0026#34;\u0026#34;\u0026#34; method_details = swagger_path[swagger_method] return { \u0026#34;path\u0026#34;: swagger_path, # 接口路径 \u0026#34;method\u0026#34;: swagger_method.upper(), # 请求方法（GET/POST等） \u0026#34;description\u0026#34;: method_details.get(\u0026#34;description\u0026#34;, \u0026#34;\u0026#34;), # 功能描述 \u0026#34;tags\u0026#34;: method_details.get(\u0026#34;tags\u0026#34;, []), # 所属模块（如\u0026#34;订单管理\u0026#34;） \u0026#34;operation_id\u0026#34;: method_details.get(\u0026#34;operationId\u0026#34;, \u0026#34;\u0026#34;) # 唯一标识 } 2.2 参数信息提取 参数是的核心，需提取参数位置、类型、约束规则（非空、长度、枚举等），支持路径参数、查询参数、请求体（body）等多种类型：\n# 解析参数信息代码片段 def extract_parameters(method_details): parameters = [] # 1. 提取路径参数、查询参数（在\u0026#34;parameters\u0026#34;字段中） for param in method_details.get(\u0026#34;parameters\u0026#34;, []): param_info = { \u0026#34;name\u0026#34;: param[\u0026#34;name\u0026#34;], # 参数名 \u0026#34;in\u0026#34;: param[\u0026#34;in\u0026#34;], # 位置：path/query/header \u0026#34;type\u0026#34;: param[\u0026#34;schema\u0026#34;][\u0026#34;type\u0026#34;], # 类型：string/number/integer等 \u0026#34;required\u0026#34;: param.get(\u0026#34;required\u0026#34;, False), # 是否必填 \u0026#34;constraints\u0026#34;: {} # 约束规则 } # 提取约束规则（如长度、范围、枚举） if \u0026#34;minLength\u0026#34; in param[\u0026#34;schema\u0026#34;]: param_info[\u0026#34;constraints\u0026#34;][\u0026#34;minLength\u0026#34;] = param[\u0026#34;schema\u0026#34;][\u0026#34;minLength\u0026#34;] if \u0026#34;maxLength\u0026#34; in param[\u0026#34;schema\u0026#34;]: param_info[\u0026#34;constraints\u0026#34;][\u0026#34;maxLength\u0026#34;] = param[\u0026#34;schema\u0026#34;][\u0026#34;maxLength\u0026#34;] if \u0026#34;enum\u0026#34; in param[\u0026#34;schema\u0026#34;]: param_info[\u0026#34;constraints\u0026#34;][\u0026#34;enum\u0026#34;] = param[\u0026#34;schema\u0026#34;][\u0026#34;enum\u0026#34;] if \u0026#34;minimum\u0026#34; in param[\u0026#34;schema\u0026#34;]: param_info[\u0026#34;constraints\u0026#34;][\u0026#34;minimum\u0026#34;] = param[\u0026#34;schema\u0026#34;][\u0026#34;minimum\u0026#34;] parameters.append(param_info) # 2. 提取请求体参数（在\u0026#34;requestBody\u0026#34;字段中，通常为JSON） request_body = method_details.get(\u0026#34;requestBody\u0026#34;, {}) if \u0026#34;content\u0026#34; in request_body and \u0026#34;application/json\u0026#34; in request_body[\u0026#34;content\u0026#34;]: json_schema = request_body[\u0026#34;content\u0026#34;][\u0026#34;application/json\u0026#34;][\u0026#34;schema\u0026#34;] # 递归解析嵌套JSON参数（如{\u0026#34;user\u0026#34;: {\u0026#34;name\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;age\u0026#34;: \u0026#34;integer\u0026#34;}}） body_params = parse_nested_schema(json_schema, parent_key=\u0026#34;body\u0026#34;) parameters.extend(body_params) return parameters # 辅助函数：解析嵌套JSON参数 def parse_nested_schema(schema, parent_key): nested_params = [] if schema[\u0026#34;type\u0026#34;] == \u0026#34;object\u0026#34; and \u0026#34;properties\u0026#34; in schema: for prop_name, prop_schema in schema[\u0026#34;properties\u0026#34;].items(): full_key = f\u0026#34;{parent_key}.{prop_name}\u0026#34; if parent_key else prop_name # 若为嵌套对象，递归解析（如address.city） if prop_schema.get(\u0026#34;type\u0026#34;) == \u0026#34;object\u0026#34;: nested_params.extend(parse_nested_schema(prop_schema, full_key)) else: # 提取基础类型参数的约束 param = { \u0026#34;name\u0026#34;: full_key, \u0026#34;in\u0026#34;: \u0026#34;body\u0026#34;, \u0026#34;type\u0026#34;: prop_schema[\u0026#34;type\u0026#34;], \u0026#34;required\u0026#34;: prop_name in schema.get(\u0026#34;required\u0026#34;, []), \u0026#34;constraints\u0026#34;: extract_constraints(prop_schema) } nested_params.append(param) return nested_params 示例输出（参数信息）：\n[ { \u0026#34;name\u0026#34;: \u0026#34;orderId\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;path\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;constraints\u0026#34;: {\u0026#34;minLength\u0026#34;: 10, \u0026#34;maxLength\u0026#34;: 20} // 订单ID长度10-20位 }, { \u0026#34;name\u0026#34;: \u0026#34;body.amount\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;body\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;number\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;constraints\u0026#34;: {\u0026#34;minimum\u0026#34;: 0.01, \u0026#34;maximum\u0026#34;: 10000} // 金额0.01-10000元 }, { \u0026#34;name\u0026#34;: \u0026#34;status\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;query\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;required\u0026#34;: false, \u0026#34;constraints\u0026#34;: {\u0026#34;enum\u0026#34;: [\u0026#34;pending\u0026#34;, \u0026#34;paid\u0026#34;, \u0026#34;cancelled\u0026#34;]} // 状态枚举 } ] 2.3 响应信息提取 提取接口的响应状态码、响应体schema（字段及类型），用于后续用例的预期结果校验：\ndef extract_responses(method_details): responses = {} for status_code, status_details in method_details.get(\u0026#34;responses\u0026#34;, {}).items(): # 提取响应体schema（如200成功响应） if \u0026#34;content\u0026#34; in status_details and \u0026#34;application/json\u0026#34; in status_details[\u0026#34;content\u0026#34;]: response_schema = status_details[\u0026#34;content\u0026#34;][\u0026#34;application/json\u0026#34;][\u0026#34;schema\u0026#34;] responses[status_code] = { \u0026#34;description\u0026#34;: status_details.get(\u0026#34;description\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;schema\u0026#34;: response_schema # 响应体字段及类型定义 } return responses # 示例输出（响应信息） { \u0026#34;200\u0026#34;: { \u0026#34;description\u0026#34;: \u0026#34;success\u0026#34;, \u0026#34;schema\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;object\u0026#34;, \u0026#34;properties\u0026#34;: { \u0026#34;code\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;integer\u0026#34;, \u0026#34;enum\u0026#34;: [200]}, \u0026#34;data\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;object\u0026#34;, \u0026#34;properties\u0026#34;: {\u0026#34;orderId\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;}}}, \u0026#34;msg\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;} } } }, \u0026#34;400\u0026#34;: { \u0026#34;description\u0026#34;: \u0026#34;bad request\u0026#34;, \u0026#34;schema\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;object\u0026#34;, \u0026#34;properties\u0026#34;: {\u0026#34;code\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;integer\u0026#34;}, \u0026#34;msg\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;}}} } } 2.4 提取 识别接口的前置依赖（如“创建订单”需先“登录获取token”），为生成“接口调用链”用例提供依据：\ndef extract_dependencies(method_details): dependencies = [] # 1. 从请求头提取token依赖（如Authorization字段） for param in method_details.get(\u0026#34;parameters\u0026#34;, []): if param.get(\u0026#34;name\u0026#34;) == \u0026#34;Authorization\u0026#34; and param.get(\u0026#34;in\u0026#34;) == \u0026#34;header\u0026#34;: # 假设登录接口返回token，路径为\u0026#34;/api/login\u0026#34; dependencies.append({ \u0026#34;type\u0026#34;: \u0026#34;token\u0026#34;, \u0026#34;source_interface\u0026#34;: \u0026#34;/api/login\u0026#34;, # 前置接口 \u0026#34;extract_rule\u0026#34;: \u0026#34;response.body.token\u0026#34; # 从登录响应提取token的规则 }) # 2. 从业务逻辑提取依赖（如“确认收货”需先“创建订单”） if \u0026#34;operationId\u0026#34; in method_details and \u0026#34;confirmReceipt\u0026#34; in method_details[\u0026#34;operationId\u0026#34;]: dependencies.append({ \u0026#34;type\u0026#34;: \u0026#34;business\u0026#34;, \u0026#34;source_interface\u0026#34;: \u0026#34;/api/order/create\u0026#34;, # 前置接口 \u0026#34;extract_rule\u0026#34;: \u0026#34;response.body.orderId\u0026#34; # 从创建订单响应提取orderId }) return dependencies 阶段3：生成（用例生成Agent核心工作） 目标：基于提取的元数据，按“正向→异常→边界→依赖”四类场景生成用例，覆盖所有参数规则和业务逻辑。\n3.1 正向用例生成（合法参数组合） 生成符合所有参数约束的用例，验证接口正常功能：\n逻辑：为每个参数选择“合法值”（如字符串取中间长度、数字取范围内值、枚举取合法选项），组合成完整请求。 示例（针对上文参数）： def generate_positive_cases(parameters): positive_params = {} for param in parameters: if param[\u0026#34;type\u0026#34;] == \u0026#34;string\u0026#34; and \u0026#34;enum\u0026#34; in param[\u0026#34;constraints\u0026#34;]: # 枚举参数：选第一个合法值 positive_params[param[\u0026#34;name\u0026#34;]] = param[\u0026#34;constraints\u0026#34;][\u0026#34;enum\u0026#34;][0] elif param[\u0026#34;type\u0026#34;] == \u0026#34;string\u0026#34; and \u0026#34;minLength\u0026#34; in param[\u0026#34;constraints\u0026#34;]: # 字符串参数：取中间长度（如min=10, max=20→取15位） length = (param[\u0026#34;constraints\u0026#34;][\u0026#34;minLength\u0026#34;] + param[\u0026#34;constraints\u0026#34;][\u0026#34;maxLength\u0026#34;]) // 2 positive_params[param[\u0026#34;name\u0026#34;]] = \u0026#34;a\u0026#34; * length elif param[\u0026#34;type\u0026#34;] == \u0026#34;number\u0026#34;: # 数字参数：取中间值（如0.01-10000→取5000.00） mid = (param[\u0026#34;constraints\u0026#34;][\u0026#34;minimum\u0026#34;] + param[\u0026#34;constraints\u0026#34;][\u0026#34;maximum\u0026#34;]) / 2 positive_params[param[\u0026#34;name\u0026#34;]] = round(mid, 2) return { \u0026#34;case_type\u0026#34;: \u0026#34;positive\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P0\u0026#34;, \u0026#34;params\u0026#34;: positive_params, \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 200, \u0026#34;schema_match\u0026#34;: True} } 正向用例示例：\n{ \u0026#34;case_id\u0026#34;: \u0026#34;API-ORDER-001\u0026#34;, \u0026#34;case_type\u0026#34;: \u0026#34;positive\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P0\u0026#34;, \u0026#34;path\u0026#34;: \u0026#34;/api/order\u0026#34;, \u0026#34;method\u0026#34;: \u0026#34;POST\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;orderId\u0026#34;: \u0026#34;aaaaaaaaaaaaaaa\u0026#34;（15位，符合10-20位约束）, \u0026#34;body.amount\u0026#34;: 5000.00（0.01-10000中间值）, \u0026#34;status\u0026#34;: \u0026#34;pending\u0026#34;（合法枚举值） }, \u0026#34;expected_response\u0026#34;: { \u0026#34;status_code\u0026#34;: 200, \u0026#34;schema_match\u0026#34;: true, \u0026#34;fields\u0026#34;: {\u0026#34;code\u0026#34;: 200, \u0026#34;msg\u0026#34;: \u0026#34;success\u0026#34;} } } 3.2 异常用例生成（参数校验失败） 生成违反参数约束的用例，验证接口的错误处理能力（如提示“参数格式错误”）：\n逻辑：针对每个参数的“约束规则”，生成“违反单条规则”的用例（避免多错误叠加导致定位困难）。 示例（针对上文参数）： def generate_abnormal_cases(parameters): abnormal_cases = [] for param in parameters: # 非空校验：必填参数不传 if param[\u0026#34;required\u0026#34;]: case = { \u0026#34;case_type\u0026#34;: \u0026#34;abnormal\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P1\u0026#34;, \u0026#34;error_type\u0026#34;: \u0026#34;missing_required_param\u0026#34;, \u0026#34;params\u0026#34;: {k: v for k, v in base_params.items() if k != param[\u0026#34;name\u0026#34;]}, # 剔除当前参数 \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 400, \u0026#34;msg_contains\u0026#34;: f\u0026#34;缺少参数{param[\u0026#39;name\u0026#39;]}\u0026#34;} } abnormal_cases.append(case) # 类型校验：参数类型错误（如数字传字符串） if param[\u0026#34;type\u0026#34;] == \u0026#34;number\u0026#34;: case = { \u0026#34;case_type\u0026#34;: \u0026#34;abnormal\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P1\u0026#34;, \u0026#34;error_type\u0026#34;: \u0026#34;type_error\u0026#34;, \u0026#34;params\u0026#34;: {**base_params, param[\u0026#34;name\u0026#34;]: \u0026#34;not_a_number\u0026#34;}, # 传字符串 \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 400, \u0026#34;msg_contains\u0026#34;: f\u0026#34;{param[\u0026#39;name\u0026#39;]}必须为数字\u0026#34;} } abnormal_cases.append(case) return abnormal_cases 异常用例示例：\n{ \u0026#34;case_id\u0026#34;: \u0026#34;API-ORDER-002\u0026#34;, \u0026#34;case_type\u0026#34;: \u0026#34;abnormal\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P1\u0026#34;, \u0026#34;error_type\u0026#34;: \u0026#34;missing_required_param\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;body.amount\u0026#34;: 5000.00, // 缺少必填参数orderId \u0026#34;status\u0026#34;: \u0026#34;pending\u0026#34; }, \u0026#34;expected_response\u0026#34;: { \u0026#34;status_code\u0026#34;: 400, \u0026#34;msg_contains\u0026#34;: \u0026#34;缺少参数orderId\u0026#34; } } 3.3 边界用例生成（临界值测试） 生成参数边界值的用例，验证接口对极限情况的处理（如“金额=10000.00”“订单ID=10位/20位”）：\n逻辑：针对“长度、范围”类约束，取“最小值、最大值、最小值-1、最大值+1”作为参数值。 示例： { \u0026#34;case_id\u0026#34;: \u0026#34;API-ORDER-003\u0026#34;, \u0026#34;case_type\u0026#34;: \u0026#34;boundary\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P1\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;orderId\u0026#34;: \u0026#34;aaaaaaaaaa\u0026#34;（10位，等于minLength）, \u0026#34;body.amount\u0026#34;: 10000.00（等于maximum）, \u0026#34;status\u0026#34;: \u0026#34;paid\u0026#34; }, \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 200, \u0026#34;msg\u0026#34;: \u0026#34;success\u0026#34;} }, { \u0026#34;case_id\u0026#34;: \u0026#34;API-ORDER-004\u0026#34;, \u0026#34;case_type\u0026#34;: \u0026#34;boundary\u0026#34;, \u0026#34;priority\u0026#34;: \u0026#34;P1\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;orderId\u0026#34;: \u0026#34;aaaaaaaaa\u0026#34;（9位，小于minLength）, \u0026#34;body.amount\u0026#34;: 10000.01（大于maximum）, \u0026#34;status\u0026#34;: \u0026#34;cancelled\u0026#34; }, \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 400, \u0026#34;msg_contains\u0026#34;: \u0026#34;orderId长度不足/金额超限\u0026#34;} } 3.4 依赖用例链生成（接口调用序列） 针对有前置依赖的接口，生成“前置接口→目标接口”的调用链用例，确保上下文连贯：\n逻辑：先执行前置接口（如登录），提取动态参数（如token），再传入目标接口（如创建订单）。 示例（登录→创建订单的用例链）： { \u0026#34;case_chain_id\u0026#34;: \u0026#34;CHAIN-001\u0026#34;, \u0026#34;cases\u0026#34;: [ { \u0026#34;case_id\u0026#34;: \u0026#34;PRE-LOGIN-001\u0026#34;, \u0026#34;path\u0026#34;: \u0026#34;/api/login\u0026#34;, \u0026#34;method\u0026#34;: \u0026#34;POST\u0026#34;, \u0026#34;params\u0026#34;: {\u0026#34;username\u0026#34;: \u0026#34;test\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;123456\u0026#34;}, \u0026#34;extract\u0026#34;: {\u0026#34;token\u0026#34;: \u0026#34;response.body.token\u0026#34;} // 提取token }, { \u0026#34;case_id\u0026#34;: \u0026#34;API-ORDER-005\u0026#34;, \u0026#34;path\u0026#34;: \u0026#34;/api/order\u0026#34;, \u0026#34;method\u0026#34;: \u0026#34;POST\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;orderId\u0026#34;: \u0026#34;aaaaaaaaaaaaaaa\u0026#34;, \u0026#34;body.amount\u0026#34;: 5000.00, \u0026#34;headers\u0026#34;: {\u0026#34;Authorization\u0026#34;: \u0026#34;Bearer {{token}}\u0026#34;} // 引用前置接口的token }, \u0026#34;expected_response\u0026#34;: {\u0026#34;status_code\u0026#34;: 200} } ] } 阶段4：用例与输出 将生成的用例转化为可执行格式，适配主流测试工具：\nPostman格式：生成Collection JSON，包含请求URL、参数、headers、测试脚本（断言）； pytest格式：生成Python脚本，调用requests库发送请求，用assert断言响应； TestRail格式：按“标题、步骤、预期结果”格式化，通过API同步至测试管理工具。 pytest脚本示例：\nimport requests def test_order_create_positive(): url = \u0026#34;https://test-api.example.com/api/order\u0026#34; headers = {\u0026#34;Content-Type\u0026#34;: \u0026#34;application/json\u0026#34;} params = { \u0026#34;orderId\u0026#34;: \u0026#34;aaaaaaaaaaaaaaa\u0026#34;, \u0026#34;status\u0026#34;: \u0026#34;pending\u0026#34; } json_data = {\u0026#34;amount\u0026#34;: 5000.00} response = requests.post(url, headers=headers, params=params, json=json_data) # 断言 assert response.status_code == 200 assert response.json()[\u0026#34;code\u0026#34;] == 200 assert \u0026#34;orderId\u0026#34; in response.json()[\u0026#34;data\u0026#34;] 三、实例演示：完整流程跑通一个接口 以“用户转账接口（POST /api/transfer）”为例，展示从解析到生成用例的完整过程：\n1. 接口文档（Swagger片段） { \u0026#34;paths\u0026#34;: { \u0026#34;/api/transfer\u0026#34;: { \u0026#34;post\u0026#34;: { \u0026#34;description\u0026#34;: \u0026#34;用户转账\u0026#34;, \u0026#34;parameters\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Authorization\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;header\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;schema\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;} } ], \u0026#34;requestBody\u0026#34;: { \u0026#34;content\u0026#34;: { \u0026#34;application/json\u0026#34;: { \u0026#34;schema\u0026#34;: { \u0026#34;type\u0026#34;: \u0026#34;object\u0026#34;, \u0026#34;properties\u0026#34;: { \u0026#34;targetAccount\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;minLength\u0026#34;: 10, \u0026#34;maxLength\u0026#34;: 20}, \u0026#34;amount\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;number\u0026#34;, \u0026#34;minimum\u0026#34;: 0.01, \u0026#34;maximum\u0026#34;: 50000}, \u0026#34;remark\u0026#34;: {\u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;maxLength\u0026#34;: 100} }, \u0026#34;required\u0026#34;: [\u0026#34;targetAccount\u0026#34;, \u0026#34;amount\u0026#34;] } } } }, \u0026#34;responses\u0026#34;: { \u0026#34;200\u0026#34;: {\u0026#34;description\u0026#34;: \u0026#34;success\u0026#34;}, \u0026#34;400\u0026#34;: {\u0026#34;description\u0026#34;: \u0026#34;参数错误\u0026#34;} } } } } } 2. 元数据提取结果 { \u0026#34;path\u0026#34;: \u0026#34;/api/transfer\u0026#34;, \u0026#34;method\u0026#34;: \u0026#34;POST\u0026#34;, \u0026#34;parameters\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Authorization\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;header\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;constraints\u0026#34;: {} }, { \u0026#34;name\u0026#34;: \u0026#34;body.targetAccount\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;body\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;constraints\u0026#34;: {\u0026#34;minLength\u0026#34;: 10, \u0026#34;maxLength\u0026#34;: 20} }, { \u0026#34;name\u0026#34;: \u0026#34;body.amount\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;body\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;number\u0026#34;, \u0026#34;required\u0026#34;: true, \u0026#34;constraints\u0026#34;: {\u0026#34;minimum\u0026#34;: 0.01, \u0026#34;maximum\u0026#34;: 50000} }, { \u0026#34;name\u0026#34;: \u0026#34;body.remark\u0026#34;, \u0026#34;in\u0026#34;: \u0026#34;body\u0026#34;, \u0026#34;type\u0026#34;: \u0026#34;string\u0026#34;, \u0026#34;required\u0026#34;: false, \u0026#34;constraints\u0026#34;: {\u0026#34;maxLength\u0026#34;: 100} } ], \u0026#34;dependencies\u0026#34;: [ {\u0026#34;type\u0026#34;: \u0026#34;token\u0026#34;, \u0026#34;source_interface\u0026#34;: \u0026#34;/api/login\u0026#34;, \u0026#34;extract_rule\u0026#34;: \u0026#34;response.body.token\u0026#34;} ] } 3. 生成的用例（节选） 正向用例：合法参数组合； 异常用例：缺失targetAccount、amount为负数； 边界用例：targetAccount=10位/20位、amount=50000.00； 依赖用例链：登录→转账（携带token）。 四、关键技术保障 嵌套参数解析能力：通过递归函数处理多层JSON参数（如body.user.address.city），避免漏解析； 约束规则完整性：覆盖Swagger支持的所有约束类型（minLength/maxLength/enum/minimum/maximum/pattern等）； 动态依赖处理：通过“参数提取规则”（如response.body.token）实现跨接口参数传递，确保用例链可执行； 去重与精简：合并重复场景（如“参数A为空”和“参数B为空”是两个独立场景，但格式一致时复用模板），避免用例冗余。 总结 Agent通过“文档同步→元数据提取→多场景用例生成→格式化输出”的全流程自动化，将接口测试用例的生成效率提升80%以上，参数覆盖率从人工的70%提升至100%。核心优势在于：\n无遗漏：严格按参数约束生成场景，避免人工经验不足导致的漏测； 可扩展：支持任意Swagger/OpenAPI接口，适配嵌套参数、复杂依赖等场景； 可执行：直接生成Postman/pytest格式用例，无需二次修改即可执行。 落地时需重点维护“参数约束规则库”和“依赖关系图谱”，确保Agent对业务逻辑的理解准确性。\n原文链接: Agent接口测试中实践 作者: 质量保障小乔\n","permalink":"http://testai666.cn/posts/agent%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95%E4%B8%AD%E5%AE%9E%E8%B7%B5/","summary":"\u003cp\u003e在接口测试中实现“自动解析文档→生成用例”的核心是通过**“文档结构化解析→元数据提取→场景规则映射→用例自动化编排”**的全流程自动化，结合的语义理解与测试领域知识，将非结构化的接口文档转化为覆盖全面、可直接执行的测试用例。\u003c/p\u003e\n\u003ch4 id=\"一整体流程框架\"\u003e一、整体流程框架\u003c/h4\u003e\n\u003cp\u003e整个过程分为\u003cstrong\u003e4个核心阶段\u003c/strong\u003e，由“接口解析Agent”和“用例生成Agent”协同完成，依赖“文档同步模块”“元数据存储模块”“规则引擎”三大支撑组件：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode class=\"language-mermaid\" data-lang=\"mermaid\"\u003egraph TD\r\n    subgraph 准备阶段\r\n        A[接口文档同步] --\u0026gt; A1[对接Swagger/OpenAPI]\r\n        A --\u0026gt; A2[监听文档变更（Webhook）]\r\n        A --\u0026gt; A3[历史文档版本管理]\r\n    end\r\n    \r\n    subgraph 解析阶段（接口解析Agent）\r\n        B[元数据提取] --\u0026gt; B1[基础信息：路径/方法/描述]\r\n        B --\u0026gt; B2[参数信息：类型/约束/位置]\r\n        B --\u0026gt; B3[响应信息：状态码/schema/字段]\r\n        B --\u0026gt; B4[依赖关系：前置接口/动态参数]\r\n    end\r\n    \r\n    subgraph 生成阶段（用例生成Agent）\r\n        C[场景规则映射] --\u0026gt; C1[正向场景：合法参数组合]\r\n        C --\u0026gt; C2[异常场景：参数校验失败]\r\n        C --\u0026gt; C3[边界场景：临界值/极限值]\r\n        C --\u0026gt; C4[依赖场景：接口调用链]\r\n    end\r\n    \r\n    subgraph 输出阶段\r\n        D[用例格式化] --\u0026gt; D1[Postman/JMeter格式]\r\n        D --\u0026gt; D2[pytest脚本格式]\r\n        D --\u0026gt; D3[测试管理工具格式（TestRail）]\r\n    end\n\u003c/code\u003e\u003c/pre\u003e\u003ch4 id=\"二具体步骤与技术实现\"\u003e二、具体步骤与技术实现\u003c/h4\u003e\n\u003ch5 id=\"阶段1同步确保解析源实时准确\"\u003e阶段1：同步——确保解析源实时准确\u003c/h5\u003e\n\u003cp\u003e\u003cstrong\u003e目标\u003c/strong\u003e：获取最新接口文档（如/OpenAPI），并监控变更以触发后续解析流程。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e具体操作\u003c/strong\u003e：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e文档接入方式\u003c/strong\u003e：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cul\u003e\n\u003cli\u003e主动拉取：通过定时任务（如每小时）调用接口文档地址（如\u003ccode\u003ehttps://api.example.com/v3/api-docs\u003c/code\u003e），获取JSON格式的Swagger文档；\u003c/li\u003e\n\u003cli\u003e被动接收：配置Git Webhook，当接口文档代码（如后端项目的Swagger配置）提交时，自动推送最新文档至Agent系统。\u003c/li\u003e\n\u003c/ul\u003e\n\u003col start=\"2\"\u003e\n\u003cli\u003e\u003cstrong\u003e文档版本管理\u003c/strong\u003e：\n将每次获取的文档存储在版本库（如Git），标记版本号（如\u003ccode\u003ev2.3.1\u003c/code\u003e），便于后续对比变更（如参数新增/删除）。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch5 id=\"阶段2元接口解析agent核心工作\"\u003e阶段2：元（接口解析Agent核心工作）\u003c/h5\u003e\n\u003cp\u003e\u003cstrong\u003e目标\u003c/strong\u003e：从接口文档中提取结构化元数据（基础信息、参数、响应、依赖），为用例生成提供“原材料”。\u003c/p\u003e","title":"Agent接口测试中实践"},{"content":"脚本依赖环境安装指南 以下是为运行 CSDN 博客导出脚本（如 export_csdn_cf.py）而准备 Linux 系统环境所执行的一系列有效命令。这些命令主要用于安装 运行时、及其依赖库。\n# 1. 安装 Python 包管理工具 pip3 apt install python3-pip -y # 2. 安装文件传输和解压工具 apt -y install lrzsz # 提供 rz/sz 命令，用于终端文件上传下载 apt -y install zip unzip # 用于处理 zip 和 unzip 格式的压缩包 # 3. 安装重命名工具 apt install rename # 强大的 Perl 脚本，用于批量重命名文件 # 4. 配置 Google Chrome 浏览器安装源 (使用清华镜像加速) # 下载并添加 Google 的 GPG 公钥以验证软件包完整性 wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - #或者上传执行如下的命令 # cat linux_signing_key.pub |sudo apt-key add - Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)). OK # 将清华镜像的 Chrome 源写入 APT 源列表 echo \u0026#34;deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/google-chrome/deb/ stable main\u0026#34; \u0026gt; /etc/apt/sources.list.d/google-chrome.list # 5. 更新软件包索引并安装 Chrome 及其系统依赖 apt update # 同步最新的软件包列表 # 安装稳定版 Google Chrome # 下载 \u0026amp;\u0026amp; dpkg -i去安装 https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb # 修复相关的依赖 apt --fix-broken install -y # 6. 安装 Chrome 运行所需的底层库 (确保 headless 模式正常工作) apt install -y \\ wget \\ unzip \\ libxss1 \\ libappindicator1 \\ libindicator7 \\ fonts-liberation \\ libasound2 \\ libatk-bridge2.0-0 \\ libgtk-3-0 \\ libdrm-common # 7. （可选）修复可能存在的依赖问题 apt --fix-broken install -y # 如果之前的安装出现中断或依赖错误，此命令可尝试自动修复 说明： 虽然核心抓取脚本export_csdn_cf.py使用的是cloudscraper，它不直接依赖浏览器，但如果您后续计划使用selenium或playwright等基于真实浏览器的自动化工具来应对更复杂的反爬机制（如 Turnstile 验证码），那么安装 Chrome 是必需的。 此处列出的lib*库是 Chrome 在无头（headless）模式下运行时常见的依赖项，能避免因缺少图形或音频库而导致的启动失败。 使用清华（TUNA）镜像可以显著加快在境内网络环境下下载 Chrome 的速度。 步骤一：安装 pip3 依赖 在开始编写和执行脚本之前，我们需要先配置好运行环境并安装必要的第三方库。\n# 1. 配置pip的源 (csdn-env) root@wanyan:/opt/csdn# cat ~/.pip/pip.conf [global] index-url = https://mirrors.aliyun.com/pypi/simple/ trusted-host = pypi.aliyun.com timeout = 120 # 2. 创建并激活 Python 虚拟环境 (推荐做法，避免依赖冲突) apt install python3.10-venv -y python3 -m venv csdn-env source csdn-env/bin/activate # Linux/Mac # 或者在 Windows 上: csdn-env\\Scripts\\activate # 3. 安装核心依赖包 pip3 install cloudscraper requests beautifulsoup4 markdownify lxml 实现原理与注释： cloudscraper: 这是本方案的核心。CSDN 网站使用了 Cloudflare 的反爬虫保护机制（如著名的“5秒盾”）。普通的requests库会直接被拦截。cloudscraper是一个专门设计用来绕过 Cloudflare 防护的库，它能模拟真实的浏览器行为，自动处理 JavaScript 挑战，从而成功获取页面内容。 requests: 提供基础的 HTTP 请求功能，cloudscraper内部也依赖于它。 beautifulsoup4 (bs4): 强大的 HTML/XML 解析库。用于解析从网页抓取下来的 HTML 文档，方便我们通过 CSS 选择器或标签名来定位和提取所需的数据（如文章标题、链接、正文）。 markdownify: 将 HTML 内容转换为 Markdown 格式的工具。这对于知识归档非常有用，因为 Markdown 文件轻量、可读性强，且易于在各种平台（如 Obsidian, Notion, VS Code）中查看和编辑。 lxml: 作为BeautifulSoup的解析后端，相比默认的html.parser，lxml速度更快、容错性更好，能更高效地处理复杂的 HTML 结构。 提示：如果国内网络访问 PyPI 较慢，可以使用 -i 参数指定镜像源，例如：pip3 install -i https://mirrors.aliyun.com/pypi/simple/ cloudscraper \u0026hellip;\n步骤二：展示源代码 (export_csdn_cf.py) 并添加注释（字已经处理了） #!/usr/bin/env python3 # -*- coding: utf-8 -*- \u0026#34;\u0026#34;\u0026#34; CSDN 文章导出 – cloudscraper 硬刚 521 版 仅对抗 CF 免费 5 秒盾；遇到 Pro/Turnstile 请升级商业方案 \u0026#34;\u0026#34;\u0026#34; import os, time, re import cloudscraper from bs4 import BeautifulSoup import markdownify BLOG_USERNAME = \u0026#34;3x7hnic1ty83t4\u0026#34; # ← 你的用户名 OUTPUT_DIR = f\u0026#34;csdn_posts_{BLOG_USERNAME}\u0026#34; MAX_PAGE = 10000 # 先抓 10 页试试水 os.makedirs(OUTPUT_DIR, exist_ok=True) # 1. 创建 scraper 实例（默认破解 CF 免费盾） scraper = cloudscraper.create_scraper( browser={\u0026#34;browser\u0026#34;: \u0026#34;chrome\u0026#34;, \u0026#34;platform\u0026#34;: \u0026#34;linux\u0026#34;, \u0026#34;mobile\u0026#34;: False} ) # 2. 抓列表页（静态 HTML 里就有 40 篇） def list_articles(): articles = [] for page in range(1, MAX_PAGE + 1): url = f\u0026#34;https://blog.csdn.net/{BLOG_USERNAME}/article/list/{page}\u0026#34; print(f\u0026#34;[+] 拉取列表页 {page} {url}\u0026#34;) resp = scraper.get(url, timeout=15) if resp.status_code != 200: print(\u0026#34;[-] CF 可能升级了，返回:\u0026#34;, resp.status_code) break soup = BeautifulSoup(resp.text, \u0026#34;lxml\u0026#34;) links = soup.select(\u0026#34;h4 a[href*=\u0026#39;article/details\u0026#39;]\u0026#34;) if not links: print(\u0026#34;[=] 本页无文章，已到尽头\u0026#34;) break for a in links: articles.append((a.text.strip(), a[\u0026#34;href\u0026#34;])) time.sleep(2) print(f\u0026#34;[#] 共拿到 {len(articles)} 篇\u0026#34;) return articles # 新增：统一的文件名清洗函数（等效于你给的 shell 命令链） def sanitize_filename(fname): # Step 1: 替换空格、括号、逗号等为下划线，合并多个下划线 fname = re.sub(r\u0026#39;[ \\(\\),]+\u0026#39;, \u0026#39;_\u0026#39;, fname) fname = re.sub(r\u0026#39;_+\u0026#39;, \u0026#39;_\u0026#39;, fname) # Step 2: 去除首尾下划线 fname = fname.strip(\u0026#39;_\u0026#39;) # Step 3: 移除开头的 \u0026#34;原创\u0026#34; 字样（含换行符） fname = re.sub(r\u0026#39;^原创\\s*\u0026#39;, \u0026#39;\u0026#39;, fname, flags=re.MULTILINE) # Step 4: 删除所有换行符 fname = fname.replace(\u0026#39;\\n\u0026#39;, \u0026#39;\u0026#39;) # Step 5: 首尾空白和多余空格/下划线清理，中间多个空格/下划线变一个空格 fname = re.sub(r\u0026#39;^[ _]+|[ _]+$\u0026#39;, \u0026#39;\u0026#39;, fname) # 去首尾 fname = re.sub(r\u0026#39;[ _]{2,}\u0026#39;, \u0026#39; \u0026#39;, fname) # 多个空格/下划线→单空格 # Step 6: 移除 \u0026amp; 和 + fname = fname.replace(\u0026#39;\u0026amp;\u0026#39;, \u0026#39;\u0026#39;).replace(\u0026#39;+\u0026#39;, \u0026#39;\u0026#39;) # Step 7: 保留字母、数字、中文、点、横线、空格（Unicode 安全） fname = re.sub(r\u0026#39;[^\\w .-]\u0026#39;, \u0026#39;\u0026#39;, fname, flags=re.UNICODE) # Step 8: 再次清理因删除特殊字符后产生的多余符号 fname = re.sub(r\u0026#39;^[ _]+|[ _]+$\u0026#39;, \u0026#39;\u0026#39;, fname) fname = re.sub(r\u0026#39;_+\u0026#39;, \u0026#39;_\u0026#39;, fname).strip(\u0026#39;_\u0026#39;) # 最终防止为空 return fname or \u0026#34;untitled\u0026#34; # 3. 抓正文 def scrape_article(title, url): print(f\u0026#34;[*] {title}\u0026#34;) try: resp = scraper.get(url, timeout=15) soup = BeautifulSoup(resp.text, \u0026#34;lxml\u0026#34;) content = soup.select_one(\u0026#34;#content_views, #article_content, .htmledit_views\u0026#34;) if not content: print(\u0026#34; 找不到正文，跳过\u0026#34;) return md = markdownify.markdownify(str(content), heading_style=\u0026#34;ATX\u0026#34;) # 【关键修改】使用增强版文件名清洗 fname_base = re.sub(r\u0026#39;[\u0026lt;\u0026gt;:/\\\\|?*\u0026#34;\\\u0026#39;\\n]+\u0026#39;, \u0026#39;_\u0026#39;, title)[:100] # 初步过滤非法字符 cleaned_fname = sanitize_filename(fname_base) fname = f\u0026#34;{cleaned_fname}.md\u0026#34; # 防止重名冲突 counter = 1 final_path = os.path.join(OUTPUT_DIR, fname) original_fname = fname while os.path.exists(final_path): name_only = os.path.splitext(original_fname)[0] ext = \u0026#34;.md\u0026#34; fname = f\u0026#34;{name_only}_{counter}{ext}\u0026#34; final_path = os.path.join(OUTPUT_DIR, fname) counter += 1 with open(final_path, \u0026#34;w\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as f: f.write(f\u0026#34;# {title}\\n\\n{md}\u0026#34;) print(f\u0026#34; 保存为: {fname}\u0026#34;) except Exception as e: print(f\u0026#34; 失败: {e}\u0026#34;) # 4. 主流程 def main(): articles = list_articles() for title, url in articles: scrape_article(title, url) print(f\u0026#34;[OK] 全部完成 → ./{OUTPUT_DIR}\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: main() 步骤三：执行 python3 命令 在确保虚拟环境已激活且依赖安装完成后，执行以下命令运行脚本：\npython3 export_csdn_cf.py 步骤四：执行输出展示 脚本运行时，会在终端实时输出日志信息。一次成功的执行过程大致如下：\n(csdn-env) root@wanyan:/opt/csdn# python3 export_csdn_cf.py [INFO] 开始抓取用户 \u0026#39;3x7hnic1ty83t4\u0026#39; 的博客文章... [+] 正在拉取列表页 1: https://blog.csdn.net/3x7hnic1ty83t4/article/list/1 [+] 正在拉取列表页 2: https://blog.csdn.net/3x7hnic1ty83t4/article/list/2 [+] 正在拉取列表页 3: https://blog.csdn.net/3x7hnic1ty83t4/article/list/3 [+] 正在拉取列表页 4: https://blog.csdn.net/3x7hnic1ty83t4/article/list/4 [+] 正在拉取列表页 5: https://blog.csdn.net/3x7hnic1ty83t4/article/list/5 [+] 正在拉取列表页 6: https://blog.csdn.net/3x7hnic1ty83t4/article/list/6 [+] 正在拉取列表页 7: https://blog.csdn.net/3x7hnic1ty83t4/article/list/7 [+] 正在拉取列表页 8: https://blog.csdn.net/3x7hnic1ty83t4/article/list/8 [=] 本页未找到文章链接，可能是最后一页，停止抓取。 [#] 列表抓取完成，共发现 320 篇文章。 [INFO] 开始抓取文章正文... [*] 正在处理: 原创 PHP项目Kubernetes部署与Jenkins CI/CD流水线全栈实践指南 [✓] 已保存: ./csdn_posts_weixin_52315708/原创_PHP项目Kubernetes部署与Jenkins_CI_CD流水线全栈实践指南.md [*] 正在处理: 原创 docker-compose部署yapi [✓] 已保存: ./csdn_posts_weixin_52315708/原创_docker_compose部署yapi.md [*] 正在处理: 原创 PHP 项目容器化与自动化部署实践：从 Docker 改造到 Jenkins Pipeline 滚动更新 [✓] 已保存: ./csdn_posts_weixin_52315708/原创_PHP_项目容器化与自动化部署实践：从_Docker_改造到_Jenkins_Pipeline_滚动更新.md ... (后续文章处理中) [OK] 全部任务完成！文章已保存至 \u0026#39;./csdn_posts_weixin_52315708\u0026#39; 目录。 关键点说明： 脚本成功识别到用户共有 8 个有效的文章列表页，总计 320 篇。 每篇文章都经过了抓取和转换，并在终端显示了成功 ([✓]) 或失败 ([✗]) 的状态。 所有.md文件均已生成在./csdn_posts_weixin_52315708目录下。 步骤五：输出文件处理与命名规范化（python程序已经处理了，理论上这个已经不需要了，但是还是保留吧） 虽然脚本已经对文件名做了基础清理，但生成的文件名仍包含“原创”字样、多余的下划线 _ 和特殊符号，不够美观。我们可以使用一系列 rename 和 sed 命令进行深度清洗。\n# 进入输出目录 cd csdn_posts_3x7hnic1ty83t4 # 1. 将空格、括号、逗号等替换为下划线，并合并连续的多个下划线 rename -v \u0026#39;s/[ \\(\\),]+/_/g; s/__+/_/g; s/^_+//; s/_$//\u0026#39; *.md # 2. 移除文件名开头的 \u0026#34;原创\u0026#34; 字样及其后的空白符（包括换行） rename -v \u0026#39;s/^原创\\s*\\n?\\s*//\u0026#39; *.md # 3. 删除文件名中可能残留的换行符 (\\n) rename -v \u0026#39;s/\\n//g\u0026#39; *.md # 4. 终极规范化：使用 sed 进行精细调整 # - 去除首尾空格和下划线 # - 将多个连续的空格或下划线合并为一个空格 # - 移除 \u0026amp; 和 + 符号 # - 只保留字母、数字、空格、点(.)、连字符(-)和中文等Unicode字符 ls *.md | while read f; do new=$(echo \u0026#34;$f\u0026#34; | \\ sed -E \u0026#39;s/^[ _]+|[ _]+$//g; \u0026#39; \\ # 去首尾空格/下划线 \u0026#39;s/[ _]{2,}/ /g; \u0026#39; \\ # 多个空格/下划线变一个空格 \u0026#39;s/[\u0026amp;+]//g; \u0026#39; \\ # 移除 \u0026amp; 和 + \u0026#39;s/[^[:alnum:] _.\\-\\p{L}\\p{N}]//g\u0026#39;) # 仅保留字母数字、空格、点、横线、Unicode字符 # 如果新旧文件名不同，则执行重命名 [ \u0026#34;$f\u0026#34; != \u0026#34;$new\u0026#34; ] \u0026amp;\u0026amp; mv \u0026#34;$f\u0026#34; \u0026#34;$new\u0026#34; done 最终效果： 经过上述清洗，原始文件名： 原创_PHP_项目容器化与自动化部署实践：从_Docker_改造到_Jenkins_Pipeline_滚动更新.md 将被完美地重命名为： PHP 项目容器化与自动化部署实践 从 Docker 改造到 Jenkins Pipeline 滚动更新.md 这样处理后的文件名简洁、规范，便于管理和阅读。\n总结 本文通过一个完整的案例，展示了如何利用 Python + cloudscraper + Shell 的组合，实现对受反爬保护网站的内容采集与后期数据清洗。整个流程自动化程度高，具有很强的实用性和可复用性，是构建个人知识库的利器。\n原文链接: CSDN博客文章批量导出 作者: 完颜振江\n","permalink":"http://testai666.cn/posts/csdn%E5%8D%9A%E5%AE%A2%E6%96%87%E7%AB%A0%E6%89%B9%E9%87%8F%E5%AF%BC%E5%87%BA/","summary":"\u003ch3 id=\"脚本依赖环境安装指南\"\u003e脚本依赖环境安装指南\u003c/h3\u003e\n\u003cp\u003e以下是为运行 CSDN 博客导出脚本（如 \u003ccode\u003eexport_csdn_cf.py\u003c/code\u003e）而准备 Linux 系统环境所执行的一系列有效命令。这些命令主要用于安装  运行时、及其依赖库。\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 1. 安装 Python 包管理工具 pip3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt install python3-pip -y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 2. 安装文件传输和解压工具\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt -y install lrzsz                    \u003cspan style=\"color:#75715e\"\u003e# 提供 rz/sz 命令，用于终端文件上传下载\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt -y install zip unzip                \u003cspan style=\"color:#75715e\"\u003e# 用于处理 zip 和 unzip 格式的压缩包\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 3. 安装重命名工具\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt install rename                      \u003cspan style=\"color:#75715e\"\u003e# 强大的 Perl 脚本，用于批量重命名文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 4. 配置 Google Chrome 浏览器安装源 (使用清华镜像加速)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#    下载并添加 Google 的 GPG 公钥以验证软件包完整性\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ewget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#或者上传执行如下的命令\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# cat linux_signing_key.pub |sudo apt-key add -\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eWarning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003esee apt-key\u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003e8\u003cspan style=\"color:#f92672\"\u003e))\u003c/span\u003e.\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eOK\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#    将清华镜像的 Chrome 源写入 APT 源列表\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eecho \u003cspan style=\"color:#e6db74\"\u003e\u0026#34;deb [arch=amd64] https://mirrors.tuna.tsinghua.edu.cn/google-chrome/deb/ stable main\u0026#34;\u003c/span\u003e \u0026gt; /etc/apt/sources.list.d/google-chrome.list\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 5. 更新软件包索引并安装 Chrome 及其系统依赖\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt update                            \u003cspan style=\"color:#75715e\"\u003e# 同步最新的软件包列表\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 安装稳定版 Google Chrome\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 下载 \u0026amp;\u0026amp; dpkg -i去安装\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ehttps://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 修复相关的依赖\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt --fix-broken install -y\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 6. 安装 Chrome 运行所需的底层库 (确保 headless 模式正常工作)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt install -y \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    wget \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    unzip \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libxss1 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libappindicator1 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libindicator7 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    fonts-liberation \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libasound2 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libatk-bridge2.0-0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libgtk-3-0 \u003cspan style=\"color:#ae81ff\"\u003e\\\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    libdrm-common\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 7. （可选）修复可能存在的依赖问题\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eapt --fix-broken install -y           \u003cspan style=\"color:#75715e\"\u003e# 如果之前的安装出现中断或依赖错误，此命令可尝试自动修复\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"说明\"\u003e说明：\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e虽然核心抓取脚本\u003ccode\u003eexport_csdn_cf.py\u003c/code\u003e使用的是\u003ccode\u003ecloudscraper\u003c/code\u003e，它不直接依赖浏览器，但如果您后续计划使用\u003ccode\u003eselenium\u003c/code\u003e或\u003ccode\u003eplaywright\u003c/code\u003e等基于真实浏览器的自动化工具来应对更复杂的反爬机制（如 Turnstile 验证码），那么安装 Chrome 是必需的。\u003c/li\u003e\n\u003cli\u003e此处列出的\u003ccode\u003elib*\u003c/code\u003e库是 Chrome 在无头（headless）模式下运行时常见的依赖项，能避免因缺少图形或音频库而导致的启动失败。\u003c/li\u003e\n\u003cli\u003e使用清华（TUNA）镜像可以显著加快在境内网络环境下下载 Chrome 的速度。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"步骤一安装-pip3-依赖\"\u003e步骤一：安装 pip3 依赖\u003c/h3\u003e\n\u003cp\u003e在开始编写和执行脚本之前，我们需要先配置好运行环境并安装必要的第三方库。\u003c/p\u003e","title":"CSDN博客文章批量导出"},{"content":" 开发板有哪些存储，存储如何测试 如何决定测试标准，和老板开发开会决定还是用行业标准 你觉得你有什么长处在测试方面 蓝牙如何测试，命令是什么 自动测试有哪些方面的应用 串口是什么协议，WiFi有哪些协议 WiFi如何测试 USB接口如何测试 固件如何测试 python深拷贝和浅拷贝区别 python进程通信有哪些 原文链接: ARM硬件测试面试题 作者: lixusocool\n","permalink":"http://testai666.cn/posts/arm%E7%A1%AC%E4%BB%B6%E6%B5%8B%E8%AF%95%E9%9D%A2%E8%AF%95%E9%A2%98/","summary":"\u003col\u003e\n\u003cli\u003e开发板有哪些存储，存储如何测试\u003c/li\u003e\n\u003cli\u003e如何决定测试标准，和老板开发开会决定还是用行业标准\u003c/li\u003e\n\u003cli\u003e你觉得你有什么长处在测试方面\u003c/li\u003e\n\u003cli\u003e蓝牙如何测试，命令是什么\u003c/li\u003e\n\u003cli\u003e自动测试有哪些方面的应用\u003c/li\u003e\n\u003cli\u003e串口是什么协议，WiFi有哪些协议\u003c/li\u003e\n\u003cli\u003eWiFi如何测试\u003c/li\u003e\n\u003cli\u003eUSB接口如何测试\u003c/li\u003e\n\u003cli\u003e固件如何测试\u003c/li\u003e\n\u003cli\u003epython深拷贝和浅拷贝区别\u003c/li\u003e\n\u003cli\u003epython进程通信有哪些\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/149785300?spm=1001.2014.3001.5502\"\u003eARM硬件测试面试题\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"ARM硬件测试面试题"},{"content":"大家好，我是玄姐。\n▼最近直播超级多，预约保你有收获\n关于 Agent 的定义目前还没有形成共识，目前有3个代表性的定义：\n流行最广的是前 OpenAI 研究与安全副总裁 Lilian Weng 对 Agent 的定义：Agent = LLM + Planning + Tools + Memory。\n除此之外，LangChain 对 Agent 的定义为：使用 LLM 决定应用程序控制流的系统。\nOpenAI 对 Agent 的定义是：Agent 是能够代表用户自主完成任务的系统。\n尽管目前对 Agent 的定义还没形成共识，但是大家对 Agentic System（智能系统）基本的共识是：Agentic System 是一种有目标、基于环境的决策系统。与 LLM 最大的区别在于，Agentic System 可以与现实世界交互，从感知环境开始，做出决策并执行，影响环境，然后基于反馈调整，不断持续迭代循环。\n—1—\nAgentic System 架构设计剖析\n一个完整的 Agentic System 架构包含四个核心组成部分：\n感知：为大模型构建上下文信息。常见的方法包括检索增强生成（RAG），查询结构化数据（比如：数据库、网页内容）或者检索历史记录（比如：长短期记忆）。\n决策：本质上是 Planning 规划过程。可以通过规则引擎（Workflow）实现，也可以由大语言模型（LLM）驱动（自主 Agent），或者借助外部规划器。在设计时需要权衡泛化能力和准确性\u0026ndash;LLM 驱动的决策泛化能力强，但不确定性较高；而基于规则的工作流泛化能力较弱，但更可控。\n执行：通过调用工具来改变环境。包括 API 调用（比如：REST、RPC、SQL、函数调用）或与图形软件的集成（比如：Anthropic 的 Computer use）。\n反馈：用于评估和迭代的机制。反馈可以通过人工标注、规则或模型生成，更新可以是离线的或在线的。\n这个闭环构成了 Agent 的基础单元（building block）。复杂的 Agent 可以由多个小 Agent 组成，复杂业务逻辑大决策通常由一系列小型决策构成。\n—2—\n多 Agentic System 架构设计原则\n当多个 Agent 协同工作时，就构成了 Multi-Agent 系统。在设计 Multi-Agent 系统时，要避免过度拆分。每个 Agent 应该代表一个明确的业务决策点，并可以通过持续反馈进行优化。只有在单个 Agent 无法满足需求时，才考虑引入更多的 Agent。\n第一、借鉴分布式系统的思路，可以把 Agent 比作一台计算机：\nLLM（大语言模型）是计算机的CPU，负责处理和运算。 Context window（上下文窗口）是计算机的内存，用于临时存储信息。 向量数据库是计算机的硬盘，用于长期存储数据。 工具（Tools）是计算机上的程序，用于执行特定任务。 分布式系统主要解决以下三个问题：\n性能不足：单台计算机的计算或存储能力有限。 容错性：单个系统容易出现故障，需要多个系统协同工作以提高可靠性。 协作：不同团队负责不同的微服务，需要协同工作。 Multi-Agent 系统的设计原则与此类似：\n解决单次 LLM 调用智力不足的问题：当单个 Agent 无法处理复杂的任务时，可以引入多个 Agent 协同工作。 提高容错性：多个 Agent 协同工作可以提高系统的可靠性和稳定性。 促进协作：不同 Agent 可以负责不同的任务或决策点，实现更复杂的业务逻辑。 第二、Agentic System 架构演进 Multi-Agent 系统的设计应从单个 Agent 开始，只有在单个 Agent 无法满足需求时，才逐步过渡到多 Agent 架构。这种逐步扩展的方式有助于保持系统的简洁性和可维护性。\n—3—\n从单 Agent 到多 Agent 智能助手案例架构演进\n智能助手的演进遵循了从单 Agent 到 Multi-Agent 的路径：\n初始阶段：仅有产品问答模块，使用简单的 RAG（检索增强生成）技术。 技能扩展：添加多种技能，但用户需要手动切换。 意图识别：开发意图识别 Agent，但仍为单 Agent 架构。 多 Agent 体系：随着场景复杂化和多团队协作需求的增加，逐步过渡到多 Agent 体系。 除架构演进外，我们还进行了多项技术优化：\nRAG 优化：增加查询改写功能，提高系统的鲁棒性。用户不一定会提出完美的问题，通过查询扩展和改写，系统能够更好地处理各种输入变化。 知识图谱：引入 GraphRAG 技术，将产品知识问答的准确度从 76% 提升到 93%。对于算法实力一般但工程能力强的团队，知识图谱是模型后训练的实用替代方案。 强化学习：在经营分析场景中，将评价体系（如 AARRR 模型）转化为强化学习的奖励函数，实现模型的持续优化。 当然，我们也在经营分析场景中基于 SFT（监督微调）和强化学习进行微调。我们之前基于经营分析 Agent 构建的数据集和评价体系，天然地过渡到了 RL（强化学习）领域的环境和奖励函数的构建。我们之前评价一个经营建议好坏的一个重要指标是思考过程是否符合 AARRR 模型，现在在 RL 中，这个指标也成为了奖励函数之一。\n原文链接: 从单 Agent 到多 Agent 的案例落地实践 作者: CC 4.0 BY-SA\n","permalink":"http://testai666.cn/posts/%E4%BB%8E%E5%8D%95_agent_%E5%88%B0%E5%A4%9A_agent_%E7%9A%84%E6%A1%88%E4%BE%8B%E8%90%BD%E5%9C%B0%E5%AE%9E%E8%B7%B5/","summary":"\u003cp\u003e大家好，我是玄姐。\u003c/p\u003e\n\u003cp\u003e▼最近直播超级多，预约保你有收获\u003c/p\u003e\n\u003cp\u003e关于 Agent 的定义目前还没有形成共识，目前有3个代表性的定义：\u003c/p\u003e\n\u003cp\u003e流行最广的是前 OpenAI 研究与安全副总裁 Lilian Weng 对 Agent 的定义：Agent = LLM + Planning + Tools + Memory。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/e757333072c125bd0c54b950372b2945.png\"\u003e\u003c/p\u003e\n\u003cp\u003e除此之外，LangChain 对 Agent 的定义为：使用 LLM 决定应用程序控制流的系统。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Agent Types\" loading=\"lazy\" src=\"/posts/images/0010fdccaae1d6b1280a256bd8539b5b.png\"\u003e\u003c/p\u003e\n\u003cp\u003eOpenAI 对 Agent 的定义是：Agent 是能够代表用户自主完成任务的系统。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/f3d9d42503f64bc7abdf1f12933895c8.png\"\u003e\u003c/p\u003e\n\u003cp\u003e尽管目前对 Agent 的定义还没形成共识，但是大家对 Agentic System（智能系统）基本的共识是：Agentic System 是一种有目标、基于环境的决策系统。与 LLM 最大的区别在于，Agentic System 可以与现实世界交互，从感知环境开始，做出决策并执行，影响环境，然后基于反馈调整，不断持续迭代循环。\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/aff439c3a8dfb100cafb830831c99fe3.jpeg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cem\u003e\u003cem\u003e\u003cstrong\u003e—\u003cem\u003e1\u003c/em\u003e\u003c/strong\u003e\u003c/em\u003e—\u003c/em\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eAgentic System 架构设计剖析\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e一个完整的 Agentic System 架构包含四个核心组成部分：\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/daa60a90b24dccd313bbebb50b993dac.png\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e感知：为大模型构建上下文信息。常见的方法包括检索增强生成（RAG），查询结构化数据（比如：数据库、网页内容）或者检索历史记录（比如：长短期记忆）。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e决策：本质上是 Planning 规划过程。可以通过规则引擎（Workflow）实现，也可以由大语言模型（LLM）驱动（自主 Agent），或者借助外部规划器。在设计时需要权衡泛化能力和准确性\u0026ndash;LLM 驱动的决策泛化能力强，但不确定性较高；而基于规则的工作流泛化能力较弱，但更可控。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e执行：通过调用工具来改变环境。包括 API 调用（比如：REST、RPC、SQL、函数调用）或与图形软件的集成（比如：Anthropic 的 Computer use）。\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e反馈：用于评估和迭代的机制。反馈可以通过人工标注、规则或模型生成，更新可以是离线的或在线的。\u003c/p\u003e","title":"从单 Agent 到多 Agent 的案例落地实践"},{"content":" 我系统是ubuntu18.04.6 LTS 执行以下命令： curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb sudo dpkg -i cloudflared.deb cloudflared tunnel login #这里会打开浏览器窗口登录cloudfare，我这里是之前绑定了域名，建议大家也绑定域名 cloudflared tunnel create vm1 #这里会生成下面所需的json文件 mkdir ./cloudflared \u0026amp;\u0026amp; cd cloudflared/ nano config.yaml #添加以下内容 tunnels: vm1 credentials-file: /home/lixu/.cloudflared/95b49dcc-XXXX.json protocol: http2 ingress: - hostname: vm.nb404.cn service: http://127.0.0.1:80 originRequest: disableChunkedEncoding: true noTLSVerify: true - service: http_status:404 cloudflared tunnel --config config.yaml route dns vm1 vm.nb404.cn #这句命令会给域名增加vm的dns记录 cloudflared tunnel --config ./config.yaml run vm1 #这里就可以访问了，大家记得改成自己的域名 enjoy\n原文链接: 使用cloudfare实现免费内网穿透 作者: CC 4.0 BY-SA\n","permalink":"http://testai666.cn/posts/%E4%BD%BF%E7%94%A8cloudfare%E5%AE%9E%E7%8E%B0%E5%85%8D%E8%B4%B9%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/","summary":"\u003col\u003e\n\u003cli\u003e我系统是ubuntu18.04.6 LTS\u003c/li\u003e\n\u003cli\u003e执行以下命令：\u003c/li\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecurl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003esudo dpkg -i cloudflared.deb\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecloudflared tunnel login\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#这里会打开浏览器窗口登录cloudfare，我这里是之前绑定了域名，建议大家也绑定域名\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecloudflared tunnel create vm1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#这里会生成下面所需的json文件\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emkdir ./cloudflared \u003cspan style=\"color:#f92672\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e cd cloudflared/\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003enano config.yaml\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#添加以下内容\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003etunnels: vm1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecredentials-file: /home/lixu/.cloudflared/95b49dcc-XXXX.json\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprotocol: http2\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eingress:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - hostname: vm.nb404.cn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    service: http://127.0.0.1:80\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    originRequest:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      disableChunkedEncoding: true\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e      noTLSVerify: true\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  - service: http_status:404\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecloudflared tunnel --config config.yaml route dns vm1 vm.nb404.cn\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#这句命令会给域名增加vm的dns记录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ecloudflared tunnel --config ./config.yaml run vm1\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e#这里就可以访问了，大家记得改成自己的域名\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eenjoy\u003c/p\u003e","title":"使用cloudfare实现免费内网穿透"},{"content":" 先安装ubuntu18的版本，使用典型安装，因为它会自动安装open vm工具实现复制粘贴桌面自动大小功能，同时不要尝试手动安装open vm工具，我试过在ubuntu20+上面试过 没用。 输入相关信息 直接自动化一键安装。 重启后，执行以下命令升级系统，这个阶段可能需要等30min+ sudo apt upgrade sudo do-release-upgrade -c 重启 即可 24.5.19更新：\n缺点：snap install node 会导致nodejs无法使用，缺少依赖 原文链接: 解决ubuntu20+不能在vmare17复制粘贴的麻烦解决方案 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E8%A7%A3%E5%86%B3ubuntu20+%E4%B8%8D%E8%83%BD%E5%9C%A8vmare17%E5%A4%8D%E5%88%B6%E7%B2%98%E8%B4%B4%E7%9A%84%E9%BA%BB%E7%83%A6%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/","summary":"\u003col\u003e\n\u003cli\u003e先安装ubuntu18的版本，使用典型安装，因为它会自动安装open vm工具实现复制粘贴桌面自动大小功能，同时不要尝试手动安装open vm工具，我试过在ubuntu20+上面试过 没用。\u003cimg loading=\"lazy\" src=\"/posts/images/33f7da4551d70987c3d389e7068c9ead.png\"\u003e\u003c/li\u003e\n\u003cli\u003e输入相关信息 直接自动化一键安装。\u003c/li\u003e\n\u003cli\u003e重启后，执行以下命令升级系统，这个阶段可能需要等30min+\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eapt\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eupgrade\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#66d9ef\"\u003edo\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003erelease\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eupgrade\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ec\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003col start=\"4\"\u003e\n\u003cli\u003e重启  即可\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/f4c2472908275cecb3c27b82727baa8c.png\"\u003e\u003c/p\u003e\n\u003cp\u003e24.5.19更新：\u003c/p\u003e\n\u003cp\u003e缺点：snap install node  会导致nodejs无法使用，缺少依赖  \u003c/p\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/139016176?spm=1001.2014.3001.5502\"\u003e解决ubuntu20+不能在vmare17复制粘贴的麻烦解决方案\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"解决ubuntu20+不能在vmare17复制粘贴的麻烦解决方案"},{"content":"\nnano ~/.bashrc export LC_ALL=en_US.UTF-8 export LANG=en_US.UTF-8 保存退出 ，重新登录终端让修改生效！\n原文链接: nano在centos7下中文乱码 作者: lixusocool\n","permalink":"http://testai666.cn/posts/nano%E5%9C%A8centos7%E4%B8%8B%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81/","summary":"\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/e451ca6f3e1c6d249a0905d23c2aedf0.png\"\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003enano\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e~\u003c/span\u003e\u003cspan style=\"color:#960050;background-color:#1e0010\"\u003e/.bashrc\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eexport\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eLC_ALL\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003een_US\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eUTF\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e  \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#66d9ef\"\u003eexport\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eLANG\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003een_US\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eUTF\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e8\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e保存退出 ，重新登录终端让修改生效！\u003cimg loading=\"lazy\" src=\"/posts/images/0a8761893e8b6c1320229ffabedfd1d5.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/f4c2472908275cecb3c27b82727baa8c.png\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/138995486?spm=1001.2014.3001.5502\"\u003enano在centos7下中文乱码\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"nano在centos7下中文乱码"},{"content":" 安全组设置： 一定要关闭本地代理才能访问，比如clash！ 原文链接: aws的linux其他端口无法访问问题 作者: lixusocool\n","permalink":"http://testai666.cn/posts/aws%E7%9A%84linux%E5%85%B6%E4%BB%96%E7%AB%AF%E5%8F%A3%E6%97%A0%E6%B3%95%E8%AE%BF%E9%97%AE%E9%97%AE%E9%A2%98/","summary":"\u003col\u003e\n\u003cli\u003e安全组设置：\u003cimg loading=\"lazy\" src=\"/posts/images/c6834a6d4afe437d4af29bbeb40b3a6e.png\"\u003e\u003c/li\u003e\n\u003cli\u003e一定要关闭本地代理才能访问，比如clash！\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/f4c2472908275cecb3c27b82727baa8c.png\"\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/138953071?spm=1001.2014.3001.5502\"\u003eaws的linux其他端口无法访问问题\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"aws的linux其他端口无法访问问题"},{"content":" sudo systemctl stop ollama sudo systemctl disable ollama sudo rm /etc/systemd/system/ollama.service sudo rm $(which ollama) sudo rm -r /usr/share/ollama sudo userdel ollama sudo groupdel ollama 我是用官方安装的，卸载比较麻烦\n建议 用 install ollama\n卸载也方便snap remove\n原文链接: linux的ollama卸载 作者: lixusocool\n","permalink":"http://testai666.cn/posts/linux%E7%9A%84ollama%E5%8D%B8%E8%BD%BD/","summary":"\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esystemctl\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003estop\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003esystemctl\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003edisable\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erm\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eetc\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003esystemd\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003esystem\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e.\u003cspan style=\"color:#a6e22e\"\u003eservice\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erm\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003e$\u003c/span\u003e(\u003cspan style=\"color:#a6e22e\"\u003ewhich\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e)\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erm\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003er\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eusr\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eshare\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e/\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003euserdel\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#a6e22e\"\u003esudo\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003egroupdel\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003eollama\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e我是用官方安装的，卸载比较麻烦\u003c/p\u003e\n\u003cp\u003e建议 用  install ollama\u003c/p\u003e\n\u003cp\u003e卸载也方便snap remove\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/138470183?spm=1001.2014.3001.5502\"\u003elinux的ollama卸载\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"linux的ollama卸载"},{"content":"查看网络：\ndocker network list 选择一个bridge网络，重新部署容器的时候加进去\u0026ndash; 1panel-network：\ndocker run -d -p 3001:8080 --network 1panel-network 这样容器就可以引入本机网络的或者其他端口，建议关闭\n原文链接: 解决docker容器不能问本机网络 作者: lixusocool\n","permalink":"http://testai666.cn/posts/%E8%A7%A3%E5%86%B3docker%E5%AE%B9%E5%99%A8%E4%B8%8D%E8%83%BD%E9%97%AE%E6%9C%AC%E6%9C%BA%E7%BD%91%E7%BB%9C/","summary":"\u003cp\u003e查看网络：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#a6e22e\"\u003edocker\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003enetwork\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003elist\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/posts/images/e311d216d3223b5fe81b6ea1fcd6761b.png\"\u003e\u003c/p\u003e\n\u003cp\u003e选择一个bridge网络，重新部署容器的时候加进去\u0026ndash; 1panel-network：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-js\" data-lang=\"js\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e \u003cspan style=\"color:#a6e22e\"\u003edocker\u003c/span\u003e \u003cspan style=\"color:#a6e22e\"\u003erun\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ed\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003ep\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e3001\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e:\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e8080\u003c/span\u003e  \u003cspan style=\"color:#f92672\"\u003e--\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003enetwork\u003c/span\u003e \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003epanel\u003c/span\u003e\u003cspan style=\"color:#f92672\"\u003e-\u003c/span\u003e\u003cspan style=\"color:#a6e22e\"\u003enetwork\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这样容器就可以引入本机网络的或者其他端口，建议关闭\u003c/p\u003e\n\u003chr\u003e\n\u003cblockquote\u003e\n\u003cp\u003e原文链接: \u003ca href=\"https://blog.csdn.net/lixusocool/article/details/138425922?spm=1001.2014.3001.5502\"\u003e解决docker容器不能问本机网络\u003c/a\u003e\n作者: lixusocool\u003c/p\u003e\n\u003c/blockquote\u003e","title":"解决docker容器不能问本机网络"}]