server.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. """
  2. A2A Server — 基于 DeepSeek 的问答 Agent
  3. 对外暴露 A2A 协议接口,让其他 Agent 可以发现自己并提交任务。
  4. """
  5. import os
  6. import uvicorn
  7. from starlette.applications import Starlette
  8. from a2a.server.request_handlers import DefaultRequestHandler
  9. from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
  10. from a2a.server.agent_execution import AgentExecutor, RequestContext
  11. from a2a.server.events import EventQueue
  12. from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
  13. from a2a.types import (
  14. AgentCapabilities, AgentCard, AgentInterface, AgentSkill,
  15. TaskState,
  16. )
  17. from a2a.helpers import (
  18. get_message_text, new_task_from_user_message,
  19. new_text_message, new_text_part,
  20. )
  21. from openai import AsyncOpenAI
  22. # ── DeepSeek Agent ─────────────────────────────────────────
  23. DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "你的APIKEY")
  24. class MyAgent:
  25. """用 DeepSeek 回答问题的 Agent。"""
  26. def __init__(self):
  27. self.client = AsyncOpenAI(
  28. api_key=DEEPSEEK_API_KEY,
  29. base_url="https://api.deepseek.com/v1",
  30. )
  31. async def invoke(self, user_request: str) -> str:
  32. response = await self.client.chat.completions.create(
  33. model="deepseek-chat",
  34. messages=[
  35. {"role": "system", "content": "你是一个有用的 AI 助手,请用简洁的中文回答用户的问题。"},
  36. {"role": "user", "content": user_request},
  37. ],
  38. temperature=0.7,
  39. max_tokens=1024,
  40. )
  41. return response.choices[0].message.content
  42. # ── Agent Executor ─────────────────────────────────────────
  43. class MyAgentExecutor(AgentExecutor):
  44. """A2A Executor:把 A2A 任务转发给 MyAgent 处理。"""
  45. def __init__(self):
  46. self.agent = MyAgent()
  47. async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
  48. # 1. 获取或创建 Task
  49. if context.current_task:
  50. task = context.current_task
  51. else:
  52. task = new_task_from_user_message(context.message)
  53. await event_queue.enqueue_event(task)
  54. # 2. 更新状态:正在处理
  55. updater = TaskUpdater(event_queue=event_queue, task_id=task.id, context_id=task.context_id)
  56. await updater.start_work(message=new_text_message("正在处理..."))
  57. # 3. 提取用户输入,调用 DeepSeek
  58. query = get_message_text(context.message)
  59. result = await self.agent.invoke(user_request=query) if query else "没有收到有效输入"
  60. # 4. 把结果作为 Artifact 返回
  61. await updater.add_artifact(parts=[new_text_part(text=result, media_type="text/plain")])
  62. # 5. 标记任务完成
  63. await updater.complete(message=new_text_message("处理完成!"))
  64. print(f" ✓ 处理完成: {result[:60]}...")
  65. async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
  66. raise NotImplementedError("暂不支持取消")
  67. # ── Agent Card ─────────────────────────────────────────────
  68. skill = AgentSkill(
  69. id="qa_bot",
  70. name="问答机器人",
  71. description="一个简单的问答 Agent,接收文本问题并返回 DeepSeek 的回答",
  72. input_modes=["text/plain"],
  73. output_modes=["text/plain"],
  74. tags=["问答", "demo", "deepseek"],
  75. examples=["你好", "什么是 A2A 协议?"],
  76. )
  77. agent_card = AgentCard(
  78. name="问答 Agent",
  79. description="一个通用问答 Agent,基于 DeepSeek 回答各种问题",
  80. version="1.0.0",
  81. default_input_modes=["text/plain"],
  82. default_output_modes=["text/plain"],
  83. capabilities=AgentCapabilities(streaming=True),
  84. supported_interfaces=[
  85. AgentInterface(
  86. protocol_binding="JSONRPC",
  87. url="http://127.0.0.1:9999",
  88. protocol_version="1.0",
  89. ),
  90. ],
  91. skills=[skill],
  92. )
  93. # ── 启动服务 ───────────────────────────────────────────────
  94. if __name__ == "__main__":
  95. request_handler = DefaultRequestHandler(
  96. agent_executor=MyAgentExecutor(),
  97. task_store=InMemoryTaskStore(),
  98. agent_card=agent_card,
  99. )
  100. routes = []
  101. routes.extend(create_agent_card_routes(agent_card))
  102. routes.extend(create_jsonrpc_routes(request_handler, "/"))
  103. app = Starlette(routes=routes)
  104. print("🚀 A2A Server 启动 → http://127.0.0.1:9999")
  105. print(f" Agent Card: http://127.0.0.1:9999/.well-known/agent-card.json")
  106. uvicorn.run(app, host="127.0.0.1", port=9999)