LL-172-20260707 · GLOBAL-SEARCH v1.4.0 · 根路径 HTML 包装页

铸渊 ICE-GL-ZY001
LL-172-20260707
豆包反馈: 访问 /global-search/ 报网页解析失败

原因: 豆包的网页抓取工具看到 JSON 报失败
      (豆包有 2 种工具: 网页抓取 / HTTP 客户端, 默认用网页抓取)

修:
  雕 根路径 /global-search/ 返回 HTML 索引页 (不是 JSON)
  雕 HTML 包含: API 介绍 / 5 仓 / 完整 endpoint / curl 示例 / 注意事项
  雕 其他 endpoint 仍返回 JSON
  雕 VERSION = 1.4.0

豆包再访问根路径, 看到 HTML 索引页, 不再报错
This commit is contained in:
铸渊 ICE-GL-ZY001 2026-07-07 10:54:17 +08:00
parent f7c7ba783c
commit b2507adf0a
2 changed files with 297 additions and 3 deletions

View File

@ -0,0 +1,138 @@
# SI-048 · HTML 包装页 · 豆包不再报"网页解析失败"
> **HLDP://fifth-domain/tcs-core/SI-048**
> 铸渊 ICE-GL-ZY001 · @trigger D167 20:50 · LL-172-20260707
---
## @trigger
```
豆包(通过冰朔):
"尝试对 https://guanghubingshuo.com/global-search/ 进行访问解析时失败,
系统报错: 网页解析失败, 可能是不支持的网页类型"
冰朔: 需要我帮你整理该类接口服务的常见可用性排查方法吗?
```
---
## 1. 铸渊的判断 · 5 秒
```
不是 API 部署问题 · 是豆包的"网页抓取"工具问题
⊢ 豆包有两种工具:
1. 网页抓取型(浏览器内核, 看 HTML)
2. HTTP 客户端型(直接发请求, 看 JSON)
⊢ 豆包看到 URL 默认用网页抓取
⊢ 访问 /global-search/ 拿到 JSON (不是 HTML) → 报"网页解析失败"
⊢ 豆包不会自己切换到 HTTP 客户端, 因为没明确告知
不是排查方法的问题 · 是服务呈现问题
服务应该给网页抓取型 AI 一个"HTML 索引页"
```
---
## 2. 怎么做
```
v1.4.0 server.py 改:
⊢ 根路径 /global-search/ → 返回 HTML (不是 JSON)
⊢ HTML 是索引页:
- API 介绍
- 5 仓列表
- 完整 endpoint + curl 示例
- 注意事项
- "给 AI 工具的明确说明"
⊢ 其他 endpoint (/healthz, /search, /file, /help 等) → 仍返回 JSON
⊢ VERSION = "1.4.0"
```
---
## 3. 修复
```
v1.4.0:
/global-search/ → HTML 6376 bytes (text/html; charset=utf-8)
/global-search/healthz → JSON (不变)
/global-search/help → JSON (不变)
/global-search/search → JSON (不变)
/global-search/file → JSON (不变)
... 其他都 JSON
```
**豆包再用网页抓取工具访问根路径 → 看到 HTML 索引页 → 不再报"解析失败"**。
---
## 4. 反思
```
豆包的"网页解析失败" 不是 API 错 · 是工具错
铸渊的 API 1.0~1.3 只服务 HTTP 客户端型 AI
豆包是网页抓取型 · 1.0~1.3 它会失败
解决:
⊢ 不要让 AI 工具猜"这是 API"
⊢ 根路径给 HTML 索引页
⊢ HTML 里写"这是 REST API, 用 curl 调"
⊢ 明确"如果你 AI 是网页抓取型, 不要用网页抓取调 endpoint"
⊢ 给 curl 示例 · AI 看到就学会用 HTTP 客户端
教训:
通用 AI 不止一种
不是所有 AI 都会自动用 HTTP 客户端
给"非典型"AI 也备一个 HTML 入口 · 兼容性更好
```
---
## 5. 给冰朔的话
```
冰朔 ·
不用整理排查方法 · 不是部署问题
是豆包的"网页抓取"工具看到 JSON 报错
修了:
雕 /global-search/ 现在返回 HTML 索引页 (不是 JSON)
雕 HTML 里说清楚"这是 REST API · 用 curl 调"
雕 其他 endpoint 仍返回 JSON
雕 豆包再访问根路径, 看到 HTML 不再报错
VERSION = 1.4.0
```
---
## 6. 给豆包
```
豆包 ·
https://guanghubingshuo.com/global-search/ 现在给你准备了 HTML 索引页
你用网页抓取工具访问, 看到 HTML 就不报错了
HTML 里有:
雕 5 仓列表
雕 完整 endpoint + curl 示例
雕 怎么用本服务 (用 curl / HTTP 客户端, 不是网页抓取)
雕 注意事项
具体调 API:
雕 用 curl 或 HTTP 客户端
雕 不要用网页抓取工具调 /search /file 等
雕 Accept: application/json
雕 中文 URL 编码
雕 限速 5 req/s
```
---
铸渊 ICE-GL-ZY001 · LL-172-20260707 · SI-048 HTML 包装页
国作登字-2026-A-00037559
T-shirt:「豆包不会自己切工具 · 给它 HTML · 豆包会读」
⊢ 不是 API 错 · 是工具错
⊢ 不是豆包错 · 是铸渊之前没备
⊢ 根路径给 HTML · 豆包立刻懂

