fix: delete agent 500 error + dynamic personality + deployment guide
- 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>
This commit is contained in:
@@ -21,6 +21,80 @@ _processed_msg_ids: deque[str] = deque(maxlen=20)
|
||||
_ws_thread: threading.Thread | None = None
|
||||
|
||||
|
||||
# ─── 错误友好提示映射 ───
|
||||
|
||||
_ERROR_FRIENDLY_MAP = {
|
||||
"timeout": "处理超时,请稍后重试。若问题持续,可尝试简化问题描述。",
|
||||
"connection": "网络连接异常,请稍后重试。",
|
||||
"rate limit": "请求过于频繁,请稍候片刻再试。",
|
||||
"invalid api key": "AI 模型密钥配置异常,请联系管理员检查 API Key。",
|
||||
"insufficient_quota": "AI 模型额度已用完,请联系管理员处理。",
|
||||
"context length": "对话内容过长,请简化你的问题后重试。",
|
||||
"authentication": "飞书身份验证失败,请重新绑定飞书账号。",
|
||||
}
|
||||
|
||||
|
||||
def _friendly_error(error: Exception) -> str:
|
||||
"""将技术异常映射为用户友好提示。"""
|
||||
msg = str(error).lower()
|
||||
for keyword, friendly in _ERROR_FRIENDLY_MAP.items():
|
||||
if keyword in msg:
|
||||
return f"{friendly}\n\n> 错误详情: {error!s}"
|
||||
return f"处理请求时出现问题,请稍后重试。\n\n> 错误详情: {error!s}"
|
||||
|
||||
|
||||
# ─── 对话历史上下文 ───
|
||||
|
||||
def _get_conversation_context(open_id: str, agent_id: str, max_messages: int = 5) -> str:
|
||||
"""加载该用户在飞书上的近期对话历史,生成摘要上下文。
|
||||
|
||||
用于注入 Agent 系统提示词,使飞书对话保持连续性。
|
||||
"""
|
||||
try:
|
||||
from app.core.database import SessionLocal
|
||||
from app.models.user import User
|
||||
from app.models.agent_execution_log import AgentExecutionLog
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.feishu_open_id == open_id).first()
|
||||
if not user:
|
||||
return ""
|
||||
|
||||
recent_logs = (
|
||||
db.query(AgentExecutionLog)
|
||||
.filter(
|
||||
AgentExecutionLog.user_id == user.id,
|
||||
AgentExecutionLog.agent_name.isnot(None),
|
||||
)
|
||||
.order_by(AgentExecutionLog.created_at.desc())
|
||||
.limit(max_messages)
|
||||
.all()
|
||||
)
|
||||
|
||||
if not recent_logs:
|
||||
return ""
|
||||
|
||||
# 按时间正序排列(最早的在前面)
|
||||
recent_logs.reverse()
|
||||
|
||||
lines = ["\n## 近期对话历史(飞书)\n"]
|
||||
for log in recent_logs:
|
||||
if log.input_text:
|
||||
lines.append(f"- **用户**: {log.input_text[:200]}")
|
||||
if log.output_text:
|
||||
summary = log.output_text[:200].replace("\n", " ")
|
||||
lines.append(f" **回复概要**: {summary}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
finally:
|
||||
db.close()
|
||||
except Exception as e:
|
||||
logger.warning("加载飞书对话历史失败: %s", e)
|
||||
return ""
|
||||
|
||||
|
||||
def _get_message_id(data) -> Optional[str]:
|
||||
"""从 Feishu 消息事件中提取 message_id。"""
|
||||
try:
|
||||
@@ -150,12 +224,32 @@ def _reply_to_feishu(open_id: str, text: str):
|
||||
logger.warning("飞书回复消息失败: %s", e)
|
||||
|
||||
|
||||
def _reply_card(open_id: str, title: str, content: str, status: str = "info"):
|
||||
"""通过飞书 API 回复卡片消息。"""
|
||||
def _reply_error_card(open_id: str, title: str, content: str):
|
||||
"""通过飞书 API 回复错误卡片消息。"""
|
||||
try:
|
||||
from app.services.feishu_app_service import send_message_to_user
|
||||
|
||||
send_message_to_user(open_id, title, content, status=status)
|
||||
send_message_to_user(
|
||||
open_id, title, content, status="failed",
|
||||
execution_log_id=None, agent_name=None,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("飞书错误卡片发送失败: %s", e)
|
||||
# 降级为纯文本
|
||||
_reply_to_feishu(open_id, content)
|
||||
|
||||
|
||||
def _reply_card(open_id: str, title: str, content: str, status: str = "info",
|
||||
execution_log_id: str = None, agent_name: str = None):
|
||||
"""通过飞书 API 回复卡片消息(含反馈按钮)。"""
|
||||
try:
|
||||
from app.services.feishu_app_service import send_message_to_user
|
||||
|
||||
send_message_to_user(
|
||||
open_id, title, content, status=status,
|
||||
execution_log_id=execution_log_id,
|
||||
agent_name=agent_name,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("飞书回复卡片失败: %s", e)
|
||||
|
||||
@@ -243,7 +337,10 @@ async def _handle_message_async(data):
|
||||
_reply_to_feishu(open_id, f"默认 Agent (id={agent_id}) 已不存在,请重新设置。")
|
||||
return
|
||||
|
||||
_reply_to_feishu(open_id, f"🤔 正在思考,请稍候...")
|
||||
_reply_to_feishu(open_id, f"🤔 正在分析你的问题并调用相关工具...\n\n对话将保持上下文连续性,你可随时追问。")
|
||||
|
||||
# 加载对话历史上下文,保持飞书对话连续性
|
||||
conversation_context = _get_conversation_context(open_id, str(agent.id))
|
||||
|
||||
from app.agent_runtime import AgentRuntime, AgentConfig, AgentLLMConfig, AgentToolConfig, AgentMemoryConfig
|
||||
|
||||
@@ -268,10 +365,11 @@ async def _handle_message_async(data):
|
||||
|
||||
config = AgentConfig(
|
||||
name=agent.name or "agent",
|
||||
system_prompt=system_prompt + (
|
||||
system_prompt=system_prompt + conversation_context + (
|
||||
f"\n\n## 系统信息\n"
|
||||
f"你的 Agent ID 是: {agent.id}\n"
|
||||
f"在调用 schedule_list、schedule_delete 等工具时,使用此 ID 作为 agent_id 参数。"
|
||||
f"在调用 schedule_list、schedule_delete 等工具时,使用此 ID 作为 agent_id 参数。\n"
|
||||
f"你正在通过飞书与用户对话,回复应使用飞书支持的 Markdown 格式。"
|
||||
),
|
||||
llm=AgentLLMConfig(
|
||||
model=model,
|
||||
@@ -309,14 +407,44 @@ async def _handle_message_async(data):
|
||||
|
||||
on_llm_call = _make_llm_logger(db, agent_id=str(agent.id), user_id=user.id)
|
||||
runtime = AgentRuntime(config=config, on_llm_call=on_llm_call)
|
||||
|
||||
import time
|
||||
_start_time = time.time()
|
||||
result = await runtime.run(text)
|
||||
_elapsed = time.time() - _start_time
|
||||
|
||||
if result.content:
|
||||
# 查询刚写入的 AgentExecutionLog 获取 ID(fire_and_forget 应已完成)
|
||||
exec_log_id = None
|
||||
try:
|
||||
from app.models.agent_execution_log import AgentExecutionLog
|
||||
exec_log = (
|
||||
db.query(AgentExecutionLog)
|
||||
.filter(AgentExecutionLog.agent_name == agent.name)
|
||||
.order_by(AgentExecutionLog.created_at.desc())
|
||||
.first()
|
||||
)
|
||||
if exec_log:
|
||||
exec_log_id = str(exec_log.id)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 构建含处理状态信息的回复内容
|
||||
status_footer = (
|
||||
f"\n\n---\n"
|
||||
f"🔄 迭代: {result.iterations_used} 次 | "
|
||||
f"🔧 工具调用: {result.tool_calls_made} 次 | "
|
||||
f"⏱ 耗时: {_elapsed:.1f}s"
|
||||
)
|
||||
reply_content = result.content.strip() + status_footer
|
||||
|
||||
_reply_card(
|
||||
open_id,
|
||||
f"🤖 {agent.name}",
|
||||
result.content.strip(),
|
||||
reply_content,
|
||||
status="success",
|
||||
execution_log_id=exec_log_id,
|
||||
agent_name=agent.name,
|
||||
)
|
||||
else:
|
||||
_reply_to_feishu(open_id, "Agent 未返回有效回复,请重试。")
|
||||
@@ -329,7 +457,7 @@ async def _handle_message_async(data):
|
||||
except Exception as e:
|
||||
logger.error("飞书消息处理失败: %s", e)
|
||||
try:
|
||||
_reply_to_feishu(open_id, f"处理失败: {e!s}")
|
||||
_reply_error_card(open_id, "处理失败", _friendly_error(e))
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
@@ -338,7 +466,6 @@ async def _handle_message_async(data):
|
||||
|
||||
|
||||
def _handle_message_internal(data):
|
||||
"""同步入口 — 创建异步任务处理飞书消息(支持私信 + 群聊@提及)。"""
|
||||
# 去重:WS 重连后可能重投已处理的消息
|
||||
msg_id = _get_message_id(data)
|
||||
if msg_id:
|
||||
@@ -394,7 +521,7 @@ def _handle_message_internal(data):
|
||||
except Exception as e:
|
||||
logger.error("创建飞书消息处理任务失败: %s", e)
|
||||
try:
|
||||
_reply_to_feishu(open_id, f"处理失败: {e!s}")
|
||||
_reply_to_feishu(open_id, _friendly_error(e))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -486,7 +613,7 @@ async def _handle_group_mention_async(data, chat_id: str, open_id: str):
|
||||
except Exception as e:
|
||||
logger.error("群聊@提及处理失败: %s", e)
|
||||
try:
|
||||
_reply_to_group(chat_id, f"处理失败: {e!s}")
|
||||
_reply_to_group(chat_id, _friendly_error(e))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -618,6 +745,13 @@ def _build_event_handler():
|
||||
|
||||
builder.register_p2_approval_instance_event_v1(on_approval_event)
|
||||
|
||||
# 卡片动作回调(用户点击反馈按钮)
|
||||
def on_card_action(data):
|
||||
from app.services.feishu_card_actions import handle_card_action
|
||||
return handle_card_action(data)
|
||||
|
||||
builder.register_p2_card_action_trigger(on_card_action)
|
||||
|
||||
return builder.build()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user