diff --git a/README.md b/README.md index 69679f7e..ce08b5ce 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ ## 目录 - [快速开始](#快速开始) · [生产部署](#生产部署) · [核心能力](#核心能力) · [受限动态编排](#受限动态编排模板定骨架agent-填关节) -- [可点击证据](#可点击证据报告里的每个数都点得开出处) · [数据目录](#数据目录让助手和-agent-看懂业务数据) · [工作流形态](#工作流形态) · [节点类型](#节点类型) · [架构](#架构) +- [可点击证据](#可点击证据报告里的每个数都点得开出处) · [应用](#应用已发布版本的使用面) · [身份与权限](#身份与权限) · [能力登记与端点](#能力登记与端点) +- [数据目录](#数据目录让助手和-agent-看懂业务数据) · [工作流形态](#工作流形态) · [节点类型](#节点类型) · [架构](#架构) - [关键设计决策](#关键设计决策) · [运行环境](#运行环境) · [常见问题](#常见问题) · [开发与验证](#开发与验证) --- @@ -40,8 +41,9 @@ conda env create -f environment.yml # 首次:创建 agentlab 环境并安装 服务启动于 http://localhost:5273 。 要让同一网络里的手机、别的电脑打开,用 `./scripts/dev.sh --host`:前端监听所有网卡,启动时打印局域网地址; -后端仍只听 127.0.0.1,页面里的 `/api` 由前端开发服务器在本机转发。**AgentLab 没有登录**,同一网络里打得开 -页面的人都能用它——跑工作流、花你配的模型额度、读数据源、执行沙箱代码,只在信得过的网络里开。 +后端仍只听 127.0.0.1,页面里的 `/api` 由前端开发服务器在本机转发。**默认不开登录**:同一网络里打得开页面的人 +都能用全部功能——跑工作流、花你配的模型额度、读数据源、执行沙箱代码,审批和发起记的是各自填的署名、未经核实。 +只在信得过的网络里这样用;多人使用请开登录,见[身份与权限](#身份与权限)。 认不出的参数直接报错,`./scripts/dev.sh --help` 看用法。 **无需配置 API Key 即可完整体验。** 内置 Mock provider 产出流式回复、工具调用与 @@ -60,7 +62,8 @@ Mock 不会写引用标记:探索运行里报告撰写节点照样跑完,但 **不用 Docker:** `./scripts/prod.sh` 构建前端并由后端一并托管页面,单进程、不热重载,启动于 http://127.0.0.1:8000 。前端产物不存在或比源码旧时才重新构建,`--build` / `--no-build` 强制构建或跳过。 -`--port`(或 `AGENTLAB_PORT`)改端口;`--host` 监听所有网卡,同样**没有登录**,只在信得过的网络里开。 +`--port`(或 `AGENTLAB_PORT`)改端口;`--host` 监听所有网卡,默认同样**不开登录**,多人使用请开登录 +([身份与权限](#身份与权限))。 数据目录默认为仓库下的 `data/`,`AGENTLAB_DATA_DIR` 可改。只能单进程运行:运行管理器在进程内、数据库是 SQLite。 上传表格的版本存储同样只支持单进程运行(不要给 uvicorn 加 `--workers`):启动时会锁住数据目录,同一个数据目录 起了第二个进程时,后起的那个不做任何清理,上传、启用版本等写操作一律返回 503。 @@ -77,6 +80,16 @@ http://127.0.0.1:8000 。前端产物不存在或比源码旧时才重新构建 先用临时容器试启动一台 microVM,起得来就把 `/dev/kvm` 交给容器,代码在 microVM 里执行,部署时不用联网。 没有 KVM(Mac、Windows 上的 Docker Desktop,多数云主机)时,默认权限下代码沙箱退回本地子进程,隔离边界只有容器本身。 + +**从旧版本升级(应用平台)**:升级后第一次启动会自动做几件事,都幂等: + +- 存量的待审批补建成待办;自定义工具的请求头、MCP 的环境变量里的明文加密一遍;能力登记同步一遍。 +- 记级别之前发布过、有正式运行凭据的老版本补记为「已发布」。子工作流、口径卡固定的版本须已发布且未退役,没有凭据 + 的老版本被固定着时,正式运行会被拒绝,按提示改为固定一个已发布的版本。 +- 指向本机或内网的 http MCP 服务,自动登记一个开了内网访问的端点(`mcp-<服务名>`)并绑定,写审计。之后新建的内网 + MCP 要管理员先在「工具 → 端点」登记。 +- `/api/copilot/generate` 改图时只出改动操作,不再是整张新图;每次运行多一条 `run.origin` 事件(在封存范围内)。 + --- ## 核心能力 @@ -87,10 +100,11 @@ http://127.0.0.1:8000 。前端产物不存在或比源码旧时才重新构建 | **对话式取数** | 「问数据」页:自然语言提问,系统自行接入数据源、生成工作流、执行并给出结论;答案里有数字时经报告撰写节点输出、逐段点得开;对话持久化、可分享链接 | | **多模型接入** | Anthropic / OpenAI / 任意 OpenAI 兼容服务(DeepSeek、Kimi、通义、智谱、硅基流动、Ollama 等)+ Mock | | **工具链** | 15 个内置工具 + 自定义工具(HTTP 模板 / 沙箱 Python)+ MCP server 接入;MCP 与自定义工具默认每次等审批,可逐个改成门控模型把关或始终允许 | +| **能力登记与端点** | 所有可调用的工具登记在一处,逐项治理读写效果、审批、字段语义、遮罩;外部系统按测试 / 生产两个环境登记成端点,只有登记的端点能连内网,认证头加密;端点读回的表格和查询快照一样能作证([详见](#能力登记与端点)) | | **Agent 护栏** | 同样的调用不重复执行、连续拿不到新信息就收尾、令牌与金额预算、上下文接近窗口先压缩再收尾;步数只留兜底(默认 100) | | **代码沙箱** | 双档隔离:系统沙箱(Seatbelt / bubblewrap,冷启动约 25ms)或 microVM(独立 Linux 内核,内存限额真实生效) | -| **人工介入** | 执行暂停并落盘,人工批准 / 驳回 / 改稿后从断点继续;进程重启不丢状态 | -| **断点续跑** | 失败的运行可从失败节点继续,已完成的节点不重复执行;允许修改节点配置,但拓扑必须一致 | +| **人工介入** | 执行暂停并落盘,人工批准 / 驳回 / 改稿后从断点继续;进程重启不丢状态;可派给岗位、发起人或按表达式算出的人,会签(任一人 / 全体同意,一票否决 / 至少 m 人),设时限,超时提醒、升级或驳回,永远不会自动批准 | +| **断点续跑** | 失败的运行可从失败节点继续,已完成的节点不重复执行;探索运行允许修改节点配置(拓扑必须一致),正式运行固定在发布的版本上、不能改配置后继续;继续运行不改发起人 | | **知识库** | 向量 + BM25 混合检索、倒排索引、可选模型重排;PDF / Word / PowerPoint / HTML / Markdown 解析;向量模型可接本地端点 | | **表格入库** | Excel / CSV 上传后转为可用 SQL 查询的表,自动生成 `db_query__` 工具——数字由 SQL 计算得出,而非模型读取 | | **数据目录** | 给数据源的每张表记下粒度、业务主键、业务日期、有效记录条件,每个字段的含义、单位、度量类型和码值,以及表与表的关联;由外键、命名推断、数据剖析和模型起草,逐项确认后生效;表详情里可以和助手对话完善一张表,审阅队列按影响排好先审哪些,目录能导出、导入到别的实例;助手建图、agent 查结构、SQL 检查都读它 | @@ -98,14 +112,16 @@ http://127.0.0.1:8000 。前端产物不存在或比源码旧时才重新构建 | **跨库合并** | 合并查询节点:各库先聚合到同一个键,再在库外按键合并;截断的输入拒收,证据一路追到两边的原始查询 | | **长期记忆** | 跨运行的记忆读写与自动去重;Copilot 建图时主动记录应当记住的事实 | | **Skill 管理** | 将方法论抽象为可复用单元,挂载到节点上注入 system prompt | -| **自然语言编排** | Copilot:一句话生成或改写整张图,自动排版;表多的库先按需求挑表、沿关联补上中间表,只给相关表的全部字段和数据目录;交付前按运行时同一套规则自查,查出会跑挂的问题先交回去改,改不好就不自动运行 | +| **自然语言编排** | 助手:一句话生成或改写整张图,自动排版;system 里每个数据源只给一行概览,要看表结构、能力参数、工作流版本时由助手自己「查看」,一次给齐字段、目录和连接条件;交付前按运行时同一套规则自查,查出会运行失败的问题先交回去改,改不好就不自动运行([详见](#助手按需查看)) | | **结构化成果** | JSON Schema 校验,不合格时将错误回传模型自动返工 | | **产出复核** | 运行结束后规则扫描事件流,发现异常才调用模型复核;复核可重组答案但**不得引入原答案中不存在的数字** | | **追踪与成本** | 全量事件落库可回放;逐节点耗时、token 用量、按目录价折算的成本 | | **三档出具** | 口径卡(受控指标集)+ 出具契约:叙述中每个数字回指指标集,或由报告撰写节点按引用逐段核对,formal / degraded / withheld 三档判定 | | **可点击证据** | 报告撰写节点只写引用标记,数字和表格由系统照快照填值;数字、表名字段名、知识库原话都点得开,一路指到查询结果里的那一格;结论句可要求挂依据或交给另一个模型裁判;证据清单可导出,报告导出为自包含 HTML(数字点得开口径与出处),另有封存哈希核对件「可验证报告」 | | **发布前检查** | 受管级别加证据门禁 G1–G5;问题给出一键修复 / 选项 / 交给 Copilot 三类修法,一律先预览、不许降低要求;老工作流一键升级为可追溯结构 | -| **正式 / 探索分级** | 正式运行仅从已发布的不可变版本发起(按图哈希钉死);画布试跑自动标记为探索性 | +| **正式 / 探索分级** | 正式运行只从已发布、未退役的不可变版本发起(按图哈希固定),子工作流、口径卡固定的版本同样须已发布且未退役;画布试跑自动标记为探索性;每次运行记下来源(问数据、画布、定时任务、应用入口、外部系统……),进封存范围 | +| **应用** | 已发布工作流的使用面:从版本确定性派生出入口表单、视图、待办和动作,助手只润色布局和文案;视图只读封存的运行,每个数点得开出处,印章写明截至何时、哪版口径、出具档位;待办箱、实例进度、写外部系统的确认与对账([详见](#应用已发布版本的使用面)) | +| **身份与权限** | 默认不开登录;可开本地账号或通用 OIDC 单点登录,管理员 / 搭建者 / 使用者三种角色加应用级授权,管理操作留审计;开登录后使用者看到的数据一律遮罩([详见](#身份与权限)) | --- @@ -257,15 +273,141 @@ flowchart LR --- +## 应用:已发布版本的使用面 + +工作流发布出去之后,看数的人、办事的人在「应用」里用它。应用不是新引擎,也不是嵌进来的低代码平台:每个数都来自 +固定版本封存的正式运行,每次发起都是固定版本的正式运行。界面层不放逻辑——绑定只取已有的值,可见条件只能比较、 +做布尔运算、取值和 `in`,没有算术,求值都在服务端,不可见的组件和遮罩值不出服务端。 + +**怎么生成一个应用** + +1. 在画布上发布工作流,在发布成功的提示里点「做成应用」;或者在问数据的回答上点「做成应用」:先把这次运行提取成 + 工作流(只保留实际执行的步骤),在画布上发布,再从发布成功的提示进来。 +2. 系统按这一版确定性地派生出应用草稿,不调模型:入口字段生成表单,口径卡生成指标块,报告生成报告块,取数节点生成 + 表格,人工节点生成待办(岗位留空等人绑定),写外部系统的调用工具节点生成动作确认和动作状态。视图优先绑定已有的 + 定时任务。 +3. 在应用编辑里调整:页面树和属性面板、按草稿预览(不发起运行)、检查。右侧助手只能润色布局、文案和控件,改不了 + 固定的版本、入口映射和表单的数据约束。 +4. 绑定岗位到人或组(流程类应用必须)、授权、入站钩子、通知订阅,然后发布。固定的工作流版本比上一版应用新时, + 发布时要为它选一种处置:按新口径重算、两版并列、前后不可比。 + +**能做哪几类应用** + +| 类型 | 用到的能力 | +|---|---| +| 数据看板 | 视图读定时任务最近一次封存的正式运行:印章、指标块、多期趋势(按升版处置画)、表格、折线 / 柱状图、报告 | +| 参数化报表 | 入口表单发起一次正式运行,在实例页看结果;或者页面上的参数表单按需运行并缓存 | +| 审批流程 | 待办派给岗位 / 发起人 / 表达式算出的人,任一人 / 全体 / 至少 m 人,时限与升级,待办箱、「我发起的」 | +| 写回外部系统 | 调用工具节点写登记的端点,确认卡是遮罩后的预览和内容哈希,结果未知转人工核实 | +| 外部系统触发 / 回调 | 签名的入站钩子:发起实例,或者回调「等外部系统」的那一步 | +| 混合 | 看板上发起处置流程,最后写回系统 | + +**视图与印章** 每个视图带印章:截至何时(含统计期、数据版本)、哪一版口径(工作流名 vN · 哈希前 8 位,加口径卡的 +口径名和版本)、哪一档出具、数据从哪来(定时任务「…」第 n 次 / 按需运行 / 本实例)。定时任务已改跑新版本、应用还 +固定在旧版本时照实写明。按需视图默认点「刷新」才运行,同一组参数在进程里只运行一份,查活库的必须设时效,每个应用 +每天有上限(设置项 `apps.on_demand_daily_limit`,默认 200)。 + +**流程与待办** 一次中断对应一条审批(给引擎)和一条待办(给人),所有人的决定经待办服务汇总、凑齐才恢复一次运行。 +图里只写岗位名,岗位绑定到人或组在应用里做。没有应用的运行(画布、定时任务)派给任何人,行为和以前一样;老接口 +`/api/approvals/{id}/decide`、`/api/runs/{id}/resume` 照旧可用。时限由调度循环每 15 秒检查一次——调度关着 +(`AGENTLAB_SCHEDULER=0`)时不提醒、不升级、不自动核实。 + +**写外部系统** 写入能力只能放在「调用工具」节点里。先写生效记录再发送,同一次执行发第 n 次的幂等键是 +`{action_key}:{n}`;确认卡显示遮罩后的预览和内容哈希,确认之后上游变了就拒发。结果未知时运行挂起、不封存:登记了 +查询能力的自动按幂等键核实,没有的开核实待办(已生效并填外部单号 / 未生效重新发送 / 未生效放弃),结果未知永远 +不会自动重发。正式运行写生产环境;探索运行写测试环境,没有测试环境时演练、不发请求。有生效记录的运行不能删除。 + +**入站钩子** 在 spec 里按名字声明(发起实例 / 回调待办),在应用编辑里生成密钥(只显示一次)。外部系统向 +`/api/hooks/{钩子 id}` 发签名请求:`X-AgentLab-Timestamp`、`X-AgentLab-Signature: sha256=HMAC-SHA256(密钥, "时间戳." + 请求体)`, +时间窗 5 分钟,`Idempotency-Key` 必填。钩子做什么以应用当前发布版的声明为准,没发布或停用的拒收。 + +**按新口径补算** 固定版本升版、处置选了「按新口径重算」时,管理者可以点「按新口径补算历史」:取旧版本在同一来源上 +最近几期的输入,用新版本逐个发起正式运行(来源记为按新口径补算),同一输入算过的跳过。有外部副作用的工作流(流程、 +人工节点、写外部系统)不补算。 + +**升级安全** 流程实例可能一等几天,中间升级是常态。停在人工节点、停在写入节点开头等确认的实例,升级后都能继续; +写入结果未知的核实后继续、不会重发;停在 Agent 半路审批的实例升级后无法继续,所以发布时 `process.wait_mid_agent` +会提醒,应用按流程引用时直接拦下。 + +**验收用的假外部系统**:`python scripts/fake-erp.py --port 8765`,可以切换「正常 / 写成功后超时 / 500 / 4xx」,接口见 +脚本开头。 + +## 身份与权限 + +三种登录方式,环境变量 `AGENTLAB_AUTH_MODE`(`off` / `local` / `oidc`)优先于「设置 → 身份」: + +- **off**(默认):即以前的行为。`X-Actor` 自报署名,所有人等同管理员,应用首页上写明这一点。 +- **local**:用户名 + 密码(`hashlib.scrypt`)。从未开启切过来时,必须在同一次保存里建出第一个管理员;环境变量 + 直接指定 `local` 的新部署,在登录页建第一个管理员(只在还没有管理员时可用)。 +- **oidc**:通用 OIDC,授权码 + PKCE;飞书、企业微信这类非标准协议暂不支持。要配身份提供方地址、客户端标识和密钥, + 至少配一个管理员映射(管理员用户名或管理员组)。每次登录按映射重算角色,`groups` 声明同步成只读的组。回调地址是 + `<对外地址>/api/auth/oidc/callback`,在反向代理后面时设 `AGENTLAB_PUBLIC_URL`。 + +会话放在 HttpOnly、SameSite=Lax 的 cookie 里,库里只存 token 的哈希;闲置 12 小时、最长 7 天过期 +(`AGENTLAB_AUTH_SESSION_IDLE_HOURS`、`AGENTLAB_AUTH_SESSION_MAX_DAYS`),是否带 Secure 按请求是不是 https 决定 +(`AGENTLAB_AUTH_COOKIE_SECURE` 可强制)。开了登录后,非 GET 请求必须带 `X-AgentLab-Request` 头;带了 `Origin` +的,必须是本站或 `AGENTLAB_CORS_ORIGINS` 里的地址。登录连续失败后等待时间逐次增加。 + +忘了密码、单点登录配错时,在服务器上执行: + +```bash +cd backend && python -m app.auth reset-admin --username admin # 不给 --password 时随机生成并打印一次 +``` + +**角色**:管理员 ⊇ 搭建者 ⊇ 使用者。应用另有三档授权:使用、查看全部实例、管理(授给使用者的「管理」按「查看全部 +实例」算)。 + +| 能做的事 | 管理员 | 搭建者 | 使用者 | +|---|---|---|---| +| 问数据、助手 | ✓ | ✓ | 默认不能,管理员可在「设置 → 身份」开启 | +| 编排、探索运行、以已发布级发布、数据源、数据目录、定时任务、知识库、自定义工具 | ✓ | ✓ | ✗ | +| 以受管级发布、版本退役 | ✓ | ✗ | ✗ | +| 记录页、运行的实时推送 | ✓ | ✓ | 只限自己的问数据运行 | +| MCP 的 stdio、端点和凭据、能力治理、模型接入 | ✓ | 只读 | ✗ | +| 应用:使用 / 查看全部实例 / 搭建发布 | ✓ | 能用能看;管理要创建者或有管理授权 | 按授权使用、查看 | +| 处理派给自己的待办、核实动作 | ✓ | ✓ | ✓ | +| 用户、组、登录方式、审计 | ✓ | ✗ | ✗ | + +**使用者的问数据**(管理员开启后)只能发起探索运行:图里只许输入、模型调用、Agent、报告撰写、成果、知识检索、 +数据整形、条件分支、口径卡、合并查询;Agent 只能用数据源查询和只读的内置工具;查询结果在工具边界先遮、数据源一律 +只读;记忆范围固定为自己的。对话按建立人隔离。 + +**哪些角色看得到原值**:数据源设置的遮罩列,对开了登录的使用者是边界——查询快照按派生遮罩处理后才给,工具快照的 +原文、运行事件里的结果预览一律不给,工件按白名单给。对搭建者、管理员,以及未开启登录时的所有人,遮罩只减少暴露, +完整快照仍能按工件 id 取到。管理类操作(登录方式、用户和角色、组、端点、能力治理、应用发布、版本退役)写进审计。 + +## 能力登记与端点 + +「工具 → 能力」把内置工具、数据源、自定义工具、MCP 工具登记在一处,名字就是图里的工具名。启动时和各对象增删改时 +自动同步,来源删掉的标为「缺失」,不删除。管理员可以逐项治理:效果(只读 / 写入 / 视参数而定)、审批(跟随信任 +设置 / 总要确认 / 免审批)、是否允许无人值守写入、返回的表格位置、字段语义(形状和数据目录的「项」相同,推断的项 +进「待审阅」)、遮罩字段、凭据模式、查询能力。每改一次版本号加一,正式运行在发起时固定下来,发布时记下版本, +之后有变化只提醒。 + +- **正式运行免审批**:声明为只读、审批设为免审批的能力,在正式运行里直接执行,带 MCP、自定义工具的 Agent 不再停在 + 半路等审批。可写数据源上的写语句不会因为一句声明而免审批。 +- **写入能力只能放在「调用工具」节点里**:Agent、多 Agent 协作的工具清单里有写入能力时,发布检查各级都报错,运行时 + 也拒绝。 +- **登记的端点**:管理员按测试、生产两个环境登记外部系统的地址和认证头。只有登记过、开了「允许访问内网」的端点能连 + 内网。绑定端点的 HTTP 工具只填方法、路径模板、查询参数和请求体,路径参数逐段编码代入,模型拿不到任意地址,不跟随 + 跳转。正式运行走生产环境;探索运行里写入走测试环境(没有就演练),读取走生产环境。凭据模式选「服务凭据 + 身份 + 断言」时,请求另带 `X-AgentLab-Assertion`(EdDSA JWT,5 分钟有效,`aud` 是端点名,`sub` 是运行发起人,确认动作 + 的人在 `act` 里),公钥在 `/api/auth/jwks`。 +- **端点读取可以作证**:给只读的绑定端点工具声明了表格(行的位置、各列的位置)后,结果落成和查询快照形状相同的 + 端点快照:报告里照样编号 Q1…、单元格点得开出处,运行对比照样比(比的是请求),遮罩按列名。Python 工具和 MCP + 不在此列。 + ## 数据目录:让助手和 agent 看懂业务数据 表名和字段名说不清业务:一行代表一张门票还是一次检票,`visitor_count` 能不能按天相加,两张表按哪一列连、 连上之后会不会一对多。助手(Copilot)建图、agent 写 SQL 时只能猜,猜错了查询照样能跑,数却是错的。 数据目录把这些事实记下来,交给三处读: -- **助手建图**:表多的库先按需求挑表,再沿关联关系补上连接路径上的中间表,只给这些表的全部字段、目录和 - 可以照抄的连接条件,其余表只列名字。挑不出时退回只列表名,不阻断生成;只探查了一部分表时如实说明。 -- **运行时查结构**:`db_schema` 返回表结构时附上目录,问数据页里现场写 SQL 的 agent 也用得上。 +- **助手建图**:system 里每个库一行概览(表数、几张有目录、有没有遮罩),不列表名、不列字段。助手要看表时自己 + 「查看」,服务端一次给齐(见[助手按需查看](#助手按需查看))。 +- **运行时查结构**:`db_schema` 返回表结构时附上目录。**遮罩列只给列名和类型**:它的注释、目录里这一列的中文名、 + 码值和说明都不交给模型。助手查看、`db_schema`、`/api/datasources/{id}/schema?table=` 是同一份说法 + (`data/schema_view.py`);代价是运行时 agent 看到的目录少了遮罩列那部分。 - **SQL 检查**:按目录检查七类问题——一对多关联后重复计算、存量跨时间相加、比率被相加或平均、漏了有效记录条件、 码值不存在、没用约定的业务日期、关联条件对不上已知关系。助手自查时交回修正,受管模板发布时挡住,运行时标在 查询快照上,契约指标的来源查询有错误时出具降档。解析不了的写法一律跳过:宁可漏报,不误报。 @@ -294,6 +436,25 @@ flowchart LR --- +### 助手按需查看 + +以前每一轮开始前,服务端按库的大小把表结构塞进助手的 system:说一句「你好」也要先挑表,再带着上万字的表结构去 +生成。现在要不要看由助手自己定: + +- **默认只给概览**:每个数据源一行;画布和问数据另列能力登记里的能力,问数据只列只读的。闲聊、改提示词只调一次模型。 +- **查看**:助手输出一行 `{"op":"look","need":…,"targets":[{"kind":"table","source":…,"name":…}]}`,服务端读到就停止 + 这次输出,查看完把结果作为下一条消息交回,助手接着写,已经落地的操作不重复。能看三类对象:表;能力(名字、参数、 + 返回字段、读写、要不要确认,不给地址和凭据);工作流版本(只列已发布且未退役的)。点名的直接取;只给需求时候选 + 不超过 12 张就全给,否则按需求挑(关思考、20 秒时限),沿关联补上两跳以内的中间表,一次最多 16 张、12,000 字。 + 挑不出、挑出来是空的照实说,把目录交回去让助手点名。每轮最多查看 3 次,自查修正另给 1 次。 +- **系统代为查看**:发布前修复、升级证据按要修的 SQL 点名查看用到的表;应用润色先查看应用固定的工作流版本。 +- **留痕**:每次查看发一条 look 事件(看了哪些、怎么来的、没找到的、目录版本、用时),存进这一轮的 `meta.looks`, + 下一轮写「上一轮查看过:…」,追问时直接点名看。 +- **先问再做**:必须先问清才能动手时,助手输出 `{"op":"ask","question":…}`,这一轮就此结束,已经落地的改动照常交付、 + 不自动运行。 +- **一个生成过程**:起草 → 查看 → 接着写 → 自查修正只有一份(`api/copilot_gen.py`),画布、问数据、`/generate`、 + 发布前修复、升级证据、应用润色共用。 + ## 工作流形态 **人工介入与分支**——执行在 `human` 节点暂停并落盘,人工决策后沿对应出口继续: @@ -346,7 +507,7 @@ flowchart LR | 执行 | `tool` `code` `merge` | 直接调用工具 / 沙箱内执行代码 / 合并几次查询的结果(跨库按键对齐) | | 控制 | `branch` `loop` `subgraph` | 条件或语义分支 / 遍历与条件循环 / 嵌套工作流 | | 上下文 | `memory` `retrieve` `transform` | 长期记忆读写 / 知识检索 / 数据整形 | -| 把关 | `human` `validate` `metrics` | 人工介入 / Schema 校验与自动返工 / 口径卡 | +| 把关 | `human` `validate` `metrics` | 人工介入(可派给岗位、会签、设时限与超时处理、表单可由上游 Agent 拟定)/ Schema 校验与自动返工 / 口径卡 | --- @@ -546,7 +707,10 @@ Excel / CSV 若传入知识库,只能被切块检索,数字即成为模型 - MCP 与自定义工具平台看不出它做什么,默认每次调用都等审批(三档:`ask` / `gated` / `always`)。工具页可逐个改成「始终允许 · 门控把关」 (每次调用先由门控模型审一遍参数,拿不准、出错、超时都交还给人)或「始终允许」;审批卡上的 「始终允许」等于前者。档位在发起运行时快照,恢复和接着跑沿用同一份;正式运行不看档位,一律等审批 -- API Key 经 Fernet 加密落库,仅向前端返回掩码 +- API Key 经 Fernet 加密落库,仅向前端返回掩码;端点的认证头、自定义工具的请求头、MCP 的环境变量与请求头同样加密、只回掩码 +- 内网只对登记的端点开放(管理员开「允许访问内网」),元数据地址和高危端口照样拦;模型给出的地址照旧做完整的 SSRF 检查 +- 开了登录:会话 cookie 在库里只存 token 的哈希;非 GET 请求核对来源标记头和 Origin;使用者只能读有授权的运行,看到的查询结果一律遮罩 +- 写外部系统必须经「调用工具」节点:先留生效记录再发送,确认卡上的内容哈希在发送前重算比对;结果未知永不自动重发 --- @@ -686,14 +850,14 @@ loop 节点给它定轮数上限的环:用 loop 节点包住它。确实需要 ## 开发与验证 ```bash -# 后端:约 6600 个测试,-n auto 并行约 40 秒 +# 后端:约 12800 个测试,-n auto 并行约 1 分钟 cd backend && env -u ANTHROPIC_API_KEY -u ANTHROPIC_BASE_URL -u ANTHROPIC_AUTH_TOKEN -u OPENAI_API_KEY \ python -m pytest -n auto # 前端类型检查 cd frontend && npx tsc -b -# 前端检查:29 项,默认 8 道并行。连 5273 / 8000,先起 ./scripts/dev.sh +# 前端检查:33 项,默认 8 道并行。连 5273 / 8000,先起 ./scripts/dev.sh node scripts/check-all.mjs # 另起 2 套只给检查用的「后端 + vite」,各用一份数据拷贝,约 6.5 分钟;边改代码边跑也不被打断 CHECK_DATA_SRC=<数据目录> node scripts/check-all.mjs --stacks 2 --lanes 12 @@ -704,7 +868,7 @@ node scripts/check-all.mjs evidence publish pytest 前面的 `env -u` 不能省:没配 Key 的 provider 会退回环境变量里的密钥,启动时的 seed 也会拿它种一个 provider——环境里留着真密钥,测试就可能真的去调接口。 -`check-all` 的 29 项按 `scripts/check-all.mjs` 里 `ORDER` 的顺序排: +`check-all` 的 33 项按 `scripts/check-all.mjs` 里 `ORDER` 的顺序排: | 类别 | 检查 | |---|---| @@ -713,10 +877,12 @@ provider——环境里留着真密钥,测试就可能真的去调接口。 | 外壳、回答流与运行态 | `shell` `stream` `evidence`(可点击证据)`run-states`(节点卡)`canvas-fx`(画布表层) | | 各页面 | `studio` `publish`(发布前检查与自动修复)`chat` `runs` `manage` `versions`(上传表格的版本)`catalog`(数据目录)`catalog-loop`(表助手、审阅队列、导出导入)`ui`(端到端,连真数据) | | 复核套件与导出 | `schedules`(定时任务)`compare`(运行对比)`verify`(双盲复算、反方审稿)`inquiry`(审批质询)`challenge`(反方审稿)`bundle`(可验证报告)`whatif`(数字推演)`report-export`(报告导出) | +| 应用平台 | `apps`(应用页、印章、点数字看出处、入口校验、实例页)`inbox`(会签进度、超时、动作确认的哈希卡、核实)`app-editor`(应用编辑、派生、检查、发布时选处置、助手)`capabilities`(能力、端点、身份、通知、审计) | 完整输出写进 `/tmp/agentlab-checks/<时间>/`,终端只给汇总;并行时没过的最后单独重跑一遍,重跑才过的单列成「偶发」。 `--stacks` 每套拷一份数据、端口自动挑空闲的,后端不热重载、vite 不监听文件,跑完、出错或被中断都收干净。 `scripts/e2e-check.mjs` 真发起一次运行(Mock,不花钱),会在库里留下记录,所以不在 check-all 里。 +`scripts/check-auth.mjs`(登录、按角色收起的导航、401 跳登录)要另起一套开着本地账号登录的服务单独跑,也不在 check-all 里。 三层前端验证的分工:`check-decode` 守「翻译对不对」,`check-stream` 守「画出来对不对」, `check-ui` 守「整条路走得通不通」。三者可各自通过而合起来是坏的——例如 Step 字段全部正确, diff --git a/deploy/README.md b/deploy/README.md index cf661ecf..4570f34c 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -88,6 +88,10 @@ cp ../agentlab-<旧版本>-linux-__AGENTLAB_ARCH__/agentlab.env . # 沿用原 新容器没通过健康检查时,原来的容器会保留为 `agentlab-previous`,屏幕上会打印回退命令。 旧镜像也留着,确认新版本没问题后可以 `docker rmi agentlab:<旧版本>` 删掉。 +从没有「应用」的版本升上来时,新版本第一次启动会自动整理一遍(都只做一次,重复启动不改东西):存量的待审批补建成 +待办;自定义工具的请求头、MCP 的环境变量里的明文加密;记级别之前发布过、有正式运行凭据的老版本补记为已发布; +指向本机或内网的 http MCP 服务自动登记成开了内网访问的端点并绑定。升级前照常备份数据目录。 + ## 查看状态和日志 ```bash @@ -115,11 +119,15 @@ cp ../agentlab-<旧版本>-linux-__AGENTLAB_ARCH__/agentlab.env . # 沿用原 ## 安全提示 -- **AgentLab 没有登录。** 同一网络里打得开页面的人都能用它:跑工作流、花你配的模型额度、 - 读你接入的数据源、在沙箱里执行代码。只在信得过的网络里开。 +- **默认不开登录。** 不开登录时,同一网络里打得开页面的人都能用它:跑工作流、花你配的模型额度、 + 读你接入的数据源、在沙箱里执行代码,审批记的是各自填的署名、未经核实。只在信得过的网络里这样用。 + 多人使用请开登录:`agentlab.env` 里设 `AGENTLAB_AUTH_MODE=local`(账号密码)或 `oidc`(单点登录), + `./deploy.sh install` 生效,第一次打开登录页时建第一个管理员。角色、权限和遮罩的边界见仓库 README 的「身份与权限」。 +- 忘了管理员密码:`docker exec -it agentlab python -m app.auth reset-admin`(不给 `--password` 时随机生成并打印一次)。 - 用防火墙限制能访问端口的来源。注意 Docker 发布的端口会绕过 ufw 的默认规则,要么把 `BIND_ADDR` 设成具体网卡的地址或 `127.0.0.1`,要么在 `DOCKER-USER` 链里加规则。 -- 需要多人使用时,把 `BIND_ADDR` 改成 `127.0.0.1`,前面另配一个带登录的反向代理。 +- 放在反向代理后面时,设 `AGENTLAB_PUBLIC_URL` 为对外地址(OIDC 回调和来源核对按它认);代理终结了 https 的, + 设 `AGENTLAB_AUTH_COOKIE_SECURE=true`。 - API Key 加密存在数据目录里,密钥也在数据目录里:数据目录的备份要和 Key 一样保管好。 ## 代码沙箱 diff --git a/deploy/agentlab.env b/deploy/agentlab.env index 68e50790..15b615d8 100644 --- a/deploy/agentlab.env +++ b/deploy/agentlab.env @@ -13,11 +13,24 @@ HOST_PORT=8000 # 监听哪个地址。0.0.0.0 表示所有网卡,同一网络里的电脑、手机都能打开。 # -# 注意:AgentLab 没有登录。同一网络里打得开页面的人都能用它——跑工作流、花你配的模型额度、 -# 读你接入的数据源、在沙箱里执行代码。只在信得过的网络里开,并用防火墙限制来源。 -# 只给本机用(比如另配反向代理做登录),改成 127.0.0.1。 +# 注意:默认不开登录(见下面的「登录」一节)。不开登录时,同一网络里打得开页面的人都能用它——跑工作流、 +# 花你配的模型额度、读你接入的数据源、在沙箱里执行代码,审批记的是各自填的署名、未经核实。 +# 这种情况下只在信得过的网络里开,并用防火墙限制来源;多人使用请开登录。只给本机用,改成 127.0.0.1。 BIND_ADDR=0.0.0.0 +# ---- 登录(可选)---- +# off(默认,不开登录)| local(账号密码)| oidc(通用 OIDC 单点登录)。设了这一项就以它为准,界面上「设置 → 身份」 +# 不能再改登录方式。local:第一次打开登录页时建第一个管理员(只在还没有管理员时可用);oidc 的身份提供方地址、 +# 客户端、管理员映射在「设置 → 身份」里配。忘了密码:docker exec -it agentlab python -m app.auth reset-admin +# AGENTLAB_AUTH_MODE=local +# 对外的地址(在反向代理后面时填):OIDC 的回调地址按它拼,非 GET 请求的 Origin 核对也认它 +# AGENTLAB_PUBLIC_URL=https://agentlab.example.internal +# 会话:闲置多少小时、最长多少天过期 +# AGENTLAB_AUTH_SESSION_IDLE_HOURS=12 +# AGENTLAB_AUTH_SESSION_MAX_DAYS=7 +# 会话 cookie 是否带 Secure:留空按请求是不是 https 决定;反向代理终结了 https 时填 true +# AGENTLAB_AUTH_COOKIE_SECURE= + # ---- 数据 ---- # 数据目录:数据库、执行断点、上传的文件、沙箱工作区,还有加密密钥 .secret_key 都在这里。 # 要备份的就是这个目录(丢了 .secret_key,存进去的 API Key 就解不开了)。