iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0

Day 18: 人機協作介面設計

今天我們要打造直覺友善的人機協作介面!透過精心設計的互動流程和視覺回饋,讓使用者能夠輕鬆地與 AI 助理協作,共同完成複雜任務。

🤝 什麼是人機協作?

人機協作不是單純的問答,而是人類和 AI 共同工作:

  • 💬 互動式對話:動態調整對話流程
  • 🎯 漸進式引導:分步驟引導完成任務
  • 確認與修正:AI 建議 + 人工審核
  • 🔄 迭代優化:根據反饋持續改進
  • 📊 視覺化回饋:清晰的進度和狀態展示

良好的人機協作介面能讓複雜任務變得簡單,提升工作效率和滿意度。

🏗 專案結構

human_ai_collaboration/
├── main.py                          # 主程式
├── core/
│   ├── __init__.py
│   ├── collaboration_engine.py      # 協作引擎
│   └── interaction_manager.py       # 互動管理器
├── interfaces/
│   ├── __init__.py
│   ├── guided_workflow.py           # 引導式工作流程
│   ├── confirmation_dialog.py       # 確認對話框
│   └── progress_tracker.py          # 進度追蹤器
├── ui/
│   ├── __init__.py
│   ├── console_ui.py                # 終端介面
│   └── ui_components.py             # UI 組件
└── utils/
    ├── __init__.py
    └── input_validator.py           # 輸入驗證器

🔧 核心實作

1. UI 組件 (ui/ui_components.py)

from typing import List, Dict, Any
import time

class UIComponents:
    """終端 UI 組件庫"""
    
    @staticmethod
    def print_header(title: str, width: int = 60):
        """列印標題"""
        print("\n" + "=" * width)
        print(f"{title:^{width}}")
        print("=" * width)
    
    @staticmethod
    def print_section(title: str):
        """列印區段標題"""
        print(f"\n{'─' * 50}")
        print(f"📌 {title}")
        print('─' * 50)
    
    @staticmethod
    def print_step(step_num: int, total_steps: int, description: str):
        """列印步驟資訊"""
        progress = "●" * step_num + "○" * (total_steps - step_num)
        print(f"\n[{progress}] 步驟 {step_num}/{total_steps}")
        print(f"🎯 {description}")
    
    @staticmethod
    def print_options(options: List[str], title: str = "請選擇"):
        """列印選項列表"""
        print(f"\n{title}:")
        for i, option in enumerate(options, 1):
            print(f"  {i}. {option}")
    
    @staticmethod
    def print_confirmation(message: str, details: Dict[str, Any] = None):
        """列印確認訊息"""
        print(f"\n⚠️  {message}")
        
        if details:
            print("\n詳細資訊:")
            for key, value in details.items():
                print(f"  • {key}: {value}")
    
    @staticmethod
    def print_success(message: str):
        """列印成功訊息"""
        print(f"\n✅ {message}")
    
    @staticmethod
    def print_error(message: str):
        """列印錯誤訊息"""
        print(f"\n❌ {message}")
    
    @staticmethod
    def print_info(message: str):
        """列印資訊訊息"""
        print(f"\nℹ️  {message}")
    
    @staticmethod
    def print_progress_bar(current: int, total: int, prefix: str = "進度"):
        """列印進度條"""
        percentage = current / total if total > 0 else 0
        bar_length = 30
        filled = int(bar_length * percentage)
        bar = "█" * filled + "░" * (bar_length - filled)
        
        print(f"\r{prefix}: [{bar}] {percentage:.0%} ({current}/{total})", end="")
        
        if current >= total:
            print()  # 完成時換行
    
    @staticmethod
    def animate_thinking(duration: float = 2.0):
        """思考動畫"""
        frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
        end_time = time.time() + duration
        
        i = 0
        while time.time() < end_time:
            print(f"\r🤔 思考中 {frames[i % len(frames)]}", end="")
            time.sleep(0.1)
            i += 1
        
        print("\r" + " " * 20 + "\r", end="")  # 清除動畫
    
    @staticmethod
    def create_box(content: str, width: int = 50):
        """創建文字框"""
        lines = content.split('\n')
        
        print("┌" + "─" * (width - 2) + "┐")
        
        for line in lines:
            padding = width - len(line) - 4
            print(f"│ {line}{' ' * padding} │")
        
        print("└" + "─" * (width - 2) + "┘")

2. 互動管理器 (core/interaction_manager.py)

from typing import Dict, Any, List, Callable, Optional
from ui.ui_components import UIComponents

