feat: 添加AI记忆模块和事件调度系统

- 新增AI记忆模块,支持本地记忆召回和自动写入功能
- 实现事件调度系统,支持批量处理待定事件和重试机制
- 集成心跳监控机制,跟踪API、调度器和工作节点状态
- 扩展仪表板数据统计,包含AI记忆条目和心跳概要
- 添加企业运营分析报告功能,提供财务、采购等多维度分析
- 更新配置设置,增加事件调度和AI记忆相关参数
- 优化任务队列,添加事件分发任务类型
- 扩展审计日志,记录AI记忆操作和事件调度行为
- 实现领域事件模型,支持事件持久化和状态管理
- 添加观察性服务,监控系统组件健康状况
```
This commit is contained in:
2026-07-09 17:26:19 +08:00
parent 0a153b264a
commit 0cda45238a
33 changed files with 1591 additions and 34 deletions

View File

@@ -29,6 +29,8 @@ class AIResponseKey(StrEnum):
TEXT = "text"
PIPELINE = "pipeline"
HERMES_RECALL = "hermes_recall"
LOCAL_MEMORY = "local_memory"
MEMORY_WRITE = "memory_write"
HERMES_ANSWER = "hermes_answer"
HERMES_REMEMBER = "hermes_remember"
OPENCLAW = "openclaw"
@@ -49,6 +51,9 @@ class AIContextKey(StrEnum):
OPENCLAW_SESSION_KEY = "openclaw_session_key"
AGENT_PIPELINE = "agent_pipeline"
HERMES_MEMORY = "hermes_memory"
LOCAL_MEMORY = "local_memory"
MEMORY_SCOPE = "memory_scope"
MEMORY_SUBJECT = "memory_subject"
OPENCLAW = "openclaw"
MODE = "mode"
USER_PROMPT = "user_prompt"

View File

@@ -14,10 +14,13 @@ from app.modules.ai_agent.constants import (
AI_AUDIT_SENSITIVE_KEYS,
AI_AUDIT_TRUNCATED_VALUE,
AIToolAuditKey,
AIContextKey,
AIProviderName,
AIRequestKey,
AIResponseKey,
)
from app.modules.ai_memory.constants import AIMemoryPayloadKey, AIMemoryScope
from app.modules.ai_memory.service import AIMemoryService
from app.modules.ai_agent.skills import AISkillId, get_ai_skill
from app.modules.audit.constants import (
AuditAction,
@@ -44,11 +47,37 @@ class AIService:
source: str = AuditSource.API,
) -> dict[str, Any]:
adapter = get_adapter()
result = adapter.ask(prompt, context or {})
original_context = context or {}
adapter_context = dict(original_context)
memory_service = AIMemoryService(self.db)
local_memory = memory_service.recall(
query=prompt,
scope=_memory_scope(original_context),
subject=_memory_subject(original_context),
actor=actor,
)
if local_memory:
adapter_context[AIContextKey.LOCAL_MEMORY] = local_memory
result = adapter.ask(prompt, adapter_context)
answer = result[AIResponseKey.ANSWER]
raw = dict(result.get(AIResponseKey.RAW, {}))
if local_memory:
raw[AIResponseKey.LOCAL_MEMORY] = local_memory
memory_record = memory_service.auto_write(
prompt=prompt,
context=original_context,
answer=answer,
actor=actor,
)
if memory_record is not None:
raw[AIResponseKey.MEMORY_WRITE] = {
AIMemoryPayloadKey.CODE: memory_record.code,
AIMemoryPayloadKey.STATUS: memory_record.status,
}
response = {
AIResponseKey.PROVIDER: adapter.provider_name,
AIResponseKey.ANSWER: result[AIResponseKey.ANSWER],
AIResponseKey.RAW: result.get(AIResponseKey.RAW, {}),
AIResponseKey.ANSWER: answer,
AIResponseKey.RAW: raw,
}
self.audit.log(
AuditLogCreate(
@@ -197,3 +226,16 @@ def _audit_safe_payload(value: Any, depth: int = 0) -> Any:
if isinstance(value, str) and len(value) > AI_AUDIT_MAX_TEXT_LENGTH:
return value[:AI_AUDIT_MAX_TEXT_LENGTH] + AI_AUDIT_TRUNCATED_VALUE
return value
def _memory_scope(context: dict[str, Any]) -> str:
return str(
context.get(AIContextKey.MEMORY_SCOPE)
or context.get(AIMemoryPayloadKey.SCOPE)
or AIMemoryScope.GLOBAL
)
def _memory_subject(context: dict[str, Any]) -> str | None:
value = context.get(AIContextKey.MEMORY_SUBJECT) or context.get(AIMemoryPayloadKey.SUBJECT)
return str(value) if value else None