разделение workspace и system файлов, использование CompositeBackend

This commit is contained in:
Егор Кандрушин 2026-04-27 17:14:42 +03:00
parent 74e884179a
commit 7907f57971
7 changed files with 85 additions and 32 deletions

View file

@ -2,23 +2,49 @@ import os
import pwd
import subprocess
from typing import Any
from deepagents.backends.local_shell import LocalShellBackend
import uuid
from pathlib import Path
from deepagents.backends.local_shell import DEFAULT_EXECUTE_TIMEOUT
from deepagents.backends.protocol import SandboxBackendProtocol
class IsolatedShellBackend(LocalShellBackend):
class IsolatedShellBackend(SandboxBackendProtocol):
"""LocalShellBackend с изоляцией shell-команд через отдельного пользователя."""
def __init__(
self,
user: str,
**kwargs: Any,
root_dir: str,
timeout: int = DEFAULT_EXECUTE_TIMEOUT,
max_output_bytes: int = 100_000,
env: dict[str, str] | None = None,
inherit_env: bool = False,
):
super().__init__(**kwargs)
self._user = user
self._uid = pwd.getpwnam(user).pw_uid # type: ignore[attr-defined]
self._gid = pwd.getpwnam(user).pw_gid # type: ignore[attr-defined]
if timeout <= 0:
msg = f"timeout must be positive, got {timeout}"
raise ValueError(msg)
# Store execution parameters
self._default_timeout = timeout
self._max_output_bytes = max_output_bytes
self.cwd = Path(root_dir).resolve() if root_dir else Path.cwd()
# Build environment based on inherit_env setting
if inherit_env:
self._env = os.environ.copy()
if env is not None:
self._env.update(env)
else:
self._env = env if env is not None else {}
# Generate unique sandbox ID
self._sandbox_id = f"local-{uuid.uuid4().hex[:8]}"
def execute(
self,
command: str,

View file

@ -1,5 +1,6 @@
import os
from deepagents import create_deep_agent
from deepagents import create_deep_agent, FilesystemPermission
from deepagents.backends import CompositeBackend, FilesystemBackend
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph.state import CompiledStateGraph
@ -14,7 +15,7 @@ class Agent(CompiledStateGraph):
"""
Временный (надеюсь) костыль, чтобы дать доступ сервису к файловой системе агента.
"""
backend: IsolatedShellBackend
backend: CompositeBackend
def create_agent() -> Agent:
@ -32,10 +33,10 @@ def create_agent() -> Agent:
workspace_dir = os.environ["WORKSPACE_DIR"]
agent_user = os.environ.get("AGENT_USER", "agent")
backend = IsolatedShellBackend(
user=agent_user,
root_dir=workspace_dir,
virtual_mode=True,
backend = CompositeBackend(
routes={
workspace_dir: FilesystemBackend(workspace_dir, virtual_mode=True),
}
)
# noinspection PyTypeChecker
@ -46,6 +47,18 @@ def create_agent() -> Agent:
checkpointer=MemorySaver(),
tools=tools + [send_file],
backend=backend,
permissions=[
FilesystemPermission(
operations=["read", "write"],
paths=["/workspace/**"],
mode="allow",
),
FilesystemPermission(
operations=["read", "write"],
paths=["/**"],
mode="deny"
)
]
)
agent.backend = backend