class InteractionManager:
    """互動管理器"""
    
    def __init__(self):
        self.ui = UIComponents()
        self.interaction_history = []
    
    def guided_input(self, prompt: str, 
                    input_type: str = "text",
                    options: List[str] = None,
                    validator: Callable = None,
                    default: Any = None) -> Any:
        """引導式輸入"""
        
        while True:
            # 顯示選項(如果有)
            if options:
                self.ui.print_options(options, prompt)
                user_input = input("\n請選擇:").strip()
                
                # 驗證選項
                if user_input.isdigit() and 1 <= int(user_input) <= len(options):
                    result = options[int(user_input) - 1]
                    self._log_interaction(prompt, result)
                    return result
                else:
                    self.ui.print_error("無效的選擇,請重新輸入")
                    continue
            
            # 一般輸入
            display_prompt = f"\n{prompt}"
            if default:
                display_prompt += f" (預設: {default})"
            display_prompt += ":"
            
            user_input = input(display_prompt).strip()
            
            # 使用預設值
            if not user_input and default is not None:
                user_input = str(default)
            
            # 類型轉換
            try:
                if input_type == "int":
                    result = int(user_input)
                elif input_type == "float":
                    result = float(user_input)
                elif input_type == "bool":
                    result = user_input.lower() in ['yes', 'y', 'true', '是']
                else:
                    result = user_input
                
                # 自訂驗證
                if validator and not validator(result):
                    self.ui.print_error("輸入不符合要求,請重新輸入")
                    continue
                
                self._log_interaction(prompt, result)
                return result
                
            except ValueError:
                self.ui.print_error(f"請輸入有效的 {input_type} 格式")
    
    def confirm_action(self, action: str, 
                      details: Dict[str, Any] = None,
                      default: bool = True) -> bool:
        """確認動作"""
        self.ui.print_confirmation(f"確認要{action}嗎?", details)
        
        default_text = "Y/n" if default else "y/N"
        response = input(f"\n請確認 ({default_text}):").strip().lower()
        
        if not response:
            return default
        
        result = response in ['y', 'yes', '是']
        self._log_interaction(f"確認{action}", result)
        
        return result
    
    def multi_step_input(self, steps: List[Dict[str, Any]]) -> Dict[str, Any]:
        """多步驟輸入"""
        results = {}
        total_steps = len(steps)
        
        self.ui.print_header("多步驟資料收集")
        
        for i, step in enumerate(steps, 1):
            self.ui.print_step(i, total_steps, step['description'])
            
            result = self.guided_input(
                prompt=step.get('prompt', step['description']),
                input_type=step.get('type', 'text'),
                options=step.get('options'),
                validator=step.get('validator'),
                default=step.get('default')
            )
            
            results[step['key']] = result
        
        self.ui.print_success("資料收集完成!")
        
        return results
    
    def show_preview(self, title: str, data: Dict[str, Any]):
        """顯示預覽"""
        self.ui.print_section(f"{title} - 預覽")
        
        for key, value in data.items():
            print(f"  • {key}: {value}")
    
    def iterative_refinement(self, initial_result: str,
                           refine_func: Callable,
                           max_iterations: int = 3) -> str:
        """迭代式優化"""
        current_result = initial_result
        iteration = 0
        
        while iteration < max_iterations:
            iteration += 1
            
            self.ui.print_section(f"第 {iteration} 次結果")
            self.ui.create_box(current_result)
            
            # 詢問是否滿意
            satisfied = self.confirm_action(
                "接受此結果",
                {"迭代次數": iteration, "剩餘機會": max_iterations - iteration}
            )
            
            if satisfied:
                self.ui.print_success("結果已確認!")
                return current_result
            
            # 獲取改進建議
            if iteration < max_iterations:
                feedback = input("\n請描述您希望如何改進:").strip()
                
                if feedback:
                    self.ui.animate_thinking(1.5)
                    current_result = refine_func(current_result, feedback)
        
        self.ui.print_info("已達到最大迭代次數,使用最後的結果")
        return current_result
    
    def _log_interaction(self, prompt: str, response: Any):
        """記錄互動歷史"""
        self.interaction_history.append({
            'prompt': prompt,
            'response': response,
            'timestamp': time.time()
        })

3. 引導式工作流程 (interfaces/guided_workflow.py)

from typing import Dict, Any, List
from core.interaction_manager import InteractionManager
from ui.ui_components import UIComponents
import google.generativeai as genai
import os

genai.configure(api_key=os.getenv('GEMINI_API_KEY'))

