mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 18:31:59 +00:00
964eeaa829
在画布上拖拽编排 agent 工作流,并实时看着它执行。 后端 FastAPI + LangGraph:把画布 JSON 编译成 StateGraph,15 种节点 (模型/Agent/多Agent、工具/沙箱、分支/循环/子图、记忆/检索/整形、 人工介入/结构校验)。选 LangGraph 是因为可视化编排真正难的是暂停、 恢复和回放,而 checkpointer + interrupt() 正好把这几件事做在了底层。 前端 React + React Flow:节点元数据驱动属性面板与连接桩,运行时 节点高亮、边流动画、卡片内流式 token 全部由同一份事件流驱动。 关键实现: - Agent 内工具审批用 @task 缓存模型调用,interrupt 重放时不重复计费 - 沙箱保持只读根文件系统,代码经 exec+base64 写入(put_archive 会被 只读容器拒绝),断网、非 root、内存/CPU/超时限额 - 检索为向量与 BM25 混合,本地哈希 embedding 零配置可用 - 分支条件走 AST 白名单求值器;HTTP 工具逐 IP 拦截内网与元数据地址 - Mock provider 让无 API Key 也能跑通完整链路 7 个内置模板全部冒烟通过,含真实模型端到端验证。
84 lines
2.6 KiB
Python
84 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, Literal
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
from app.sandbox.base import SandboxLimits
|
|
from app.sandbox.manager import sandbox_manager
|
|
from app.tools.registry import ToolContext, register
|
|
|
|
|
|
class PythonExecArgs(BaseModel):
|
|
code: str = Field(description="要执行的 Python 代码。用 print() 输出结果。")
|
|
timeout: int = Field(default=30, ge=1, le=300, description="超时秒数")
|
|
|
|
|
|
@register(
|
|
name="python_exec",
|
|
category="沙箱",
|
|
description=(
|
|
"在隔离沙箱里执行 Python 代码并返回 stdout/stderr。"
|
|
"默认断网、只读根文件系统,/workspace 可写且在同一次运行内持久。"
|
|
),
|
|
args_schema=PythonExecArgs,
|
|
dangerous=True,
|
|
)
|
|
async def python_exec(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
|
|
args = PythonExecArgs(**kwargs)
|
|
result = await sandbox_manager.run(
|
|
args.code,
|
|
language="python",
|
|
limits=SandboxLimits(timeout=args.timeout),
|
|
session_id=ctx.sandbox_session,
|
|
)
|
|
return result.model_dump(exclude={"files_written"} if not result.files_written else set())
|
|
|
|
|
|
class ShellExecArgs(BaseModel):
|
|
command: str = Field(description="要执行的 shell 命令")
|
|
timeout: int = Field(default=30, ge=1, le=300)
|
|
|
|
|
|
@register(
|
|
name="shell_exec",
|
|
category="沙箱",
|
|
description="在隔离沙箱里执行 shell 命令。同样断网、非 root。",
|
|
args_schema=ShellExecArgs,
|
|
dangerous=True,
|
|
)
|
|
async def shell_exec(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
|
|
args = ShellExecArgs(**kwargs)
|
|
result = await sandbox_manager.run(
|
|
args.command,
|
|
language="bash",
|
|
limits=SandboxLimits(timeout=args.timeout),
|
|
session_id=ctx.sandbox_session,
|
|
)
|
|
return result.model_dump()
|
|
|
|
|
|
class RunCodeArgs(BaseModel):
|
|
code: str
|
|
language: Literal["python", "bash", "node"] = "python"
|
|
timeout: int = Field(default=30, ge=1, le=300)
|
|
network: bool = Field(default=False, description="是否允许联网")
|
|
|
|
|
|
@register(
|
|
name="run_code",
|
|
category="沙箱",
|
|
description="在沙箱里执行任意支持语言的代码,可单独打开网络。",
|
|
args_schema=RunCodeArgs,
|
|
dangerous=True,
|
|
)
|
|
async def run_code(ctx: ToolContext, **kwargs: Any) -> dict[str, Any]:
|
|
args = RunCodeArgs(**kwargs)
|
|
result = await sandbox_manager.run(
|
|
args.code,
|
|
language=args.language,
|
|
limits=SandboxLimits(timeout=args.timeout, network=args.network),
|
|
session_id=ctx.sandbox_session,
|
|
)
|
|
return result.model_dump()
|