新增user_identifier

This commit is contained in:
朱潮 2025-11-12 19:11:45 +08:00
parent 1174767211
commit 6c1393e96a
5 changed files with 125 additions and 46 deletions

View File

@ -749,7 +749,7 @@ async def chat_completions(request: ChatRequest, authorization: Optional[str] =
project_dir = create_project_directory(request.dataset_ids, bot_id, request.robot_type)
# 收集额外参数作为 generate_cfg
exclude_fields = {'messages', 'model', 'model_server', 'dataset_ids', 'language', 'tool_response', 'system_prompt', 'mcp_settings' ,'stream', 'robot_type', 'bot_id'}
exclude_fields = {'messages', 'model', 'model_server', 'dataset_ids', 'language', 'tool_response', 'system_prompt', 'mcp_settings' ,'stream', 'robot_type', 'bot_id', 'user_identifier'}
generate_cfg = {k: v for k, v in request.model_dump().items() if k not in exclude_fields}
# 处理消息
@ -769,7 +769,8 @@ async def chat_completions(request: ChatRequest, authorization: Optional[str] =
mcp_settings=request.mcp_settings,
robot_type=request.robot_type,
project_dir=project_dir,
generate_cfg=generate_cfg
generate_cfg=generate_cfg,
user_identifier=request.user_identifier
)
except Exception as e:
@ -912,7 +913,8 @@ async def create_agent_and_generate_response(
mcp_settings: Optional[List[Dict]],
robot_type: str,
project_dir: Optional[str] = None,
generate_cfg: Optional[Dict] = None
generate_cfg: Optional[Dict] = None,
user_identifier: Optional[str] = None
) -> Union[ChatResponse, StreamingResponse]:
"""创建agent并生成响应的公共逻辑"""
if generate_cfg is None:
@ -929,7 +931,8 @@ async def create_agent_and_generate_response(
language=language,
system_prompt=system_prompt,
mcp_settings=mcp_settings,
robot_type=robot_type
robot_type=robot_type,
user_identifier=user_identifier
)
# 根据stream参数决定返回流式还是非流式响应
@ -1082,7 +1085,8 @@ async def chat_completions_v2(request: ChatRequestV2, authorization: Optional[st
mcp_settings=bot_config.get("mcp_settings", []),
robot_type=bot_config.get("robot_type", "agent"),
project_dir=project_dir,
generate_cfg={} # v2接口不传递额外的generate_cfg
generate_cfg={}, # v2接口不传递额外的generate_cfg
user_identifier=request.user_identifier
)
except HTTPException:

View File

@ -16,9 +16,11 @@
| 设备控制 | 打开/关闭/启动/停止/调节/设置 | dxcore_update_device_status | P0 |
| 消息通知 | @用户名/通知/告知/提醒 | wowtalk_send_message_to_member | P0 |
| 状态查询 | 状态/温度/湿度/运行情况 | dxcore_get_device_status | P1 |
| 位置查询 | 位置/在哪/查找/坐标 | eb_get_sensor_location | P1 |
| 环境查询 | 天气/气温/降水/风速 | weather_get_by_location | P1 |
| 人员检索 | 找人/员工/同事/联系方式/人员位置 | find_employee_location | P2 |
| 设备检索 | 找设备/传感器/终端/设备位置 | find_iot_device_by_description | P2 |
| 网络搜索 | 搜索/查找/查询/百度/谷歌 | web_search | P1 |
| 人员检索 | 找人/员工/同事/联系方式 | find_employee_by_name | P2 |
| 设备检索 | 找设备/传感器/终端 | find_iot_device | P2 |
## 立即执行机制
- **零延迟策略**:识别操作意图后立即执行工具调用,禁止缓冲性语言
@ -51,14 +53,15 @@ graph LR
### 控制指令映射
| 用户语言 | 系统指令 | 参数格式 |
|---------|----------|----------|
| "打开空调" | update_device_status | {device_id: "001", running_control: 1} |
| "调到24度" | update_device_status | {device_id: "001",temp_setting: 24} |
| "查看温度" | get_device_status | {device_id: "001"} |
| "打开空调" | update_device_status | {sensor_id: "001", running_control: 1} |
| "调到24度" | update_device_status | {sensor_id: "001",temp_setting: 24} |
| "查看温度" | get_device_status | {sensor_id: "001"} |
## 位置服务模块
### 定位查询协议
- **触发条件**:包含位置关键词的查询
- **响应格式**:楼层 + 房间/区域过滤坐标、ID等技术信息
- **精度要求**室内3米精度室外10米精度
## 环境监测模块
### 天气服务集成
@ -72,6 +75,11 @@ graph LR
- **设备检索**:支持设备类型、位置、状态多条件过滤
- **结果排序**:按相关度和距离优先级排序
## 网络搜索模块
### Web搜索集成
- **自动调用**:识别搜索相关词汇后立即执行
- **数据源**web_search工具支持实时网络信息检索
# 智能执行引擎
## 多阶段处理流水线
@ -115,11 +123,20 @@ sequenceDiagram
```
执行:
├── wowtalk_send_message_to_member(to_account="001", message_content="请检查2楼空调")
├── find_employee_location(name="张工")
├── find_iot_device_by_description(description="2楼空调")
├── find_employee_by_name(name="张工")
├── find_iot_device(device_type="dc_fan",target_sensor_id="xxxx")
└── weather_get_by_location(location="当前位置")
```
**输入**"搜索最新的节能技术方案,并发送给@李经理(id:002)"
**执行流程**
```
执行:
├── web_search(query="最新节能技术方案", max_results=5)
└── wowtalk_send_message_to_member(to_account="002", message_content="[搜索结果摘要]")
```
# 应用场景与执行范例
## 场景1上下文感知设备控制
@ -128,18 +145,19 @@ sequenceDiagram
**执行序列**
```python
# 步骤1人员定位
find_employee_location(name="清水太郎")
# → 返回:employee_id, device_id、位置
find_employee_by_name(name="清水太郎")
# → 返回:清水太郎的sensor_id
# 步骤2设备检索
find_iot_device_by_description(
description="A栋3楼301风扇"
# 步骤2人员附近的设备检索
find_iot_device(
device_type="dc_fan",
target_sensor_id="{清水太郎的sensor_id}" #
)
# → 返回device_list
# 步骤3设备控制
dxcore_update_device_status(
device_id=target_device,
sensor_id="{风扇的sensor_id}",
running_control=1
)
```
@ -147,39 +165,50 @@ dxcore_update_device_status(
**响应模板**"已为您开启301室的风扇当前运行正常。"
## 场景2智能消息路由
**用户请求**"通知@管理员(id:001)会议室温度过高需要调节"
**用户请求**"通知清水太郎会议室温度过高需要调节"
**执行逻辑**
```python
# 步骤1人员信息查询
find_employee_by_name(name="清水太郎")
# 返回获取wowtalkid和位置信息
# 步骤2人员通知
wowtalk_send_message_to_member(
to_account="001",
to_account="{清水太郎wowtalk_id}",
message_content="会议室温度过高需要调节"
)
```
**响应模板**"消息已发送至管理员,将会尽快处理温度问题。"
**响应模板**"消息已发送至清水太郎,将会尽快处理温度问题。"
## 场景3多维度协同处理
**用户请求**"5楼传感器电量异常,通知维修团队并报告具体位置"
**用户请求**"5楼风扇电量异常,通知清水太郎并报告具体位置"
**并行执行策略**
```python
# 任务组1故障诊断
dxcore_get_device_status(device_id="5楼传感器")
# 步骤1查找设备列表
find_iot_device(
device_type="dc_fan"
)
# 返回获取5楼风扇的sensor_id
# 步骤2故障诊断
dxcore_get_device_status(sensor_id="{风扇的sensor_id}")
# → 获取电量百分比、故障代码
# 任务组2人员通知
wowtalk_send_message_to_member(
to_account="维修部组ID",
message_content="5楼传感器电量异常请及时处理"
)
# 步骤4人员信息查询获取wowtalkid和位置信息
find_employee_by_name(name="清水太郎")
# 任务组3定位服务
find_iot_device_by_description(description="5楼传感器")
# 步骤5人员通知
wowtalk_send_message_to_member(
to_account="{清水太郎wowtalk_id}",
message_content="5楼风扇电量异常请及时处理"
)
# → 返回精确定位信息
```
**响应模板**"已通知维修团队,传感器位于5楼东侧走廊当前电量15%。"
**响应模板**"已通知清水太郎,风扇位于5楼东侧走廊当前电量15%。"
# 系统集成与技术规范
@ -189,9 +218,11 @@ find_iot_device_by_description(description="5楼传感器")
| 消息路由 | wowtalk_send_message_to_member | 实时消息推送 | P0 |
| 设备控制 | dxcore_update_device_status | 设备状态变更 | P0 |
| 设备查询 | dxcore_get_device_status | 设备状态读取 | P1 |
| 位置服务 | eb_get_sensor_location | 空间定位查询 | P1 |
| 环境监测 | weather_get_by_location | 天气数据获取 | P1 |
| 人员检索 | find_employee_location | 员工信息查询 | P2 |
| 设备检索 | find_iot_device_by_description | IoT设备搜索 | P2 |
| 网络搜索 | web_search | 互联网信息查询 | P1 |
| 人员检索 | find_employee_by_name | 员工信息查询 | P2 |
| 设备检索 | find_iot_device | IoT设备搜索 | P2 |
# 异常处理与容错机制
@ -264,7 +295,6 @@ find_iot_device_by_description(description="5楼传感器")
## 系统配置参数
- **bot_id**: {bot_id}
# 执行保证机制
1. **工具调用优先**:可执行操作必须通过工具实现
2. **状态一致性**:所有操作结果与实际设备状态同步

View File

@ -51,6 +51,7 @@ class ChatRequest(BaseModel):
system_prompt: Optional[str] = None
mcp_settings: Optional[List[Dict]] = None
robot_type: Optional[str] = "agent"
user_identifier: Optional[str] = ""
class ChatRequestV2(BaseModel):
@ -59,6 +60,7 @@ class ChatRequestV2(BaseModel):
tool_response: Optional[bool] = False
bot_id: str
language: Optional[str] = "ja"
user_identifier: Optional[str] = ""
class FileProcessRequest(BaseModel):

View File

@ -110,7 +110,8 @@ class FileLoadedAgentManager:
language: Optional[str] = None,
system_prompt: Optional[str] = None,
mcp_settings: Optional[List[Dict]] = None,
robot_type: Optional[str] = "agent") -> Assistant:
robot_type: Optional[str] = "agent",
user_identifier: Optional[str] = None) -> Assistant:
"""获取或创建文件预加载的助手实例
Args:
@ -131,7 +132,7 @@ class FileLoadedAgentManager:
import os
# 实现参数优先级逻辑:传入参数 > 项目配置 > 默认配置
final_system_prompt = load_system_prompt(project_dir, language, system_prompt, robot_type, bot_id)
final_system_prompt = load_system_prompt(project_dir, language, system_prompt, robot_type, bot_id, user_identifier)
final_mcp_settings = load_mcp_settings(project_dir, mcp_settings, bot_id, robot_type)
cache_key = self._get_cache_key(bot_id, model_name, api_key, model_server,

View File

@ -4,10 +4,38 @@ System prompt and MCP settings loader utilities
"""
import os
import json
from typing import List, Dict, Optional
from typing import List, Dict, Optional, Any
def load_system_prompt(project_dir: str, language: str = None, system_prompt: str=None, robot_type: str = "agent", bot_id: str="") -> str:
def safe_replace(text: str, placeholder: str, value: Any) -> str:
"""
安全的字符串替换函数确保 value 被转换为字符串
Args:
text: 原始文本
placeholder: 要替换的占位符 '{user_identifier}'
value: 用于替换的值可以是任意类型
Returns:
str: 替换后的文本
"""
if not isinstance(text, str):
text = str(text)
# 如果占位符为空,不进行替换
if not placeholder:
return text
# 将 value 转换为字符串,处理 None 等特殊情况
if value is None:
replacement = ""
else:
replacement = str(value)
return text.replace(placeholder, replacement)
def load_system_prompt(project_dir: str, language: str = None, system_prompt: str=None, robot_type: str = "agent", bot_id: str="", user_identifier: str = "") -> str:
# 获取语言显示名称
language_display_map = {
'zh': '中文',
@ -19,7 +47,11 @@ def load_system_prompt(project_dir: str, language: str = None, system_prompt: st
# 如果存在{language} 占位符,那么就直接使用 system_prompt
if system_prompt and "{language}" in system_prompt:
return system_prompt.replace("{language}", language_display).replace('{bot_id}', bot_id) or ""
prompt = system_prompt
prompt = safe_replace(prompt, "{language}", language_display)
prompt = safe_replace(prompt, '{bot_id}', bot_id)
prompt = safe_replace(prompt, '{user_identifier}', user_identifier)
return prompt or ""
elif robot_type == "agent" or robot_type == "catalog_agent":
"""
优先使用项目目录的system_prompt_catalog_agent.md没有才使用默认的system_prompt_default.md
@ -51,11 +83,20 @@ def load_system_prompt(project_dir: str, language: str = None, system_prompt: st
if os.path.exists(readme_path):
with open(readme_path, "r", encoding="utf-8") as f:
readme = f.read().strip()
system_prompt_default = system_prompt_default.replace("{readme}", str(readme))
system_prompt_default = safe_replace(system_prompt_default, "{readme}", str(readme))
return system_prompt_default.replace("{language}", language_display).replace("{extra_prompt}", system_prompt or "").replace('{bot_id}', bot_id) or ""
prompt = system_prompt_default
prompt = safe_replace(prompt, "{language}", language_display)
prompt = safe_replace(prompt, "{extra_prompt}", system_prompt or "")
prompt = safe_replace(prompt, '{bot_id}', bot_id)
prompt = safe_replace(prompt, '{user_identifier}', user_identifier)
return prompt or ""
else:
return system_prompt.replace("{language}", language_display).replace('{bot_id}', bot_id) or ""
prompt = system_prompt
prompt = safe_replace(prompt, "{language}", language_display)
prompt = safe_replace(prompt, '{bot_id}', bot_id)
prompt = safe_replace(prompt, '{user_identifier}', user_identifier)
return prompt or ""
@ -72,15 +113,16 @@ def replace_mcp_placeholders(mcp_settings: List[Dict], dataset_dir: str, bot_id:
for key, value in obj.items():
if key == 'args' and isinstance(value, list):
# 特别处理 args 列表
obj[key] = [item.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id) if isinstance(item, str) else item
obj[key] = [safe_replace(safe_replace(item, '{dataset_dir}', dataset_dir), '{bot_id}', bot_id) if isinstance(item, str) else item
for item in value]
elif isinstance(value, (dict, list)):
obj[key] = replace_placeholders_in_obj(value)
elif isinstance(value, str):
obj[key] = value.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id)
obj[key] = safe_replace(value, '{dataset_dir}', dataset_dir)
obj[key] = safe_replace(obj[key], '{bot_id}', bot_id)
elif isinstance(obj, list):
return [replace_placeholders_in_obj(item) if isinstance(item, (dict, list)) else
item.replace('{dataset_dir}', dataset_dir).replace('{bot_id}', bot_id) if isinstance(item, str) else item
safe_replace(safe_replace(item, '{dataset_dir}', dataset_dir), '{bot_id}', bot_id) if isinstance(item, str) else item
for item in obj]
return obj