class GuidedWorkflow:
    """引導式工作流程"""
    
    def __init__(self):
        self.interaction = InteractionManager()
        self.ui = UIComponents()
        self.model = genai.GenerativeModel('gemini-2.5-flash')
    
    def document_creation_workflow(self) -> Dict[str, Any]:
        """文件創建工作流程"""
        self.ui.print_header("📝 AI 輔助文件創建工作流程")
        
        # 步驟 1: 收集基本資訊
        self.ui.print_section("步驟 1: 文件類型選擇")
        
        doc_type = self.interaction.guided_input(
            "選擇要創建的文件類型",
            options=["商業企劃書", "技術文件", "會議記錄", "專案報告"]
        )
        
        # 步驟 2: 多步驟資訊收集
        self.ui.print_section("步驟 2: 詳細資訊收集")
        
        info_steps = [
            {
                'key': 'title',
                'description': '文件標題',
                'prompt': '請輸入文件標題',
                'type': 'text'
            },
            {
                'key': 'target_audience',
                'description': '目標讀者',
                'prompt': '請描述目標讀者',
                'type': 'text',
                'default': '一般讀者'
            },
            {
                'key': 'length',
                'description': '預期長度',
                'prompt': '預期字數',
                'type': 'int',
                'default': 1000
            },
            {
                'key': 'tone',
                'description': '寫作風格',
                'prompt': '選擇寫作風格',
                'options': ['正式', '輕鬆', '技術性', '說服性']
            }
        ]
        
        details = self.interaction.multi_step_input(info_steps)
        details['doc_type'] = doc_type
        
        # 步驟 3: 預覽確認
        self.interaction.show_preview("文件資訊", details)
        
        if not self.interaction.confirm_action("開始生成文件", details):
            self.ui.print_info("已取消")
            return {}
        
        # 步驟 4: 生成初稿
        self.ui.print_section("步驟 3: AI 生成初稿")
        self.ui.animate_thinking(2.0)
        
        draft = self._generate_document(details)
        
        # 步驟 5: 迭代優化
        self.ui.print_section("步驟 4: 迭代優化")
        
        final_document = self.interaction.iterative_refinement(
            initial_result=draft,
            refine_func=self._refine_document,
            max_iterations=3
        )
        
        return {
            'document': final_document,
            'metadata': details
        }
    
    def _generate_document(self, details: Dict[str, Any]) -> str:
        """生成文件"""
        prompt = f"""
        請生成一份{details['doc_type']},要求如下:
        
        標題:{details['title']}
        目標讀者:{details['target_audience']}
        預期長度:約 {details['length']} 字
        寫作風格:{details['tone']}
        
        請生成結構完整、內容充實的文件。
        """
        
        try:
            response = self.model.generate_content(prompt)
            return response.text
        except Exception as e:
            return f"生成失敗:{str(e)}"
    
    def _refine_document(self, current_doc: str, feedback: str) -> str:
        """優化文件"""
        prompt = f"""
        請根據以下反饋改進文件:
        
        當前文件:
        {current_doc}
        
        改進要求:
        {feedback}
        
        請提供改進後的完整文件。
        """
        
        try:
            response = self.model.generate_content(prompt)
            return response.text
        except Exception as e:
            return current_doc
    
    def task_planning_workflow(self) -> Dict[str, Any]:
        """任務規劃工作流程"""
        self.ui.print_header("🎯 AI 輔助任務規劃工作流程")
        
        # 收集任務資訊
        task_info = self.interaction.multi_step_input([
            {
                'key': 'task_name',
                'description': '任務名稱',
                'prompt': '請輸入任務名稱',
                'type': 'text'
            },
            {
                'key': 'deadline',
                'description': '截止日期',
                'prompt': '截止日期(例如:2024-12-31)',
                'type': 'text'
            },
            {
                'key': 'complexity',
                'description': '複雜度評估',
                'prompt': '任務複雜度',
                'options': ['簡單', '中等', '複雜', '非常複雜']
            },
            {
                'key': 'team_size',
                'description': '團隊規模',
                'prompt': '參與人數',
                'type': 'int',
                'default': 1
            }
        ])
        
        # 生成任務計劃
        self.ui.print_section("生成任務計劃")
        self.ui.animate_thinking(2.0)
        
        plan = self._generate_task_plan(task_info)
        
        # 顯示計劃
        self.ui.create_box(plan)
        
        # 確認並儲存
        if self.interaction.confirm_action("接受並儲存此計劃", task_info):
            self.ui.print_success("任務計劃已儲存!")
            return {
                'plan': plan,
                'task_info': task_info
            }
        
        return {}
    
    def _generate_task_plan(self, task_info: Dict[str, Any]) -> str:
        """生成任務計劃"""
        prompt = f"""
        請為以下任務生成詳細的執行計劃:
        
        任務名稱:{task_info['task_name']}
        截止日期:{task_info['deadline']}
        複雜度:{task_info['complexity']}
        團隊規模:{task_info['team_size']} 人
        
        請包含:
        1. 任務分解(子任務列表)
        2. 時間規劃
        3. 資源分配
        4. 風險評估
        5. 關鍵里程碑
        """
        
        try:
            response = self.model.generate_content(prompt)
            return response.text
        except Exception as e:
            return f"生成失敗:{str(e)}"

