Initial commit: Air Agent Framework v2:全双工对话智能体(打断、插话、主动开口)

This commit is contained in:
WpyQwq
2026-09-19 12:09:51 +08:00
commit fa0cb5824d
26 changed files with 2362 additions and 0 deletions
View File
+89
View File
@@ -0,0 +1,89 @@
from dataclasses import dataclass, field
from typing import Optional
import json
import os
@dataclass
class LLMSettings:
provider: str = "openai"
model: str = "gpt-4o"
api_key: str = ""
base_url: str = "https://api.openai.com/v1"
max_tokens: int = 4096
temperature: float = 0.7
timeout: int = 60
@dataclass
class InterruptionSettings:
enabled: bool = True
cooldown_seconds: float = 3.0
pause_threshold: float = 1.5
max_per_minute: int = 6
@dataclass
class PersonalitySettings:
name: str = "Air"
style: str = "casual"
proactive_chat: bool = True
idle_timeout: int = 15
humor_enabled: bool = True
empathy_enabled: bool = True
@dataclass
class AppSettings:
llm: LLMSettings = field(default_factory=LLMSettings)
interruption: InterruptionSettings = field(default_factory=InterruptionSettings)
personality: PersonalitySettings = field(default_factory=PersonalitySettings)
data_dir: str = os.path.expanduser("~/.air_agent")
def save(self, path: str = ""):
path = path or os.path.join(self.data_dir, "config.json")
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
json.dump({
"llm": {
"provider": self.llm.provider,
"model": self.llm.model,
"api_key": self.llm.api_key,
"base_url": self.llm.base_url,
"max_tokens": self.llm.max_tokens,
"temperature": self.llm.temperature,
"timeout": self.llm.timeout,
},
"interruption": {
"enabled": self.interruption.enabled,
"cooldown_seconds": self.interruption.cooldown_seconds,
"pause_threshold": self.interruption.pause_threshold,
"max_per_minute": self.interruption.max_per_minute,
},
"personality": {
"name": self.personality.name,
"style": self.personality.style,
"proactive_chat": self.personality.proactive_chat,
"idle_timeout": self.personality.idle_timeout,
"humor_enabled": self.personality.humor_enabled,
"empathy_enabled": self.personality.empathy_enabled,
},
"data_dir": self.data_dir,
}, f, indent=2, ensure_ascii=False)
@classmethod
def load(cls, path: str = "") -> "AppSettings":
path = path or os.path.join(os.path.expanduser("~/.air_agent"), "config.json")
if not os.path.exists(path):
return cls()
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
llm_data = data.get("llm", {})
int_data = data.get("interruption", {})
per_data = data.get("personality", {})
return cls(
llm=LLMSettings(**llm_data),
interruption=InterruptionSettings(**int_data),
personality=PersonalitySettings(**per_data),
data_dir=data.get("data_dir", cls().data_dir),
)