feat: 增强对话线程的 agent_config_id 绑定逻辑,确保元数据一致性

This commit is contained in:
Wenjie Zhang 2026-03-27 01:15:51 +08:00
parent 3b5514e9b5
commit 5fe367b77b
10 changed files with 95 additions and 6 deletions

View File

@ -9,6 +9,6 @@ PROMPT = """
- /home/gem/user-data/uploads/用于存放用户上传的文件
如果启用了知识库除了使用知识库工具之外
当需要精准获取信息的时候还可以直接访问知识库文件系统 路径为 /home/gem/kbs/来获取信息
当需要精准获取信息的时候或者 query_kb 中没有找到相关的内容还可以直接访问知识库文件系统 路径为 /home/gem/kbs/来获取信息
源文件可能无法解析可以在 /home/gem/kbs/<db_name>/parsed/ 中找到解析后的 markdown 文件
"""

View File

@ -81,12 +81,27 @@ class ConversationRepository:
metadata["attachments"] = list(metadata.get("attachments", []))
return metadata
def _normalize_agent_config_id(self, agent_config_id: int | None) -> int | None:
if agent_config_id is None:
return None
return int(agent_config_id)
async def _save_metadata(self, conversation: Conversation, metadata: dict) -> None:
conversation.extra_metadata = metadata
conversation.updated_at = utc_now_naive()
await self.db.commit()
await self.db.refresh(conversation)
async def bind_agent_config(self, thread_id: str, agent_config_id: int) -> Conversation | None:
conversation = await self.get_conversation_by_thread_id(thread_id)
if not conversation:
return None
metadata = self._ensure_metadata(conversation)
metadata["agent_config_id"] = self._normalize_agent_config_id(agent_config_id)
await self._save_metadata(conversation, metadata)
return conversation
async def add_message(
self,
conversation_id: int,

View File

@ -79,6 +79,10 @@ async def create_agent_run_view(
conversation = await conv_repo.get_conversation_by_thread_id(thread_id)
if not conversation or conversation.user_id != str(current_user_id) or conversation.status == "deleted":
raise HTTPException(status_code=404, detail="对话线程不存在")
if (conversation.extra_metadata or {}).get("agent_config_id") != int(agent_config_id):
conversation = await conv_repo.bind_agent_config(thread_id, agent_config_id)
if not conversation:
raise HTTPException(status_code=404, detail="对话线程不存在")
request_id = str((meta or {}).get("request_id") or uuid.uuid4())
config = {

View File

@ -371,6 +371,27 @@ async def check_and_handle_interrupts(
logger.error(traceback.format_exc())
async def _ensure_thread_bound_agent_config(
*,
conv_repo: ConversationRepository,
thread_id: str,
user_id: str,
agent_id: str,
agent_config_id: int,
) -> None:
conversation = await conv_repo.get_conversation_by_thread_id(thread_id)
if not conversation:
conversation = await conv_repo.create_conversation(
user_id=user_id,
agent_id=agent_id,
thread_id=thread_id,
)
current_agent_config_id = (conversation.extra_metadata or {}).get("agent_config_id")
if current_agent_config_id != int(agent_config_id):
await conv_repo.bind_agent_config(thread_id, agent_config_id)
async def agent_chat(
*,
query: str,
@ -462,6 +483,13 @@ async def agent_chat(
try:
conv_repo = ConversationRepository(db)
await _ensure_thread_bound_agent_config(
conv_repo=conv_repo,
thread_id=thread_id,
user_id=user_id,
agent_id=agent_id,
agent_config_id=agent_config_id,
)
try:
await conv_repo.add_message_by_thread_id(
@ -631,6 +659,13 @@ async def stream_agent_chat(
try:
conv_repo = ConversationRepository(db)
await _ensure_thread_bound_agent_config(
conv_repo=conv_repo,
thread_id=thread_id,
user_id=user_id,
agent_id=agent_id,
agent_config_id=agent_config_id,
)
try:
await conv_repo.add_message_by_thread_id(

View File

@ -287,6 +287,7 @@ async def create_thread_view(
"title": conversation.title,
"created_at": conversation.created_at.isoformat(),
"updated_at": conversation.updated_at.isoformat(),
"metadata": conversation.extra_metadata or {},
}
@ -316,6 +317,7 @@ async def list_threads_view(
"is_pinned": bool(conv.is_pinned),
"created_at": conv.created_at.isoformat(),
"updated_at": conv.updated_at.isoformat(),
"metadata": conv.extra_metadata or {},
}
for conv in conversations
]
@ -356,6 +358,7 @@ async def update_thread_view(
"is_pinned": bool(updated_conv.is_pinned),
"created_at": updated_conv.created_at.isoformat(),
"updated_at": updated_conv.updated_at.isoformat(),
"metadata": updated_conv.extra_metadata or {},
}

View File

@ -231,6 +231,7 @@ class Conversation(Base):
)
def to_dict(self) -> dict[str, Any]:
metadata = self.extra_metadata or {}
return {
"id": self.id,
"thread_id": self.thread_id,
@ -241,7 +242,7 @@ class Conversation(Base):
"is_pinned": bool(self.is_pinned),
"created_at": format_utc_datetime(self.created_at),
"updated_at": format_utc_datetime(self.updated_at),
"metadata": self.extra_metadata or {},
"metadata": metadata,
}

View File

@ -636,6 +636,7 @@ class ThreadResponse(BaseModel):
is_pinned: bool = False
created_at: str
updated_at: str
metadata: dict[str, Any] = Field(default_factory=dict)
class AttachmentResponse(BaseModel):

View File

@ -98,6 +98,16 @@ async def test_chat_agent_sync_with_thread_id(test_client, admin_headers):
f"thread_id mismatch: expected {thread_id}, got {payload.get('thread_id')}"
)
threads_response = await test_client.get("/api/chat/threads", headers=admin_headers)
assert threads_response.status_code == 200, threads_response.text
threads = threads_response.json()
target_thread = next((item for item in threads if item.get("id") == thread_id), None)
assert target_thread is not None, f"thread not found in thread list: {thread_id}"
assert (target_thread.get("metadata") or {}).get("agent_config_id") == agent_config_id, (
"agent_config_id mismatch: "
f"expected {agent_config_id}, got {(target_thread.get('metadata') or {}).get('agent_config_id')}"
)
async def test_chat_agent_sync_with_meta(test_client, admin_headers):
"""测试非流式对话传递 meta 参数"""

View File

@ -20,3 +20,19 @@ def test_normalize_title_trims_spaces():
normalized = repo._normalize_title(" hello world ")
assert normalized == "hello world"
def test_normalize_agent_config_id_casts_to_int():
repo = ConversationRepository(None) # type: ignore[arg-type]
normalized = repo._normalize_agent_config_id("12") # type: ignore[arg-type]
assert normalized == 12
def test_normalize_agent_config_id_allows_none():
repo = ConversationRepository(None) # type: ignore[arg-type]
normalized = repo._normalize_agent_config_id(None)
assert normalized is None

View File

@ -626,12 +626,16 @@ const setThreadAgentConfigId = (threadId, agentConfigId) => {
if (!threadId) return
const thread = threads.value.find((item) => item.id === threadId)
if (thread) {
thread.agent_config_id = agentConfigId ?? null
thread.metadata = {
...(thread.metadata || {}),
agent_config_id: agentConfigId ?? null
}
}
}
const syncSelectedConfigForThread = async (thread) => {
if (!thread?.agent_config_id) return
const threadAgentConfigId = thread?.metadata?.agent_config_id
if (!threadAgentConfigId) return
const targetAgentId = thread.agent_id || currentAgentId.value
if (!targetAgentId) return
@ -641,8 +645,8 @@ const syncSelectedConfigForThread = async (thread) => {
await agentStore.fetchAgentConfigs(targetAgentId)
}
if (selectedAgentConfigId.value !== thread.agent_config_id) {
await agentStore.selectAgentConfig(thread.agent_config_id)
if (selectedAgentConfigId.value !== threadAgentConfigId) {
await agentStore.selectAgentConfig(threadAgentConfigId)
}
}