feat(backend): update database schema and agent service

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-11 08:47:53 +08:00
parent 3e39b40a50
commit ac49c13965
3 changed files with 337 additions and 3 deletions

View File

@@ -42,6 +42,9 @@ from app.services.rollback_controller import RollbackController
from app.services.runtime_observability import build_runtime_observability_report
from app.agents.tools.time_reasoning import extract_reference_datetime
from app.agents.state import initial_state
from app.services.agent_runtime.base import RuntimePreparedContext
from app.services.agent_runtime.hermes_runtime import hermes_runtime_adapter
from app.services.agent_runtime.hermes_session_manager import hermes_session_manager
logger = logging.getLogger(__name__)
@@ -378,6 +381,9 @@ class AgentService:
def __init__(self, db: AsyncSession):
self.db = db
def _resolve_runtime(self, runtime: str | None) -> str:
return runtime or "jarvis"
async def _try_auto_summarize_background(self, user_id: str, conversation_id: str) -> None:
async with async_session() as session:
await memory_service.try_auto_summarize(session, user_id, conversation_id)
@@ -662,10 +668,12 @@ class AgentService:
conversation_id: str | None = None,
file_ids: list[str] | None = None,
model_name: str | None = None,
runtime: str | None = None,
) -> tuple[str, str, AsyncGenerator[dict[str, Any], None]]:
"""
处理对话请求(流式)
"""
runtime_name = self._resolve_runtime(runtime)
user_llm_config = await self._get_user_llm_config(user_id, model_name)
model_name_used = model_name
if model_name and not user_llm_config:
@@ -758,7 +766,7 @@ class AgentService:
conversation_id=conversation_id,
role="assistant",
content="",
model=model_name_used or "jarvis",
model=(model_name_used or "jarvis") if runtime_name == "jarvis" else runtime_name,
attachments=None,
)
self.db.add(assistant_msg)
@@ -773,10 +781,78 @@ class AgentService:
"title": "Assistant message",
"content_summary": content[:500],
"raw_excerpt": content[:2000],
"metadata_": {"role": "assistant"},
"metadata_": {"role": "assistant", "runtime": runtime_name},
"importance_signal": 0.8,
}
if runtime_name == "hermes":
user = await self.db.get(User, user_id)
if user is None:
raise ValueError("用户不存在")
prepared = RuntimePreparedContext(
user=user,
conversation=conv,
user_message=user_msg,
assistant_message=assistant_msg,
raw_message=message,
full_message=full_message,
file_ids=file_ids or [],
model_name=model_name_used,
memory_context=memory_ctx,
)
async def run_hermes():
collected = ""
stream_failed = False
try:
async for event in hermes_runtime_adapter.chat_stream(prepared):
if event.get("type") == "chunk":
collected += str(event.get("content", ""))
elif event.get("type") == "error":
stream_failed = True
yield event
finally:
try:
session_handle = hermes_session_manager.get_or_create(
conversation_id=conv.id,
user_id=user_id,
)
assistant_msg.content = collected if collected else ("Hermes 执行失败,请检查运行配置。" if stream_failed else "")
assistant_msg.model = str(session_handle.metadata.get("model") or "hermes")
assistant_msg.attachments = [
{
"kind": "runtime_info",
"runtime": "hermes",
"session_id": session_handle.hermes_session_id,
"model": session_handle.metadata.get("model"),
"last_error": session_handle.metadata.get("last_error"),
}
]
conv.agent_state = {
"runtime": "hermes",
"runtime_state": {
"hermes": {
"session_id": session_handle.hermes_session_id,
"message_id": assistant_msg.id,
"model": session_handle.metadata.get("model"),
"last_error": session_handle.metadata.get("last_error"),
}
},
}
await BrainService(self.db).create_event(
user_id,
**_build_assistant_event_payload(assistant_msg.content),
)
await self.db.commit()
await self.db.refresh(assistant_msg)
except Exception:
logger.exception("save_hermes_assistant_message_failed")
asyncio.create_task(self._try_auto_summarize_background(user_id, conversation_id))
asyncio.create_task(self._extract_memories_background(user_id, conversation_id))
return conversation_id, assistant_msg.id, run_hermes()
async def run_agent():
collected = ""
state: dict[str, Any] | None = None
@@ -1003,10 +1079,12 @@ class AgentService:
conversation_id: str | None = None,
file_ids: list[str] | None = None,
model_name: str | None = None,
runtime: str | None = None,
) -> tuple[str, str, str, str | None]:
"""
简单同步版对话
"""
runtime_name = self._resolve_runtime(runtime)
user_llm_config = await self._get_user_llm_config(user_id, model_name)
model_name_used = model_name
if model_name and not user_llm_config:
@@ -1043,7 +1121,7 @@ class AgentService:
conversation_id=conversation_id,
role="assistant",
content="",
model=model_name_used or "jarvis",
model=(model_name_used or "jarvis") if runtime_name == "jarvis" else runtime_name,
attachments=None,
)
self.db.add(assistant_msg)
@@ -1072,6 +1150,70 @@ class AgentService:
if recall_ctx:
memory_ctx = f"{memory_ctx}\n{recall_ctx}" if memory_ctx else recall_ctx
if runtime_name == "hermes":
user = await self.db.get(User, user_id)
if user is None:
raise ValueError("用户不存在")
prepared = RuntimePreparedContext(
user=user,
conversation=conv,
user_message=user_msg,
assistant_message=assistant_msg,
raw_message=message,
full_message=message,
file_ids=file_ids or [],
model_name=model_name_used,
memory_context=memory_ctx,
)
response_content, resolved_model_name = await hermes_runtime_adapter.chat_once(prepared)
assistant_msg.content = response_content
assistant_msg.model = resolved_model_name or "hermes"
assistant_msg.attachments = [{
"kind": "runtime_info",
"runtime": "hermes",
"session_id": hermes_session_manager.get_or_create(
conversation_id=conv.id,
user_id=user_id,
).hermes_session_id,
"model": resolved_model_name,
}]
conv.agent_state = {
"runtime": "hermes",
"runtime_state": {
"hermes": {
"session_id": hermes_session_manager.get_or_create(
conversation_id=conv.id,
user_id=user_id,
).hermes_session_id,
"message_id": assistant_msg.id,
"model": resolved_model_name,
}
},
}
await brain_service.create_event(
user_id,
source_type="conversation",
source_id=conversation_id,
event_type="message_created",
title="Assistant message",
content_summary=response_content[:500],
raw_excerpt=response_content[:2000],
metadata_={"role": "assistant", "runtime": "hermes"},
importance_signal=0.8,
)
await self.db.commit()
await self.db.refresh(assistant_msg)
schedule_retrospective_job(
user_id=user_id,
conversation_id=conversation_id,
request_message_id=user_msg.id,
response_message_id=assistant_msg.id,
query_text=message,
final_response=response_content,
state=None,
)
return conversation_id, assistant_msg.id, response_content, assistant_msg.model
set_current_user(user_id)
try:
graph = get_agent_graph()