Files
aiagent/backend/app/services/feedback_learner.py
renjianbo beff3fac8d 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>
2026-06-29 01:17:21 +08:00

212 lines
7.5 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.
"""
用户反馈学习服务 — 采集反馈信号,自动调整 Agent 策略
"""
from __future__ import annotations
import logging
from typing import Any, Dict, List, Optional
from collections import Counter, defaultdict
from sqlalchemy import func, desc
from sqlalchemy.orm import Session
from app.core.database import SessionLocal
from app.models.feedback_record import FeedbackRecord
from app.models.agent_execution_log import AgentExecutionLog
logger = logging.getLogger(__name__)
class FeedbackLearner:
"""从用户反馈中学习,自动调整 Agent 策略"""
def record_feedback(
self,
user_id: str,
signal_type: str,
*,
execution_log_id: Optional[str] = None,
agent_name: Optional[str] = None,
task_id: Optional[str] = None,
original_output: Optional[str] = None,
user_correction: Optional[str] = None,
feedback_context: Optional[Dict[str, Any]] = None,
) -> Optional[str]:
"""记录一条用户反馈。"""
db: Optional[Session] = None
try:
db = SessionLocal()
severity = 0.5
if signal_type == "reject_approval":
severity = 0.9
elif signal_type == "thumbs_down":
severity = 0.7
elif signal_type == "manual_edit":
severity = 0.6
elif signal_type == "retry_command":
severity = 0.4
elif signal_type == "thumbs_up":
severity = 0.1
entry = FeedbackRecord(
user_id=user_id,
signal_type=signal_type,
severity=severity,
execution_log_id=execution_log_id,
agent_name=agent_name,
task_id=task_id,
original_output=original_output[:5000] if original_output else None,
user_correction=user_correction[:5000] if user_correction else None,
feedback_context=feedback_context,
)
db.add(entry)
db.commit()
db.refresh(entry)
# 标记相关执行日志的反馈
if execution_log_id:
exec_log = db.query(AgentExecutionLog).filter(
AgentExecutionLog.id == execution_log_id
).first()
if exec_log:
exec_log.user_rating = 1 if signal_type in ("thumbs_down", "reject_approval") else (5 if signal_type == "thumbs_up" else 3)
exec_log.user_feedback = signal_type
db.commit()
return str(entry.id)
except Exception as e:
logger.error("记录反馈失败: %s", e)
if db:
try:
db.rollback()
except Exception:
pass
return None
finally:
if db:
try:
db.close()
except Exception:
pass
def analyze_feedback_patterns(self, agent_name: Optional[str] = None, days: int = 7) -> Dict[str, Any]:
"""分析反馈模式,识别需要调整的策略。"""
db: Optional[Session] = None
try:
db = SessionLocal()
from datetime import datetime, timedelta
since = datetime.now() - timedelta(days=days)
q = db.query(FeedbackRecord).filter(FeedbackRecord.created_at >= since)
if agent_name:
q = q.filter(FeedbackRecord.agent_name == agent_name)
records = q.all()
if not records:
return {"total_feedback": 0, "message": "近期无反馈"}
# 统计信号类型
signal_dist = Counter(r.signal_type for r in records)
# 按 Agent 分组
by_agent = defaultdict(lambda: {"total": 0, "negative": 0, "patterns": []})
for r in records:
name = r.agent_name or "unknown"
by_agent[name]["total"] += 1
if r.signal_type in ("thumbs_down", "reject_approval"):
by_agent[name]["negative"] += 1
# 生成策略建议
strategy_advice = []
total = len(records)
negative_rate = (signal_dist.get("thumbs_down", 0) + signal_dist.get("reject_approval", 0)) / total if total > 0 else 0
if negative_rate > 0.3:
strategy_advice.append({
"type": "adjust_temperature",
"reason": f"负面反馈率 {negative_rate:.1%},建议降低 temperature",
"action": "temperature -= 0.1",
})
if signal_dist.get("retry_command", 0) / total > 0.2 if total > 0 else False:
strategy_advice.append({
"type": "enhance_prompt",
"reason": "用户频繁要求重试,输出可能不够精准",
"action": "在 system prompt 中增加更具体的输出要求",
})
if signal_dist.get("manual_edit", 0) / total > 0.2 if total > 0 else False:
strategy_advice.append({
"type": "suggest_review",
"reason": "输出频繁被手动修改,建议开启 self_review",
"action": "开启输出质量自检",
})
# 推荐有问题的 Agent
problematic_agents = [
{"agent": name, "negative_rate": round(data["negative"] / data["total"], 2)}
for name, data in by_agent.items()
if data["total"] >= 3 and data["negative"] / data["total"] > 0.3
]
return {
"total_feedback": total,
"period_days": days,
"signal_distribution": dict(signal_dist),
"overall_negative_rate": round(negative_rate, 3),
"problematic_agents": problematic_agents,
"strategy_advice": strategy_advice,
}
except Exception as e:
logger.error("分析反馈模式失败: %s", e)
return {"error": str(e)}
finally:
if db:
try:
db.close()
except Exception:
pass
def generate_negative_examples(self, agent_name: str, limit: int = 5) -> List[Dict[str, Any]]:
"""为 Agent 生成反例(用于更新 system prompt"""
db: Optional[Session] = None
try:
db = SessionLocal()
records = (
db.query(FeedbackRecord)
.filter(
FeedbackRecord.agent_name == agent_name,
FeedbackRecord.original_output.isnot(None),
FeedbackRecord.user_correction.isnot(None),
FeedbackRecord.signal_type.in_(["thumbs_down", "manual_edit"]),
)
.order_by(desc(FeedbackRecord.created_at))
.limit(limit)
.all()
)
examples = []
for r in records:
examples.append({
"original": (r.original_output or "")[:500],
"corrected": (r.user_correction or "")[:500],
"signal": r.signal_type,
})
return examples
except Exception as e:
logger.error("生成反例失败: %s", e)
return []
finally:
if db:
try:
db.close()
except Exception:
pass
feedback_learner = FeedbackLearner()