fix(agent): serialize retrieval trace responses

This commit is contained in:
2026-07-13 19:37:12 +08:00
parent 128d444a76
commit e4a391aa08
4 changed files with 51 additions and 4 deletions

View File

@@ -16,6 +16,10 @@ def register_exception_handlers(app: FastAPI) -> None:
async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
return JSONResponse(status_code=422, content=api_error(10000, "请求参数不正确", exc.errors()))
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse:
return JSONResponse(status_code=500, content=api_error(50000, "系统内部错误,请稍后重试"))
def _code_from_status(status_code: int) -> int:
if status_code in {401, 403}:

View File

@@ -127,7 +127,7 @@ class KnowledgeAgentService:
log.selected_knowledge_ids = ",".join(str(item) for item in selected_ids)
log.query_terms = json.dumps(terms, ensure_ascii=False)
log.final_section_ids = ",".join(str(item.section.id) for item in selected)
log.tool_trace = json.dumps(trace, ensure_ascii=False)
log.tool_trace = _dump_trace(trace)
log.tool_cost_ms = int((perf_counter() - started) * 1000)
log.status = "prepared"
db.add(log)
@@ -152,7 +152,7 @@ class KnowledgeAgentService:
except Exception as exc:
log.status = "failed"
log.error_message = str(exc)
log.tool_trace = json.dumps(trace, ensure_ascii=False)
log.tool_trace = _dump_trace(trace)
log.tool_cost_ms = int((perf_counter() - started) * 1000)
db.add(log)
raise
@@ -431,6 +431,18 @@ class KnowledgeAgentService:
return content if len(content) <= 3200 else content[:3200].rstrip() + "\n[章节内容已按保护规则截断]"
def _dump_trace(trace: list[dict]) -> str:
"""Persist tool traces without failing on database datetime values."""
return json.dumps(trace, ensure_ascii=False, default=_json_trace_value)
def _json_trace_value(value):
isoformat = getattr(value, "isoformat", None)
if callable(isoformat):
return isoformat()
return str(value)
def _extract_json(value: str) -> str:
match = re.search(r"\{[\s\S]*\}", value)
if not match:

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
import asyncio
import json
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
@@ -11,6 +13,7 @@ from app.models.knowledge import (
Knowledge,
KnowledgeChunk,
KnowledgeManifest,
KnowledgeRetrievalLog,
KnowledgeSection,
KnowledgeSourceSnapshot,
KnowledgeVersion,
@@ -140,6 +143,22 @@ def test_course_question_searches_chunk_and_reads_parent_section():
assert any(item["tool"] == "read_knowledge_sections" for item in result.tool_trace)
def test_tool_trace_serializes_published_datetime():
with _database() as db:
knowledge = _add_published_knowledge(db, knowledge_id=1, name="亲子课程")
version = db.get(KnowledgeVersion, knowledge.current_version_id)
assert version is not None
version.published_at = datetime(2026, 7, 13, 10, 30, 0)
db.commit()
result = asyncio.run(KnowledgeAgentService.build_result(db, question="课程里如何进行亲子沟通?"))
log = db.get(KnowledgeRetrievalLog, result.retrieval_log_id)
trace = json.loads(log.tool_trace)
catalog_call = next(item for item in trace if item["tool"] == "get_knowledge_catalog")
assert catalog_call["items"][0]["publishedAt"] == "2026-07-13T10:30:00"
def test_contextual_follow_up_is_rewritten_before_agent_decision():
with _database() as db:
history = [