Files
DPM/backend/app/tools.py
T
Pine 18d28be943 feat: 添加后端部署和API基本检索的Docker Compose配置
- 引入新的`docker-compose.yml`文件,便于后端和MQTT代理(EMQX)的部署。
- 更新多个组件中的API基本检索,使用函数进行动态解析。
- 加强了多个组件中API调用的错误处理和日志记录。
- 优化了AI聊天面板离线场景的回退响应。
- 更新DataScreen和MediaScreen组件中的数据显示和统计,以反映准确的指标。
- 重构MQTT连接逻辑,以支持动态凭证和客户端ID。
2026-08-18 06:50:50 +08:00

135 lines
6.2 KiB
Python

# -*- coding: utf-8 -*-
"""智能体工具注册表 —— LLM function calling 的后端执行器
- TOOLS:工具名 → 描述 + JSON Schema 参数(给 LLM 声明用)
- exec_tool(name, args):执行工具,返回给 LLM 的字符串结果(JSON)
- 供 POST /api/tools/exec 调用;前端 s2s 客户端收到 toolcall 事件后转发到此执行
"""
import json
import logging
from datetime import datetime
log = logging.getLogger("dpm.tools")
# ── 工具实现 ────────────────────────────────────────────────────────────
def _get_park_overview(args):
"""园区实时概览(官方统计口径 + 省级绩效累计口径 + AI 平台运行指标)"""
from .sim_engine import sim_engine
d = sim_engine.snapshot()
return json.dumps({
"数据口径": "官方统计(《2026年7月运行情况统计表》);累计口径(附件1省级材料,截至2026年5月);AI平台指标:TOKEN/工具调用",
"实际入驻企业数(家)": d["projects"]["inPark"],
"园区可容纳企业数(个)": d["projects"]["capacity"],
"开园以来累计投入运营资金(万元)": d["projects"]["invested"],
"带动就业人数(人,当年累计)": d["jobs"]["total"],
"生产经营总额(万元,当年累计)": d["revenue"]["total"],
"上缴税利总额(万元,当年累计)": d["revenue"]["tax"],
"累计孵化企业(家,省级绩效累计)": d["cumulative"]["incubated"],
"当前实有在孵实体(家,省级绩效累计)": d["cumulative"]["inIncubation"],
"累计成功孵化出园企业(家,开园以来累计)": d["cumulative"]["graduated"],
"入驻团队发明专利(项,累计)": d["cumulative"]["patents"],
"累计带动就业(人,省级绩效累计)": d["cumulative"]["jobs"],
"入驻团队累计经营收入(万元)": d["cumulative"]["revenue"],
"入驻团队累计税收(万元)": d["cumulative"]["tax"],
"创业指导专家团队(人)": d["cumulative"]["mentors"],
"近3年入孵实体孵化成功率(%)": d["cumulative"]["successRate"],
"园区建筑面积(㎡)": d["park"]["area"],
"TOKEN月均消耗(亿)": 200,
"TOKEN实时速率(t/s)": d["token"]["rate"],
"今日TOKEN消耗(万)": d["token"]["today"],
}, ensure_ascii=False)
def _query_companies(args):
"""园区入驻企业名录查询(关键词过滤)"""
from .sim_engine import COMPANY_NAMES
q = (args.get("keyword") or "").strip()
names = [n for n in COMPANY_NAMES if q in n] if q else list(COMPANY_NAMES)
return json.dumps({
"total": len(names),
"keyword": q,
"companies": names[:20],
}, ensure_ascii=False)
def _control_display(args):
"""大屏显示控制:切页 / 弹通知 / 媒体播放暂停(经 MQTT 广播到大屏)"""
from .mqtt import hub
action = (args.get("action") or "").strip()
target = (args.get("target") or "").strip()
if action not in ("switch_page", "alert", "media_play", "media_pause"):
return json.dumps({"error": f"不支持的 action: {action}"}, ensure_ascii=False)
params = {"target": target} if target else {}
ok = hub.publish_command(action, params).get("published", False)
return json.dumps({"ok": ok, "action": action, "target": target}, ensure_ascii=False)
def _get_time(args):
"""当前日期时间"""
return json.dumps({"datetime": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}, ensure_ascii=False)
# ── 注册表 ──────────────────────────────────────────────────────────────
TOOLS = {
"get_park_overview": {
"fn": _get_park_overview,
"description": "获取园区实时运营概览:在园项目数、累计孵化企业、带动就业、营收(今日/累计)、设备在线率、在园人数、能耗等。回答园区数据类问题时使用。",
"parameters": {"type": "object", "properties": {}},
},
"query_companies": {
"fn": _query_companies,
"description": "查询园区入驻企业名录,支持按企业名关键词过滤。回答'有哪些企业/某企业是否入驻'时使用。",
"parameters": {
"type": "object",
"properties": {"keyword": {"type": "string", "description": "企业名关键词,可为空字符串"}},
},
},
"control_display": {
"fn": _control_display,
"description": "控制大屏显示:switch_page 切换页面、alert 弹通知、media_play/media_pause 控制媒体播放。用户要求控制大屏时使用。",
"parameters": {
"type": "object",
"properties": {
"action": {"type": "string", "enum": ["switch_page", "alert", "media_play", "media_pause"]},
"target": {"type": "string", "description": "目标:页面路径(如 /、/twin、/ai、/voice)或通知文本"},
},
},
},
"get_time": {
"fn": _get_time,
"description": "获取当前日期时间。用户问'现在几点/今天几号'时使用。",
"parameters": {"type": "object", "properties": {}},
},
}
def tool_schemas():
"""给 LLM 的 tools 定义(OpenAI function calling 格式)"""
return [
{
"type": "function",
"name": name,
"description": t["description"],
"parameters": t["parameters"],
}
for name, t in TOOLS.items()
]
def exec_tool(name: str, args: dict):
"""执行工具 → 返回给 LLM 的字符串结果(JSON)"""
t = TOOLS.get(name)
if not t:
log.warning("tools: 未知工具 %s", name)
return json.dumps({"error": f"未知工具: {name}"}, ensure_ascii=False)
try:
result = t["fn"](args or {})
log.info("tools: %s args=%s -> %s", name, json.dumps(args, ensure_ascii=False)[:120], str(result)[:120])
return result
except Exception as e: # noqa: BLE001
log.error("tools: %s 执行失败 %s", name, e, exc_info=True)
return json.dumps({"error": f"{name} 执行失败: {e}"}, ensure_ascii=False)