6.9 KiB
6.9 KiB
Batch J Worker A:旧 HTTP proxy / page AI fallback 调用链审查
创建时间:2026-05-21
只读审查,不修改代码。
1. 读取过的关键文件
| 文件 | 行数 | 内容 |
|---|---|---|
rust/crates/mnote-web/src/routes/hermes_client.rs |
8202 | 首页面 AI Hermes 路由:session/run/tool 的 proxy 和 ACP 双路径 |
rust/crates/mnote-web/src/page_ai_workflow.rs |
— | 不存在(不在根目录,在 routes/ 下) |
rust/crates/mnote-web/src/routes/page_ai_workflow.rs |
500+ | 页面 AI block-edit fast path |
rust/crates/mnote-web/src/acp_runtime.rs |
500+ | ACP runtime manager |
rust/crates/mnote-web/src/acp_session_manager.rs |
700+ | ACP session lifecycle |
rust/crates/mnote-web/src/routes/mod.rs |
200+ | 全路线由注册 |
rust/crates/mnote-web/src/routes/compat.rs |
200+ | 旧 /api/ai-agent/run 退役 guard |
2. 调用链分析
2.1 旧 HTTP proxy 路径
判断函数: hermes_client.rs:2844 hermes_http_proxy_enabled()
fn hermes_http_proxy_enabled() -> bool {
["MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY", "MNOTE_ENABLE_HERMES_HTTP_PROXY"]
.into_iter().find_map(env_or_dotenv)
.map(|v| matches!(v.trim().to_ascii_lowercase(), "1" | "true" | "yes"))
.unwrap_or(false)
}
默认关闭(unwrap_or(false))。HTTP proxy 仅在显式设 MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY=true 时启用。
上游 URL 来源: configured_upstream()(hermes_client.rs:2813)
- 检查
MNOTE_WEB_HERMES_UPSTREAM_URL→MNOTE_HERMES_UPSTREAM_URL→MNOTE_HERMES_API_BASE_URL - 均不存在时返回
None
is_acp_profile() 路由逻辑(hermes_client.rs:2871):
fn is_acp_profile(profile: &str) -> bool {
if !hermes_http_proxy_enabled() { return true; } // 默认全走 ACP
if profile == "reasonix" || profile == "hermes" { return true; }
// 还可通过 MNOTE_WEB_{PROFILE}_RUNTIME_TYPE=acp 环境变量控制
...
}
2.2 各路由入口分类
| Route | Handler | HTTP proxy 条件 | 默认走 | 分类 |
|---|---|---|---|---|
GET /client/sessions |
list_sessions |
!is_acp_profile(profile) && configured_upstream_for_profile(profile).is_some() |
ACP | compat |
GET /client/sessions/search |
search_sessions |
同上 | ACP | compat |
GET /client/gateway/health |
gateway_health |
!is_acp_profile(&profile) → HTTP proxy |
ACP(对于非 ACP profile 走 HTTP proxy) | compat |
POST /client/runs |
create_run |
configured_upstream_for_profile(®istration.profile).is_some() |
ACP | compat |
GET /client/events/{run_id} |
stream_events |
先查 acp_runtime_for_run(),再 fallback configured_upstream_for_profile() |
ACP | compat |
POST /client/runs/{run_id}/abort |
abort_run |
同上 | ACP | compat |
GET /client/models |
list_models |
直接 configured_upstream() |
HTTP proxy(与 ACP 无关的 model 列表) | compat |
| 队列自动处理 | auto_start_next_queued_run(line 5377) |
configured_upstream_for_profile(queued.profile) |
ACP | compat |
POST /api/ai-agent/run |
compat::next_ai_agent_run |
— | 始终返回 410 GONE | retired |
POST /api/page-ai/block-edit-workflow |
page_ai_workflow::block_edit_workflow |
绕过 ACP,直接调模型 API + execute_mnote_tool_call |
debug-only fast path | debug-only |
2.3 page_ai_workflow 是否绕过 ACP / tool executor?
调用链:
POST /api/page-ai/block-edit-workflow
→ looks_like_block_edit(message) ← 中文关键词匹配
→ call_block_edit_model() ← 直调 DeepSeek/etc API(HTTP)
→ extract_markdown_plan_from_model_text()
→ 构造 ToolCallInput { tool_name: "mnote.doc.markdown_edit", capability_scope: ["block.write","page.write"], ... }
→ hermes_tools::execute_mnote_tool_call() ← 共享 tool executor
→ is_mnote_tool_disabled() & ensure_write_authorized() & 实际执行
结论:
- 不经过旧 HTTP proxy ✓ — 没有调用
configured_upstream_for_profile - 不经过 ACP session manager — 直接调用
execute_mnote_tool_call,没有session/new+session/prompt生命周期 - 但经过共享 tool executor — 受
is_mnote_tool_disabled()、ensure_write_authorized()、is_shared_read_scope()等守卫保护 - 设计意图:
7-15设计文档说明 "local-first 普通正文编辑默认不经过它",该路径是给合模型快速做 search/replace 块编辑的 fast path,不是常规 AI 执行路径
2.4 compat::next_ai_agent_run 状态
routes/compat.rs:10 的 next_ai_agent_run() 始终返回 410 GONE + 消息"旧 /api/ai-agent/run 页面 AI 主链已退场"。已确认退役,无 P0 风险。
3. 各入口分类汇总
| 分类 | 条目 | 说明 |
|---|---|---|
default |
无 | HTTP proxy 默认关闭;ACP 是默认 runtime |
compat |
list_sessions, search_sessions, gateway_health, create_run, stream_events, abort_run, list_models, 队列处理 |
仅当 MNOTE_WEB_ENABLE_HERMES_HTTP_PROXY=true 时激活;不影响正常用户 |
debug-only |
page_ai_workflow::block_edit_workflow |
bypass ACP session,但受工具 executor 守卫保护 |
retired |
compat::next_ai_agent_run |
已返回 410,无降级路径 |
未被调用 |
configured_runtime_for_profile()(line 3368) |
标记 #[allow(dead_code)] |
4. P0 缺口评估
当前没有 P0 必须修复的缺口。
理由:
- HTTP proxy 默认关闭(
hermes_http_proxy_enabled() = false) compat::next_ai_agent_run已正确返回 410page_ai_workflow经过共享 tool executor 守卫,不绕过权限检查- 所有路由的第一分支都是 ACP
5. 建议的后续修改范围
| 项 | 建议 | 紧急程度 |
|---|---|---|
| HTTP proxy 完整退役 | 删除 configured_upstream() / configured_upstream_for_profile() / proxy_json() 相关代码 |
P2(cleanup) |
list_models 从 HTTP proxy 迁移 |
改为 ACP 的 session/initialize 返回 model 信息 |
P2(依赖 ACP 协议增强) |
page_ai_workflow 检查驱动 |
确保测试覆盖其 bypass ACP session 的行为的非期望路径(task-page-block-ai-smoke.js → 已存在) |
已有 |
| 旧环境变量清理 | 文档标记 MNOTE_WEB_HERMES_UPSTREAM_URL 等为退役 |
P2 |
6. 建议测试
# 确认 HTTP proxy 默认关闭(infra 测试)
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web hermes_http_proxy -- --test-threads=1
# 确认 compat 路由返回 410(已存在)
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web direct_ai_agent_run_returns_legacy_retired_guard -- --test-threads=1
# 确认 page_ai_workflow 受守卫保护(已存在)
cd /mnt/Data1T/mnote/rust && cargo test -p mnote-web block_edit_workflow_respects_disabled_markdown_edit_tool -- --test-threads=1