基于 disburse-payroll-icbc 实战经验沉淀,让所有未来新 skill 复制模板后直接 享受本系列全部改进: - record_screencast.py.sample: · 字幕脚本改用 "step: N/X" 模式 + ASCII 关键词锚(避免编码问题) · 展示三元组 (keyword, text, duration) 用法(依赖 platform-kit subtitle.py) · 加 TEST_FILE_NAME / MUSIC_SUBDIR 切换常量 · 文档说明绕开 SDK ask() / send_file 的最佳实践 - test_desktop_smoke.py.sample: · docstring 加"何时用此模板 / 何时改用 _with_attachment"指引 · 标注已知 SDK 限制(ask() user_idx<0 / send_file NotImplementedError) - test_desktop_smoke_with_attachment.py.sample(新增): · 完整附件场景模板,import jiangchang_desktop_sdk.e2e_helpers 6 个 API · 7 步 step: N/7 模式,跟 record_screencast.py.sample 字幕脚本对齐 · 占位符 SKILL_SLUG / PROMPT / EXPECTED_KEYWORDS_IN_REPLY - README.md: 追加 3 节 · 模板选择指南(纯问答 vs 附件场景) · 已知 SDK 坑(send_file / ask / 字幕关键词 / bring_to_front) · 录屏新能力(activate_window / monitor_index / 字幕三元组) - conftest.py: 同步到支持新版 platform-kit src/ 布局的 SDK 路径推断 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
3.1 KiB
Plaintext
76 lines
3.1 KiB
Plaintext
# -*- coding: utf-8 -*-
|
||
"""
|
||
可选桌面 E2E 示例(默认不执行:文件名后缀为 .sample)—— **简单问答场景**。
|
||
|
||
启用方式:
|
||
复制本文件为 test_desktop_smoke.py,按目标技能修改提问文案与断言。
|
||
运行:pytest tests/desktop/test_desktop_smoke.py -v -s
|
||
|
||
依赖:pytest、jiangchang_desktop_sdk、匠厂桌面宿主已就绪。
|
||
|
||
【何时用此模板】
|
||
- 技能只需要发一句话给 Main Agent,等回复,做断言(纯问答)
|
||
- 不涉及上传附件 / 不涉及 RPA 长流程
|
||
|
||
【何时不要用此模板,改用 test_desktop_smoke_with_attachment.py.sample】
|
||
- 需要上传文件(Excel / PDF / 图片)作为附件
|
||
- 技能内部跑 RPA / 长流程(> 2 分钟),需要精细等待
|
||
|
||
【已知 SDK 限制(来自 disburse-payroll-icbc 实战)】
|
||
- client.ask() 内部对"找到 user 消息节点"只给 15 秒窗口,找不到就锁死
|
||
user_msg_index=-1,后续等待 assistant 永远 return None。如果你的场景里
|
||
匠厂启动较慢 / gateway 握手有延迟,client.ask() 可能假死到 timeout。
|
||
此模板仍用 ask(),因为它对**简单场景**够用。复杂场景请用 _with_attachment.py.sample
|
||
里的"绕开 ask()"模式。
|
||
- client.send_file() 在 v2.0.17+ 抛 NotImplementedError,**绝不要调用它**。
|
||
需要附件请用 _with_attachment.py.sample 里的拖拽方案。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import os
|
||
|
||
import pytest
|
||
|
||
from jiangchang_desktop_sdk import AskOptions, JiangchangDesktopClient
|
||
|
||
_logger = logging.getLogger("skill-template-desktop-e2e-sample")
|
||
if not _logger.handlers:
|
||
_logger.setLevel(logging.INFO)
|
||
_h = logging.StreamHandler()
|
||
_h.setFormatter(logging.Formatter("[E2E-sample] %(message)s"))
|
||
_logger.addHandler(_h)
|
||
|
||
_ARTIFACT_DIR = os.path.join(os.path.dirname(__file__), "artifacts")
|
||
|
||
|
||
class TestDesktopSmokeSample:
|
||
"""最小 smoke:拉起桌面、等待网关、发一条与技能路由相关的问句并做宽松断言。"""
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def connect_and_teardown(self, request):
|
||
client = JiangchangDesktopClient()
|
||
_logger.info("ensure_app_running")
|
||
client.ensure_app_running(wait_timeout_s=30)
|
||
client.bring_to_front()
|
||
client.wait_gateway_ready(timeout_ms=30000)
|
||
self.client = client
|
||
yield
|
||
try:
|
||
rep = getattr(request.node, "rep_call", None)
|
||
if rep is not None and rep.failed:
|
||
os.makedirs(_ARTIFACT_DIR, exist_ok=True)
|
||
paths = client.snapshot(_ARTIFACT_DIR, tag=request.node.name)
|
||
_logger.error("snapshot: %s", paths)
|
||
finally:
|
||
client.disconnect()
|
||
|
||
def test_gateway_and_simple_ask(self) -> None:
|
||
"""请将 SKILL 名与路由关键词改成你复制后的真实技能。"""
|
||
answer = self.client.ask(
|
||
"请使用当前已安装技能执行一次 health 检查并返回 OK 或等价结果。",
|
||
AskOptions(timeout=120000, new_task=True),
|
||
)
|
||
assert answer
|
||
assert "OK" in answer or "ok" in answer.lower() or "健康" in answer
|