你问的是:
> 在我们之前提出的 **第五步:主执行流程 —— 动态调用 AI 决定的函数** 中,`TaskOrchestrator.execute_from_ai_decision` 方法需要如何修改才能适配你当前完整的 `SystemController` 类?
---
✅ **目标明确**:
你现在已经有完整功能的 `SystemController`(包括音乐、文件、应用、提醒等),并且已经用 `@ai_callable` 装饰器标注了可调用方法。
现在要确保 **`TaskOrchestrator` 的主执行流程能正确解析 AI 输出,并安全地调用这些方法**。
---
## ✅ 需要修改的内容
我们需要对原来的 `execute_from_ai_decision` 做以下几项关键修改:
| 修改点 | 说明 |
|-------|------|
| 1. 支持返回结构化结果 | 不只是打印日志,还要把结果返回给上层(如语音播报模块) |
| 2. 参数类型校验与转换 | 用户输入可能是字符串 `"10"`,但函数需要 `int` |
| 3. 处理 `url` 参数透传问题 | 特别是 `open_application` 需要将 `url` 正确传递 |
| 4. 添加调度循环支持 | 如定时任务 `set_reminder` 后需持续运行 `schedule.run_pending()` |
| 5. 错误友好提示 | 返回自然语言消息,便于前端或语音反馈 |
---
## ✅ 最终版:修改后的 `TaskOrchestrator.execute_from_ai_decision`
```python
import threading
from typing import Dict, Any, Tuple
class TaskOrchestrator:
def __init__(self, system_controller):
self.system_controller = system_controller
self.function_map = self._build_function_map()
self.running_scheduled_tasks = False
logger.info(f"🔧 任务编排器已加载 {len(self.function_map)} 个可调用函数")
def _build_function_map(self) -> Dict[str, callable]:
"""构建函数名 → 方法对象的映射"""
from utils.ai_tools import FUNCTION_SCHEMA
mapping = {}
for item in FUNCTION_SCHEMA:
func_name = item["name"]
func = getattr(self.system_controller, func_name, None)
if func and callable(func):
mapping[func_name] = func
else:
logger.warning(f"⚠️ 未找到或不可调用: {func_name}")
return mapping
def _convert_arg_types(self, func: callable, args: dict) -> dict:
"""
尝试将参数转为函数期望的类型(简单启发式)
注意:Python 没有原生参数类型签名,这里做基础转换
"""
converted = {}
sig = inspect.signature(func)
for name, param in sig.parameters.items():
value = args.get(name)
if value is None:
continue
# 简单类型推断(基于默认值)
ann = param.annotation
if isinstance(ann, type):
try:
if ann == int and not isinstance(value, int):
converted[name] = int(value)
elif ann == float and not isinstance(value, float):
converted[name] = float(value)
else:
converted[name] = value
except (ValueError, TypeError):
converted[name] = value # 保持原始值,让函数自己处理
else:
converted[name] = value
return converted
def _start_scheduled_task_loop(self):
"""后台线程运行定时任务"""
def run_loop():
while self.running_scheduled_tasks:
schedule.run_pending()
time.sleep(1)
if not self.running_scheduled_tasks:
self.running_scheduled_tasks = True
thread = threading.Thread(target=run_loop, daemon=True)
thread.start()
logger.info("⏰ 已启动定时任务监听循环")
def execute_from_ai_decision(self, user_input: str) -> Dict[str, Any]:
"""
主入口:接收用户输入 → AI 决策 → 执行函数 → 返回结构化结果
"""
from utils.ai_tools import call_llm_to_choose_function # 假设已定义
# Step 1: AI 决策(模拟或真实 LLM)
decision = call_llm_to_choose_function(user_input)
if not decision:
return {
"success": False,
"message": "抱歉,我没有理解您的请求。",
"data": None
}
func_name = decision.get("function")
args = decision.get("arguments", {})
if not func_name:
return {
"success": False,
"message": "AI 返回的函数名为空。",
"data": None
}
# Step 2: 查找函数
func = self.function_map.get(func_name)
if not func:
logger.warning(f"❌ 函数不存在: {func_name}")
return {
"success": False,
"message": f"系统不支持操作:{func_name}",
"data": None
}
# Step 3: 参数预处理(类型转换)
try:
safe_args = self._convert_arg_types(func, args)
except Exception as e:
logger.warning(f"参数转换失败: {e}")
safe_args = args # 使用原始参数
# Step 4: 执行函数
try:
logger.info(f"🚀 正在执行: {func_name}({safe_args})")
result = func(**safe_args)
# 统一返回格式:(success: bool, message: str 或 dict)
if isinstance(result, tuple):
success, msg = result
elif isinstance(result, dict):
success = result.get("success", False)
msg = result.get("message", str(result))
else:
success = True
msg = str(result)
# === 特殊逻辑:如果设置了提醒,启动后台调度循环 ===
if func_name == "set_reminder" and success:
self._start_scheduled_task_loop()
# 返回标准格式
return {
"success": success,
"message": msg,
"data": None,
"operation": func_name,
"input": args
}
except TypeError as e:
error_msg = f"参数错误,请检查输入格式: {str(e)}"
logger.error(error_msg)
return {
"success": False,
"message": error_msg,
"data": None
}
except Exception as e:
logger.exception(f"💥 执行 {func_name} 时发生异常")
return {
"success": False,
"message": f"执行失败:{str(e)}",
"data": None
}
```
---
## ✅ 补充:`call_llm_to_choose_function` 示例(用于测试)
```python
# utils/ai_tools.py
import json
def call_llm_to_choose_function(user_query: str) -> dict:
mock_responses = {
"播放音乐": {"function": "play_music", "arguments": {"path": None}},
"打开记事本": {"function": "open_application", "arguments": {"app_name": "记事本"}},
"用chrome打开知乎": {
"function": "open_application",
"arguments": {"app_name": "chrome", "url": "https://www.zhihu.com"}
},
"创建一个叫 hello.txt 的文件,内容是 你好世界": {
"function": "create_file",
"arguments": {"file_path": "hello.txt", "content": "你好世界"}
},
"读取 hello.txt": {
"function": "read_file",
"arguments": {"file_path": "hello.txt"}
},
"设置一个提醒,5分钟后让我喝水": {
"function": "set_reminder",
"arguments": {"message": "喝水", "delay_minutes": 5}
},
"告诉我系统信息": {
"function": "get_system_info",
"arguments": {}
},
"停止音乐": {
"function": "stop_music",
"arguments": {}
}
}
return mock_responses.get(user_query.strip(), {})
```
---
## ✅ 使用示例(main 测试)
```python
if __name__ == "__main__":
controller = SystemController()
orchestrator = TaskOrchestrator(controller)
test_inputs = [
"播放音乐",
"打开记事本",
"用chrome打开知乎",
"创建一个叫 hello.txt 的文件,内容是 你好世界",
"读取 hello.txt",
"告诉我系统信息",
"设置一个提醒,5分钟后让我喝水",
"停止音乐"
]
for inp in test_inputs:
print(f"\n👤 用户: {inp}")
result = orchestrator.execute_from_ai_decision(inp)
print(f"🤖 助手: {result['message']}")
if not result['success']:
print(f"⚠️ 错误码: {result}")
```
---
## ✅ 关键修改总结
| 原版本问题 | 新版本修复 |
|----------|-----------|
| ❌ 只打印日志,无法返回结果 | ✅ 返回标准 JSON 结构 `{success, message}` |
| ❌ 不支持参数类型转换(如 `"5"` → `5`) | ✅ 加入 `_convert_arg_types` |
| ❌ 没有处理 `set_reminder` 需要后台运行的问题 | ✅ 自动启动 `schedule` 循环 |
| ❌ 缺少异常隔离和日志追踪 | ✅ 全流程 logging + try/catch |
| ❌ AI 输入 hardcode | ✅ 可替换为真实 LLM 接口 |
---