feat: 增强科研软件的 a11y tree 支持
- 扩展 heuristic_retrieve.py 白名单以覆盖科研软件 GUI 框架:
- 新增 prefix 规则: sunawt (Java Swing), qt5q/qt6q (Qt), ovito, pymol,
contentspanel, wx (wxWidgets), afx (MFC), thunderrt (VB6)
- 新增 endswith 规则: edit, widget, box, dialog, view, frame, menuitem,
menubar, toolbar, tabitem, treeitem, window
- 新增 Qt 控件和 Win32 控件的精确匹配
- 在 agent.py 中添加原始 a11y tree 的调试日志
- 修复 run.py 中 agent 初始化缺少 platform='windows' 的问题
- 添加 NO_PROXY 绕过本地/VM IP (兼容 Clash 全局代理)
- lib_run_single.py 中应用启动等待时间增加到 15 秒
- 新增 test_each_domain_a11y_tree.json (每个域一个任务用于 a11y 验证)
This commit is contained in:
@@ -329,7 +329,7 @@ class PromptAgent:
|
||||
raise ValueError("Invalid action space: " + action_space)
|
||||
else:
|
||||
raise ValueError("Invalid experiment type: " + observation_type)
|
||||
|
||||
|
||||
self.system_message = self.system_message.format(CLIENT_PASSWORD=self.client_password, SCREEN_WIDTH=self.screen_width, SCREEN_HEIGHT=self.screen_height)
|
||||
|
||||
def predict(self, instruction: str, obs: Dict) -> List:
|
||||
@@ -502,9 +502,30 @@ class PromptAgent:
|
||||
]
|
||||
})
|
||||
elif self.observation_type == "a11y_tree":
|
||||
# Debug: log raw a11y tree XML to help diagnose missing elements
|
||||
raw_tree = obs["accessibility_tree"]
|
||||
if raw_tree:
|
||||
# Log first 2000 chars of raw XML and count total nodes
|
||||
root = ET.fromstring(raw_tree)
|
||||
all_tags = set()
|
||||
total_nodes = 0
|
||||
for node in root.iter():
|
||||
all_tags.add(node.tag)
|
||||
total_nodes += 1
|
||||
logger.info("Raw a11y tree: %d total nodes, unique tags: %s", total_nodes, all_tags)
|
||||
logger.debug("Raw a11y tree XML (first 2000 chars): %s", raw_tree[:2000])
|
||||
# Also log nodes containing 'avogadro' or 'qt5' in their attributes
|
||||
for node in root.iter():
|
||||
node_str = ET.tostring(node, encoding="unicode")
|
||||
if 'avogadro' in node_str.lower() or 'qt5' in node_str.lower():
|
||||
logger.info("Avogadro/Qt5 node: tag=%s, name=%s, visible=%s, enabled=%s",
|
||||
node.tag, node.get("name", ""),
|
||||
node.get("{https://accessibility.windows.example.org/ns/state}visible", "?"),
|
||||
node.get("{https://accessibility.windows.example.org/ns/state}enabled", "?"))
|
||||
linearized_accessibility_tree = linearize_accessibility_tree(accessibility_tree=obs["accessibility_tree"],
|
||||
platform=self.platform)
|
||||
logger.debug("LINEAR AT: %s", linearized_accessibility_tree)
|
||||
logger.info("Linearized a11y tree lines: %d", len(linearized_accessibility_tree.split('\n')) if linearized_accessibility_tree else 0)
|
||||
|
||||
if linearized_accessibility_tree:
|
||||
linearized_accessibility_tree = trim_accessibility_tree(linearized_accessibility_tree,
|
||||
@@ -670,7 +691,7 @@ class PromptAgent:
|
||||
return response.json()['choices'][0]['message']['content']
|
||||
elif self.model.startswith("gpt"):
|
||||
# Support custom OpenAI base URL via environment variable
|
||||
base_url = os.environ.get('OPENAI_BASE_URL', 'https://api.openai.com')
|
||||
base_url = os.environ.get('OPENAI_BASE_URL', os.environ.get('OPENAI_API_BASE', 'https://api.openai.com'))
|
||||
# Smart handling: avoid duplicate /v1 if base_url already ends with /v1
|
||||
api_url = f"{base_url}/chat/completions" if base_url.endswith('/v1') else f"{base_url}/v1/chat/completions"
|
||||
headers = {
|
||||
@@ -1153,7 +1174,7 @@ class PromptAgent:
|
||||
except Exception as e:
|
||||
print("Failed to call LLM: " + str(e))
|
||||
return ""
|
||||
|
||||
|
||||
else:
|
||||
raise ValueError("Invalid model: " + self.model)
|
||||
|
||||
@@ -1192,4 +1213,4 @@ class PromptAgent:
|
||||
|
||||
self.thoughts = []
|
||||
self.actions = []
|
||||
self.observations = []
|
||||
self.observations = []
|
||||
|
||||
Reference in New Issue
Block a user