4. 主程式 (main.py)

from interfaces.guided_workflow import GuidedWorkflow
from ui.ui_components import UIComponents

def main():
    """人機協作介面主程式"""
    ui = UIComponents()
    workflow = GuidedWorkflow()
    
    ui.print_header("🤝 人機協作智能系統")
    
    print("""
    本系統提供以下人機協作功能:
    
    📝 文件創建:AI 輔助生成各類文件,支援迭代優化
    🎯 任務規劃:智能生成專案計劃和時間表
    💬 互動對話:引導式對話流程,輕鬆完成複雜任務
    ✅ 確認機制:關鍵步驟需要人工確認,確保準確性
    """)
    
    while True:
        ui.print_section("主選單")
        
        options = [
            "📝 創建文件",
            "🎯 規劃任務",
            "📊 查看互動歷史",
            "🚪 退出系統"
        ]
        
        ui.print_options(options, "請選擇功能")
        
        try:
            choice = input("\n請選擇 (1-4):").strip()
            
            if choice == '1':
                result = workflow.document_creation_workflow()
                
                if result:
                    ui.print_success("文件創建完成!")
                    
                    # 詢問是否儲存
                    if workflow.interaction.confirm_action("將文件儲存到檔案"):
                        filename = input("檔案名稱:").strip() or "document.txt"
                        
                        try:
                            with open(filename, 'w', encoding='utf-8') as f:
                                f.write(result['document'])
                            ui.print_success(f"已儲存到 {filename}")
                        except Exception as e:
                            ui.print_error(f"儲存失敗:{e}")
            
            elif choice == '2':
                result = workflow.task_planning_workflow()
                
                if result:
                    # 詢問是否匯出
                    if workflow.interaction.confirm_action("匯出任務計劃"):
                        filename = input("檔案名稱:").strip() or "task_plan.txt"
                        
                        try:
                            with open(filename, 'w', encoding='utf-8') as f:
                                f.write(result['plan'])
                            ui.print_success(f"已匯出到 {filename}")
                        except Exception as e:
                            ui.print_error(f"匯出失敗:{e}")
            
            elif choice == '3':
                history = workflow.interaction.interaction_history
                
                if history:
                    ui.print_section("互動歷史記錄")
                    for i, interaction in enumerate(history[-10:], 1):
                        print(f"\n{i}. {interaction['prompt']}")
                        print(f"   回應: {interaction['response']}")
                else:
                    ui.print_info("尚無互動記錄")
            
            elif choice == '4':
                if workflow.interaction.confirm_action("退出系統"):
                    ui.print_success("感謝使用!再見 👋")
                    break
            
            else:
                ui.print_error("無效的選擇")
        
        except KeyboardInterrupt:
            print("\n")
            if workflow.interaction.confirm_action("退出系統"):
                ui.print_success("感謝使用!再見 👋")
                break
        
        except Exception as e:
            ui.print_error(f"發生錯誤:{e}")
            print("請重試或選擇其他功能")

if __name__ == "__main__":
    main()

🎯 系統特色

引導式互動:分步驟引導使用者完成複雜任務
視覺化回饋:進度條、狀態指示、格式化輸出
確認機制:關鍵操作需要人工確認
迭代優化:支援多輪修改和改進
友善提示:清晰的操作指引和錯誤訊息

🚀 使用示範

===========================================================
              📝 AI 輔助文件創建工作流程
===========================================================

──────────────────────────────────────────────────
📌 步驟 1: 文件類型選擇
──────────────────────────────────────────────────

選擇要創建的文件類型:
  1. 商業企劃書
  2. 技術文件
  3. 會議記錄
  4. 專案報告

請選擇:2

[●○○○] 步驟 1/4
🎯 文件標題
請輸入文件標題:Python 入門指南

[●●○○] 步驟 2/4
🎯 目標讀者
請描述目標讀者 (預設: 一般讀者):程式設計初學者

...

✅ 資料收集完成!

🤔 思考中 ⠸

┌────────────────────────────────────────────────┐
│ # Python 入門指南                              │
│                                                │
│ ## 簡介                                        │
│ Python 是一門易學且強大的程式語言...          │
└────────────────────────────────────────────────┘

⚠️  確認要接受此結果嗎?

詳細資訊:
  • 迭代次數: 1
  • 剩餘機會: 2

請確認 (Y/n):

今天我們打造了直覺友善的人機協作介面,讓使用者能夠輕鬆地與 AI 助理互動。明天我們將學習資料分析助手實作,探索 AI 在數據處理領域的應用!


上一篇
Day 17: 並行處理與效能優化
下一篇
Day 19: 資料分析助手實作
系列文
30 天從零到 AI 助理:Gemini CLI 與 LangGraph 輕鬆上手20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言