mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 04:41:57 +00:00
2714890d0d
README.zh-CN.md / README.md 只留:这是什么、为什么用它、主要功能(按领域一行一条,链到文档)、快速开始、部署、文档索引、 开发与测试、许可(原文不动)。原来的每一节按读者和任务分到 17 篇主题文档:ask-data、workflows、evidence、 data-sources(新增:接入数据库、只读保证、上传表格、跨库合并、遮罩列)、data-catalog、tools(新增:工具、审批档位、 能力登记、端点、网络规则——它们管所有工作流,不只应用)、apps、evaluation-and-ledger、schedules(含通知)、deployment、 configuration、postgresql-and-scaling、identity-and-security、architecture、development、upgrade-notes(按时间倒序, 每波先写要做的事)、faq。删掉的只有模块名(api/copilot_gen.py、chat/drive.py、data/schema_view.py)、重复的话和 「以前…现在…」的叙述;操作步骤、命令、配置名、限制、权限边界都有归宿。中文是原文,英文按新的中文写;英文里界面文字 只写英文名,开头注明界面目前只有简体中文,中文占位符换成英文占位符,中文数字的例子改成不依赖中文的说法。 数据剖析可以同时剖析 2–3 张表(baf23998)收进 data-catalog。 新增 scripts/check-docs.py:英文文档(README.md、docs/en/**)不许有中日韩字符和全角符号(代码块也算), README、docs/**、deploy/README.md 的相对链接和锚点都要通,docs/zh-CN 与 docs/en 文件一一对应、标题层级相同。 中文提示里指向 README 章节的地方改指新文档:cluster/guard.py、cluster/version.py、db/base.py 两处报错, data/schema_view.py 文档串,.env.example 三处,scripts/prod.sh、scripts/test-postgres.sh,terms.ts 两处、 DataSourcesTab.tsx、check-manage.mjs 注释,deploy/README.md 三处链接。deploy/agentlab.env 未动。 验收:check-docs 0 问题(英文文档 18 个 0 处中文,相对链接 369 条 0 断链,18 对标题层级一致);相关后端测试 117 通过、7 跳过;tsc -b、check-copy 通过。
111 lines
7.1 KiB
Markdown
111 lines
7.1 KiB
Markdown
[English](../en/architecture.md) | 简体中文
|
||
|
||
# 架构与设计决策
|
||
|
||
这篇讲 AgentLab 的总体架构,以及几个关键设计决策的理由。给想改代码或想理解系统为什么这样设计的人看。目录结构和开发流程见[开发](development.md)。
|
||
|
||
## 总体架构
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph FE["前端 · React 19 + React Flow + Zustand"]
|
||
F1["画布编辑"]
|
||
F2["问数据"]
|
||
F3["运行追踪 · 证据面板"]
|
||
F4["decode.ts<br/><i>事件 → 人类可读的唯一翻译层</i>"]
|
||
end
|
||
subgraph BE["后端 · FastAPI + LangGraph"]
|
||
B1["engine<br/>编译 · 调度 · 中断恢复 · 出具与证据"]
|
||
B2["providers<br/>多模型接入与成本估算"]
|
||
B3["tools<br/>注册表 · MCP · 数据源"]
|
||
B4["memory<br/>记忆 · 知识库 · 混合检索"]
|
||
B5["sandbox<br/>microVM / Seatbelt / bubblewrap"]
|
||
end
|
||
subgraph ST["存储"]
|
||
S1[("agentlab.db 或 PostgreSQL<br/>业务数据")]
|
||
S2[("checkpoints.db 或同一个 PostgreSQL<br/>执行断点")]
|
||
S3[("artifacts/<br/>内容寻址工件库")]
|
||
end
|
||
FE <-->|REST + WebSocket| BE
|
||
B1 --> S1 & S2 & S3
|
||
```
|
||
|
||
## 受限动态编排
|
||
|
||
大多数实际任务的步骤是已知的:写周报、核对数据、审批后发布。让模型每次现场重新规划,既是在重复发明已知答案,也让结果不可复现——不可复现就没法验收。
|
||
|
||
所以 AgentLab 的引擎是**编译式**的:运行时模型改不了图结构,它的自由被结构性地限制在 agent 节点内部和分支决策里。在此基础上分三层:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
T["<b>模板层</b> · 骨架固定<br/><br/>已发布的不可变版本<br/>WorkflowVersion + 图哈希<br/>governed 级须过治理 lint"]
|
||
J["<b>关节层</b> · 有边界的自由<br/><br/>agent 工具白名单<br/>branch 语义分类<br/>缺数据时降档"]
|
||
E["<b>探索层</b> · 不进正式归档<br/><br/>画布试跑 / 临时图 / 追问<br/>跑通的路径提取为草稿"]
|
||
T ==>|约束| J
|
||
E -.人工审核后发布.-> T
|
||
```
|
||
|
||
- **模板层**:正式运行只认已发布的不可变版本;受管级发布要过治理检查和证据门禁。
|
||
- **关节层**:授权范围内的自由;工具白名单衰减报表把「已授权但从未使用」的工具显出来,为收缩权限提供依据。
|
||
- **探索层**:结果标成探索性,不进正式归档;跑通的路径经「提取模板」沉淀为草稿,人工审核后发布。
|
||
|
||
怎么发布、怎么从探索走到模板,见[工作流](workflows.md#发布与版本)。
|
||
|
||
## 选用 LangGraph
|
||
|
||
可视化编排的难点不在画节点和连线,而在暂停、恢复与回放。LangGraph 的 checkpointer 和 `interrupt()` 把这几件事做在了底层:
|
||
|
||
- 人工介入是一次中断加一次 `Command(resume=...)`,进程重启后仍可从断点继续。
|
||
- `aget_state_history()` 直接给出每一步的状态快照。
|
||
|
||
由此还有一项收益:**agent 内部的工具审批不重复计费**。`interrupt()` 会让整个节点重放,朴素实现会重复执行之前的模型调用。agent 节点里的模型调用和工具执行都包在 LangGraph 的 `@task` 里,结果进断点,重放时直接读缓存。
|
||
|
||
## 事件流是唯一的可视化数据源
|
||
|
||
节点通过 `get_stream_writer()` 发出事件,runner 把它们转成 `RunEvent`,落库并广播。画布高亮、token 流、工具卡片、时间线全部由同一份事件驱动:刷新页面或中途接入,都能拿到完整过程(WebSocket 先补历史,再接实时)。
|
||
|
||
前端对应地只有一个翻译层 `run/decode.ts`,画布右栏、问数据页、运行详情页共用它。分设几套的后果是:同一次运行在三个页面讲出三个版本,使用者没法判断哪个是真的。
|
||
|
||
## 画布动效只表示真实发生的事
|
||
|
||
画布上每个会动的东西都对应一件真实事件(含义见[工作流](workflows.md#动效表示什么))。跑一张十几个节点的图,一眼要能看出「谁在动」;纯装饰的动效做不到,只会让画布变吵。
|
||
|
||
- 协作矩阵和右栏泳道读的是**同一份** `reduceTeam`(`run/decode.ts`),两处不会给出不同的并行度。
|
||
- 系统关掉动效时,光效整体不绘制,而不是停住:停在最后一帧的光弧和堆在起点的光点,看着像渲染坏了。
|
||
|
||
## 用护栏代替固定步数
|
||
|
||
固定步数想挡的是四件事:循环不收敛、花费越滚越大、上下文会满、真正失控。每件事都有更贴切的挡法(`engine/guards.py`):重复调用不再执行、令牌与金额预算、接近窗口先压缩再收尾,步数只留兜底。具体的值见[工作流](workflows.md#护栏)。
|
||
|
||
上限在运行发起时快照,恢复和接着跑沿用同一份:审批恢复时节点整个重放,上限一变,循环停的位置就和断点对不上。
|
||
|
||
## 沙箱分两档
|
||
|
||
轻量档用操作系统自带的访问控制(Seatbelt、bubblewrap),冷启动快,但 macOS 上限不住内存;重量档 microVM 有独立内核,内存限额真实生效,代价是第一次拉镜像要等。所以 auto 模式在镜像没缓存时先用轻量档。两档的保证、局限和实测数据见[身份、权限与安全](identity-and-security.md#代码沙箱)。
|
||
|
||
## 为什么检索是混合的
|
||
|
||
默认的 embedding 是本地特征哈希:不联网、不下载模型、零配置可用,但语义泛化能力弱。BM25 补上精确的关键词匹配。
|
||
|
||
实测(24 篇文档、16 个问题的评测集,hit@1):
|
||
|
||
| 配置 | hit@1 | 字面类 | 语义类 |
|
||
|---|---|---|---|
|
||
| 纯关键词(alpha=0) | 88% | 100% | 75% |
|
||
| 纯向量(本地哈希,alpha=1) | 81% | 88% | 75% |
|
||
| 混合 + Qwen3-Embedding-4B | 94% | 100% | 88% |
|
||
| 上述配置叠加模型重排 | 100% | — | — |
|
||
|
||
- 纯向量(81%)低于纯关键词(88%),混合才到 94%。专有名词靠 BM25,同义改写靠向量,两者各担一半——在这份语料上,混合检索是必要的,不是装饰。
|
||
- 样本只有 16 题,一题就是 6 个百分点。结论的方向可信,精度不要过度解读。
|
||
|
||
向量模型的维度靠实际探测,不按模型名推断。本地部署的模型维度各异(bge-m3 是 1024,Qwen3-Embedding-4B 是 2560);推断错了,后果不是偏差,而是全部存量向量被判定为和当前 embedder 不匹配,检索整体退回关键词。
|
||
|
||
## 为什么表格必须成为表
|
||
|
||
Excel / CSV 如果放进知识库,只能被切块检索,数字就成了模型从片段里「读」出来的。而本项目的基础约定是「所有算术下沉到 SQL 或口径卡」:产出复核能拦住凭空出现的数字,拦不住「从一堆片段里读错了一格」。所以表格上传后落成 SQLite 文件、注册成数据源(见[数据源](data-sources.md#上传表格))。
|
||
|
||
## 连线走线
|
||
|
||
自动排版之后,层与层之间按「穿过这条走廊的连线,在竖直方向最多重叠几条」预留走廊宽度。前端的 `canvas/routing.ts` 据此给每条线分配独立的竖直车道,并错开端口、让跨层的长边从没有节点的空档绕过去。`check-canvas-layout` 用几何断言守这部分(见[开发](development.md#前端检查))。
|