83 lines
2.6 KiB
Python
83 lines
2.6 KiB
Python
from __future__ import annotations
|
|
import asyncio
|
|
import time
|
|
from typing import Optional
|
|
|
|
from .kernel import EventBus, Event, EventType
|
|
from .llm import LLMClient
|
|
from .conversation import ConversationManager
|
|
|
|
|
|
class Thinker:
|
|
def __init__(
|
|
self,
|
|
bus: EventBus,
|
|
llm: LLMClient,
|
|
conversation: ConversationManager,
|
|
config=None,
|
|
):
|
|
self.bus = bus
|
|
self.llm = llm
|
|
self.conversation = conversation
|
|
self._last_user_time = time.time()
|
|
self._idle_threshold = (config.personality.idle_timeout
|
|
if config else 15)
|
|
self._proactive = (config.personality.proactive_chat
|
|
if config else True)
|
|
self._running = False
|
|
self._last_idle_topic_time = 0.0
|
|
|
|
async def run(self):
|
|
self._running = True
|
|
while self._running:
|
|
await asyncio.sleep(5)
|
|
if not self._proactive:
|
|
continue
|
|
idle_time = time.time() - self._last_user_time
|
|
if idle_time < self._idle_threshold:
|
|
continue
|
|
if time.time() - self._last_idle_topic_time < 120:
|
|
continue
|
|
await self._generate_idle_topic(idle_time)
|
|
|
|
def stop(self):
|
|
self._running = False
|
|
|
|
def notify_user_activity(self):
|
|
self._last_user_time = time.time()
|
|
|
|
async def _generate_idle_topic(self, idle_time: float):
|
|
self._last_idle_topic_time = time.time()
|
|
context = self.conversation.get_context_window(6)
|
|
ctx_text = "\n".join(
|
|
f"{m['role']}: {m['content'][-200:]}"
|
|
for m in context[-4:]
|
|
) if context else "暂无对话"
|
|
|
|
prompt = f"""你是一个喜欢主动聊天的 AI,当前对话已沉默 {idle_time:.0f} 秒。
|
|
最近话题:
|
|
{ctx_text}
|
|
|
|
你想开启什么话题?自然一点,像朋友随口说。
|
|
输出话题(20字内),不要解释。"""
|
|
try:
|
|
resp = await self.llm.chat(
|
|
messages=[
|
|
{"role": "system", "content": "简短输出一句话,20字内。"},
|
|
{"role": "user", "content": prompt},
|
|
],
|
|
stream=False,
|
|
max_tokens=50,
|
|
)
|
|
topic = resp.content.strip()
|
|
if topic:
|
|
await self.bus.emit(Event(
|
|
EventType.AGENT_THOUGHT,
|
|
data={"type": "idle_chat", "suggestion": topic},
|
|
))
|
|
except Exception:
|
|
pass
|
|
|
|
async def on_input_change(self, text: str, pause: float, is_end: bool):
|
|
pass
|