fix: memory scope_id column overflow and API read mismatch
Some checks failed
CI/CD Pipeline / Backend — Lint & Test (push) Has been cancelled
CI/CD Pipeline / Frontend — Lint & Build (push) Has been cancelled
CI/CD Pipeline / Docker — Build Check (push) Has been cancelled

AgentRuntime writes scope_id as {user_id}:{agent_id} (73 chars) but:
- DB columns were CHAR(36)/VARCHAR(36), causing Data too long errors
- Memory API queried with agent_id only, returning empty results

- Add _mem_scope() helper to build correct scope_id format
- Update all CRUD queries in agent_memory.py to use _mem_scope()
- Enhance login failure logging in auth.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-01 08:05:23 +08:00
parent e33aa6e0b7
commit f1326bff95
3 changed files with 78 additions and 40 deletions

View File

@@ -11,7 +11,6 @@ from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from sqlalchemy import or_
from app.core.database import get_db
from app.api.auth import get_current_user
@@ -30,6 +29,11 @@ router = APIRouter(
SCOPE_AGENT = "agent"
def _mem_scope(user: User, agent_id: str) -> str:
"""构建记忆 scope_id与 AgentRuntime 写入的格式保持一致({user_id}:{agent_id})。"""
return f"{user.id}:{agent_id}"
def _check_agent(db: Session, agent_id: str, user: User):
agent = db.query(Agent).filter(Agent.id == agent_id).first()
if not agent:
@@ -218,9 +222,10 @@ def list_global_knowledge(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
q = db.query(GlobalKnowledge).filter(
GlobalKnowledge.scope_kind == SCOPE_AGENT,
GlobalKnowledge.scope_id == agent_id,
GlobalKnowledge.scope_id == scope_id,
)
if search:
q = q.filter(GlobalKnowledge.content.contains(search))
@@ -237,13 +242,14 @@ def create_global_knowledge(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
gk = GlobalKnowledge(
id=str(uuid.uuid4()),
content=body.content,
tags=body.tags,
confidence=body.confidence,
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
source_user_id=current_user.id,
)
db.add(gk)
@@ -261,10 +267,11 @@ def update_global_knowledge(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
gk = db.query(GlobalKnowledge).filter(
GlobalKnowledge.id == knowledge_id,
GlobalKnowledge.scope_kind == SCOPE_AGENT,
GlobalKnowledge.scope_id == agent_id,
GlobalKnowledge.scope_id == scope_id,
).first()
if not gk:
raise HTTPException(status_code=404, detail="知识条目不存在")
@@ -287,10 +294,11 @@ def delete_global_knowledge(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
gk = db.query(GlobalKnowledge).filter(
GlobalKnowledge.id == knowledge_id,
GlobalKnowledge.scope_kind == SCOPE_AGENT,
GlobalKnowledge.scope_id == agent_id,
GlobalKnowledge.scope_id == scope_id,
).first()
if not gk:
raise HTTPException(status_code=404, detail="知识条目不存在")
@@ -312,9 +320,10 @@ def list_knowledge_entities(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
q = db.query(KnowledgeEntity).filter(
KnowledgeEntity.scope_kind == SCOPE_AGENT,
KnowledgeEntity.scope_id == agent_id,
KnowledgeEntity.scope_id == scope_id,
)
if search:
q = q.filter(
@@ -334,6 +343,7 @@ def create_knowledge_entity(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
ke = KnowledgeEntity(
id=str(uuid.uuid4()),
name=body.name,
@@ -343,7 +353,7 @@ def create_knowledge_entity(
confidence=body.confidence,
metadata_=body.tags,
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
user_id=current_user.id,
)
db.add(ke)
@@ -361,10 +371,11 @@ def update_knowledge_entity(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
ke = db.query(KnowledgeEntity).filter(
KnowledgeEntity.id == entity_id,
KnowledgeEntity.scope_kind == SCOPE_AGENT,
KnowledgeEntity.scope_id == agent_id,
KnowledgeEntity.scope_id == scope_id,
).first()
if not ke:
raise HTTPException(status_code=404, detail="实体不存在")
@@ -391,10 +402,11 @@ def delete_knowledge_entity(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
ke = db.query(KnowledgeEntity).filter(
KnowledgeEntity.id == entity_id,
KnowledgeEntity.scope_kind == SCOPE_AGENT,
KnowledgeEntity.scope_id == agent_id,
KnowledgeEntity.scope_id == scope_id,
).first()
if not ke:
raise HTTPException(status_code=404, detail="实体不存在")
@@ -420,9 +432,10 @@ def list_learning_patterns(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
q = db.query(AgentLearningPattern).filter(
AgentLearningPattern.scope_kind == SCOPE_AGENT,
AgentLearningPattern.scope_id == agent_id,
AgentLearningPattern.scope_id == scope_id,
)
total = q.count()
items = q.order_by(AgentLearningPattern.updated_at.desc()).offset(skip).limit(limit).all()
@@ -457,10 +470,11 @@ def delete_learning_pattern(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
lp = db.query(AgentLearningPattern).filter(
AgentLearningPattern.id == pattern_id,
AgentLearningPattern.scope_kind == SCOPE_AGENT,
AgentLearningPattern.scope_id == agent_id,
AgentLearningPattern.scope_id == scope_id,
).first()
if not lp:
raise HTTPException(status_code=404, detail="学习模式不存在")
@@ -482,15 +496,10 @@ def list_vector_memories(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
uid = current_user.id
# scope_id may be: agent_id, or {user_id}:{agent_id}, or {user_id}:{agent_id}:{session_id}
scope_id = _mem_scope(current_user, agent_id)
q = db.query(AgentVectorMemory).filter(
AgentVectorMemory.scope_kind == SCOPE_AGENT,
or_(
AgentVectorMemory.scope_id == agent_id,
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
AgentVectorMemory.scope_id.like(f"{uid}:%"),
),
AgentVectorMemory.scope_id == scope_id,
)
if search:
q = q.filter(AgentVectorMemory.content_text.contains(search))
@@ -507,17 +516,13 @@ def delete_vector_memory(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
uid = current_user.id
scope_id = _mem_scope(current_user, agent_id)
vm = (
db.query(AgentVectorMemory)
.filter(
AgentVectorMemory.id == memory_id,
AgentVectorMemory.scope_kind == SCOPE_AGENT,
or_(
AgentVectorMemory.scope_id == agent_id,
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
AgentVectorMemory.scope_id.like(f"{uid}:%"),
),
AgentVectorMemory.scope_id == scope_id,
)
.first()
)
@@ -549,35 +554,32 @@ def export_memory(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
gk_list = (
db.query(GlobalKnowledge)
.filter(GlobalKnowledge.scope_kind == SCOPE_AGENT, GlobalKnowledge.scope_id == agent_id)
.filter(GlobalKnowledge.scope_kind == SCOPE_AGENT, GlobalKnowledge.scope_id == scope_id)
.all()
)
ke_list = (
db.query(KnowledgeEntity)
.filter(KnowledgeEntity.scope_kind == SCOPE_AGENT, KnowledgeEntity.scope_id == agent_id)
.filter(KnowledgeEntity.scope_kind == SCOPE_AGENT, KnowledgeEntity.scope_id == scope_id)
.all()
)
kr_list = (
db.query(KnowledgeRelation)
.filter(KnowledgeRelation.scope_kind == SCOPE_AGENT, KnowledgeRelation.scope_id == agent_id)
.filter(KnowledgeRelation.scope_kind == SCOPE_AGENT, KnowledgeRelation.scope_id == scope_id)
.all()
)
lp_list = (
db.query(AgentLearningPattern)
.filter(AgentLearningPattern.scope_kind == SCOPE_AGENT, AgentLearningPattern.scope_id == agent_id)
.filter(AgentLearningPattern.scope_kind == SCOPE_AGENT, AgentLearningPattern.scope_id == scope_id)
.all()
)
vm_list = (
db.query(AgentVectorMemory)
.filter(
AgentVectorMemory.scope_kind == SCOPE_AGENT,
or_(
AgentVectorMemory.scope_id == agent_id,
AgentVectorMemory.scope_id.like(f"%:{agent_id}"),
AgentVectorMemory.scope_id.like(f"{current_user.id}:%"),
),
AgentVectorMemory.scope_id == scope_id,
)
.all()
)
@@ -600,6 +602,7 @@ def import_memory(
current_user: User = Depends(get_current_user),
):
_check_agent(db, agent_id, current_user)
scope_id = _mem_scope(current_user, agent_id)
imported = {"global_knowledge": 0, "knowledge_entities": 0, "knowledge_relations": 0, "learning_patterns": 0}
for item in body.global_knowledge:
@@ -609,7 +612,7 @@ def import_memory(
tags=item.get("tags"),
confidence=item.get("confidence", "medium"),
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
source_agent_id=item.get("source_agent_id"),
source_user_id=current_user.id,
)
@@ -626,7 +629,7 @@ def import_memory(
confidence=item.get("confidence", "medium"),
metadata_=item.get("metadata") or item.get("tags"),
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
user_id=current_user.id,
)
db.add(ke)
@@ -641,7 +644,7 @@ def import_memory(
description=item.get("description"),
weight=item.get("weight", "1.0"),
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
)
db.add(kr)
imported["knowledge_relations"] += 1
@@ -650,7 +653,7 @@ def import_memory(
lp = AgentLearningPattern(
id=str(uuid.uuid4()),
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
task_category=item.get("task_category", "general"),
task_keywords=item.get("task_keywords", ""),
suggested_tools=item.get("suggested_tools", "[]"),
@@ -668,7 +671,7 @@ def import_memory(
vm = AgentVectorMemory(
id=str(uuid.uuid4()),
scope_kind=SCOPE_AGENT,
scope_id=agent_id,
scope_id=scope_id,
session_key=item.get("session_key", ""),
content_text=item.get("content_text", ""),
metadata_=item.get("metadata"),