main.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. """赛博小镇 FastAPI 后端主程序"""
  2. from fastapi import FastAPI, HTTPException
  3. from fastapi.middleware.cors import CORSMiddleware
  4. from contextlib import asynccontextmanager
  5. import uvicorn
  6. from config import settings
  7. from models import (
  8. ChatRequest, ChatResponse,
  9. NPCStatusResponse, NPCListResponse, NPCInfo
  10. )
  11. from agents import get_npc_manager
  12. from state_manager import get_state_manager
  13. # 生命周期管理
  14. @asynccontextmanager
  15. async def lifespan(app: FastAPI):
  16. """应用生命周期管理"""
  17. # 启动时
  18. print("\n" + "="*60)
  19. print("🎮 赛博小镇后端服务启动中...")
  20. print("="*60)
  21. # 验证配置
  22. settings.validate()
  23. # 初始化NPC管理器
  24. npc_manager = get_npc_manager()
  25. # 初始化并启动状态管理器
  26. state_manager = get_state_manager(settings.NPC_UPDATE_INTERVAL)
  27. await state_manager.start()
  28. print("\n✅ 所有服务已启动!")
  29. print(f"📡 API地址: http://{settings.API_HOST}:{settings.API_PORT}")
  30. print(f"📚 API文档: http://{settings.API_HOST}:{settings.API_PORT}/docs")
  31. print("="*60 + "\n")
  32. yield
  33. # 关闭时
  34. print("\n🛑 正在关闭服务...")
  35. await state_manager.stop()
  36. print("✅ 服务已关闭\n")
  37. # 创建FastAPI应用
  38. app = FastAPI(
  39. title=settings.API_TITLE,
  40. version=settings.API_VERSION,
  41. description="赛博小镇 - 基于HelloAgents的AI NPC对话系统",
  42. lifespan=lifespan
  43. )
  44. # CORS配置
  45. app.add_middleware(
  46. CORSMiddleware,
  47. allow_origins=settings.CORS_ORIGINS,
  48. allow_credentials=True,
  49. allow_methods=["*"],
  50. allow_headers=["*"],
  51. )
  52. # 获取全局实例
  53. npc_manager = None
  54. state_manager = None
  55. def get_managers():
  56. """获取管理器实例"""
  57. global npc_manager, state_manager
  58. if npc_manager is None:
  59. npc_manager = get_npc_manager()
  60. if state_manager is None:
  61. state_manager = get_state_manager()
  62. return npc_manager, state_manager
  63. # ==================== API路由 ====================
  64. @app.get("/")
  65. async def root():
  66. """根路径 - API信息"""
  67. return {
  68. "service": settings.API_TITLE,
  69. "version": settings.API_VERSION,
  70. "status": "running",
  71. "features": ["AI对话", "NPC记忆系统", "好感度系统", "批量状态更新"],
  72. "endpoints": {
  73. "docs": "/docs",
  74. "chat": "/chat",
  75. "npcs": "/npcs",
  76. "npcs_status": "/npcs/status",
  77. "npc_memories": "/npcs/{npc_name}/memories",
  78. "npc_affinity": "/npcs/{npc_name}/affinity",
  79. "all_affinities": "/affinities"
  80. }
  81. }
  82. @app.get("/health")
  83. async def health_check():
  84. """健康检查"""
  85. return {"status": "healthy", "timestamp": "now"}
  86. @app.post("/chat", response_model=ChatResponse)
  87. async def chat_with_npc(request: ChatRequest):
  88. """与NPC对话接口
  89. 玩家与指定NPC进行实时对话,使用独立的Agent处理
  90. """
  91. npc_mgr, _ = get_managers()
  92. # 验证NPC是否存在
  93. npc_info = npc_mgr.get_npc_info(request.npc_name)
  94. if not npc_info:
  95. raise HTTPException(
  96. status_code=404,
  97. detail=f"NPC '{request.npc_name}' 不存在"
  98. )
  99. try:
  100. # 调用NPC Agent处理对话
  101. response_text = npc_mgr.chat(request.npc_name, request.message)
  102. return ChatResponse(
  103. npc_name=request.npc_name,
  104. npc_title=npc_info["title"],
  105. message=response_text,
  106. success=True
  107. )
  108. except Exception as e:
  109. raise HTTPException(
  110. status_code=500,
  111. detail=f"对话处理失败: {str(e)}"
  112. )
  113. @app.get("/npcs", response_model=NPCListResponse)
  114. async def list_npcs():
  115. """获取所有NPC列表"""
  116. npc_mgr, _ = get_managers()
  117. npcs_data = npc_mgr.get_all_npcs()
  118. npcs = [NPCInfo(**npc) for npc in npcs_data]
  119. return NPCListResponse(
  120. npcs=npcs,
  121. total=len(npcs)
  122. )
  123. @app.get("/npcs/status", response_model=NPCStatusResponse)
  124. async def get_npcs_status():
  125. """获取所有NPC的当前状态
  126. 返回批量生成的NPC对话内容,用于显示NPC的自主行为
  127. """
  128. _, state_mgr = get_managers()
  129. state = state_mgr.get_current_state()
  130. return NPCStatusResponse(
  131. dialogues=state["dialogues"],
  132. last_update=state["last_update"],
  133. next_update_in=state["next_update_in"]
  134. )
  135. @app.post("/npcs/status/refresh")
  136. async def refresh_npcs_status():
  137. """强制刷新NPC状态
  138. 立即触发一次批量对话生成
  139. """
  140. _, state_mgr = get_managers()
  141. await state_mgr.force_update()
  142. state = state_mgr.get_current_state()
  143. return {
  144. "message": "NPC状态已刷新",
  145. "dialogues": state["dialogues"]
  146. }
  147. @app.get("/npcs/{npc_name}")
  148. async def get_npc_info(npc_name: str):
  149. """获取指定NPC的详细信息"""
  150. npc_mgr, state_mgr = get_managers()
  151. npc_info = npc_mgr.get_npc_info(npc_name)
  152. if not npc_info:
  153. raise HTTPException(
  154. status_code=404,
  155. detail=f"NPC '{npc_name}' 不存在"
  156. )
  157. # 添加当前对话
  158. current_dialogue = state_mgr.get_npc_dialogue(npc_name)
  159. npc_info["current_dialogue"] = current_dialogue
  160. return npc_info
  161. @app.get("/npcs/{npc_name}/memories")
  162. async def get_npc_memories(npc_name: str, limit: int = 10):
  163. """获取NPC的记忆列表
  164. Args:
  165. npc_name: NPC名称
  166. limit: 返回的记忆数量限制 (默认10条)
  167. Returns:
  168. NPC的记忆列表
  169. """
  170. npc_mgr, _ = get_managers()
  171. # 验证NPC是否存在
  172. npc_info = npc_mgr.get_npc_info(npc_name)
  173. if not npc_info:
  174. raise HTTPException(
  175. status_code=404,
  176. detail=f"NPC '{npc_name}' 不存在"
  177. )
  178. try:
  179. memories = npc_mgr.get_npc_memories(npc_name, limit=limit)
  180. return {
  181. "npc_name": npc_name,
  182. "memories": memories,
  183. "total": len(memories)
  184. }
  185. except Exception as e:
  186. raise HTTPException(
  187. status_code=500,
  188. detail=f"获取记忆失败: {str(e)}"
  189. )
  190. @app.delete("/npcs/{npc_name}/memories")
  191. async def clear_npc_memories(npc_name: str, memory_type: str = None):
  192. """清空NPC的记忆 (用于测试)
  193. Args:
  194. npc_name: NPC名称
  195. memory_type: 记忆类型 (working/episodic), 不指定则清空所有
  196. Returns:
  197. 操作结果
  198. """
  199. npc_mgr, _ = get_managers()
  200. # 验证NPC是否存在
  201. npc_info = npc_mgr.get_npc_info(npc_name)
  202. if not npc_info:
  203. raise HTTPException(
  204. status_code=404,
  205. detail=f"NPC '{npc_name}' 不存在"
  206. )
  207. try:
  208. npc_mgr.clear_npc_memory(npc_name, memory_type)
  209. return {
  210. "message": f"已清空{npc_name}的记忆",
  211. "npc_name": npc_name,
  212. "memory_type": memory_type or "all"
  213. }
  214. except Exception as e:
  215. raise HTTPException(
  216. status_code=500,
  217. detail=f"清空记忆失败: {str(e)}"
  218. )
  219. @app.get("/npcs/{npc_name}/affinity")
  220. async def get_npc_affinity(npc_name: str, player_id: str = "player"):
  221. """获取NPC对玩家的好感度
  222. Args:
  223. npc_name: NPC名称
  224. player_id: 玩家ID (默认为"player")
  225. Returns:
  226. 好感度信息
  227. """
  228. npc_mgr, _ = get_managers()
  229. # 验证NPC是否存在
  230. npc_info = npc_mgr.get_npc_info(npc_name)
  231. if not npc_info:
  232. raise HTTPException(
  233. status_code=404,
  234. detail=f"NPC '{npc_name}' 不存在"
  235. )
  236. try:
  237. affinity_info = npc_mgr.get_npc_affinity(npc_name, player_id)
  238. return {
  239. "npc_name": npc_name,
  240. "player_id": player_id,
  241. **affinity_info
  242. }
  243. except Exception as e:
  244. raise HTTPException(
  245. status_code=500,
  246. detail=f"获取好感度失败: {str(e)}"
  247. )
  248. @app.get("/affinities")
  249. async def get_all_affinities(player_id: str = "player"):
  250. """获取所有NPC对玩家的好感度
  251. Args:
  252. player_id: 玩家ID (默认为"player")
  253. Returns:
  254. 所有NPC的好感度信息
  255. """
  256. npc_mgr, _ = get_managers()
  257. try:
  258. affinities = npc_mgr.get_all_affinities(player_id)
  259. return {
  260. "player_id": player_id,
  261. "affinities": affinities
  262. }
  263. except Exception as e:
  264. raise HTTPException(
  265. status_code=500,
  266. detail=f"获取好感度失败: {str(e)}"
  267. )
  268. @app.put("/npcs/{npc_name}/affinity")
  269. async def set_npc_affinity(npc_name: str, affinity: float, player_id: str = "player"):
  270. """设置NPC对玩家的好感度 (用于测试)
  271. Args:
  272. npc_name: NPC名称
  273. affinity: 好感度值 (0-100)
  274. player_id: 玩家ID (默认为"player")
  275. Returns:
  276. 操作结果
  277. """
  278. npc_mgr, _ = get_managers()
  279. # 验证NPC是否存在
  280. npc_info = npc_mgr.get_npc_info(npc_name)
  281. if not npc_info:
  282. raise HTTPException(
  283. status_code=404,
  284. detail=f"NPC '{npc_name}' 不存在"
  285. )
  286. # 验证好感度范围
  287. if affinity < 0 or affinity > 100:
  288. raise HTTPException(
  289. status_code=400,
  290. detail="好感度必须在0-100之间"
  291. )
  292. try:
  293. npc_mgr.set_npc_affinity(npc_name, affinity, player_id)
  294. affinity_info = npc_mgr.get_npc_affinity(npc_name, player_id)
  295. return {
  296. "message": f"已设置{npc_name}对玩家的好感度",
  297. "npc_name": npc_name,
  298. "player_id": player_id,
  299. **affinity_info
  300. }
  301. except Exception as e:
  302. raise HTTPException(
  303. status_code=500,
  304. detail=f"设置好感度失败: {str(e)}"
  305. )
  306. # ==================== 主程序入口 ====================
  307. if __name__ == "__main__":
  308. print("\n🚀 启动赛博小镇后端服务...")
  309. print(f"📍 监听地址: {settings.API_HOST}:{settings.API_PORT}")
  310. print(f"📖 访问文档: http://localhost:{settings.API_PORT}/docs\n")
  311. uvicorn.run(
  312. "main:app",
  313. host=settings.API_HOST,
  314. port=settings.API_PORT,
  315. reload=True, # 开发模式自动重载
  316. log_level="info"
  317. )