Files
fire-safety-ymd/PROJECT_STATE.md
2026-09-06 00:25:25 +08:00

137 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# fire-safety-ymd 项目当前状态
| 项 | 内容 |
| --- | --- |
| 最近更新 | 2026-09-06 |
| 当前分支 | `main` |
| 当前阶段 | 对话、SuperAgent、空间 MCP、默认关闭的测试页面与测试环境容器部署基线已完成;公网地点候选 MCP 已现场成功一次,页面公网验收和完整 MCP 多工具链仍待验证 |
| 当前重点 | 完成 `/chat` 测试页面的配置开关、静态资源与公网验收,同时重建目标机并验证完整公网 MCP 工具链路 |
## 1. 当前 Checkpoint
- 名称:`fire-safety-ymd-public-chat-page-v1`
- 状态:Ready for Review(本地实现与验证完成;测试服务器部署待用户执行)
- 目标:提供默认关闭的 `/chat` 测试页面和 `/chat/` 规范化入口,按第三方兼容 `completion` SSE 请求,支持用户手动输入测试 `xtoken` 和同页面内存 `session_id` 复用;通过 Nginx 公网反代页面及其 CSS/JavaScript 资源,并保持 Token、真实用户认证和生产授权边界不变。
- 非目标:替用户提交或推送 Git、直接修改远程服务器、创建数据库容器、迁移生产数据、签发证书、改变 DNS/安全组、实现真实用户认证、动态授权、会话持久化或生产审计。
当前进展:
- `Dockerfile` 使用显式 Go/Alpine 版本的多阶段构建,在构建阶段执行全部 Go 测试,最终镜像只包含静态服务二进制、CA 和时区数据,并以非 root 用户运行。
- Docker build 支持通过 `FIRE_SAFETY_BUILD_GOPROXY` 选择目标机可达的可信 Go module proxy;默认仍为官方代理并保留 checksum database,`git` 只存在于 builder,构建代理配置在运行容器中强制清空。
- `compose.yaml` 只运行一个 API 实例,从未提交的 `.env` 注入配置,强制清空一次性迁移 DSN,把容器内 8080 发布到宿主机 `127.0.0.1:16587`,并设置健康检查、只读文件系统、权限收紧和日志轮转。
- 现有 PostgreSQL/PostGIS 不进入 Compose;同宿主机数据库需要使用容器可达的宿主机地址,且仍需受 `listen_addresses`、`pg_hba.conf` 和防火墙约束。
- Nginx 示例增加 HTTP 到 HTTPS 跳转和 HTTP 429 JSON 限流响应,精确反代 `/chat`、`/chat/`、`/chat/app.css`、`/chat/app.js`、兼容 completion 和 `/mcp`,仍不比较、保存或注入 Chat、MCP、SuperAgent 或数据库 Secret。
- 运维手册记录 Git 前置条件、服务器目录、Secret 权限、Compose/Nginx 启停、页面开关、Chat/MCP 冒烟、SuperAgent 回调、更新和回滚。
- SuperAgent 无法在其配置中指定 MCP 协议版本;同一 SuperAgent 中 `th-hotel-simple-superagent` 已稳定启用/调用,现场实现不读取 `initialize.params.protocolVersion`,不读取或校验 `MCP-Protocol-Version` Header,并固定返回 `2025-06-18`。消防 MCP 按该 proven profile 兼容:版本字段和 Header 不作为拒绝门禁,响应固定为 `2025-06-18`。这不表示支持任意其他版本,也不是追求最新协议;日志只使用 `direct_success`/`compatibility_success`,不记录原始版本值。
已实现验收项:
- 镜像构建上下文排除 `.env`、密钥、证书、SQL、Excel、文档和本机构建产物;运行时镜像不包含 Go 工具链或源码。
- Compose 配置不包含明文 Secret,且没有数据库容器、数据卷或迁移命令;现有业务数据库不会被部署动作重建。
- Nginx 上游固定为宿主机回环地址,兼容 Chat SSE 禁用缓冲和自动重试,未列出路径固定 404。
- 目标机执行步骤包含不渲染 `.env` 内容的 Compose 检查、`nginx -t` 前置门禁和可恢复的配置替换。
- 目标服务器已开始部署:Nginx 配置语法检查和 Docker 镜像构建已通过;曾因 Chat 短凭证门禁发生重启,随后公网 MCP 请求已到达 Go,页面、容器稳定运行和独立 `16587/health` 通过证据仍待提供。
- SuperAgent 现场日志已证明公网 MCP 完成 `initialize`、`notifications/initialized`、`tools/list`,并于 2026-09-05 22:35 成功调用 `fire_safety_search_place_candidates`;这只证明地点候选工具成功,其他消防工具和完整多工具链仍待验收。
## 2. 当前优先级
1. 由用户审查本 checkpoint 变更后提交并推送 `origin/main`;服务器只部署明确提交的 revision。
2. 在 `/home/firee-safety-ymd` 重建 Compose,确认宿主机 16587 只绑定回环地址、容器内 8080 健康可达且数据库连接正常。
3. 页面测试保持 `FIRE_SAFETY_CHAT_PAGE_ENABLED=false` 默认边界;需要开启时配置 `https://agent.nianxx.com` 精确 Origin,重建容器并验证 `/chat` 308、`/chat/` 及 CSS/JavaScript 资源 200,以及关闭后的直接 404。
4. reload 已通过语法检查的 Nginx 配置,只公开 HTTPS 页面/资源、兼容路径、`/mcp` 和可选 `/health`。
5. 使用项目专属测试 Key 和已发布消防 Profile 做兼容 `completion` 首轮/多轮真实冒烟,核对最终回答、用量和会话复用;页面必须证明使用同一请求契约。
6. 使用 SuperAgent 无版本配置的实际握手重测:通过 `direct_success` 或 `compatibility_success` 日志确认请求已按 proven profile 处理,并验证 `initialize` → `notifications/initialized` → `tools/list` → 7 个工具的 `tools/call`;当前公网地点搜索的一次成功不替代完整链路,版本字段/Header 不需要配置,也不作为拒绝门禁。
7. 为所有查询表补齐适用 GiST 索引并验证查询计划;当前小数据可做联调,但生产前必须完成索引与并发验证。
8. 设计真实用户身份、动态角色/区域授权、共享会话、限流、Secret 轮换、指标和持久审计。
## 3. 已确认事实
- 仓库已初始化 Git,当前分支是 `main`,已有远程 `origin`;当前已推送基线为 `801c0af`。
- `docs/import/字段组.xlsx` 和 `docs/import/数据表映射.xlsx` 是用户已有、已暂存的变更,本 checkpoint 未修改。
- 用户已使用项目只读 probe 成功连接数据库 `fire_safety_ymd`;PostGIS 报告版本 `3.3 USE_GEOS=1 USE_PROJ=1 USE_STATS=1`,8 表合计 4,055 条记录。
- 实库 4,048 条非空几何已在 2026-09-05 的受控事务中从 SRID 0 补齐为 SRID 4326;坐标 extent 约为经度 121.16 至 121.93、纬度 37.07 至 37.49,类型与预期一致且未发现 WGS84 数值越界。数据提供方确认 8 表源数据均为 EPSG:4326 且无坐标偏移,严格 readiness 已通过。
- 实库包含 7 条空几何;防火网格 4 条、林区工矿企业 4 条、墓地坟区 27 条无效面几何。用户决定首版不修复原始几何,由 MCP 排除并明确告警。
- 实库仅防火网格表报告存在 GiST 几何索引,其余 7 张表尚未发现 GiST 几何索引。
- 防火通道首次重导在约第 256 条因 `Geometry has Z dimension but column does not` 中止,证明源数据混合二维与 Z 维度;用户将 `st_2_xianyoufanghuotongdao.geom` 改为不限定 typmod 的 `geometry` 后报告重导成功。原始列不得无审查改回二维 `geometry(GEOMETRY)`,也不得静默 `ST_Force2D` 丢弃 Z。
- 每张表的 2 条本地样例足以确定首版字段映射;样例不能证明全库质量或实时状态。
- 原始导出样例的 `geom` 列均声明为 `geometry(GEOMETRY)`,但该声明不兼容防火通道中的 Z 记录;防火通道实库导入列已按用户现场操作改为裸 `geometry`。数据提供方允许当前二维算法忽略 Z,但原始 Z 仍须保留,不能对原始表执行 `ST_Force2D`。
- Go module 当前使用临时名称 `fire-safety-ymd`,本地工具链为 Go `1.26.6`。
- 直接第三方依赖为 `github.com/jackc/pgx/v5 v5.10.0`;HTTP、JSON-RPC/MCP 和测试仍使用 Go 标准库。
- 服务默认监听 `:8080`;`GET /health` 仍只是 liveness,不访问外部依赖。
- 用户对话 API、SuperAgent Open API Adapter、默认关闭的 `/chat` 测试页面和 MCP endpoint 都默认关闭;Chat Bearer、页面手动输入的 xtoken、Open API Key 和 MCP Token 属于三个独立信任方向并禁止复用。
- `/api/chat` 已实现单进程内存会话映射、同会话并发 Run 冲突和严格 SSE 最终回答;模拟 Provider 端到端测试通过,真实 SuperAgent 尚未通过该入口联调。
- 可选兼容入口已实现截图所示路径、`xtoken`、`input.prompt/session_id` 和 `event: result` 外形;正文仍只在严格成功的 `stop` 事件中出现,不是 DashScope 全量 API。
- 可选 `/chat` 测试页面已纳入 Go 路由边界:页面开关默认关闭,关闭时 `/chat`、`/chat/`、`/chat/app.css`、`/chat/app.js` 均直接 404;开启时 `/chat` 返回 308 到 `/chat/`,`/chat/` 与 `/chat/app.css`、`/chat/app.js` 提供同源页面资源。页面不嵌入或持久化 Token,用户手动输入 xtoken,页面仅在内存中复用 `session_id`。
- `Dockerfile`、`.dockerignore` 和 `compose.yaml` 已建立测试部署基线;容器单实例运行,容器内 8080 仅发布到宿主机 `127.0.0.1:16587`,一次性迁移 DSN 在服务容器中强制为空。
- `deploy/nginx/fire-safety-ymd.conf.example` 已将公网页面/资源、兼容对话和 MCP 调用指向宿主机 `127.0.0.1:16587`,再由 Docker 映射到容器 8080;配置不保存或注入任何 Provider/Chat/MCP Secret。目标机仅 `nginx -t` 语法检查已通过,reload、页面资源和 HTTPS 实际响应尚未验证。
- 用户确认真实数据包含大量镇街,环境变量不适合枚举全量值;MCP 现支持显式数据库全范围 `all` 和默认镇街白名单 `town_allowlist` 两种服务端范围。
- 用户选择先实现简单地名能力、后续再优化;当前只查询既有森林防火记录,不调用外部地图服务,也不把候选代表点自动认定为演练点。
- 本地真实 MCP 冒烟已完成:7 个工具均成功访问实库,响应和错误边界符合契约;该结果不等于公网、SuperAgent 或生产并发已验证。
- SuperAgent 无法配置 MCP 协议版本;已稳定接通的 `th-hotel-simple-superagent` 作为 proven profile 参照,不读取/校验 initialize 版本字段或 `MCP-Protocol-Version` Header,并固定返回 `2025-06-18`。消防服务按该 profile 处理,版本字段/Header 不作为拒绝门禁;固定返回不等于支持任意其他版本,也不是追求最新协议。
- 防火通道现有字段不能支持可靠路线规划;防火网格现有字段不能支持实时队伍位置或正式集结点。
## 4. Known Issues 与未确认项
- 目标 PostgreSQL 的只读连接和 PostGIS 3.3 已由 probe 验证;数据库版本、TLS/网络生产拓扑、凭证轮换和审计仍未确认。
- 35 条无效面几何和 7 条空几何会被查询排除;尤其 4 条无效防火网格可能造成所属网格和责任中队结果缺口,27 条无效墓地面可能造成风险区域漏项。
- 除防火网格外 7 张表缺少 GiST 几何索引;当前 geography 距离表达式的生产索引方案需根据实库查询计划确认。
- 水源/设施 `syzt`、水源 `hc_datetime` 等字段的枚举、单位、时区和更新责任人尚未确认。
- SuperAgent MCP 的公网 URL 已有现场成功证据:Bearer、`Content-Type`、JSON-RPC 通过,随后记录了 `initialize`、`notifications/initialized`、`tools/list`,并于 2026-09-05 22:35 成功调用 `fire_safety_search_place_candidates`。同一 SuperAgent 的 th-hotel 服务已稳定调用,为 proven profile 参照;消防服务其余 6 个工具、完整多工具链、TLS、网络白名单和 Token 轮换仍待联调。是否发送协议 Header 不构成兼容阻塞。
- `/api/chat` 静态 Bearer、兼容路径 `xtoken` 和 `/chat` 测试页面只适用于受控联调,浏览器用户可以看到手动输入的 Token;页面默认关闭,开启时要求 `FIRE_SAFETY_CHAT_ALLOWED_ORIGINS` 包含精确 `https://agent.nianxx.com`。真实用户身份、动态授权、生产速率限制和滥用防护尚未实现。Chat 短凭证仅可通过默认关闭的显式 legacy 开关在受控测试/迁移窗口使用,MCP Token 仍要求至少 32 个可打印 ASCII 字符,三种凭证必须不同。
- 目标公网机器的 Docker/Compose 和 Nginx 版本、配置 include 层级、证书、DNS、安全组及 PostgreSQL 网络拓扑尚未完整验证;用户已开始远程部署,仓库资产与服务器现场配置仍需完成一致性核验。
- 目标服务器此前连续两次访问 `proxy.golang.org:443` 均在约 91 秒后超时,随后已通过可达的构建路径完成 Docker 镜像构建;该事实不代表所有外部 HTTPS 都可达。
- 目标机曾因已交付的 Chat 短凭证未通过默认配置门禁而反复重启;`801c0af` 已包含显式兼容方案。最新公网 `/mcp` 请求已完成地点候选工具调用,说明该启动阻塞已不再是当前首要问题;但独立 health、页面公网响应、兼容 Chat 多轮、MCP 其余工具和完整链路仍未形成通过证据。
- Chat 会话只在单个 Go 进程内存中保存;重启或多实例切换会丢失上下文,且当前没有历史查询、持久审计或主动取消 Provider Run。
- 当前 `all`/`town_allowlist` 都是服务账号静态范围,不是最终用户级授权;`all` 会授权当前数据库中 MCP 固定查询表内所有镇街和镇街字段为空的记录,身份提供方、角色、租户和精确位置权限尚未确定。
- 地名搜索是无索引的有界包含匹配;真实数据量下的耗时、重名率和名称字段质量尚未验证,生产优化可能需要标准地名表、别名词典或 `pg_trgm` 索引。
- 镇街或村庄名称可能匹配多条资源/网格;线面只返回只读计算的代表点,不能直接作为真实演练点。
- 只实现最小结构化运行日志,没有持久审计、指标、限流网关或数据源版本。
- 路线规划缺少路网拓扑、坡度、路面、宽度、车辆限制、封路、实时火场和天气数据。
- 队伍集结缺少正式集结点、实时定位、战备状态、人员/车辆/装备和容量数据。
- `fire-safety-ymd` 正式 module path 与 CI/部署 Go 版本仍待确认。
## 5. Next Checkpoint
建议:`fire-safety-ymd-container-public-entry-live-smoke-test`。
完成条件:
- 用户提交并推送本 checkpoint 后,目标机能在 `/home/firee-safety-ymd` clone 或 `git pull --ff-only` 到明确 revision。
- `docker compose build --pull`、`up -d` 和容器健康检查通过,宿主机 16587 只绑定 `127.0.0.1`,运行容器中没有迁移凭证。
- 在目标机替换安全 App ID、执行 `nginx -t` 后 reload,并验证 TLS、HTTP 到 HTTPS 跳转、429 和未列出路径 404。
- 验证页面开关关闭时 `/chat`、`/chat/`、`/chat/app.css` 和 `/chat/app.js` 均直接返回 404;开启并 recreate 后 `/chat` 返回 308、`/chat/` 返回 200,两个资源也返回 200;页面 Origin 白名单包含 `https://agent.nianxx.com`。
- 使用无敏感信息的问题验证兼容首轮 `null -> stop`、后续 `session_id` 复用、错误 xtoken、断流和超时。
- 使用浏览器测试页面手动输入静态测试 xtoken,确认页面不写入 Token、同源发送兼容 completion SSE,并在第二轮复用内存 `session_id`;页面不作为生产认证。
- 配置 SuperAgent 对公网 `/mcp` 的独立 Bearer,验证真实工具调用、TLS 和 warning 保留。
- 不在输出、命令历史、Nginx、镜像层或 Git 中记录任何 Secret。
## 6. 验证记录
- `gofmt -w ./cmd ./internal`:通过。
- `GOCACHE=/private/tmp/fire-safety-ymd-go-cache go test -count=1 ./...`:通过;新增覆盖页面开关依赖、开启/关闭路由、HTML/静态资源、安全响应头和应用装配;原有 Chat、SuperAgent、MCP/PostGIS 覆盖继续通过。
- `GOCACHE=/private/tmp/fire-safety-ymd-go-cache go vet ./...`:通过。
- `GOCACHE=/private/tmp/fire-safety-ymd-go-cache go test -race -count=1 ./...`:通过。
- `node --check internal/handler/chatpage/app.js`:通过。
- 本地真实浏览器:首轮兼容 SSE、第二轮 `session_id` 复用、错误凭证、Nginx 风格 429、未完成 SSE 断流后清会话、恶意 HTML 纯文本显示均通过;375px 视口无横向溢出,4 个按钮高度均为 44px,控制台无错误。
- `ruby -e 'require "yaml"; YAML.load_file("compose.yaml")'`:通过基础 YAML 解析;开发机没有 Docker CLI,未执行 `docker compose config --quiet`。
- Chat legacy 短凭证回归:默认仍拒绝少于 32 个字符的 Chat Token;仅在 `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=true` 时接受非空、最多 4096 字节且仅含 ASCII `0x21-0x7e` 的短值;原生与兼容入口均通过,MCP 至少 32 字符和三凭证分离门禁保持不变。
- 模拟 SuperAgent Chat 端到端:原生与兼容入口均通过;应用创建 Provider Session、发送消息、解析严格完成事件,原生返回 `conversation/message/done`,兼容入口返回 `result` 且最终 `finish_reason=stop`。
- Nginx:配置已完成静态检查且未包含真实 Secret;开发机未安装 Nginx。据用户截图,目标机 `nginx -t` 已通过,reload 和 HTTPS 实际响应尚未验证。
- 测试页面:已补齐默认关闭开关、`/chat` 规范化入口、`/chat/` HTML、`/chat/app.css`、`/chat/app.js`、同源兼容 SSE、手动 xtoken/内存 session_id 以及开启/关闭/回滚文档;Go 自动化测试、JavaScript 语法检查和本地真实浏览器首轮/续轮、错误凭证、429、断流、纯文本注入与 375px 移动端验收通过,公网静态资源和真实 SuperAgent 多轮仍待部署验收。
- Docker/Compose:部署文件已通过 YAML/静态安全断言;开发机未安装 Docker。目标机 Docker 镜像构建已成功,曾因 Chat 短凭证默认门禁反复重启;最新公网 `/mcp` 请求证明服务随后已恢复处理请求,但 `docker compose config --quiet`、独立 health 和完整公网链路仍待现场确认。
- 构建代理回归检查:修复前静态反馈命令返回 `RED: Docker build has no configurable GOPROXY`;修复后确认 Compose build arg、Dockerfile `GOPROXY` 和运行容器清空边界,返回 GREEN。
- `CGO_ENABLED=0 GOOS=linux go build -buildvcs=false -trimpath ./cmd/server`:通过,生成 Linux 静态服务二进制;不替代目标机真实 Docker build。
- 目标机首次 Docker 构建:失败;`go mod download` 获取 `github.com/jackc/pgpassfile@v1.0.0` 时连接 `proxy.golang.org:443` 超时,镜像/容器未生成,随后 `curl http://127.0.0.1:16587/health` 得到 connection refused,符合前置构建失败。
- 目标机后续 Docker 构建:成功;但 `docker compose logs --tail=100 api` 报 `FIRE_SAFETY_CHAT_AUTH_TOKEN must contain at least 32 printable ASCII characters`,容器因配置校验失败重启,故 `curl http://127.0.0.1:16587/health` 仍未形成通过证据。
- 目标机 Nginx:用户截图显示 `nginx -t` 配置语法检查成功;是否已 reload 以及 HTTPS 实际响应仍待确认。
- SuperAgent MCP 公网现场:用户提供的 2026-09-05 22:35 日志证明请求已到达 Go 服务,Bearer、`Content-Type` 和 JSON-RPC 校验通过,完成 initialize/notifications/tools-list 后,`fire_safety_search_place_candidates` 已返回 success。该证据只覆盖地点候选工具;数据库 readiness 虽已在本地通过,但其余 6 个工具和完整公网多工具链仍待验收。
- MCP proven-profile 回归:旧实现对 SuperAgent 请求返回截图中的 `MCP_PROTOCOL_VERSION_UNSUPPORTED`(RED);兼容实现对版本字段缺失、空值、非字符串、直接匹配或其他值均固定返回 `2025-06-18`,并覆盖带/不带/不同值 Header;同一 Handler 内的 `initialize` → `notifications/initialized` → `tools/list` → `tools/call` 回归通过,日志仅输出 `direct_success` 或 `compatibility_success`(GREEN)。公网完整链路仍待部署验证。
- SRID 迁移预检:通过;使用临时 `admin` 连接确认 4,048 条候选、8 表 UPDATE 权限和 7 表 ALTER 权限,未输出 DSN 或业务记录。
- SRID 数据迁移:通过;事务更新 4,048 条非空几何,7 张二维表改为 `geometry(Geometry,4326)`,防火通道保持裸 `geometry`,迁移前后几何载荷指纹与维度一致。
- 真实 PostGIS 严格 audit:通过;运行时只读账号报告 8 表 SRID 均为 4326、类型与范围门禁通过。预期保留 7 条空几何、35 条无效面几何和 7 张缺 GiST 索引表 warning。
- 本地真实 MCP 冒烟:通过;health、鉴权、Origin、方法限制、协议初始化、7 工具发现、7 tools/call、无结果、非法参数、响应一致性、字段脱敏、warning 和结构化日志均符合契约。第二轮实库查询约 0.35 至 0.99 秒。
- 数据导入:用户报告 8 份 SQL 已导入,防火通道在使用裸 `geometry` 保留混合二维/Z 后重导成功;这是现场反馈,不替代项目只读 probe 的最终验证。
- 真实 SuperAgent 对话与 MCP 联调:对话 API 仅完成模拟 Provider 端到端测试,真实消防 Profile 尚未完成 Chat 页面/多轮验收;MCP readiness 和本地真实工具冒烟已通过。公网 `/mcp` 已完成一次 `fire_safety_search_place_candidates`,但其余 6 个工具和完整多工具链尚待验证,不能宣称公网工具联调或部署完成。th-hotel 成功只证明同一 SuperAgent 的兼容档案可行,不替代消防 endpoint 的完整链路验收。
- 样例 SQL:未执行;含受限数据的 `*.sql` 已被 Git 忽略。
- Git:`801c0af`(Chat 门禁兼容修复)已由用户提交并推送到 `origin/main`;本次 MCP 协商变更尚未提交,未执行自动 commit/push。