fix(deploy): reduce backend worker footprint
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
"""手动触发 worker-general 定时任务:生成反馈报告
|
"""手动触发 worker-agent 定时任务:生成反馈报告
|
||||||
|
|
||||||
用法:
|
用法:
|
||||||
cd /home/qzl/Code/eryao/.worktrees/feat-user-feedback
|
cd /home/qzl/Code/eryao/.worktrees/feat-user-feedback
|
||||||
@@ -12,13 +12,13 @@ from pathlib import Path
|
|||||||
|
|
||||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
|
||||||
|
|
||||||
from core.taskiq.app import worker_general_broker
|
from core.taskiq.app import worker_agent_broker
|
||||||
from v1.feedback.tasks import generate_daily_feedback_report
|
from v1.feedback.tasks import generate_daily_feedback_report
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
task = generate_daily_feedback_report.kiq()
|
task = generate_daily_feedback_report.kiq()
|
||||||
result = worker_general_broker.wait_result(task, timeout=120)
|
result = worker_agent_broker.wait_result(task, timeout=120)
|
||||||
print(f"Task result: {result.return_value}")
|
print(f"Task result: {result.return_value}")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
from core.agentscope.events.agui_codec import AgentScopeAgUiCodec, to_agui_wire_event
|
|
||||||
from core.agentscope.events.pipeline import AgentScopeEventPipeline
|
|
||||||
from core.agentscope.events.redis_bus import RedisStreamBus
|
|
||||||
from core.agentscope.events.sse import to_sse_event
|
|
||||||
from core.agentscope.events.store import NullEventStore, SqlAlchemyEventStore
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"AgentScopeAgUiCodec",
|
"AgentScopeAgUiCodec",
|
||||||
"AgentScopeEventPipeline",
|
"AgentScopeEventPipeline",
|
||||||
@@ -13,3 +7,36 @@ __all__ = [
|
|||||||
"to_agui_wire_event",
|
"to_agui_wire_event",
|
||||||
"to_sse_event",
|
"to_sse_event",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(name: str):
|
||||||
|
if name in {"AgentScopeAgUiCodec", "to_agui_wire_event"}:
|
||||||
|
from core.agentscope.events.agui_codec import (
|
||||||
|
AgentScopeAgUiCodec,
|
||||||
|
to_agui_wire_event,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"AgentScopeAgUiCodec": AgentScopeAgUiCodec,
|
||||||
|
"to_agui_wire_event": to_agui_wire_event,
|
||||||
|
}[name]
|
||||||
|
if name == "AgentScopeEventPipeline":
|
||||||
|
from core.agentscope.events.pipeline import AgentScopeEventPipeline
|
||||||
|
|
||||||
|
return AgentScopeEventPipeline
|
||||||
|
if name == "RedisStreamBus":
|
||||||
|
from core.agentscope.events.redis_bus import RedisStreamBus
|
||||||
|
|
||||||
|
return RedisStreamBus
|
||||||
|
if name == "to_sse_event":
|
||||||
|
from core.agentscope.events.sse import to_sse_event
|
||||||
|
|
||||||
|
return to_sse_event
|
||||||
|
if name in {"NullEventStore", "SqlAlchemyEventStore"}:
|
||||||
|
from core.agentscope.events.store import NullEventStore, SqlAlchemyEventStore
|
||||||
|
|
||||||
|
return {
|
||||||
|
"NullEventStore": NullEventStore,
|
||||||
|
"SqlAlchemyEventStore": SqlAlchemyEventStore,
|
||||||
|
}[name]
|
||||||
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from agentscope.agent import ReActAgent
|
|||||||
from agentscope.message import Msg
|
from agentscope.message import Msg
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from core.agentscope.utils import finalize_json_response
|
from core.agentscope.utils.json_finalize import finalize_json_response
|
||||||
|
|
||||||
|
|
||||||
class JsonReActAgent(ReActAgent):
|
class JsonReActAgent(ReActAgent):
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any, Awaitable, Callable, Protocol
|
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Protocol
|
||||||
|
|
||||||
from ag_ui.core.types import RunAgentInput
|
from ag_ui.core.types import RunAgentInput
|
||||||
from agentscope.message import Msg
|
|
||||||
from openai import APIConnectionError
|
from openai import APIConnectionError
|
||||||
from core.agentscope.runtime.runner import AgentScopeRunner
|
|
||||||
from core.agentscope.runtime.protocols import PipelineLike
|
from core.agentscope.runtime.protocols import PipelineLike
|
||||||
from core.logging import get_logger
|
from core.logging import get_logger
|
||||||
from schemas.agent.runtime_config import RuntimeConfig
|
from schemas.agent.runtime_config import RuntimeConfig
|
||||||
from schemas.shared.user import UserContext
|
from schemas.shared.user import UserContext
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from agentscope.message import Msg
|
||||||
|
|
||||||
logger = get_logger("core.agentscope.runtime.orchestrator")
|
logger = get_logger("core.agentscope.runtime.orchestrator")
|
||||||
|
|
||||||
|
|
||||||
@@ -38,8 +39,12 @@ class AgentScopeRuntimeOrchestrator:
|
|||||||
pipeline: PipelineLike,
|
pipeline: PipelineLike,
|
||||||
runner: RunnerLike | None = None,
|
runner: RunnerLike | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
if runner is None:
|
||||||
|
from core.agentscope.runtime.runner import AgentScopeRunner
|
||||||
|
|
||||||
|
runner = AgentScopeRunner()
|
||||||
self._pipeline = pipeline
|
self._pipeline = pipeline
|
||||||
self._runner = runner or AgentScopeRunner()
|
self._runner = runner
|
||||||
|
|
||||||
async def run(
|
async def run(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from core.divination import derive_divination
|
|||||||
from core.agentscope.runtime.json_react_agent import JsonReActAgent
|
from core.agentscope.runtime.json_react_agent import JsonReActAgent
|
||||||
from core.agentscope.runtime.model_tracking import TrackingChatModel
|
from core.agentscope.runtime.model_tracking import TrackingChatModel
|
||||||
from core.agentscope.runtime.stage_emitter import PipelineStageEmitter
|
from core.agentscope.runtime.stage_emitter import PipelineStageEmitter
|
||||||
from core.agentscope.utils import patch_agentscope_json_repair_compat
|
from core.agentscope.utils.compat import patch_agentscope_json_repair_compat
|
||||||
from core.agentscope.utils.json_finalize import finalize_json_response
|
from core.agentscope.utils.json_finalize import finalize_json_response
|
||||||
from core.config.settings import config
|
from core.config.settings import config
|
||||||
from core.db.session import AsyncSessionLocal
|
from core.db.session import AsyncSessionLocal
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from uuid import uuid4
|
|||||||
|
|
||||||
from agentscope.message import Msg
|
from agentscope.message import Msg
|
||||||
|
|
||||||
from core.agentscope.utils import parse_tool_agent_output
|
from core.agentscope.utils.parsing import parse_tool_agent_output
|
||||||
|
|
||||||
|
|
||||||
class PipelineLike(Protocol):
|
class PipelineLike(Protocol):
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from core.taskiq.app import worker_agent_broker
|
||||||
|
|
||||||
|
|
||||||
|
@worker_agent_broker.task(task_name="tasks.agentscope.run_command.agent")
|
||||||
|
async def run_command_task_agent(command: dict[str, object]) -> dict[str, object]:
|
||||||
|
del command
|
||||||
|
raise RuntimeError("task handle is only for enqueueing")
|
||||||
|
|
||||||
|
|
||||||
|
@worker_agent_broker.task(task_name="tasks.agentscope.run_command.general")
|
||||||
|
async def run_command_task_general(command: dict[str, object]) -> dict[str, object]:
|
||||||
|
del command
|
||||||
|
raise RuntimeError("task handle is only for enqueueing")
|
||||||
@@ -3,31 +3,13 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
from typing import Any, cast
|
from typing import TYPE_CHECKING, Any, cast
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from agentscope.message import Msg
|
|
||||||
from pydantic import TypeAdapter
|
from pydantic import TypeAdapter
|
||||||
from core.agentscope.caches import create_user_context_cache
|
|
||||||
from core.agentscope.caches.attachment_content_cache import (
|
|
||||||
create_attachment_content_cache,
|
|
||||||
)
|
|
||||||
from core.agentscope.caches.context_messages_cache import (
|
|
||||||
create_context_messages_cache,
|
|
||||||
)
|
|
||||||
from core.agentscope.events import (
|
|
||||||
AgentScopeAgUiCodec,
|
|
||||||
AgentScopeEventPipeline,
|
|
||||||
RedisStreamBus,
|
|
||||||
SqlAlchemyEventStore,
|
|
||||||
)
|
|
||||||
from core.agentscope.runtime.orchestrator import AgentScopeRuntimeOrchestrator
|
|
||||||
from core.agentscope.schemas.agui_input import parse_run_input
|
from core.agentscope.schemas.agui_input import parse_run_input
|
||||||
from core.agentscope.services.context_service import AgentContextService
|
|
||||||
from core.config.settings import config
|
|
||||||
from core.db.session import AsyncSessionLocal
|
|
||||||
from core.logging import get_logger
|
from core.logging import get_logger
|
||||||
from core.taskiq.app import worker_agent_broker, worker_general_broker
|
from core.taskiq.app import worker_agent_broker
|
||||||
from schemas.agent.forwarded_props import (
|
from schemas.agent.forwarded_props import (
|
||||||
RuntimeMode,
|
RuntimeMode,
|
||||||
parse_forwarded_props_runtime_mode,
|
parse_forwarded_props_runtime_mode,
|
||||||
@@ -40,12 +22,10 @@ from schemas.domain.chat_message import (
|
|||||||
)
|
)
|
||||||
from schemas.shared.user import UserContext
|
from schemas.shared.user import UserContext
|
||||||
from schemas.shared.user import parse_profile_settings
|
from schemas.shared.user import parse_profile_settings
|
||||||
from services.base.redis import get_or_init_redis_client
|
|
||||||
from services.base.supabase import supabase_service
|
|
||||||
from v1.agent.repository import AgentRepository
|
|
||||||
from v1.points.repository import PointsRepository
|
|
||||||
from v1.users.repository import SQLAlchemyUserRepository
|
from v1.users.repository import SQLAlchemyUserRepository
|
||||||
from v1.points.service import PointsService
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from agentscope.message import Msg
|
||||||
|
|
||||||
logger = get_logger("core.agentscope.runtime.tasks")
|
logger = get_logger("core.agentscope.runtime.tasks")
|
||||||
_MAX_CONTEXT_ATTACHMENTS = 3
|
_MAX_CONTEXT_ATTACHMENTS = 3
|
||||||
@@ -176,6 +156,8 @@ def _serialize_assistant_context_from_metadata(
|
|||||||
|
|
||||||
|
|
||||||
def _load_runtime() -> type[Any]:
|
def _load_runtime() -> type[Any]:
|
||||||
|
from core.agentscope.runtime.orchestrator import AgentScopeRuntimeOrchestrator
|
||||||
|
|
||||||
return AgentScopeRuntimeOrchestrator
|
return AgentScopeRuntimeOrchestrator
|
||||||
|
|
||||||
|
|
||||||
@@ -186,6 +168,8 @@ async def _build_user_context(
|
|||||||
session: Any,
|
session: Any,
|
||||||
session_id: str,
|
session_id: str,
|
||||||
) -> UserContext:
|
) -> UserContext:
|
||||||
|
from core.agentscope.caches import create_user_context_cache
|
||||||
|
|
||||||
cache = create_user_context_cache()
|
cache = create_user_context_cache()
|
||||||
cached = await cache.get(session_id=UUID(session_id))
|
cached = await cache.get(session_id=UUID(session_id))
|
||||||
if cached:
|
if cached:
|
||||||
@@ -218,6 +202,17 @@ async def _build_recent_context_messages(
|
|||||||
runtime_mode: RuntimeMode = RuntimeMode.CHAT,
|
runtime_mode: RuntimeMode = RuntimeMode.CHAT,
|
||||||
context_config: "MessageContextConfig",
|
context_config: "MessageContextConfig",
|
||||||
) -> list[Msg]:
|
) -> list[Msg]:
|
||||||
|
from agentscope.message import Msg
|
||||||
|
from core.agentscope.caches.attachment_content_cache import (
|
||||||
|
create_attachment_content_cache,
|
||||||
|
)
|
||||||
|
from core.agentscope.caches.context_messages_cache import (
|
||||||
|
create_context_messages_cache,
|
||||||
|
)
|
||||||
|
from core.agentscope.services.context_service import AgentContextService
|
||||||
|
from services.base.supabase import supabase_service
|
||||||
|
from v1.agent.repository import AgentRepository
|
||||||
|
|
||||||
context_cache = create_context_messages_cache()
|
context_cache = create_context_messages_cache()
|
||||||
attachment_cache = create_attachment_content_cache()
|
attachment_cache = create_attachment_content_cache()
|
||||||
raw_messages = await context_cache.get(
|
raw_messages = await context_cache.get(
|
||||||
@@ -353,6 +348,18 @@ async def _build_recent_context_messages(
|
|||||||
|
|
||||||
|
|
||||||
async def run_agentscope_task(command: dict[str, Any]) -> dict[str, object]:
|
async def run_agentscope_task(command: dict[str, Any]) -> dict[str, object]:
|
||||||
|
from core.agentscope.events import (
|
||||||
|
AgentScopeAgUiCodec,
|
||||||
|
AgentScopeEventPipeline,
|
||||||
|
RedisStreamBus,
|
||||||
|
SqlAlchemyEventStore,
|
||||||
|
)
|
||||||
|
from core.config.settings import config
|
||||||
|
from core.db.session import AsyncSessionLocal
|
||||||
|
from services.base.redis import get_or_init_redis_client
|
||||||
|
from v1.points.repository import PointsRepository
|
||||||
|
from v1.points.service import PointsService
|
||||||
|
|
||||||
command_type = str(command.get("command", "run")).strip().lower()
|
command_type = str(command.get("command", "run")).strip().lower()
|
||||||
raw_owner_id = command.get("owner_id")
|
raw_owner_id = command.get("owner_id")
|
||||||
raw_owner_email = command.get("owner_email")
|
raw_owner_email = command.get("owner_email")
|
||||||
@@ -485,6 +492,6 @@ async def run_command_task_agent(command: dict[str, object]) -> dict[str, object
|
|||||||
return await run_agentscope_task(command)
|
return await run_agentscope_task(command)
|
||||||
|
|
||||||
|
|
||||||
@worker_general_broker.task(task_name="tasks.agentscope.run_command.general")
|
@worker_agent_broker.task(task_name="tasks.agentscope.run_command.general")
|
||||||
async def run_command_task_general(command: dict[str, object]) -> dict[str, object]:
|
async def run_command_task_general(command: dict[str, object]) -> dict[str, object]:
|
||||||
return await run_agentscope_task(command)
|
return await run_agentscope_task(command)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ def _build_broker(queue_name: str) -> ListQueueBroker:
|
|||||||
|
|
||||||
|
|
||||||
worker_agent_broker = _build_broker("agent")
|
worker_agent_broker = _build_broker("agent")
|
||||||
worker_general_broker = _build_broker("general")
|
worker_general_broker = worker_agent_broker
|
||||||
|
|
||||||
broker = worker_agent_broker
|
broker = worker_agent_broker
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ from __future__ import annotations
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import dashscope
|
|
||||||
from dashscope.audio.asr import Recognition, RecognitionCallback
|
|
||||||
|
|
||||||
from core.config.settings import config
|
from core.config.settings import config
|
||||||
from core.logging import get_logger
|
from core.logging import get_logger
|
||||||
|
|
||||||
@@ -28,6 +25,9 @@ class AsrService:
|
|||||||
|
|
||||||
async def transcribe_file(self, file_path: str, filename: str) -> str:
|
async def transcribe_file(self, file_path: str, filename: str) -> str:
|
||||||
try:
|
try:
|
||||||
|
import dashscope
|
||||||
|
from dashscope.audio.asr import Recognition, RecognitionCallback
|
||||||
|
|
||||||
dashscope.api_key = self._get_api_key()
|
dashscope.api_key = self._get_api_key()
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class TaskiqQueueClient:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _select_queue_task(command: dict[str, object]) -> Any:
|
def _select_queue_task(command: dict[str, object]) -> Any:
|
||||||
from core.agentscope.runtime.tasks import (
|
from core.agentscope.runtime.task_handles import (
|
||||||
run_command_task_agent,
|
run_command_task_agent,
|
||||||
run_command_task_general,
|
run_command_task_general,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,15 +5,11 @@ from pathlib import Path
|
|||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from structlog import get_logger
|
from structlog import get_logger
|
||||||
from taskiq_redis import RedisScheduleSource
|
|
||||||
|
|
||||||
from core.config.settings import config
|
from core.config.settings import config
|
||||||
from core.db.session import AsyncSessionLocal
|
from core.db.session import AsyncSessionLocal
|
||||||
from core.email.sender import EmailAttachment, EmailMessage, EmailSender
|
from core.taskiq.app import worker_agent_broker
|
||||||
from core.email.template_loader import load_template
|
|
||||||
from core.taskiq.app import worker_general_broker
|
|
||||||
from models.user_feedback import UserFeedback
|
from models.user_feedback import UserFeedback
|
||||||
from v1.feedback.report import generate_feedback_report
|
|
||||||
|
|
||||||
logger = get_logger("v1.feedback.tasks")
|
logger = get_logger("v1.feedback.tasks")
|
||||||
|
|
||||||
@@ -52,6 +48,8 @@ def _build_report_email_html(
|
|||||||
end_time: datetime,
|
end_time: datetime,
|
||||||
push_hour: int,
|
push_hour: int,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
from core.email.template_loader import load_template
|
||||||
|
|
||||||
template = load_template("feedback", "daily_report.html")
|
template = load_template("feedback", "daily_report.html")
|
||||||
return template.substitute(
|
return template.substitute(
|
||||||
start_date=start_time.strftime("%Y-%m-%d"),
|
start_date=start_time.strftime("%Y-%m-%d"),
|
||||||
@@ -70,6 +68,8 @@ def _build_no_feedback_email_html(
|
|||||||
end_time: datetime,
|
end_time: datetime,
|
||||||
push_hour: int,
|
push_hour: int,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
from core.email.template_loader import load_template
|
||||||
|
|
||||||
template = load_template("feedback", "no_feedback.html")
|
template = load_template("feedback", "no_feedback.html")
|
||||||
return template.substitute(
|
return template.substitute(
|
||||||
start_date=start_time.strftime("%Y-%m-%d"),
|
start_date=start_time.strftime("%Y-%m-%d"),
|
||||||
@@ -87,6 +87,8 @@ async def _send_feedback_email(
|
|||||||
push_hour: int,
|
push_hour: int,
|
||||||
report_path: Path | None = None,
|
report_path: Path | None = None,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
|
from core.email.sender import EmailAttachment, EmailMessage, EmailSender
|
||||||
|
|
||||||
sender = EmailSender()
|
sender = EmailSender()
|
||||||
|
|
||||||
if feedbacks:
|
if feedbacks:
|
||||||
@@ -123,12 +125,14 @@ async def _send_feedback_email(
|
|||||||
|
|
||||||
|
|
||||||
# type: ignore reportArgumentType for taskiq decorator
|
# type: ignore reportArgumentType for taskiq decorator
|
||||||
@worker_general_broker.on_event("startup") # pyright: ignore[reportArgumentType]
|
@worker_agent_broker.on_event("startup") # pyright: ignore[reportArgumentType]
|
||||||
async def _register_feedback_report_schedule() -> None:
|
async def _register_feedback_report_schedule() -> None:
|
||||||
if not config.feedback_report.enabled:
|
if not config.feedback_report.enabled:
|
||||||
logger.info("Feedback report scheduling disabled")
|
logger.info("Feedback report scheduling disabled")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
from taskiq_redis import RedisScheduleSource
|
||||||
|
|
||||||
schedule_source = RedisScheduleSource(
|
schedule_source = RedisScheduleSource(
|
||||||
url=config.taskiq_broker_url,
|
url=config.taskiq_broker_url,
|
||||||
prefix="schedule:feedback",
|
prefix="schedule:feedback",
|
||||||
@@ -146,12 +150,14 @@ async def _register_feedback_report_schedule() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@worker_general_broker.task(task_name="tasks.feedback.generate_daily_report")
|
@worker_agent_broker.task(task_name="tasks.feedback.generate_daily_report")
|
||||||
async def generate_daily_feedback_report() -> str | None:
|
async def generate_daily_feedback_report() -> str | None:
|
||||||
if not config.feedback_report.enabled:
|
if not config.feedback_report.enabled:
|
||||||
logger.info("Feedback report is disabled, skipping")
|
logger.info("Feedback report is disabled, skipping")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
from v1.feedback.report import generate_feedback_report
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
push_hour = 10
|
push_hour = 10
|
||||||
end_time = now.replace(hour=push_hour, minute=0, second=0, microsecond=0)
|
end_time = now.replace(hour=push_hour, minute=0, second=0, microsecond=0)
|
||||||
@@ -192,6 +198,8 @@ async def generate_daily_feedback_report() -> str | None:
|
|||||||
|
|
||||||
|
|
||||||
async def generate_all_feedback_report() -> Path:
|
async def generate_all_feedback_report() -> Path:
|
||||||
|
from v1.feedback.report import generate_feedback_report
|
||||||
|
|
||||||
async with AsyncSessionLocal() as session:
|
async with AsyncSessionLocal() as session:
|
||||||
stmt = select(UserFeedback).order_by(UserFeedback.created_at.desc())
|
stmt = select(UserFeedback).order_by(UserFeedback.created_at.desc())
|
||||||
result = await session.execute(stmt)
|
result = await session.execute(stmt)
|
||||||
|
|||||||
+18
-1
@@ -71,6 +71,24 @@ ERYAO_DEPLOY_BIND_HOST=127.0.0.1
|
|||||||
ERYAO_DEPLOY_BIND_HOST=0.0.0.0
|
ERYAO_DEPLOY_BIND_HOST=0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 进程配置建议
|
||||||
|
|
||||||
|
生产 Compose 只启动一个 `worker-agent` 容器。Agent 任务、低频通用任务和反馈日报任务共用 `agent` 队列,不再单独常驻 `worker-general` 进程。
|
||||||
|
|
||||||
|
2 核 2G 机器建议使用:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ERYAO_WEB__WORKERS=1
|
||||||
|
ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY=2
|
||||||
|
```
|
||||||
|
|
||||||
|
4G 以上机器可按流量提高 Web 或 Agent worker 数量:
|
||||||
|
|
||||||
|
```text
|
||||||
|
ERYAO_WEB__WORKERS=2
|
||||||
|
ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY=2
|
||||||
|
```
|
||||||
|
|
||||||
## 登录 ECR
|
## 登录 ECR
|
||||||
|
|
||||||
进入部署目录,并把 `.env` 加载到当前 shell:
|
进入部署目录,并把 `.env` 加载到当前 shell:
|
||||||
@@ -128,7 +146,6 @@ cd deploy
|
|||||||
docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers ps
|
docker compose --env-file ./.env -f docker-compose.prod.yml --profile workers ps
|
||||||
docker logs -f eryao-prod-backend
|
docker logs -f eryao-prod-backend
|
||||||
docker logs -f eryao-prod-worker-agent
|
docker logs -f eryao-prod-worker-agent
|
||||||
docker logs -f eryao-prod-worker-general
|
|
||||||
docker logs -f eryao-prod-redis
|
docker logs -f eryao-prod-redis
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -34,21 +34,7 @@ services:
|
|||||||
command:
|
command:
|
||||||
- sh
|
- sh
|
||||||
- -c
|
- -c
|
||||||
- exec taskiq worker core.taskiq.app:worker_agent_broker core.agentscope.runtime.tasks --workers ${ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY:-2}
|
- exec taskiq worker core.taskiq.app:worker_agent_broker core.agentscope.runtime.tasks v1.feedback.tasks --workers ${ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY:-2}
|
||||||
|
|
||||||
worker-general:
|
|
||||||
<<: *backend-common
|
|
||||||
container_name: eryao-prod-worker-general
|
|
||||||
profiles: ["workers"]
|
|
||||||
environment:
|
|
||||||
ERYAO_RUNTIME__ENVIRONMENT: prod
|
|
||||||
ERYAO_RUNTIME__SERVICE_NAME: worker-general
|
|
||||||
ERYAO_REDIS__HOST: redis
|
|
||||||
ERYAO_REDIS__PORT: 6379
|
|
||||||
command:
|
|
||||||
- sh
|
|
||||||
- -c
|
|
||||||
- exec taskiq worker core.taskiq.app:worker_general_broker core.agentscope.runtime.tasks v1.feedback.tasks --workers ${ERYAO_WORKER__GROUPS__GENERAL__CONCURRENCY:-1}
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
image: redis:7.4.2-alpine
|
image: redis:7.4.2-alpine
|
||||||
|
|||||||
@@ -153,14 +153,12 @@ start() {
|
|||||||
|
|
||||||
WEB_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=web uv run uvicorn app:app --host ${ERYAO_WEB__HOST:-0.0.0.0} --port ${WEB_PORT} --workers ${ERYAO_WEB__WORKERS:-2} --log-level ${UVICORN_LOG_LEVEL}"
|
WEB_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=web uv run uvicorn app:app --host ${ERYAO_WEB__HOST:-0.0.0.0} --port ${WEB_PORT} --workers ${ERYAO_WEB__WORKERS:-2} --log-level ${UVICORN_LOG_LEVEL}"
|
||||||
|
|
||||||
WORKER_AGENT_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=worker-agent uv run taskiq worker core.taskiq.app:worker_agent_broker core.agentscope.runtime.tasks --workers ${ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY:-2}"
|
WORKER_AGENT_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=worker-agent uv run taskiq worker core.taskiq.app:worker_agent_broker core.agentscope.runtime.tasks v1.feedback.tasks --workers ${ERYAO_WORKER__GROUPS__AGENT__CONCURRENCY:-2}"
|
||||||
WORKER_GENERAL_CMD="cd '$ROOT_DIR' && PYTHONPATH=backend/src ERYAO_RUNTIME__SERVICE_NAME=worker-general uv run taskiq worker core.taskiq.app:worker_general_broker core.agentscope.runtime.tasks v1.feedback.tasks --workers ${ERYAO_WORKER__GROUPS__GENERAL__CONCURRENCY:-1}"
|
|
||||||
|
|
||||||
echo "Starting tmux web process in session '$SESSION_NAME'..."
|
echo "Starting tmux web process in session '$SESSION_NAME'..."
|
||||||
|
|
||||||
tmux new-session -d -s "$SESSION_NAME" -n web "bash -lc \"$WEB_CMD; echo '[web] exited'; exec bash\""
|
tmux new-session -d -s "$SESSION_NAME" -n web "bash -lc \"$WEB_CMD; echo '[web] exited'; exec bash\""
|
||||||
tmux new-window -t "$SESSION_NAME" -n worker-agent "bash -lc \"$WORKER_AGENT_CMD; echo '[worker-agent] exited'; exec bash\""
|
tmux new-window -t "$SESSION_NAME" -n worker-agent "bash -lc \"$WORKER_AGENT_CMD; echo '[worker-agent] exited'; exec bash\""
|
||||||
tmux new-window -t "$SESSION_NAME" -n worker-general "bash -lc \"$WORKER_GENERAL_CMD; echo '[worker-general] exited'; exec bash\""
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "=== App Started ==="
|
echo "=== App Started ==="
|
||||||
@@ -170,7 +168,6 @@ start() {
|
|||||||
echo "Log files will be created in logs/ directory:"
|
echo "Log files will be created in logs/ directory:"
|
||||||
echo " - web.log, web.error.log"
|
echo " - web.log, web.error.log"
|
||||||
echo " - worker-agent.log, worker-agent.error.log"
|
echo " - worker-agent.log, worker-agent.error.log"
|
||||||
echo " - worker-general.log, worker-general.error.log"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "tmux attach -t $SESSION_NAME"
|
echo "tmux attach -t $SESSION_NAME"
|
||||||
echo "tmux list-windows -t $SESSION_NAME"
|
echo "tmux list-windows -t $SESSION_NAME"
|
||||||
|
|||||||
Reference in New Issue
Block a user