mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 22:51:58 +00:00
03c18c4d78
一、文案(前端约 1600 处,后端传到界面的约 880 处) - 术语统一:工作流层面用「运行」(继续运行、重新运行、试运行),节点和工具层面用「执行」,不再用「跑」; 总时长 / 执行时长 / 等待审批;服务端;助手;Agent;token;工作流;固定版本;模型接入;系统提示;批准 / 驳回;受管级别 - 口语改书面:取不到、没取回来、读不懂、连不上、多半、照样、兜底、揉成这类说法改成「无法 / 未能 / 失败 / 请」;去掉拟人写法 - 不再露出实现细节:界面上不写 checkpoint、同一拍、哈希(凭证处除外),不写改版历史(「以前…」「老数据」); 报错、校验、门禁、自动修复里裸写的配置键名和枚举值换成界面上的中文标签,由新的 backend/app/engine/labels.py 统一提供; 发布弹窗里结论句的选项不再是 off / require_citation - 同一件事只留一种说法:生成阶段文案、出具档位说明、画布锁定提示各合成一份 - 去掉界面上的工业场景示例(MES、ERP、「张工」「王工」)和真实产品名,换成通用示例 二、说错的提示(都有测试证明) - 数据库测连接失败时的专用提示以前从不出现(按报错文字全等比较,永远比不上):改为按错误类型判断,不再拼出重复的前缀 - 「设置 → 供应商」改指「设置 → 模型接入」;「在设置里建数据源」改指「数据」页 - 缺少 API Key、模型接入已停用的报错不再夹英文,写明去哪里填 - 两处原因说错(「这种引用在后续版本支持」「这一版还不支持」)改成真实原因 - 节点说明里不准确的说法改正:代码节点在 microVM 或系统沙箱中执行;报告撰写、口径卡的说明不再说过头 - 导出的证据 CSV 里两列同名,第三列改为「片段编号」 三、前后端对齐 - 前端按后端原文识别日志、报错的地方,新旧两种写法都认得(历史运行里存的是旧文字),逐条补了新旧原文的核对 - 后端交回模型的原指令放进单独的 for_model 字段,前端在解析响应时去掉,不会显示到界面上 四、检查 - 检查脚本里按原文匹配的断言改成新文案;改完会变成永远成立的反向断言逐条修正,并做了变异验证 - 新增 check-copy(静态扫描前端界面字符串和后端上界面的文案):再出现「跑」「后端」「多半」「令牌」、 界面上的 Copilot、裸写的配置键名、半角标点等就报错;确需保留的写进 scripts/copy-allow.json 并注明理由
97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
from __future__ import annotations
|
||
|
||
import abc
|
||
from typing import Any
|
||
from uuid import uuid4
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
from app.core.config import settings
|
||
|
||
# 入口脚本的前缀。列"这次执行产出了哪些文件"时要把它们排掉——
|
||
# 它们是执行机制自己的临时文件,不是用户代码的产出。
|
||
ENTRY_PREFIX = "__entry_"
|
||
|
||
|
||
def entry_name(ext: str) -> str:
|
||
"""给这一次执行生成独立的入口脚本名。
|
||
|
||
同一个会话共用一个工作区(跨节点传文件要靠它),所以入口脚本**不能**
|
||
用固定的 main.py:一张图里并排的两个代码节点会在同一 superstep 并发执行,
|
||
都写 main.py 就会互相覆盖——后写的赢,而每个节点都以为跑的是自己的代码,
|
||
还都返回 ok。这是静默的结果污染,比报错难查得多。
|
||
"""
|
||
return f"{ENTRY_PREFIX}{uuid4().hex[:12]}{ext}"
|
||
|
||
|
||
class SandboxLimits(BaseModel):
|
||
timeout: int = Field(default_factory=lambda: settings.sandbox_timeout)
|
||
memory_mb: int = Field(default_factory=lambda: settings.sandbox_memory_mb)
|
||
cpus: float = Field(default_factory=lambda: settings.sandbox_cpus)
|
||
network: bool = Field(default_factory=lambda: settings.sandbox_network)
|
||
max_output: int = Field(default_factory=lambda: settings.sandbox_max_output)
|
||
|
||
|
||
class ExecResult(BaseModel):
|
||
ok: bool = True
|
||
exit_code: int = 0
|
||
stdout: str = ""
|
||
stderr: str = ""
|
||
duration_ms: int = 0
|
||
timed_out: bool = False
|
||
truncated: bool = False
|
||
backend: str = ""
|
||
files_written: list[str] = Field(default_factory=list)
|
||
error: str | None = None
|
||
# 这次执行导致会话工作区被重置了(比如 microVM 因超时或限额变更被销毁)。
|
||
# 会话契约说"同 session_id 共享文件系统,可以先写文件下一步再读回来",
|
||
# 所以工作区没了必须说出来——否则下一个节点只会看到 FileNotFoundError,
|
||
# 完全不知道是谁把文件弄丢的。
|
||
session_reset: bool = False
|
||
|
||
def summary(self) -> str:
|
||
if self.timed_out:
|
||
return "执行超时"
|
||
if self.error:
|
||
return self.error
|
||
return self.stdout or self.stderr or "(无输出)"
|
||
|
||
|
||
class Sandbox(abc.ABC):
|
||
"""代码执行后端的统一接口。"""
|
||
|
||
name: str = "base"
|
||
|
||
@abc.abstractmethod
|
||
async def run(
|
||
self,
|
||
code: str,
|
||
*,
|
||
language: str = "python",
|
||
limits: SandboxLimits | None = None,
|
||
session_id: str | None = None,
|
||
env: dict[str, str] | None = None,
|
||
files: dict[str, str] | None = None,
|
||
) -> ExecResult:
|
||
"""执行一段代码。session_id 相同的调用共享同一个文件系统,
|
||
这样 agent 可以先写文件、再在下一步读回来。"""
|
||
|
||
async def health(self) -> dict[str, Any]:
|
||
return {"backend": self.name, "available": True}
|
||
|
||
async def cleanup(self, session_id: str) -> None:
|
||
return None
|
||
|
||
async def close(self) -> None:
|
||
return None
|
||
|
||
|
||
def truncate(text: str, limit: int) -> tuple[str, bool]:
|
||
if len(text) <= limit:
|
||
return text, False
|
||
half = limit // 2
|
||
return (
|
||
text[:half] + f"\n\n…(省略 {len(text) - limit} 字符)…\n\n" + text[-half:],
|
||
True,
|
||
)
|