| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- """赛博小镇 FastAPI 后端主程序"""
- from fastapi import FastAPI, HTTPException
- from fastapi.middleware.cors import CORSMiddleware
- from contextlib import asynccontextmanager
- import uvicorn
- from config import settings
- from models import (
- ChatRequest, ChatResponse,
- NPCStatusResponse, NPCListResponse, NPCInfo
- )
- from agents import get_npc_manager
- from state_manager import get_state_manager
- # 生命周期管理
- @asynccontextmanager
- async def lifespan(app: FastAPI):
- """应用生命周期管理"""
- # 启动时
- print("\n" + "="*60)
- print("🎮 赛博小镇后端服务启动中...")
- print("="*60)
-
- # 验证配置
- settings.validate()
-
- # 初始化NPC管理器
- npc_manager = get_npc_manager()
-
- # 初始化并启动状态管理器
- state_manager = get_state_manager(settings.NPC_UPDATE_INTERVAL)
- await state_manager.start()
-
- print("\n✅ 所有服务已启动!")
- print(f"📡 API地址: http://{settings.API_HOST}:{settings.API_PORT}")
- print(f"📚 API文档: http://{settings.API_HOST}:{settings.API_PORT}/docs")
- print("="*60 + "\n")
-
- yield
-
- # 关闭时
- print("\n🛑 正在关闭服务...")
- await state_manager.stop()
- print("✅ 服务已关闭\n")
- # 创建FastAPI应用
- app = FastAPI(
- title=settings.API_TITLE,
- version=settings.API_VERSION,
- description="赛博小镇 - 基于HelloAgents的AI NPC对话系统",
- lifespan=lifespan
- )
- # CORS配置
- app.add_middleware(
- CORSMiddleware,
- allow_origins=settings.CORS_ORIGINS,
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
- )
- # 获取全局实例
- npc_manager = None
- state_manager = None
- def get_managers():
- """获取管理器实例"""
- global npc_manager, state_manager
- if npc_manager is None:
- npc_manager = get_npc_manager()
- if state_manager is None:
- state_manager = get_state_manager()
- return npc_manager, state_manager
- # ==================== API路由 ====================
- @app.get("/")
- async def root():
- """根路径 - API信息"""
- return {
- "service": settings.API_TITLE,
- "version": settings.API_VERSION,
- "status": "running",
- "features": ["AI对话", "NPC记忆系统", "好感度系统", "批量状态更新"],
- "endpoints": {
- "docs": "/docs",
- "chat": "/chat",
- "npcs": "/npcs",
- "npcs_status": "/npcs/status",
- "npc_memories": "/npcs/{npc_name}/memories",
- "npc_affinity": "/npcs/{npc_name}/affinity",
- "all_affinities": "/affinities"
- }
- }
- @app.get("/health")
- async def health_check():
- """健康检查"""
- return {"status": "healthy", "timestamp": "now"}
- @app.post("/chat", response_model=ChatResponse)
- async def chat_with_npc(request: ChatRequest):
- """与NPC对话接口
-
- 玩家与指定NPC进行实时对话,使用独立的Agent处理
- """
- npc_mgr, _ = get_managers()
-
- # 验证NPC是否存在
- npc_info = npc_mgr.get_npc_info(request.npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{request.npc_name}' 不存在"
- )
-
- try:
- # 调用NPC Agent处理对话
- response_text = npc_mgr.chat(request.npc_name, request.message)
-
- return ChatResponse(
- npc_name=request.npc_name,
- npc_title=npc_info["title"],
- message=response_text,
- success=True
- )
-
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"对话处理失败: {str(e)}"
- )
- @app.get("/npcs", response_model=NPCListResponse)
- async def list_npcs():
- """获取所有NPC列表"""
- npc_mgr, _ = get_managers()
-
- npcs_data = npc_mgr.get_all_npcs()
- npcs = [NPCInfo(**npc) for npc in npcs_data]
-
- return NPCListResponse(
- npcs=npcs,
- total=len(npcs)
- )
- @app.get("/npcs/status", response_model=NPCStatusResponse)
- async def get_npcs_status():
- """获取所有NPC的当前状态
-
- 返回批量生成的NPC对话内容,用于显示NPC的自主行为
- """
- _, state_mgr = get_managers()
-
- state = state_mgr.get_current_state()
-
- return NPCStatusResponse(
- dialogues=state["dialogues"],
- last_update=state["last_update"],
- next_update_in=state["next_update_in"]
- )
- @app.post("/npcs/status/refresh")
- async def refresh_npcs_status():
- """强制刷新NPC状态
-
- 立即触发一次批量对话生成
- """
- _, state_mgr = get_managers()
-
- await state_mgr.force_update()
- state = state_mgr.get_current_state()
-
- return {
- "message": "NPC状态已刷新",
- "dialogues": state["dialogues"]
- }
- @app.get("/npcs/{npc_name}")
- async def get_npc_info(npc_name: str):
- """获取指定NPC的详细信息"""
- npc_mgr, state_mgr = get_managers()
- npc_info = npc_mgr.get_npc_info(npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{npc_name}' 不存在"
- )
- # 添加当前对话
- current_dialogue = state_mgr.get_npc_dialogue(npc_name)
- npc_info["current_dialogue"] = current_dialogue
- return npc_info
- @app.get("/npcs/{npc_name}/memories")
- async def get_npc_memories(npc_name: str, limit: int = 10):
- """获取NPC的记忆列表
- Args:
- npc_name: NPC名称
- limit: 返回的记忆数量限制 (默认10条)
- Returns:
- NPC的记忆列表
- """
- npc_mgr, _ = get_managers()
- # 验证NPC是否存在
- npc_info = npc_mgr.get_npc_info(npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{npc_name}' 不存在"
- )
- try:
- memories = npc_mgr.get_npc_memories(npc_name, limit=limit)
- return {
- "npc_name": npc_name,
- "memories": memories,
- "total": len(memories)
- }
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"获取记忆失败: {str(e)}"
- )
- @app.delete("/npcs/{npc_name}/memories")
- async def clear_npc_memories(npc_name: str, memory_type: str = None):
- """清空NPC的记忆 (用于测试)
- Args:
- npc_name: NPC名称
- memory_type: 记忆类型 (working/episodic), 不指定则清空所有
- Returns:
- 操作结果
- """
- npc_mgr, _ = get_managers()
- # 验证NPC是否存在
- npc_info = npc_mgr.get_npc_info(npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{npc_name}' 不存在"
- )
- try:
- npc_mgr.clear_npc_memory(npc_name, memory_type)
- return {
- "message": f"已清空{npc_name}的记忆",
- "npc_name": npc_name,
- "memory_type": memory_type or "all"
- }
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"清空记忆失败: {str(e)}"
- )
- @app.get("/npcs/{npc_name}/affinity")
- async def get_npc_affinity(npc_name: str, player_id: str = "player"):
- """获取NPC对玩家的好感度
- Args:
- npc_name: NPC名称
- player_id: 玩家ID (默认为"player")
- Returns:
- 好感度信息
- """
- npc_mgr, _ = get_managers()
- # 验证NPC是否存在
- npc_info = npc_mgr.get_npc_info(npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{npc_name}' 不存在"
- )
- try:
- affinity_info = npc_mgr.get_npc_affinity(npc_name, player_id)
- return {
- "npc_name": npc_name,
- "player_id": player_id,
- **affinity_info
- }
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"获取好感度失败: {str(e)}"
- )
- @app.get("/affinities")
- async def get_all_affinities(player_id: str = "player"):
- """获取所有NPC对玩家的好感度
- Args:
- player_id: 玩家ID (默认为"player")
- Returns:
- 所有NPC的好感度信息
- """
- npc_mgr, _ = get_managers()
- try:
- affinities = npc_mgr.get_all_affinities(player_id)
- return {
- "player_id": player_id,
- "affinities": affinities
- }
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"获取好感度失败: {str(e)}"
- )
- @app.put("/npcs/{npc_name}/affinity")
- async def set_npc_affinity(npc_name: str, affinity: float, player_id: str = "player"):
- """设置NPC对玩家的好感度 (用于测试)
- Args:
- npc_name: NPC名称
- affinity: 好感度值 (0-100)
- player_id: 玩家ID (默认为"player")
- Returns:
- 操作结果
- """
- npc_mgr, _ = get_managers()
- # 验证NPC是否存在
- npc_info = npc_mgr.get_npc_info(npc_name)
- if not npc_info:
- raise HTTPException(
- status_code=404,
- detail=f"NPC '{npc_name}' 不存在"
- )
- # 验证好感度范围
- if affinity < 0 or affinity > 100:
- raise HTTPException(
- status_code=400,
- detail="好感度必须在0-100之间"
- )
- try:
- npc_mgr.set_npc_affinity(npc_name, affinity, player_id)
- affinity_info = npc_mgr.get_npc_affinity(npc_name, player_id)
- return {
- "message": f"已设置{npc_name}对玩家的好感度",
- "npc_name": npc_name,
- "player_id": player_id,
- **affinity_info
- }
- except Exception as e:
- raise HTTPException(
- status_code=500,
- detail=f"设置好感度失败: {str(e)}"
- )
- # ==================== 主程序入口 ====================
- if __name__ == "__main__":
- print("\n🚀 启动赛博小镇后端服务...")
- print(f"📍 监听地址: {settings.API_HOST}:{settings.API_PORT}")
- print(f"📖 访问文档: http://localhost:{settings.API_PORT}/docs\n")
-
- uvicorn.run(
- "main:app",
- host=settings.API_HOST,
- port=settings.API_PORT,
- reload=True, # 开发模式自动重载
- log_level="info"
- )
|