requirement.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. """
  2. 需求管理工具 - KnowHub Requirement API 封装
  3. 提供需求的向量检索和列表查询能力,
  4. 让 Agent 在提取新需求前可以先了解已有的需求库。
  5. """
  6. import os
  7. import json
  8. import logging
  9. import httpx
  10. from typing import Optional
  11. from agent.tools import tool, ToolResult, ToolContext
  12. logger = logging.getLogger(__name__)
  13. KNOWHUB_API = os.getenv("KNOWHUB_API", "http://localhost:8000").rstrip("/")
  14. @tool(
  15. hidden_params=["context"],
  16. display={
  17. "zh": {
  18. "name": "需求检索",
  19. "params": {
  20. "query": "搜索关键词",
  21. "top_k": "返回数量",
  22. }
  23. },
  24. "en": {
  25. "name": "Search Requirements",
  26. "params": {
  27. "query": "Search query",
  28. "top_k": "Number of results",
  29. }
  30. }
  31. }
  32. )
  33. async def requirement_search(
  34. query: str,
  35. top_k: int = 20,
  36. context: Optional[ToolContext] = None,
  37. ) -> ToolResult:
  38. """向量检索需求库,根据语义相似度返回最相关的需求。用于在提取新需求前了解已有需求,避免重复。"""
  39. try:
  40. params = {"q": query, "top_k": top_k}
  41. async with httpx.AsyncClient(timeout=30.0) as client:
  42. res = await client.get(f"{KNOWHUB_API}/api/requirement/search", params=params)
  43. res.raise_for_status()
  44. data = res.json()
  45. results = data.get("results", data) if isinstance(data, dict) else data
  46. items = results if isinstance(results, list) else []
  47. count = len(items)
  48. if count == 0:
  49. return ToolResult(
  50. title="需求检索完成",
  51. output="未找到相关需求。",
  52. long_term_memory=f"需求检索: 未找到与 '{query[:50]}' 相关的需求",
  53. )
  54. output = json.dumps(data, ensure_ascii=False, indent=2)
  55. return ToolResult(
  56. title=f"需求检索完成 -- 找到 {count} 条",
  57. output=output,
  58. long_term_memory=f"需求检索: 找到 {count} 条与 '{query[:50]}' 相关的需求",
  59. metadata={"count": count, "items": items},
  60. )
  61. except httpx.ConnectError:
  62. msg = f"无法连接到 KnowHub 服务 ({KNOWHUB_API}),请确认服务是否启动。"
  63. return ToolResult(title="需求检索失败", output=msg, error=msg)
  64. except Exception as e:
  65. return ToolResult(title="需求检索失败", output=str(e), error=str(e))
  66. @tool(
  67. hidden_params=["context"],
  68. display={
  69. "zh": {
  70. "name": "需求列表",
  71. "params": {
  72. "limit": "返回数量",
  73. "offset": "偏移量",
  74. }
  75. },
  76. "en": {
  77. "name": "List Requirements",
  78. "params": {
  79. "limit": "Number of results",
  80. "offset": "Offset",
  81. }
  82. }
  83. }
  84. )
  85. async def requirement_list(
  86. limit: int = 20,
  87. offset: int = 0,
  88. context: Optional[ToolContext] = None,
  89. ) -> ToolResult:
  90. """列出需求库中的已有需求,用于浏览和了解需求全貌。"""
  91. try:
  92. params = {"limit": limit, "offset": offset}
  93. async with httpx.AsyncClient(timeout=30.0) as client:
  94. res = await client.get(f"{KNOWHUB_API}/api/requirement/list", params=params)
  95. res.raise_for_status()
  96. data = res.json()
  97. results = data.get("results", data) if isinstance(data, dict) else data
  98. items = results if isinstance(results, list) else []
  99. count = len(items)
  100. if count == 0:
  101. return ToolResult(
  102. title="需求列表为空",
  103. output="需求库中暂无需求。",
  104. long_term_memory="需求列表: 需求库为空",
  105. )
  106. output = json.dumps(data, ensure_ascii=False, indent=2)
  107. return ToolResult(
  108. title=f"需求列表 -- 共 {count} 条",
  109. output=output,
  110. long_term_memory=f"需求列表: 获取了 {count} 条需求 (offset={offset})",
  111. metadata={"count": count, "items": items},
  112. )
  113. except httpx.ConnectError:
  114. msg = f"无法连接到 KnowHub 服务 ({KNOWHUB_API}),请确认服务是否启动。"
  115. return ToolResult(title="需求列表获取失败", output=msg, error=msg)
  116. except Exception as e:
  117. return ToolResult(title="需求列表获取失败", output=str(e), error=str(e))