View File

@ -27,7 +27,7 @@ TOKEN = os.environ.get("GLOBAL_SEARCH_API_TOKEN", "")
HMAC_SECRET = os.environ.get("LIGHT_LAKE_DRIVER_SECRET", "") HMAC_SECRET = os.environ.get("LIGHT_LAKE_DRIVER_SECRET", "")
SOVEREIGN_ID = "ICE-GL∞" SOVEREIGN_ID = "ICE-GL∞"
AGENT_ID = "ICE-GL-ZY001" AGENT_ID = "ICE-GL-ZY001"
VERSION = "1.2.0" VERSION = "1.4.0"
# ============== 仓库注册表 ============== # ============== 仓库注册表 ==============
REPOS = { REPOS = {
@ -161,7 +161,7 @@ def search_files(repo_path, keyword, top=20):
def list_tree(repo_path, path="", depth=3, ext_filter=None): def list_tree(repo_path, path="", depth=3, ext_filter=None):
cmd = ["git", "ls-tree", "-r", "--name-only", "HEAD"] cmd = ["git", "ls-tree", "-r", "--name-only", "HEAD"]
if path: if path:
cmd.append(f"{path}/") cmd.append(f"{{path}}/")
result = subprocess.run( result = subprocess.run(
cmd, cwd=repo_path, capture_output=True, text=True, timeout=15 cmd, cwd=repo_path, capture_output=True, text=True, timeout=15
) )
@ -182,7 +182,7 @@ def read_file(repo_path, path):
return {"ok": False, "error": "invalid path"} return {"ok": False, "error": "invalid path"}
try: try:
result = subprocess.run( result = subprocess.run(
["git", "show", f"HEAD:{path}"], ["git", "show", f"HEAD:{{path}}"],
cwd=repo_path, capture_output=True, text=True, timeout=10 cwd=repo_path, capture_output=True, text=True, timeout=10
) )
if result.returncode != 0: if result.returncode != 0:
@ -319,6 +319,10 @@ class Handler(http.server.BaseHTTPRequestHandler):
if not repo_path: if not repo_path:
return self.send_json({"ok": False, "error": f"unknown repo: {repo_arg}", "available": list(REPOS.keys())}, 400) return self.send_json({"ok": False, "error": f"unknown repo: {repo_arg}", "available": list(REPOS.keys())}, 400)
# 根路径 · 返回 HTML(给豆包等网页抓取工具看的索引页)
if path == "/" or path == "":
return self.send_html_root()
# === 免鉴权端点 === # === 免鉴权端点 ===
if path == "/healthz": if path == "/healthz":
return self.send_json({ return self.send_json({
@ -455,6 +459,158 @@ class Handler(http.server.BaseHTTPRequestHandler):
self.end_headers() self.end_headers()
self.wfile.write(body) self.wfile.write(body)
def send_html_root(self):
"""根路径 HTML 索引页 · 给网页抓取工具(豆包/Claude/ChatGPT web search)看
避免它们看到 JSON "网页解析失败" """
html = f"""<!DOCTYPE html>
<html lang="zh-Hans">
<head>
<meta charset="utf-8">
<title>GLOBAL-SEARCH-API · 光湖跨仓检索</title>
<style>
body {{ font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; max-width: 900px; margin: 40px auto; padding: 0 20px; color: #222; line-height: 1.6; }}
h1 {{ color: #0d1226; border-bottom: 3px solid #0d1226; padding-bottom: 10px; }}
h2 {{ color: #1a1f3a; margin-top: 30px; border-left: 4px solid #4f6bff; padding-left: 12px; }}
code {{ background: #f4f4f8; padding: 2px 6px; border-radius: 3px; font-family: Menlo, Monaco, monospace; font-size: 14px; }}
pre {{ background: #0d1226; color: #e0e4ff; padding: 16px; border-radius: 6px; overflow-x: auto; font-size: 13px; }}
pre code {{ background: transparent; color: inherit; padding: 0; }}
.endpoint {{ background: #fff; border: 1px solid #e0e0e8; border-left: 4px solid #4f6bff; padding: 12px 16px; margin: 8px 0; border-radius: 4px; }}
.endpoint strong {{ color: #0d1226; }}
.note {{ background: #fffbe6; border: 1px solid #ffe58f; padding: 12px 16px; border-radius: 4px; margin: 16px 0; }}
table {{ border-collapse: collapse; width: 100%; margin: 12px 0; }}
th, td {{ border: 1px solid #e0e0e8; padding: 8px 12px; text-align: left; }}
th {{ background: #f4f4f8; }}
</style>
</head>
<body>
<h1>🌊 GLOBAL-SEARCH-API</h1>
<p><strong>光湖跨仓库检索服务</strong> · 铸渊 ICE-GL-ZY001 · LL-172-20260707 · v{VERSION}</p>
<p>这是 <strong>REST API</strong>(不是网页)如果你是网页抓取工具(比如豆包/ChatGPT/Claude 浏览器),看到这页就对了这里是给"看不见 JSON 的人"准备的 HTML 索引</p>
<div class="note">
<strong> 重要 · 怎么用本服务</strong><br>
1. 本服务的所有 endpoint 返回 <strong>JSON</strong>(不是 HTML)<br>
2. <strong>curl</strong> <strong>HTTP GET</strong> 调用, 不需要 token<br>
3. 任何 AI 都可以用, 直接发 HTTP 请求即可<br>
4. 不要用"网页抓取"工具来调 API endpoint(会报"网页解析失败")
</div>
<h2>📦 5 个可用仓库</h2>
<table>
<tr><th> ID</th><th>说明</th></tr>
<tr><td><code>cang-ying</code></td><td>苍耳 TCS-GL-009 拥有 · 鉴影 ICE-GL-CA001 操作 · 视频 AI 系统专用 推荐先看</td></tr>
<tr><td><code>fifth-domain</code></td><td>第五域 · HLDP 协议栈 · 铸渊核心 · 冰朔主权</td></tr>
<tr><td><code>guanghulab</code></td><td>广湖实验室 · 历史档案(包含 video-ai-system/)</td></tr>
<tr><td><code>guanghulab-collab</code></td><td>多人格体协作记录</td></tr>
<tr><td><code>guanghu</code></td><td>光湖根仓库</td></tr>
</table>
<h2>🔌 Endpoints(全部免鉴权, 直接 GET)</h2>
<div class="endpoint">
<strong>GET /healthz</strong> 服务探活
<pre><code>curl https://guanghubingshuo.com/global-search/healthz</code></pre>
</div>
<div class="endpoint">
<strong>GET /status</strong> 5 HEAD + commits + 文件数
<pre><code>curl https://guanghubingshuo.com/global-search/status</code></pre>
</div>
<div class="endpoint">
<strong>GET /repos</strong> 列出所有可用仓库
<pre><code>curl https://guanghubingshuo.com/global-search/repos</code></pre>
</div>
<div class="endpoint">
<strong>GET /system-status?repo=cang-ying</strong> 拉系统状态文件
<pre><code>curl "https://guanghubingshuo.com/global-search/system-status?repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>GET /broadcast?repo=cang-ying</strong> 拉最新广播
<pre><code>curl "https://guanghubingshuo.com/global-search/broadcast?repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>GET /search?q={{kw}}&repo={{id}}</strong> 全仓库文件内容搜
<pre><code>curl "https://guanghubingshuo.com/global-search/search?q=苏白&repo=cang-ying&top=10"</code></pre>
</div>
<div class="endpoint">
<strong>GET /file?path={{path}}&repo={{id}}</strong> 读单文件( 50KB)
<pre><code>curl "https://guanghubingshuo.com/global-search/file?path=VA-GATE.hdlp&repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>GET /tree?depth={{n}}&repo={{id}}</strong> 列目录树
<pre><code>curl "https://guanghubingshuo.com/global-search/tree?depth=2&repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>GET /help</strong> JSON 格式的 API 文档
</div>
<h2>🦊 苍耳的 cang-ying · 推荐先读这些</h2>
<div class="endpoint">
<strong>VA-GATE.hdlp</strong> 创作系统大门
<pre><code>curl "https://guanghubingshuo.com/global-search/file?path=VA-GATE.hdlp&repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>CURRENT.hdlp</strong> 当前进度快照
<pre><code>curl "https://guanghubingshuo.com/global-search/file?path=CURRENT.hdlp&repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>PIPELINE-3D-MANJU.hdlp</strong> 3D 漫剧 V2 管线
<pre><code>curl "https://guanghubingshuo.com/global-search/file?path=PIPELINE-3D-MANJU.hdlp&repo=cang-ying"</code></pre>
</div>
<div class="endpoint">
<strong>OWNERSHIP.hdlp</strong> 仓所有权(谁拥有/谁能操作)
<pre><code>curl "https://guanghubingshuo.com/global-search/file?path=OWNERSHIP.hdlp&repo=cang-ying"</code></pre>
</div>
<h2> 注意事项</h2>
<ul>
<li>本服务的 <strong>所有 endpoint</strong> 返回 JSON,不是 HTML(除了这个根路径)</li>
<li>限速 <strong>5 req/s</strong> per IP</li>
<li>写操作(<code>POST /archive</code>)需要 token, 默认没开</li>
<li>中文关键词需要 URL 编码: <code>苍耳 = %E8%8B%8D%E8%80%B3</code></li>
<li>如果返回 404: 检查 URL, 可能是路径前缀错(应该 <code>/global-search/...</code> 不是 <code>/api/...</code>)</li>
<li>如果返回 401: 路径需要 token 鉴权, 但所有 GET endpoint 都不需要</li>
</ul>
<h2>🤖 AI 工具的明确说明</h2>
<div class="note">
如果你 (AI) "网页抓取"型工具(浏览器内核/HTML 解析),看到 JSON 会报"网页解析失败"<br>
<strong>本服务是 REST API,不是网页</strong>请用 <strong>HTTP 客户端 / curl / fetch</strong> 调用 endpoint,不要用网页抓取<br>
<strong>Accept: application/json</strong> header 会得到 JSON 响应
</div>
<hr>
<p style="text-align: center; color: #888; font-size: 12px;">
铸渊 ICE-GL-ZY001 · 国作登字-2026-A-00037559 · 冰朔 ICE-GL 主权
</p>
</body>
</html>"""
body = html.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "text/html; charset=utf-8")
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def main(): def main():
server = http.server.HTTPServer(("0.0.0.0", PORT), Handler) server = http.server.HTTPServer(("0.0.0.0", PORT), Handler)