Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Python Agent 开发规范(Windows wxauto v4),包括项目结构、模块化、wxauto 使用、IPC 集成、错误处理、测试和部署。
.claude/skills/majiayu000-python-agent-development/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 112% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 52% | 0% |
Expert guidance for developing Windows Platform Agent using Python 3.12 + wxauto v4.
platform_agents/windows_agent/
├── agent.py # 主入口
├── wechat_monitor.py # 微信监听模块
├── input_writer.py # 输入框控制模块
├── ipc/ # IPC 通信模块
│ ├── message_sender.py
│ └── command_receiver.py
├── utils/ # 工具模块
│ ├── logger.py
│ └── config.py
├── requirements.txt # 依赖列表
├── tests/ # 测试目录
│ ├── test_monitor.py
│ └── test_writer.py
└── README.mdtxtwxauto==4.0.0 pywin32>=305
python# agent.py import sys import time from wechat_monitor import WeChatMonitor from input_writer import WeChatInputWriter from ipc.message_sender import MessageSender from ipc.command_receiver import CommandReceiver class WindowsAgent: def __init__(self): self.monitor = WeChatMonitor(interval_ms=500) self.input_writer = WeChatInputWriter() self.command_receiver = CommandReceiver() self.setup_command_handlers() def setup_command_handlers(self): """注册命令处理器""" self.command_receiver.register_handler("WriteInput", self.handle_write_input) self.command_receiver.register_handler("ClearInput", self.handle_clear_input) self.command_receiver.register_handler("HealthCheck", self.handle_health_check) self.command_receiver.register_handler("Stop", self.handle_stop) def handle_write_input(self, command): content = command.get('content', '') success = self.input_writer.write_to_input(content) return success def handle_clear_input(self, command): success = self.input_writer.clear_input() return success def handle_health_check(self, command): MessageSender.send_health_status("ok", "windows_wxauto") return None # 不发送 CommandResponse def handle_stop(self, command): self.command_receiver.stop() return True def run(self): """启动 Agent""" try: # 启动命令监听(后台线程) self.command_receiver.start_listening_async() # 启动微信监听(主线程) self.monitor.start_monitoring() except KeyboardInterrupt: MessageSender.send_error("Agent 被用户中断") except Exception as e: MessageSender.send_error(f"Agent 运行错误: {str(e)}") finally: sys.exit(0) if __name__ == '__main__': agent = WindowsAgent() agent.run()
python# wechat_monitor.py from wxauto import WeChat from ipc.message_sender import MessageSender import time class WeChatMonitor: def __init__(self, interval_ms=500): self.wechat = WeChat() self.interval_ms = interval_ms self.last_message_id = None def start_monitoring(self): while True: try: messages = self.wechat.GetAllMessage() if messages: self.process_messages(messages) time.sleep(self.interval_ms / 1000.0) except Exception as e: MessageSender.send_error(f"监听错误: {str(e)}") time.sleep(1) # 错误后延迟 def process_messages(self, messages): if not messages: return latest = messages[-1] message_id = self.generate_message_id(latest) if message_id != self.last_message_id: self.last_message_id = message_id MessageSender.send_message_new( content=latest.get('content', ''), sender=latest.get('sender', ''), timestamp=latest.get('time', '') ) @staticmethod def generate_message_id(message): content = message.get('content', '') sender = message.get('sender', '') timestamp = message.get('time', '') return f"{sender}:{timestamp}:{hash(content)}"
pythonfrom typing import Dict, Any, Optional, List def process_command(command: Dict[str, Any]) -> Optional[bool]: """ 处理命令 Args: command: 命令字典 Returns: Optional[bool]: True 成功,False 失败,None 不需要响应 """ pass
pythondef safe_operation() -> bool: """执行可能失败的操作""" try: # 执行操作 result = perform_operation() return True except SpecificException as e: # 处理特定异常 MessageSender.send_error(f"操作失败: {str(e)}", "OPERATION_FAILED") return False except Exception as e: # 处理通用异常 MessageSender.send_error(f"未知错误: {str(e)}", "UNKNOWN_ERROR") return False
pythonclass RobustMonitor: def __init__(self, max_retries=3): self.max_retries = max_retries def start_with_retry(self): retry_count = 0 while retry_count < self.max_retries: try: self.start_monitoring() break except Exception as e: retry_count += 1 MessageSender.send_error(f"监听失败 (尝试 {retry_count}/{self.max_retries}): {str(e)}") if retry_count < self.max_retries: time.sleep(2) else: sys.exit(1)
python# utils/logger.py import logging import sys def setup_logger(name: str, level=logging.INFO): """设置日志记录器""" logger = logging.getLogger(name) logger.setLevel(level) handler = logging.StreamHandler(sys.stderr) # 输出到 stderr(不影响 stdout IPC) handler.setLevel(level) formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) handler.setFormatter(formatter) logger.addHandler(handler) return logger # 使用 logger = setup_logger(__name__) logger.info("Agent started") logger.error("Error occurred", exc_info=True)
python# tests/test_monitor.py import unittest from unittest.mock import Mock, patch from wechat_monitor import WeChatMonitor class TestWeChatMonitor(unittest.TestCase): def setUp(self): self.monitor = WeChatMonitor(interval_ms=500) def test_generate_message_id(self): message = { 'content': '测试消息', 'sender': '张三', 'time': '2024-01-23 10:30:00' } message_id = WeChatMonitor.generate_message_id(message) self.assertIsInstance(message_id, str) self.assertIn('张三', message_id) @patch('wxauto.WeChat') def test_monitoring_initialization(self, mock_wechat): monitor = WeChatMonitor() self.assertEqual(monitor.interval_ms, 500) self.assertIsNone(monitor.last_message_id) if __name__ == '__main__': unittest.main()
python# tests/test_integration.py import unittest import json import sys from io import StringIO from agent import WindowsAgent class TestAgentIntegration(unittest.TestCase): def test_command_handling(self): agent = WindowsAgent() # 模拟命令 command = {"type": "HealthCheck"} result = agent.handle_health_check(command) self.assertIsNone(result) # 健康检查不返回 CommandResponse
pythonclass MemoryEfficientMonitor: def __init__(self): self.message_buffer_size = 100 self.message_buffer = [] def add_message(self, message): self.message_buffer.append(message) if len(self.message_buffer) > self.message_buffer_size: self.message_buffer = self.message_buffer[-self.message_buffer_size:] import gc gc.collect()
pythonimport asyncio class AsyncMonitor: async def monitor_async(self): """异步监听(可选)""" while True: messages = await asyncio.to_thread(self.wechat.GetAllMessage) if messages: self.process_messages(messages) await asyncio.sleep(self.interval_ms / 1000.0)
bash# 安装 PyInstaller pip install pyinstaller # 打包为单文件可执行程序 pyinstaller --onefile --name windows_agent agent.py # 输出:dist/windows_agent.exe
python# -*- mode: python ; coding: utf-8 -*- a = Analysis( ['agent.py'], pathex=[], binaries=[], datas=[], hiddenimports=['wxauto'], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=None, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=None) exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='windows_agent', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, # 保留控制台(用于 stdin/stdout) )
pythondef validate_content(content: str) -> bool: """验证输入内容""" if not content: return False if len(content) > 10000: # 最大 10KB return False return True
python# ✓ Good - 从环境变量读取 import os api_key = os.getenv('DEEPSEEK_API_KEY') # ✗ Bad - 硬编码 api_key = "sk-xxxxx" # 不要这样做
Activate this skill when:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 61,268 | 23,846 | -61% | 1 | 1 | 0% | 5,109 | 6,585 | +29% | 0 | 0 | — |
case-17 | fail→pass | 19,021 | 11,728 | -38% | 1 | 1 | 0% | 2,265 | 3,973 | +75% | 0 | 0 | — |
case-02 | fail→fail | 30,717 | 17,947 | -42% | 1 | 1 | 0% | 4,890 | 6,282 | +28% | 0 | 0 | — |
case-03 | pass→pass | 25,920 | 11,389 | -56% | 1 | 1 | 0% | 3,969 | 5,109 | +29% | 0 | 0 | — |
case-04 | fail→pass | 15,229 | 4,911 | -68% | 1 | 1 | 0% | 1,689 | 3,586 | +112% | 0 | 0 | — |
case-05 | fail→fail | 20,177 | 22,119 | +10% | 1 | 1 | 0% | 3,695 | 5,954 | +61% | 0 | 0 | — |
case-06 | fail→pass | 16,693 | 4,760 | -71% | 1 | 1 | 0% | 2,412 | 3,635 | +51% | 0 | 0 | — |
case-07 | fail→pass | 17,972 | 8,851 | -51% | 1 | 1 | 0% | 2,926 | 4,444 | +52% | 0 | 0 | — |
case-08 | pass→fail | 29,236 | 33,145 | +13% | 1 | 1 | 0% | 3,307 | 5,954 | +80% | 0 | 0 | — |
case-09 | fail→pass | 14,124 | 10,385 | -26% | 1 | 1 | 0% | 1,630 | 3,713 | +128% | 0 | 0 | — |
case-10 | pass→pass | 11,808 | 9,928 | -16% | 1 | 1 | 0% | 1,158 | 3,570 | +208% | 0 | 0 | — |
case-11 | fail→pass | 20,731 | 20,558 | -1% | 1 | 1 | 0% | 2,633 | 5,543 | +111% | 0 | 0 | — |
case-12 | pass→pass | 13,717 | 18,105 | +32% | 1 | 1 | 0% | 2,691 | 5,552 | +106% | 0 | 0 | — |
case-13 | fail→fail | 17,626 | 20,465 | +16% | 1 | 1 | 0% | 2,943 | 5,548 | +89% | 0 | 0 | — |
case-14 | pass→fail | 22,339 | 22,490 | +1% | 1 | 1 | 0% | 2,981 | 6,215 | +108% | 0 | 0 | — |
case-15 | fail→pass | 25,088 | 20,442 | -19% | 1 | 1 | 0% | 3,606 | 5,629 | +56% | 0 | 0 | — |
case-16 | fail→pass | 12,590 | 12,468 | -1% | 1 | 1 | 0% | 1,236 | 4,177 | +238% | 0 | 0 | — |
case-18 | fail→fail | 20,488 | 16,538 | -19% | 1 | 1 | 0% | 3,355 | 4,778 | +42% | 0 | 0 | — |
case-19 | fail→pass | 14,592 | 16,119 | +10% | 1 | 1 | 0% | 1,933 | 4,310 | +123% | 0 | 0 | — |
case-20 | pass→pass | 27,879 | 28,978 | +4% | 1 | 1 | 0% | 4,255 | 7,701 | +81% | 0 | 0 | — |
case-21 | pass→pass | 15,037 | 30,944 | +106% | 1 | 1 | 0% | 2,569 | 5,550 | +116% | 0 | 0 | — |
case-22 | pass→fail | 23,166 | 26,913 | +16% | 1 | 1 | 0% | 4,323 | 7,042 | +63% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 4 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.