Files
iot-device-management-service/app/services/agent/tools/order_tools.py
16337 8156f54004 重构 Agent:引入 LangGraph StateGraph 替代手写 FC 循环
架构变更:
- 新增 app/services/agent/ 模块(state/prompts/graph/tools)
- 7 个工具从 _tool_xxx 方法提取为 @tool 装饰器函数
- 构建 assistant + ToolNode 的 ReAct 图
- agent_dispatcher.py 改为薄壳入口,支持 USE_LANGGRAPH 开关
- MemorySaver checkpoint 持久化对话(thread_id=wechat-{user_id})
- 新增依赖:langchain-core, langchain-openai, langgraph

向后兼容:
- USE_LANGGRAPH=false 可切回旧版 FC 循环
- LangGraph 初始化失败自动降级到 Legacy 模式
- 企微图片处理/VLM分析逻辑不变
2026-03-25 13:52:55 +08:00

150 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
工单工具:查询我的工单、提交处理结果
"""
import json
from typing import List, Optional
from langchain_core.tools import tool
from langchain_core.runnables import RunnableConfig
from app.utils.logger import logger
from .alarm_query import ALARM_TYPE_NAMES, _get_camera_display_name
from .alarm_action import _get_order_id_for_alarm
@tool
def list_my_orders(config: RunnableConfig, status: str = "HANDLING") -> str:
"""查询我的待处理工单列表
Args:
status: 工单状态筛选 HANDLING=处理中 ALL=全部
"""
from app.services.alarm_event_service import get_alarm_event_service
svc = get_alarm_event_service()
user_id = config.get("configurable", {}).get("user_id", "")
alarms, total = svc.get_alarms(
alarm_status="CONFIRMED",
page=1,
page_size=20,
)
my_alarms = [a for a in alarms if a.handler == user_id]
if not my_alarms:
return json.dumps({"total": 0, "items": [], "message": "当前没有待处理的工单"}, ensure_ascii=False)
items = []
for a in my_alarms:
cam_name = _get_camera_display_name(a.device_id)
event_time = ""
if a.event_time:
try:
event_time = a.event_time.strftime("%m-%d %H:%M")
except Exception:
event_time = str(a.event_time)[:16]
items.append({
"alarm_id": a.alarm_id,
"type": ALARM_TYPE_NAMES.get(a.alarm_type, a.alarm_type),
"camera": cam_name,
"time": event_time,
})
return json.dumps({"total": len(my_alarms), "items": items}, ensure_ascii=False)
@tool
async def submit_order_result(
alarm_id: str,
result_text: str,
config: RunnableConfig,
image_urls: Optional[List[str]] = None,
) -> str:
"""提交工单处理结果(文字描述+处理后照片URL
Args:
alarm_id: 关联的告警ID
result_text: 处理结果描述
image_urls: 处理后照片URL列表COS永久URL
"""
from app.services.alarm_event_service import get_alarm_event_service
from app.services.wechat_service import get_wechat_service
user_id = config.get("configurable", {}).get("user_id", "")
svc = get_alarm_event_service()
wechat = get_wechat_service()
if image_urls is None:
image_urls = []
# 检查告警是否存在
detail = svc.get_alarm(alarm_id)
if not detail:
return json.dumps({"error": f"未找到告警: {alarm_id}"}, ensure_ascii=False)
# 合并 session 中暂存的图片
from app.services.session_manager import get_session_manager
session = get_session_manager().get(user_id)
if session.pending_images:
image_urls = session.pending_images + image_urls
session.pending_images = []
# 获取关联工单ID
order_id = _get_order_id_for_alarm(alarm_id)
from app.services.work_order_client import get_work_order_client
wo_client = get_work_order_client()
remark = f"企微Agent结单: {result_text}"
if image_urls:
remark += f" (附{len(image_urls)}张图片)"
if order_id and wo_client.enabled:
if not await wo_client.submit_order(
order_id,
result=f"{result_text} by {user_id}",
result_img_urls=image_urls or None,
):
remark += "(IoT降级)"
svc.handle_alarm(alarm_id=alarm_id, alarm_status="CLOSED",
handle_status="DONE", handler=user_id, remark=remark)
# 持久化处理结果图片到 alarm_event_ext
if image_urls:
try:
from app.models import get_session as get_db_session, AlarmEventExt
db = get_db_session()
try:
ext = AlarmEventExt(
alarm_id=alarm_id,
ext_type="HANDLER_RESULT",
ext_data={"result_text": result_text, "image_urls": image_urls, "handler": user_id},
)
db.add(ext)
db.commit()
except Exception as e:
db.rollback()
logger.error(f"持久化处理结果图片失败: {e}")
finally:
db.close()
except Exception as e:
logger.error(f"保存处理结果失败: {e}")
# 更新卡片到终态
response_code = wechat.get_response_code(alarm_id)
if response_code:
await wechat.update_alarm_card_terminal(
response_code=response_code, user_ids=[user_id],
alarm_id=alarm_id, action="complete", operator_name=user_id,
)
result = {
"success": True,
"message": f"工单已提交: {alarm_id}",
"result": result_text,
"images_count": len(image_urls),
}
return json.dumps(result, ensure_ascii=False)