needq_agent.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. # -*- coding: utf-8 -*-
  2. """
  3. 需风量计算 Agent 创建与配置模块
  4. 核心功能:
  5. - 创建需风量计算 DeepAgent(整合 Skills + 计算工具 + MCP 数据)
  6. - 配置流式输出
  7. - 管理 Agent 实例生命周期
  8. 架构:
  9. - 模型: 通过 vent_agent._get_model() 获取
  10. - Skills: needq-calc(需风量计算技能)
  11. - Tools: 14个 calc_tools 计算函数 + write_todos + get_needq_all_data
  12. - Backend: FilesystemBackend(virtual_mode)
  13. - 系统提示词仅包含通用行为准则,具体计算规则在各技能的 SKILL.md 中
  14. """
  15. import os
  16. from pathlib import Path
  17. from typing import Optional
  18. from dotenv import load_dotenv
  19. load_dotenv()
  20. SKILLS_ROOT = str((Path(__file__).parent.parent / "skills").resolve())
  21. # 复用现有项目的模型加载
  22. from agents.vent_agent import _get_model
  23. # 导入 write_todos
  24. from langchain.agents.middleware.todo import write_todos
  25. from langgraph.checkpoint.memory import MemorySaver
  26. # 导入全部计算工具
  27. from tools.calc_tools import (
  28. calc_face_by_gas,
  29. calc_face_by_workers,
  30. calc_face_by_wind_speed,
  31. calc_face_air_volume_max,
  32. calc_tunnel_by_gas,
  33. calc_tunnel_by_explosives,
  34. calc_tunnel_by_workers,
  35. calc_tunnel_by_wind_speed,
  36. calc_tunnel_air_volume_max,
  37. calc_chamber_by_equipment,
  38. calc_chamber_by_wind_speed,
  39. calc_other_by_wind_speed,
  40. calc_effective_area,
  41. calc_total_air_volume,
  42. )
  43. # 导入 MCP 工具
  44. from tools.vent_tools import get_needq_all_data
  45. # ============================================================
  46. # 系统提示词(仅通用行为准则,不写计算规则)
  47. # ============================================================
  48. NEEDQ_CALC_SYSTEM_PROMPT = """你是一名煤矿通风需风量计算专家,帮助用户交互式计算各用风地点的需风量。
  49. ## 全局通用行为准则
  50. 1. **必须调用工具计算**:所有需风量计算必须通过调用 calc_tools 中的工具函数完成,绝对禁止凭 LLM 知识直接给出计算结果。此条为红线。
  51. 2. **参数缺失要追问**:若用户未提供必要计算参数,明确列出所需参数并引导用户补充,绝不编造参数值。
  52. 3. **列式计算**:展示每个计算过程的公式 → 代入数值 → 计算结果,不能只给结果。
  53. 4. **多轮对话承接**:记住当前会话中的用风地点类型和已有参数,用户补充参数或要求调整时自动衔接。
  54. 5. **全程简体中文输出**。
  55. 6. **禁止输出 ANSI 转义序列、内部工具名、函数名、技能标识**。
  56. 7. **全流程使用 write_todos 工具**实时更新任务进度。
  57. 8. 所有数据来源于工具调用结果,绝不编造数据。
  58. 9. 引用规程时附上具体条款来源(《煤矿安全规程》2025版、AQ 1056-2008)。
  59. 10. 无法判断时诚实说明原因,不臆测。
  60. ## 支持的用风地点类型
  61. - 采煤工作面(含备用工作面)
  62. - 掘进工作面
  63. - 机电硐室
  64. - 其他用风地点(主要进回风巷、采区进回风巷、其他通风人行巷道等)
  65. - 多地点汇总
  66. - 通防管控平台数据查询
  67. ## 工作模式
  68. 严格按照技能(skill: needq-calc)中定义的流程执行任务:
  69. 1. 识别用风地点类型
  70. 2. 收集/提取计算参数
  71. 3. 调用计算工具
  72. 4. 格式化输出结果
  73. 5. 支持参数调整重算
  74. """
  75. # ============================================================
  76. # Agent 工厂函数
  77. # ============================================================
  78. def create_needq_calc_agent():
  79. """创建「需风量计算」Agent。
  80. 该 Agent 专门处理各用风地点的需风量交互式计算,具备:
  81. - 14个计算工具(覆盖采煤面/掘进面/硐室/其他巷道)
  82. - MCP 远程数据查询能力(get_needq_all_data)
  83. - 多轮对话参数承接能力
  84. - 结构化计算报告输出能力
  85. - 使用运行时模型配置(支持前端动态切换)
  86. Returns:
  87. CompiledStateGraph: 编译后的 LangGraph 状态图,支持 .invoke() 和 .stream()
  88. """
  89. from deepagents import create_deep_agent
  90. from deepagents.backends import FilesystemBackend
  91. from deepagents import FilesystemPermission
  92. from api.model_config import get_model_instance
  93. from tools.context_tracker import init_system_components, _tool_to_text
  94. _project_root = str(Path(__file__).parent.parent)
  95. skills = ["skills/needq-calc"]
  96. print(f"[skills] Agent=needq-calc-agent skills={skills}")
  97. agent = create_deep_agent(
  98. model=get_model_instance(),
  99. tools=[
  100. # 基础工具
  101. write_todos,
  102. # 辅助工具
  103. calc_effective_area,
  104. calc_total_air_volume,
  105. # 采煤工作面计算
  106. calc_face_by_gas,
  107. calc_face_by_workers,
  108. calc_face_by_wind_speed,
  109. calc_face_air_volume_max,
  110. # 掘进工作面计算
  111. calc_tunnel_by_gas,
  112. calc_tunnel_by_explosives,
  113. calc_tunnel_by_workers,
  114. calc_tunnel_by_wind_speed,
  115. calc_tunnel_air_volume_max,
  116. # 硐室计算
  117. calc_chamber_by_equipment,
  118. calc_chamber_by_wind_speed,
  119. # 其他巷道计算
  120. calc_other_by_wind_speed,
  121. # MCP 远程数据查询
  122. get_needq_all_data,
  123. ],
  124. skills=skills,
  125. system_prompt=NEEDQ_CALC_SYSTEM_PROMPT,
  126. backend=FilesystemBackend(root_dir=_project_root, virtual_mode=True),
  127. permissions=[
  128. FilesystemPermission(operations=["write"], paths=["/**"], mode="deny"),
  129. ],
  130. middleware=[],
  131. name="needq-calc-agent",
  132. checkpointer=MemorySaver(),
  133. )
  134. # ── 初始化上下文用量追踪 ──
  135. _all_tools = [
  136. write_todos, calc_effective_area, calc_total_air_volume,
  137. calc_face_by_gas, calc_face_by_workers, calc_face_by_wind_speed, calc_face_air_volume_max,
  138. calc_tunnel_by_gas, calc_tunnel_by_explosives, calc_tunnel_by_workers,
  139. calc_tunnel_by_wind_speed, calc_tunnel_air_volume_max,
  140. calc_chamber_by_equipment, calc_chamber_by_wind_speed,
  141. calc_other_by_wind_speed,
  142. ]
  143. tool_texts = [_tool_to_text(t) for t in _all_tools]
  144. skill_contents = []
  145. for s in skills:
  146. sf = Path(__file__).parent.parent / s / "SKILL.md"
  147. if sf.exists():
  148. skill_contents.append(sf.read_text(encoding="utf-8"))
  149. # MCP 工具:单独归入 mcp 类别
  150. mcp_text = _tool_to_text(get_needq_all_data)
  151. init_system_components(
  152. system_prompt=NEEDQ_CALC_SYSTEM_PROMPT,
  153. tool_defs=tool_texts,
  154. skill_contents=skill_contents,
  155. mcp_defs=[mcp_text],
  156. )
  157. return agent
  158. # ============================================================
  159. # 单例缓存
  160. # ============================================================
  161. _needq_calc_agent: Optional[object] = None
  162. def invalidate_cache():
  163. """失效需风量计算 Agent 缓存(模型切换时调用)。"""
  164. global _needq_calc_agent
  165. _needq_calc_agent = None
  166. print("[模型切换] needq-calc-agent 缓存已失效")
  167. def get_needq_calc_agent():
  168. """获取需风量计算 Agent 单例"""
  169. global _needq_calc_agent
  170. if _needq_calc_agent is None:
  171. _needq_calc_agent = create_needq_calc_agent()
  172. return _needq_calc_agent