- Fix delete agent 500: clean up FK records (agent_llm_logs, permissions, schedules, executions, team_members) and unbind goals/tasks before delete - Remove hardcoded personality templates in Android, replace with dynamic system prompt generation from name + description - Set promptSectionsEnabled=false to bypass PromptComposer for personality - Add Tencent Cloud Linux deployment guide (Docker Compose) - Accumulated backend service updates, frontend UI fixes, Android app changes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
136 lines
4.1 KiB
Python
136 lines
4.1 KiB
Python
"""
|
||
飞书消息卡片构建器 — 统一的交互卡片生成,含反馈按钮
|
||
|
||
所有飞书 App Service 和 WS Handler 共用此模块构建回复卡片。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import logging
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 反馈按钮 action tag 标识
|
||
ACTION_FEEDBACK_THUMBS_UP = "feedback_thumbs_up"
|
||
ACTION_FEEDBACK_THUMBS_DOWN = "feedback_thumbs_down"
|
||
ACTION_FEEDBACK_REGENERATE = "feedback_regenerate"
|
||
|
||
|
||
def build_reply_card(
|
||
title: str,
|
||
content: str,
|
||
*,
|
||
execution_log_id: Optional[str] = None,
|
||
agent_name: Optional[str] = None,
|
||
status: str = "info",
|
||
detail_link: Optional[str] = None,
|
||
include_feedback: bool = True,
|
||
) -> Dict[str, Any]:
|
||
"""构建 Agent 回复消息卡片。
|
||
|
||
Args:
|
||
title: 卡片标题
|
||
content: 卡片正文(Markdown 格式)
|
||
execution_log_id: 关联的执行日志 ID(用于反馈追踪)
|
||
agent_name: Agent 名称
|
||
status: info / success / failed
|
||
detail_link: 详情链接(可选)
|
||
include_feedback: 是否包含反馈按钮
|
||
|
||
Returns:
|
||
飞书交互卡片 JSON
|
||
"""
|
||
color_map = {"success": "green", "failed": "red", "info": "blue"}
|
||
color = color_map.get(status, "blue")
|
||
|
||
elements: List[Dict[str, Any]] = [
|
||
{"tag": "markdown", "content": content},
|
||
]
|
||
|
||
# 反馈按钮行
|
||
if include_feedback and execution_log_id:
|
||
feedback_value = {
|
||
"execution_log_id": execution_log_id,
|
||
"agent_name": agent_name or "",
|
||
"card_title": title,
|
||
}
|
||
elements.append({
|
||
"tag": "action",
|
||
"actions": [
|
||
{
|
||
"tag": "button",
|
||
"text": {"tag": "plain_text", "content": "👍 有用"},
|
||
"type": "primary",
|
||
"value": json.dumps({**feedback_value, "action": ACTION_FEEDBACK_THUMBS_UP}),
|
||
},
|
||
{
|
||
"tag": "button",
|
||
"text": {"tag": "plain_text", "content": "👎 无用"},
|
||
"type": "default",
|
||
"value": json.dumps({**feedback_value, "action": ACTION_FEEDBACK_THUMBS_DOWN}),
|
||
},
|
||
{
|
||
"tag": "button",
|
||
"text": {"tag": "plain_text", "content": "🔄 重试"},
|
||
"type": "default",
|
||
"value": json.dumps({**feedback_value, "action": ACTION_FEEDBACK_REGENERATE}),
|
||
},
|
||
],
|
||
})
|
||
|
||
# 详情链接
|
||
if detail_link:
|
||
elements.append({
|
||
"tag": "action",
|
||
"actions": [
|
||
{
|
||
"tag": "button",
|
||
"text": {"tag": "plain_text", "content": "查看详情"},
|
||
"url": detail_link,
|
||
"type": "default",
|
||
}
|
||
],
|
||
})
|
||
|
||
return {
|
||
"config": {"wide_screen_mode": True},
|
||
"header": {
|
||
"title": {"tag": "plain_text", "content": title},
|
||
"template": color,
|
||
},
|
||
"elements": elements,
|
||
}
|
||
|
||
|
||
def build_feedback_response_card(
|
||
original_title: str,
|
||
original_content: str,
|
||
action: str,
|
||
*,
|
||
detail_link: Optional[str] = None,
|
||
) -> Dict[str, Any]:
|
||
"""构建反馈确认卡片(替换原有卡片)。
|
||
|
||
当用户点击反馈按钮后,替换原卡片为去反馈按钮的确认卡片。
|
||
"""
|
||
if action == ACTION_FEEDBACK_THUMBS_UP:
|
||
note = "✅ 已收到你的反馈:**有用**,感谢支持!"
|
||
color = "green"
|
||
elif action == ACTION_FEEDBACK_THUMBS_DOWN:
|
||
note = "💡 已收到你的反馈:**无用**,我们会持续改进。"
|
||
color = "red"
|
||
else:
|
||
note = "🔄 正在为你重新生成回复..."
|
||
color = "blue"
|
||
|
||
content = f"{note}\n\n---\n{original_content}"
|
||
|
||
return build_reply_card(
|
||
title=original_title,
|
||
content=content,
|
||
status="info",
|
||
detail_link=detail_link,
|
||
include_feedback=False,
|
||
)
|