server.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """FastAPI 后端 — SSE 实时推送 Agent 执行 trace"""
  2. import json, asyncio
  3. from fastapi import FastAPI
  4. from fastapi.staticfiles import StaticFiles
  5. from fastapi.responses import FileResponse, StreamingResponse
  6. from pydantic import BaseModel
  7. from config import APP_PORT
  8. from orchestrator import run_supervisor, run_pipeline, run_debate
  9. app = FastAPI(title="Multi-Agent Demo")
  10. class ExecuteRequest(BaseModel):
  11. mode: str # supervisor | pipeline | debate
  12. query: str
  13. MODE_RUNNERS = {
  14. "supervisor": run_supervisor,
  15. "pipeline": run_pipeline,
  16. "debate": run_debate,
  17. }
  18. PRESETS = {
  19. "supervisor": {
  20. "title": "Agent 面试题调研 + 数据分析",
  21. "query": "搜索 2025 年最新的 Agent 开发面试题,重点找 RAG、多 Agent 协同、上下文工程相关的题目,统计各方向的题目数量占比",
  22. },
  23. "pipeline": {
  24. "title": "面试题竞品分析报告",
  25. "query": "搜索 LangChain、CrewAI、AutoGen 三个多 Agent 框架的核心差异、社区活跃度、适用场景,生成一份结构化的对比分析报告",
  26. },
  27. "debate": {
  28. "title": "技术选型辩论",
  29. "query": "Agent 开发应该用 LangGraph 还是从零手撸?LangGraph 框架 vs 纯代码手写,哪种更适合生产环境?",
  30. },
  31. }
  32. @app.get("/")
  33. async def index():
  34. return FileResponse("index.html")
  35. @app.get("/api/presets")
  36. async def get_presets():
  37. return PRESETS
  38. @app.post("/api/execute")
  39. async def execute(req: ExecuteRequest):
  40. runner = MODE_RUNNERS.get(req.mode)
  41. if not runner:
  42. return {"error": f"Unknown mode: {req.mode}"}
  43. async def stream():
  44. queue = asyncio.Queue()
  45. def emit(event):
  46. queue.put_nowait(event)
  47. # 在线程中运行(LLM 调用是阻塞的)
  48. import threading
  49. result_holder = {}
  50. def run():
  51. try:
  52. result = runner(req.query, emit=emit)
  53. result_holder["result"] = result
  54. except Exception as e:
  55. result_holder["error"] = str(e)
  56. finally:
  57. queue.put_nowait(None) # 结束信号
  58. thread = threading.Thread(target=run, daemon=True)
  59. thread.start()
  60. # 流式输出事件
  61. while True:
  62. event = await queue.get()
  63. if event is None:
  64. break
  65. yield f"data: {json.dumps(event, ensure_ascii=False)}\n\n"
  66. # 发送最终结果
  67. if "error" in result_holder:
  68. yield f"data: {json.dumps({'type': 'error', 'message': result_holder['error']}, ensure_ascii=False)}\n\n"
  69. elif "result" in result_holder:
  70. yield f"data: {json.dumps({'type': 'final', **result_holder['result']}, ensure_ascii=False)}\n\n"
  71. return StreamingResponse(stream(), media_type="text/event-stream")
  72. if __name__ == "__main__":
  73. import uvicorn
  74. uvicorn.run(app, host="0.0.0.0", port=APP_PORT)