sse_app.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. #!/usr/bin/env python3
  2. """
  3. SSE + FastAPI 实时对话窗口
  4. 用户输入消息,后端随机生成回复
  5. """
  6. import asyncio
  7. import json
  8. import time
  9. import random
  10. import os
  11. from datetime import datetime
  12. from typing import List, Dict, Any
  13. from concurrent.futures import ThreadPoolExecutor
  14. from fastapi import FastAPI, Request
  15. from fastapi.responses import StreamingResponse, HTMLResponse
  16. import uvicorn
  17. import dotenv
  18. # Agent相关导入
  19. try:
  20. from agno.agent import Agent
  21. from agno.memory.v2.db.sqlite import SqliteMemoryDb
  22. from agno.memory.v2.memory import Memory
  23. from agno.models.openai import OpenAILike
  24. from agno.storage.sqlite import SqliteStorage
  25. AGENT_AVAILABLE = True
  26. print("✅ Agent依赖已加载")
  27. except ImportError as e:
  28. print(f"⚠️ Agent依赖未安装: {e}")
  29. AGENT_AVAILABLE = False
  30. # 加载环境变量
  31. dotenv.load_dotenv()
  32. # 全局消息队列
  33. message_queue: List[Dict[str, Any]] = []
  34. # 全局Agent实例和Memory
  35. global_agent = None
  36. global_memory = None
  37. global_user_id = "user_web_chat"
  38. # 线程池执行器
  39. thread_executor = ThreadPoolExecutor(max_workers=2)
  40. # Agent工具函数
  41. def get_current_time():
  42. """获取当前时间"""
  43. return f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
  44. def get_user_info():
  45. """获取用户信息"""
  46. return "用户信息: 当前用户正在使用web实时对话窗口"
  47. async def call_agent_async(agent, message, user_id):
  48. """异步调用Agent,避免阻塞事件循环"""
  49. loop = asyncio.get_event_loop()
  50. try:
  51. print(f"🔄 开始异步调用Agent... (消息: {message[:50]})")
  52. # 在线程池中执行同步的Agent调用,设置超时
  53. response = await asyncio.wait_for(
  54. loop.run_in_executor(
  55. thread_executor,
  56. lambda: agent.run(message, user_id=user_id)
  57. ),
  58. timeout=30.0 # 30秒超时
  59. )
  60. print(f"✅ Agent异步调用完成")
  61. return response
  62. except asyncio.TimeoutError:
  63. print(f"⏰ Agent调用超时 (30秒)")
  64. return None
  65. except Exception as e:
  66. print(f"❌ Agent异步调用失败: {e}")
  67. return None
  68. def create_agent():
  69. """创建具有Memory功能的Agent实例"""
  70. global global_agent, global_memory
  71. if not AGENT_AVAILABLE:
  72. print("❌ Agent依赖不可用,将使用随机回复")
  73. return None
  74. # 检查环境变量
  75. api_key = os.getenv("BAILIAN_API_KEY")
  76. base_url = os.getenv("BAILIAN_API_BASE_URL")
  77. if not api_key or not base_url:
  78. print("⚠️ 环境变量未设置,Agent功能将不可用,使用随机回复")
  79. return None
  80. try:
  81. print("🚀 创建具有Memory功能的Agent实例...")
  82. # 创建模型
  83. model = OpenAILike(
  84. id="qwen3-32b",
  85. api_key=api_key,
  86. base_url=base_url,
  87. request_params={"extra_body": {"enable_thinking": False}},
  88. )
  89. # 数据库文件
  90. db_file = "tmp/agent_memory.db"
  91. os.makedirs("tmp", exist_ok=True)
  92. # 初始化Memory v2
  93. memory = Memory(
  94. model=model, # 使用相同的模型进行记忆管理
  95. db=SqliteMemoryDb(table_name="user_memories", db_file=db_file),
  96. )
  97. # 初始化存储
  98. storage = SqliteStorage(table_name="agent_sessions", db_file=db_file)
  99. # 定义记忆工具函数
  100. def remember_info(info: str):
  101. """主动记住信息的工具函数"""
  102. return f"我已经记住了这个信息: {info}"
  103. # 创建Agent with Memory功能
  104. agent = Agent(
  105. model=model,
  106. # Store memories in a database
  107. memory=memory,
  108. # Give the Agent the ability to update memories
  109. enable_agentic_memory=True,
  110. # Run the MemoryManager after each response
  111. enable_user_memories=True,
  112. # Store the chat history in the database
  113. storage=storage,
  114. # Add the chat history to the messages
  115. add_history_to_messages=True,
  116. # Number of history runs to include
  117. num_history_runs=3,
  118. # Tools
  119. tools=[get_current_time, get_user_info, remember_info],
  120. markdown=False, # 简单文本回复
  121. show_tool_calls=False, # 关闭工具调用显示,避免影响web显示
  122. instructions="""
  123. 你是一个具有记忆功能的友好AI助手,正在通过web实时对话窗口与用户交流。
  124. 🧠 **记忆功能**:
  125. - 你可以记住用户的姓名、偏好和兴趣
  126. - 保持跨会话的对话连贯性
  127. - 基于历史对话提供个性化建议
  128. - 记住之前讨论过的话题
  129. 💬 **对话原则**:
  130. - 使用简洁、自然的中文回答
  131. - 语气友好、热情
  132. - 回答要有帮助性
  133. - 可以调用工具获取信息
  134. - 主动记住重要信息
  135. - 基于记忆提供个性化回应
  136. 🎯 **个性化服务**:
  137. - 如果用户告诉你他们的姓名,主动记住
  138. - 记住用户的偏好和兴趣
  139. - 在后续对话中引用之前的内容
  140. - 提供基于历史的个性化建议
  141. 请与用户进行愉快的对话!我会记住我们的每次交流。
  142. """,
  143. )
  144. global_agent = agent
  145. global_memory = memory
  146. print("✅ Memory Agent创建成功!")
  147. print(f"📱 模型: qwen3-32b")
  148. print(f"🧠 记忆: SQLite数据库 ({db_file})")
  149. print(f"💾 存储: 会话历史记录")
  150. print(f"👤 用户ID: {global_user_id}")
  151. # 简单测试Agent是否正常工作
  152. try:
  153. test_response = agent.run("你好", user_id=global_user_id)
  154. print(f"🧪 Agent测试成功: {str(test_response)[:50]}...")
  155. except Exception as e:
  156. print(f"⚠️ Agent测试失败: {e}")
  157. return agent
  158. except Exception as e:
  159. print(f"❌ Agent创建失败: {e}")
  160. return None
  161. # 随机回复内容(Agent不可用时的备用)
  162. RANDOM_REPLIES = [
  163. "这是一个有趣的观点!",
  164. "我完全同意你的看法。",
  165. "让我想想这个问题...",
  166. "你说得很有道理。",
  167. "这让我想到了另一个话题。",
  168. "非常好的问题!",
  169. "我觉得你可以试试这样做。",
  170. "这确实是个挑战。",
  171. "你的想法很有创意!",
  172. "我需要更多信息来帮助你。",
  173. "这个话题很深入呢。",
  174. "你考虑得很周全。"
  175. ]
  176. # 创建FastAPI应用
  177. app = FastAPI(title="SSE实时对话", description="简单的实时聊天系统", version="1.0.0")
  178. # 应用启动时初始化Agent
  179. @app.on_event("startup")
  180. async def startup_event():
  181. print("🚀 启动SSE实时对话系统...")
  182. print("📍 访问地址: http://localhost:8000")
  183. # 初始化Agent
  184. try:
  185. create_agent()
  186. if global_agent:
  187. print("✅ Memory Agent已就绪,将提供具有记忆功能的智能回复")
  188. print("🧠 记忆功能: 可记住用户信息和对话历史")
  189. print("💬 特殊命令: 在对话中输入 '记忆' 查看记忆状态")
  190. else:
  191. print("⚠️ Agent不可用,将使用随机回复")
  192. except Exception as e:
  193. print(f"❌ Agent创建过程中出错: {e}")
  194. print("⚠️ 系统将使用随机回复模式")
  195. @app.get("/")
  196. async def home():
  197. """主页 - 对话界面"""
  198. html_content = """
  199. <!DOCTYPE html>
  200. <html lang="zh-CN">
  201. <head>
  202. <meta charset="UTF-8">
  203. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  204. <title>实时对话窗口</title>
  205. <style>
  206. * {
  207. margin: 0;
  208. padding: 0;
  209. box-sizing: border-box;
  210. }
  211. body {
  212. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  213. background: #f0f2f5;
  214. height: 100vh;
  215. display: flex;
  216. justify-content: center;
  217. align-items: center;
  218. }
  219. .chat-container {
  220. width: 600px;
  221. height: 500px;
  222. background: white;
  223. border-radius: 10px;
  224. box-shadow: 0 4px 20px rgba(0,0,0,0.1);
  225. display: flex;
  226. flex-direction: column;
  227. overflow: hidden;
  228. }
  229. .chat-header {
  230. background: #4a90e2;
  231. color: white;
  232. padding: 15px 20px;
  233. text-align: center;
  234. font-size: 18px;
  235. font-weight: 600;
  236. }
  237. .status-bar {
  238. padding: 8px 15px;
  239. background: #e8f4f8;
  240. font-size: 12px;
  241. color: #5a5a5a;
  242. border-bottom: 1px solid #e0e0e0;
  243. }
  244. .messages-container {
  245. flex: 1;
  246. padding: 15px;
  247. overflow-y: auto;
  248. background: #fafafa;
  249. }
  250. .message {
  251. margin-bottom: 12px;
  252. display: flex;
  253. animation: slideIn 0.3s ease-out;
  254. }
  255. @keyframes slideIn {
  256. from {
  257. opacity: 0;
  258. transform: translateY(10px);
  259. }
  260. to {
  261. opacity: 1;
  262. transform: translateY(0);
  263. }
  264. }
  265. .message.user {
  266. justify-content: flex-end;
  267. }
  268. .message-bubble {
  269. max-width: 75%;
  270. padding: 10px 15px;
  271. border-radius: 18px;
  272. word-wrap: break-word;
  273. position: relative;
  274. }
  275. .message.user .message-bubble {
  276. background: #4a90e2;
  277. color: white;
  278. }
  279. .message.bot .message-bubble {
  280. background: white;
  281. color: #333;
  282. border: 1px solid #e0e0e0;
  283. box-shadow: 0 1px 3px rgba(0,0,0,0.1);
  284. }
  285. .message.system .message-bubble {
  286. background: #fff3cd;
  287. color: #856404;
  288. border: 1px solid #ffeaa7;
  289. font-style: italic;
  290. text-align: center;
  291. max-width: 100%;
  292. }
  293. .message-time {
  294. font-size: 10px;
  295. color: #999;
  296. margin-top: 3px;
  297. text-align: right;
  298. }
  299. .message.user .message-time {
  300. text-align: right;
  301. }
  302. .message.bot .message-time {
  303. text-align: left;
  304. }
  305. .input-container {
  306. padding: 15px;
  307. background: white;
  308. border-top: 1px solid #e0e0e0;
  309. display: flex;
  310. gap: 10px;
  311. }
  312. .message-input {
  313. flex: 1;
  314. padding: 10px 15px;
  315. border: 1px solid #ddd;
  316. border-radius: 20px;
  317. outline: none;
  318. font-size: 14px;
  319. transition: border-color 0.3s;
  320. }
  321. .message-input:focus {
  322. border-color: #4a90e2;
  323. box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
  324. }
  325. .send-button {
  326. background: #4a90e2;
  327. color: white;
  328. border: none;
  329. border-radius: 20px;
  330. padding: 10px 20px;
  331. cursor: pointer;
  332. font-size: 14px;
  333. font-weight: 600;
  334. transition: all 0.3s;
  335. }
  336. .send-button:hover {
  337. background: #357abd;
  338. transform: translateY(-1px);
  339. }
  340. .send-button:disabled {
  341. background: #ccc;
  342. cursor: not-allowed;
  343. transform: none;
  344. }
  345. .typing-indicator {
  346. display: none;
  347. padding: 10px 15px;
  348. color: #666;
  349. font-style: italic;
  350. font-size: 12px;
  351. }
  352. .typing-dots {
  353. display: inline-block;
  354. }
  355. .typing-dots::after {
  356. content: '';
  357. animation: typing 1.5s infinite;
  358. }
  359. @keyframes typing {
  360. 0%, 60%, 100% { content: ''; }
  361. 30% { content: '.'; }
  362. 40% { content: '..'; }
  363. 50% { content: '...'; }
  364. }
  365. </style>
  366. </head>
  367. <body>
  368. <div class="chat-container">
  369. <div class="chat-header">
  370. 💬 实时对话窗口
  371. </div>
  372. <div class="status-bar" id="statusBar">
  373. 正在连接...
  374. </div>
  375. <div class="messages-container" id="messagesContainer">
  376. <!-- 消息显示区域 -->
  377. </div>
  378. <div class="typing-indicator" id="typingIndicator">
  379. 机器人正在输入<span class="typing-dots"></span>
  380. </div>
  381. <div class="input-container">
  382. <input
  383. type="text"
  384. id="messageInput"
  385. class="message-input"
  386. placeholder="输入您的消息... (输入 '记忆' 查看我的记忆状态)"
  387. maxlength="500"
  388. >
  389. <button id="sendButton" class="send-button">发送</button>
  390. </div>
  391. </div>
  392. <script>
  393. let eventSource = null;
  394. let isConnected = false;
  395. const messagesContainer = document.getElementById('messagesContainer');
  396. const messageInput = document.getElementById('messageInput');
  397. const sendButton = document.getElementById('sendButton');
  398. const statusBar = document.getElementById('statusBar');
  399. const typingIndicator = document.getElementById('typingIndicator');
  400. // 页面加载完成后初始化
  401. window.addEventListener('load', function() {
  402. console.log('页面加载完成');
  403. // 启用输入功能
  404. messageInput.disabled = false;
  405. sendButton.disabled = false;
  406. messageInput.focus();
  407. // 显示欢迎消息
  408. addMessage('🎉 欢迎使用AI实时对话窗口!我是您的智能助手,具有记忆功能,可以记住我们的对话历史。请开始聊天吧~', 'system');
  409. // 连接SSE
  410. connectSSE();
  411. });
  412. // 连接SSE
  413. function connectSSE() {
  414. try {
  415. console.log('连接SSE...');
  416. eventSource = new EventSource('/sse/chat');
  417. eventSource.onopen = function() {
  418. console.log('SSE连接成功');
  419. isConnected = true;
  420. statusBar.textContent = '✅ 已连接 - 消息将实时更新';
  421. statusBar.style.background = '#d4edda';
  422. statusBar.style.color = '#155724';
  423. };
  424. eventSource.onmessage = function(event) {
  425. try {
  426. const data = JSON.parse(event.data);
  427. handleMessage(data);
  428. } catch (e) {
  429. console.error('消息解析错误:', e);
  430. }
  431. };
  432. eventSource.onerror = function() {
  433. console.log('SSE连接失败');
  434. isConnected = false;
  435. statusBar.textContent = '⚠️ 连接失败 - 使用离线模式';
  436. statusBar.style.background = '#f8d7da';
  437. statusBar.style.color = '#721c24';
  438. };
  439. } catch (error) {
  440. console.error('SSE初始化错误:', error);
  441. statusBar.textContent = '📱 离线模式';
  442. }
  443. }
  444. // 处理消息
  445. function handleMessage(data) {
  446. console.log('收到消息:', data);
  447. switch(data.type) {
  448. case 'bot_message':
  449. hideTypingIndicator();
  450. addMessage(data.message, 'bot');
  451. break;
  452. case 'system_message':
  453. addMessage(data.message, 'system');
  454. break;
  455. }
  456. }
  457. // 添加消息到界面
  458. function addMessage(content, type) {
  459. console.log(`添加消息: [${type}] ${content}`);
  460. const messageDiv = document.createElement('div');
  461. messageDiv.className = `message ${type}`;
  462. const bubbleDiv = document.createElement('div');
  463. bubbleDiv.className = 'message-bubble';
  464. bubbleDiv.textContent = content;
  465. const timeDiv = document.createElement('div');
  466. timeDiv.className = 'message-time';
  467. timeDiv.textContent = new Date().toLocaleTimeString();
  468. messageDiv.appendChild(bubbleDiv);
  469. messageDiv.appendChild(timeDiv);
  470. messagesContainer.appendChild(messageDiv);
  471. messagesContainer.scrollTop = messagesContainer.scrollHeight;
  472. }
  473. // 显示输入指示器
  474. function showTypingIndicator() {
  475. typingIndicator.style.display = 'block';
  476. messagesContainer.scrollTop = messagesContainer.scrollHeight;
  477. }
  478. // 隐藏输入指示器
  479. function hideTypingIndicator() {
  480. typingIndicator.style.display = 'none';
  481. }
  482. // 显示记忆状态
  483. async function showMemoryStatus() {
  484. try {
  485. showTypingIndicator();
  486. const response = await fetch('/api/memory');
  487. const result = await response.json();
  488. hideTypingIndicator();
  489. if (result.available) {
  490. let memoryInfo = `🧠 **记忆状态信息**\n\n`;
  491. memoryInfo += `👤 用户ID: ${result.user_id}\n`;
  492. memoryInfo += `📊 记忆数量: ${result.memory_count}\n`;
  493. memoryInfo += `💾 数据库: ${result.database}\n\n`;
  494. if (result.recent_memories && result.recent_memories.length > 0) {
  495. memoryInfo += `📝 **最近的记忆:**\n`;
  496. result.recent_memories.forEach((mem, index) => {
  497. memoryInfo += `${index + 1}. ${mem.content}...\n`;
  498. });
  499. } else {
  500. memoryInfo += `📭 暂无记忆内容\n`;
  501. }
  502. memoryInfo += `\n💭 **记忆功能说明:**\n`;
  503. memoryInfo += `- 我可以记住您的姓名、偏好和兴趣\n`;
  504. memoryInfo += `- 保持跨会话的对话连贯性\n`;
  505. memoryInfo += `- 基于历史对话提供个性化建议\n`;
  506. memoryInfo += `- 记住之前讨论过的话题`;
  507. addMessage(memoryInfo, 'bot');
  508. } else {
  509. addMessage('❌ 记忆功能不可用: ' + result.message, 'system');
  510. }
  511. } catch (error) {
  512. hideTypingIndicator();
  513. addMessage('❌ 获取记忆状态失败: ' + error.message, 'system');
  514. }
  515. }
  516. // 发送消息
  517. async function sendMessage() {
  518. const message = messageInput.value.trim();
  519. console.log('准备发送消息:', message);
  520. if (!message) {
  521. console.log('消息为空');
  522. return;
  523. }
  524. // 检查特殊命令
  525. if (message === '记忆' || message.toLowerCase() === 'memory') {
  526. console.log('处理记忆查询命令');
  527. addMessage(message, 'user');
  528. messageInput.value = '';
  529. await showMemoryStatus();
  530. return;
  531. }
  532. // 立即显示用户消息
  533. addMessage(message, 'user');
  534. messageInput.value = '';
  535. // 禁用输入
  536. messageInput.disabled = true;
  537. sendButton.disabled = true;
  538. // 显示输入指示器
  539. showTypingIndicator();
  540. try {
  541. const response = await fetch('/api/chat', {
  542. method: 'POST',
  543. headers: {
  544. 'Content-Type': 'application/json',
  545. },
  546. body: JSON.stringify({
  547. message: message
  548. })
  549. });
  550. const result = await response.json();
  551. console.log('发送结果:', result);
  552. if (!result.success) {
  553. hideTypingIndicator();
  554. addMessage('❌ 发送失败: ' + result.error, 'system');
  555. }
  556. } catch (error) {
  557. console.error('发送错误:', error);
  558. hideTypingIndicator();
  559. addMessage('❌ 网络错误: ' + error.message, 'system');
  560. } finally {
  561. // 重新启用输入
  562. messageInput.disabled = false;
  563. sendButton.disabled = false;
  564. messageInput.focus();
  565. }
  566. }
  567. // 事件监听器
  568. sendButton.addEventListener('click', sendMessage);
  569. messageInput.addEventListener('keypress', function(e) {
  570. if (e.key === 'Enter' && !e.shiftKey) {
  571. e.preventDefault();
  572. sendMessage();
  573. }
  574. });
  575. // 页面关闭时断开连接
  576. window.addEventListener('beforeunload', function() {
  577. if (eventSource) {
  578. eventSource.close();
  579. }
  580. });
  581. </script>
  582. </body>
  583. </html>
  584. """
  585. return HTMLResponse(content=html_content)
  586. # SSE消息流生成器
  587. async def generate_chat_stream():
  588. """生成聊天消息流"""
  589. client_id = f"client_{int(time.time())}"
  590. last_message_count = 0
  591. try:
  592. print(f"新的SSE连接: {client_id}")
  593. while True:
  594. # 检查新消息
  595. if len(message_queue) > last_message_count:
  596. for i in range(last_message_count, len(message_queue)):
  597. message = message_queue[i]
  598. yield f"data: {json.dumps(message, ensure_ascii=False)}\n\n"
  599. last_message_count = len(message_queue)
  600. await asyncio.sleep(0.1) # 100ms检查间隔
  601. except Exception as e:
  602. print(f"SSE流错误: {e}")
  603. finally:
  604. print(f"SSE连接断开: {client_id}")
  605. @app.get("/sse/chat")
  606. async def sse_chat():
  607. """SSE聊天端点"""
  608. return StreamingResponse(
  609. generate_chat_stream(),
  610. media_type="text/event-stream",
  611. headers={
  612. "Cache-Control": "no-cache",
  613. "Connection": "keep-alive",
  614. "Access-Control-Allow-Origin": "*",
  615. }
  616. )
  617. @app.post("/api/chat")
  618. async def chat_api(request: Request):
  619. """处理聊天消息"""
  620. global global_agent, global_user_id
  621. try:
  622. data = await request.json()
  623. user_message = data.get("message", "").strip()
  624. if not user_message:
  625. return {"success": False, "error": "消息不能为空"}
  626. print(f"📨 收到用户消息: {user_message}")
  627. print(f"🤖 Agent可用性: {global_agent is not None}")
  628. # 模拟思考时间
  629. await asyncio.sleep(random.uniform(0.5, 1.5))
  630. bot_reply = None
  631. # 尝试使用Agent生成回复
  632. if global_agent:
  633. try:
  634. print("🤖 调用Memory Agent生成回复...")
  635. # 异步调用Agent处理消息,传入user_id以关联记忆
  636. response = await call_agent_async(global_agent, user_message, global_user_id)
  637. if response:
  638. bot_reply = response.content if hasattr(response, 'content') else str(response)
  639. print(f"✅ Agent回复: {bot_reply}")
  640. print(f"🧠 记忆已更新 (用户ID: {global_user_id})")
  641. else:
  642. print("❌ Agent返回空响应")
  643. bot_reply = None
  644. except Exception as e:
  645. print(f"❌ Agent调用失败: {e}")
  646. bot_reply = None
  647. # 如果Agent不可用或失败,使用随机回复
  648. if not bot_reply:
  649. bot_reply = random.choice(RANDOM_REPLIES)
  650. print(f"🎲 使用随机回复: {bot_reply}")
  651. # 确保回复不为空
  652. if not bot_reply:
  653. bot_reply = "抱歉,我暂时无法回复。请稍后再试。"
  654. print(f"⚠️ 使用默认回复: {bot_reply}")
  655. # 添加机器人回复到消息队列
  656. bot_message = {
  657. "type": "bot_message",
  658. "message": bot_reply,
  659. "timestamp": datetime.now().isoformat()
  660. }
  661. message_queue.append(bot_message)
  662. print(f"📤 机器人回复已添加到消息队列: {bot_reply[:50]}...")
  663. return {
  664. "success": True,
  665. "message": "消息已处理",
  666. "timestamp": datetime.now().isoformat()
  667. }
  668. except Exception as e:
  669. print(f"处理消息错误: {e}")
  670. return {"success": False, "error": f"处理错误: {str(e)}"}
  671. @app.get("/api/status")
  672. async def get_status():
  673. """获取系统状态"""
  674. return {
  675. "status": "running",
  676. "messages": len(message_queue),
  677. "timestamp": datetime.now().isoformat()
  678. }
  679. @app.post("/api/clear")
  680. async def clear_messages():
  681. """清空消息历史"""
  682. global message_queue
  683. message_queue.clear()
  684. # 添加清空提示消息
  685. clear_msg = {
  686. "type": "system_message",
  687. "message": "💬 消息历史已清空",
  688. "timestamp": datetime.now().isoformat()
  689. }
  690. message_queue.append(clear_msg)
  691. return {"success": True, "message": "消息历史已清空"}
  692. @app.get("/api/memory")
  693. async def get_memory_status():
  694. """获取Agent记忆状态"""
  695. global global_memory, global_user_id, global_agent
  696. if not global_agent or not global_memory:
  697. return {
  698. "available": False,
  699. "message": "Memory功能不可用",
  700. "user_id": global_user_id
  701. }
  702. try:
  703. # 尝试获取记忆信息
  704. memories = global_memory.get_user_memories(user_id=global_user_id)
  705. memory_count = len(memories) if memories else 0
  706. # 获取最近的3条记忆摘要
  707. recent_memories = []
  708. if memories:
  709. for mem in memories[-3:]:
  710. try:
  711. # UserMemory对象的属性访问
  712. content = getattr(mem, 'content', '')[:100] if hasattr(mem, 'content') else str(mem)[:100]
  713. timestamp = getattr(mem, 'created_at', '') if hasattr(mem, 'created_at') else ''
  714. recent_memories.append({
  715. "content": content,
  716. "timestamp": str(timestamp),
  717. })
  718. except Exception as e:
  719. # 备用方案:直接转换为字符串
  720. recent_memories.append({
  721. "content": str(mem)[:100],
  722. "timestamp": "",
  723. })
  724. return {
  725. "available": True,
  726. "user_id": global_user_id,
  727. "memory_count": memory_count,
  728. "recent_memories": recent_memories,
  729. "database": "tmp/agent_memory.db",
  730. "timestamp": datetime.now().isoformat()
  731. }
  732. except Exception as e:
  733. return {
  734. "available": True,
  735. "user_id": global_user_id,
  736. "memory_count": "unknown",
  737. "error": str(e),
  738. "message": "记忆系统已启用但获取详情失败",
  739. "timestamp": datetime.now().isoformat()
  740. }
  741. if __name__ == "__main__":
  742. print("🚀 启动SSE实时对话系统...")
  743. print("🌐 访问地址: http://localhost:8081")
  744. print("🤖 支持Agent智能回复 + SSE实时推送")
  745. print("📋 如需Agent功能,请配置环境变量:")
  746. print(" BAILIAN_API_KEY=your_api_key")
  747. print(" BAILIAN_API_BASE_URL=your_base_url")
  748. print("=" * 50)
  749. uvicorn.run(
  750. "sse_app:app",
  751. host="0.0.0.0",
  752. port=8081,
  753. reload=True,
  754. log_level="info"
  755. )