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
+99
View File
@@ -0,0 +1,99 @@
from __future__ import annotations
import asyncio
import sys
from agent.kernel import Event, EventType, AgentRuntime
from agent.llm import LLMClient
from agent.personality import Personality
from agent.conversation import ConversationManager
from agent.interruption import InterruptionEngine, InterruptionDecision
from agent.thinker import Thinker
from agent.orchestrator import Orchestrator
from agent.stream_input import StreamInput
from agent.display import Display
from config.settings import AppSettings
class ChatCLI:
def __init__(self, config: AppSettings):
self.config = config
self.runtime = AgentRuntime(config)
self.llm = LLMClient(config.llm)
self.bus = self.runtime.bus
self.personality = Personality.from_config({
"name": config.personality.name,
"style": config.personality.style,
"proactive_chat": config.personality.proactive_chat,
"idle_timeout": config.personality.idle_timeout,
"humor_enabled": config.personality.humor_enabled,
"empathy_enabled": config.personality.empathy_enabled,
})
self.conversation = ConversationManager()
self.interruption = InterruptionEngine(llm=self.llm, config=config)
self.thinker = Thinker(self.bus, self.llm, self.conversation, config)
self.orchestrator = Orchestrator(
self.runtime, self.llm, self.personality,
self.conversation, self.interruption, self.thinker,
)
self.display = Display(self.bus)
self.stream_input = StreamInput(self.bus)
self.conversation.create_topic("general")
self.bus.on(EventType.USER_PAUSE, self._on_user_pause)
self.bus.on(EventType.AGENT_THOUGHT, self._on_thought)
async def _on_thought(self, event: Event):
data = event.data
if data.get("type") == "idle_chat":
topic = data.get("suggestion", "")
print(f"\n\033[90m[AI 在想:{topic}]\033[0m")
async def _on_user_pause(self, event: Event):
data = event.data
text = data.get("text", "")
pause = data.get("pause", 0)
context = self.conversation.get_context_window(6)
decision = await self.interruption.evaluate(
text, pause, False, context=context,
)
if decision.should_interrupt:
self.thinker.notify_user_activity()
resp = decision.suggested_response or "..."
print(f"\n\033[33m[打断]\033[0m \033[36m{self.personality.name}\033[0m {resp}")
self.display.show_prompt()
async def _handle_message(self, text: str):
self.thinker.notify_user_activity()
self.conversation.add_message("user", text)
system_prompt = self.personality.build_system_prompt()
messages = [{"role": "system", "content": system_prompt}]
messages.extend(self.conversation.get_context_window(20))
print(f"\n\033[36m{self.personality.name}\033[0m ", end="")
full = ""
try:
async for chunk in self.llm.chat_stream_iter(messages):
print(chunk, end="", flush=True)
full += chunk
print()
except Exception as e:
print(f"\n\033[31m[错误: {e}]\033[0m")
if full.strip():
self.conversation.add_message("assistant", full)
async def run(self):
self.display.show_startup()
await self.runtime.start()
thinker_task = self.runtime.create_task(self.thinker.run())
async def on_sentence(text: str):
await self._handle_message(text)
try:
self.display.show_prompt()
await self.stream_input.listen_cli(on_sentence=on_sentence)
except KeyboardInterrupt:
print("\n再见!")
finally:
await self.runtime.stop()
await self.llm.close()