feat: implement history persistence for agent interactions and enhance cron job permission handling
This commit is contained in:
@@ -28,6 +28,7 @@ from astrbot.core.message.message_event_result import (
|
||||
from astrbot.core.platform.message_session import MessageSession
|
||||
from astrbot.core.provider.entites import ProviderRequest
|
||||
from astrbot.core.provider.register import llm_tools
|
||||
from astrbot.core.utils.history_saver import persist_agent_history
|
||||
|
||||
|
||||
class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
||||
@@ -199,6 +200,7 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
||||
extras=extras,
|
||||
message_type=session.message_type,
|
||||
)
|
||||
cron_event.role = event.role
|
||||
config = MainAgentBuildConfig(tool_call_timeout=3600)
|
||||
|
||||
req = ProviderRequest()
|
||||
@@ -221,6 +223,7 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
||||
req.prompt = (
|
||||
"Proceed according to your system instructions. "
|
||||
"Output using same language as previous conversation."
|
||||
" After completing your task, summarize and output your actions and results."
|
||||
)
|
||||
if not req.func_tool:
|
||||
req.func_tool = ToolSet()
|
||||
@@ -238,6 +241,22 @@ class FunctionToolExecutor(BaseFunctionToolExecutor[AstrAgentContext]):
|
||||
# agent will send message to user via using tools
|
||||
pass
|
||||
llm_resp = runner.get_final_llm_resp()
|
||||
task_meta = extras.get("background_task_result", {})
|
||||
summary_note = (
|
||||
f"[BackgroundTask] {task_meta.get('tool_name', tool.name)} "
|
||||
f"(task_id={task_meta.get('task_id', task_id)}) finished. "
|
||||
f"Result: {task_meta.get('result') or result_text or 'no content'}"
|
||||
)
|
||||
if llm_resp and llm_resp.completion_text:
|
||||
summary_note += (
|
||||
f"I finished the task, here is the result: {llm_resp.completion_text}"
|
||||
)
|
||||
await persist_agent_history(
|
||||
ctx.conversation_manager,
|
||||
event=cron_event,
|
||||
req=req,
|
||||
summary_note=summary_note,
|
||||
)
|
||||
if not llm_resp:
|
||||
logger.warning("background task agent got no response")
|
||||
return
|
||||
|
||||
@@ -31,9 +31,9 @@ from astrbot.core.astr_main_agent_resources import (
|
||||
LOCAL_PYTHON_TOOL,
|
||||
PYTHON_TOOL,
|
||||
SANDBOX_MODE_PROMPT,
|
||||
SEND_MESSAGE_TO_USER_TOOL,
|
||||
TOOL_CALL_PROMPT,
|
||||
TOOL_CALL_PROMPT_SKILLS_LIKE_MODE,
|
||||
SEND_MESSAGE_TO_USER_TOOL,
|
||||
retrieve_knowledge_base,
|
||||
)
|
||||
from astrbot.core.conversation_mgr import Conversation
|
||||
|
||||
@@ -15,6 +15,7 @@ from astrbot.core.db import BaseDatabase
|
||||
from astrbot.core.db.po import CronJob
|
||||
from astrbot.core.platform.message_session import MessageSession
|
||||
from astrbot.core.provider.entites import ProviderRequest
|
||||
from astrbot.core.utils.history_saver import persist_agent_history
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from astrbot.core.star.context import Context
|
||||
@@ -182,7 +183,7 @@ class CronJobManager:
|
||||
if job.job_type == "basic":
|
||||
await self._run_basic_job(job)
|
||||
elif job.job_type == "active_agent":
|
||||
await self._run_active_agent_job(job)
|
||||
await self._run_active_agent_job(job, start_time=start_time)
|
||||
else:
|
||||
raise ValueError(f"Unknown cron job type: {job.job_type}")
|
||||
except Exception as e: # noqa: BLE001
|
||||
@@ -208,7 +209,7 @@ class CronJobManager:
|
||||
if asyncio.iscoroutine(result):
|
||||
await result
|
||||
|
||||
async def _run_active_agent_job(self, job: CronJob):
|
||||
async def _run_active_agent_job(self, job: CronJob, start_time: datetime):
|
||||
payload = job.payload or {}
|
||||
session_str = payload.get("session")
|
||||
if not session_str:
|
||||
@@ -222,6 +223,7 @@ class CronJobManager:
|
||||
"type": job.job_type,
|
||||
"description": job.description,
|
||||
"note": note,
|
||||
"run_started_at": start_time.isoformat(),
|
||||
},
|
||||
"cron_payload": payload,
|
||||
}
|
||||
@@ -268,6 +270,15 @@ class CronJobManager:
|
||||
message_type=session.message_type,
|
||||
)
|
||||
|
||||
# judge user's role
|
||||
umo = cron_event.unified_msg_origin
|
||||
cfg = self.ctx.get_config(umo=umo)
|
||||
admin_ids = cfg.get("admins_id", [])
|
||||
if admin_ids:
|
||||
cron_event.role = (
|
||||
"admin" if cron_event.get_sender_id() in admin_ids else "member"
|
||||
)
|
||||
|
||||
config = MainAgentBuildConfig(
|
||||
tool_call_timeout=3600,
|
||||
llm_safety_mode=False,
|
||||
@@ -295,6 +306,7 @@ class CronJobManager:
|
||||
"You are now responding to a scheduled task"
|
||||
"Proceed according to your system instructions. "
|
||||
"Output using same language as previous conversation."
|
||||
"After completing your task, summarize and output your actions and results."
|
||||
)
|
||||
if not req.func_tool:
|
||||
req.func_tool = ToolSet()
|
||||
@@ -312,6 +324,22 @@ class CronJobManager:
|
||||
# agent will send message to user via using tools
|
||||
pass
|
||||
llm_resp = runner.get_final_llm_resp()
|
||||
cron_meta = extras.get("cron_job", {}) if extras else {}
|
||||
summary_note = (
|
||||
f"[CronJob] {cron_meta.get('name') or cron_meta.get('id', 'unknown')}: {cron_meta.get('description', '')} "
|
||||
f" triggered at {cron_meta.get('run_started_at', 'unknown time')}, "
|
||||
)
|
||||
if llm_resp and llm_resp.role == "assistant":
|
||||
summary_note += (
|
||||
f"I finished this job, here is the result: {llm_resp.completion_text}"
|
||||
)
|
||||
|
||||
await persist_agent_history(
|
||||
self.ctx.conversation_manager,
|
||||
event=cron_event,
|
||||
req=req,
|
||||
summary_note=summary_note,
|
||||
)
|
||||
if not llm_resp:
|
||||
logger.warning("Cron job agent got no response")
|
||||
return
|
||||
|
||||
@@ -157,9 +157,7 @@ class CronJob(TimestampMixin, SQLModel, table=True):
|
||||
)
|
||||
name: str = Field(max_length=255, nullable=False)
|
||||
description: str | None = Field(default=None, sa_type=Text)
|
||||
job_type: str = Field(
|
||||
max_length=32, nullable=False
|
||||
) # basic | active_agent
|
||||
job_type: str = Field(max_length=32, nullable=False) # basic | active_agent
|
||||
cron_expression: str | None = Field(default=None, max_length=255)
|
||||
timezone: str | None = Field(default=None, max_length=64)
|
||||
payload: dict = Field(default_factory=dict, sa_type=JSON)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import json
|
||||
|
||||
from astrbot import logger
|
||||
from astrbot.core.conversation_mgr import ConversationManager
|
||||
from astrbot.core.platform.astr_message_event import AstrMessageEvent
|
||||
from astrbot.core.provider.entities import ProviderRequest
|
||||
|
||||
|
||||
async def persist_agent_history(
|
||||
conversation_manager: ConversationManager,
|
||||
*,
|
||||
event: AstrMessageEvent,
|
||||
req: ProviderRequest,
|
||||
summary_note: str,
|
||||
) -> None:
|
||||
"""Persist agent interaction into conversation history."""
|
||||
if not req or not req.conversation:
|
||||
return
|
||||
|
||||
history = []
|
||||
try:
|
||||
history = json.loads(req.conversation.history or "[]")
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.warning("Failed to parse conversation history: %s", exc)
|
||||
history.append({"role": "user", "content": "Output your last task result below."})
|
||||
history.append({"role": "assistant", "content": summary_note})
|
||||
await conversation_manager.update_conversation(
|
||||
event.unified_msg_origin,
|
||||
req.conversation.cid,
|
||||
history=history,
|
||||
)
|
||||
Reference in New Issue
Block a user