Skip to content

Commit 5278ce1

Browse files
docs: 新增两份文档
1 parent e74c009 commit 5278ce1

3 files changed

Lines changed: 294 additions & 0 deletions

File tree

docs/features/web-search-tool.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# WEB_SEARCH_TOOL — 网页搜索工具
2+
3+
> 实现状态:适配器架构完成,Bing 适配器为当前默认后端
4+
> 引用数:核心工具,无 feature flag 门控(始终启用)
5+
6+
## 一、功能概述
7+
8+
WebSearchTool 让模型可以搜索互联网获取最新信息。原始实现仅支持 Anthropic API 服务端搜索(`web_search_20250305` server tool),在第三方代理端点下不可用。现已重构为适配器架构,新增 Bing 搜索页面解析作为 fallback,确保任何 API 端点都能使用搜索功能。
9+
10+
## 二、实现架构
11+
12+
### 2.1 适配器模式
13+
14+
```
15+
WebSearchTool.call()
16+
17+
18+
createAdapter() ← 适配器工厂
19+
20+
├── ApiSearchAdapter — Anthropic 官方 API 服务端搜索
21+
│ └── 使用 web_search_20250305 server tool
22+
│ 通过 queryModelWithStreaming 二次调用 API
23+
24+
└── BingSearchAdapter — Bing HTML 抓取 + 正则提取(当前默认)
25+
└── 直接抓取 Bing 搜索页 HTML
26+
正则提取 b_algo 块中的标题/URL/摘要
27+
```
28+
29+
### 2.2 模块结构
30+
31+
| 模块 | 文件 | 说明 |
32+
|------|------|------|
33+
| 工具入口 | `src/tools/WebSearchTool/WebSearchTool.ts` | `buildTool()` 定义:schema、权限、执行、输出格式化 |
34+
| 工具 prompt | `src/tools/WebSearchTool/prompt.ts` | 搜索工具的系统提示词 |
35+
| UI 渲染 | `src/tools/WebSearchTool/UI.tsx` | 搜索结果的终端渲染组件 |
36+
| 适配器接口 | `src/tools/WebSearchTool/adapters/types.ts` | `WebSearchAdapter` 接口、`SearchResult`/`SearchOptions`/`SearchProgress` 类型 |
37+
| 适配器工厂 | `src/tools/WebSearchTool/adapters/index.ts` | `createAdapter()` 工厂函数,选择后端 |
38+
| API 适配器 | `src/tools/WebSearchTool/adapters/apiAdapter.ts` | 封装原有 `queryModelWithStreaming` 逻辑,使用 server tool |
39+
| Bing 适配器 | `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 抓取 + 正则解析 |
40+
| 单元测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 32 个测试用例 |
41+
| 集成测试 | `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 真实网络请求验证 |
42+
43+
### 2.3 数据流
44+
45+
```
46+
模型调用 WebSearchTool(query, allowed_domains, blocked_domains)
47+
48+
49+
validateInput() — 校验 query 非空、allowed/block 不共存
50+
51+
52+
createAdapter() → BingSearchAdapter(当前硬编码)
53+
54+
55+
adapter.search(query, { allowedDomains, blockedDomains, signal, onProgress })
56+
57+
├── onProgress({ type: 'query_update', query })
58+
59+
├── axios.get(bing.com/search?q=...&setmkt=en-US)
60+
│ └── 13 个 Edge 浏览器请求头
61+
62+
├── extractBingResults(html) — 正则提取 <li class="b_algo"> 块
63+
│ ├── resolveBingUrl() — 解码 base64 重定向 URL
64+
│ ├── extractSnippet() — 三级降级摘要提取
65+
│ └── decodeHtmlEntities() — he.decode
66+
67+
├── 客户端域名过滤 (allowedDomains / blockedDomains)
68+
69+
├── onProgress({ type: 'search_results_received', resultCount })
70+
71+
72+
格式化为 markdown 链接列表返回给模型
73+
```
74+
75+
## 三、Bing 适配器技术细节
76+
77+
### 3.1 反爬绕过
78+
79+
使用 13 个 Edge 浏览器请求头(含 `Sec-Ch-Ua``Sec-Fetch-*` 等),避免 Bing 返回 JS 渲染的空页面:
80+
81+
```typescript
82+
const BROWSER_HEADERS = {
83+
'User-Agent': '...Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0',
84+
'Sec-Ch-Ua': '"Microsoft Edge";v="131", "Chromium";v="131", ...',
85+
'Sec-Fetch-Dest': 'document',
86+
'Sec-Fetch-Mode': 'navigate',
87+
'Sec-Fetch-Site': 'none',
88+
'Sec-Fetch-User': '?1',
89+
// ... 共 13 个标头
90+
}
91+
```
92+
93+
`setmkt=en-US` 参数强制美式英语市场,避免 IP 地理定位导致区域化结果。
94+
95+
### 3.2 URL 解码(`resolveBingUrl()`
96+
97+
Bing 返回的重定向 URL 格式:`bing.com/ck/a?...&u=a1aHR0cHM6Ly9...`
98+
99+
- `u` 参数前 2 字符为协议前缀:`a1` = https,`a0` = http
100+
- 剩余部分为 base64url 编码的真实 URL
101+
- Bing 内部链接和相对路径被过滤返回 `undefined`
102+
103+
### 3.3 摘要提取(`extractSnippet()`
104+
105+
三级降级策略:
106+
107+
1. `<p class="b_lineclamp...">` — Bing 的搜索摘要段落
108+
2. `<div class="b_caption">` 内的 `<p>` — 备选摘要位置
109+
3. `<div class="b_caption">` 直接文本 — 最终 fallback
110+
111+
### 3.4 域名过滤
112+
113+
客户端侧实现,支持子域名匹配:
114+
- `allowedDomains`:白名单,结果域名必须匹配列表中的某项(含子域名)
115+
- `blockedDomains`:黑名单,匹配的结果被过滤
116+
- 两者不可同时使用(`validateInput` 校验)
117+
118+
## 四、适配器选择逻辑
119+
120+
当前 `createAdapter()` 硬编码返回 `BingSearchAdapter`,原逻辑已注释保留:
121+
122+
```typescript
123+
export function createAdapter(): WebSearchAdapter {
124+
return new BingSearchAdapter()
125+
// 注释保留的选择逻辑:
126+
// 1. WEB_SEARCH_ADAPTER 环境变量强制指定 api|bing
127+
// 2. isFirstPartyAnthropicBaseUrl() → API 适配器
128+
// 3. 第三方端点 → Bing 适配器
129+
}
130+
```
131+
132+
恢复自动选择:取消 `index.ts` 中的注释即可。
133+
134+
## 五、接口定义
135+
136+
### WebSearchAdapter
137+
138+
```typescript
139+
interface WebSearchAdapter {
140+
search(query: string, options: SearchOptions): Promise<SearchResult[]>
141+
}
142+
143+
interface SearchResult {
144+
title: string
145+
url: string
146+
snippet?: string
147+
}
148+
149+
interface SearchOptions {
150+
allowedDomains?: string[]
151+
blockedDomains?: string[]
152+
signal?: AbortSignal
153+
onProgress?: (progress: SearchProgress) => void
154+
}
155+
156+
interface SearchProgress {
157+
type: 'query_update' | 'search_results_received'
158+
query?: string
159+
resultCount?: number
160+
}
161+
```
162+
163+
### 工具 Input Schema
164+
165+
```typescript
166+
{
167+
query: string // 搜索关键词,最少 2 字符
168+
allowed_domains?: string[] // 域名白名单
169+
blocked_domains?: string[] // 域名黑名单
170+
}
171+
```
172+
173+
## 六、文件索引
174+
175+
| 文件 | 职责 |
176+
|------|------|
177+
| `src/tools/WebSearchTool/WebSearchTool.ts` | 工具定义入口 |
178+
| `src/tools/WebSearchTool/prompt.ts` | 搜索工具 prompt |
179+
| `src/tools/WebSearchTool/UI.tsx` | 终端 UI 渲染 |
180+
| `src/tools/WebSearchTool/adapters/types.ts` | 适配器接口 |
181+
| `src/tools/WebSearchTool/adapters/index.ts` | 适配器工厂 |
182+
| `src/tools/WebSearchTool/adapters/apiAdapter.ts` | API 服务端搜索适配器 |
183+
| `src/tools/WebSearchTool/adapters/bingAdapter.ts` | Bing HTML 解析适配器 |
184+
| `src/tools/WebSearchTool/__tests__/bingAdapter.test.ts` | 单元测试 (32 cases) |
185+
| `src/tools/WebSearchTool/__tests__/bingAdapter.integration.ts` | 集成测试 |
186+
| `src/tools.ts` | 工具注册 |

docs/internals/sentry-setup.mdx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
---
2+
title: "自定义 Sentry 错误上报配置"
3+
description: "通过环境变量连接自托管或 Cloud Sentry,实现 CLI 运行时的错误捕获与上报。不配置则完全静默。"
4+
keywords: ["sentry", "错误上报", "监控", "DSN", "自托管"]
5+
---
6+
7+
## 概述
8+
9+
Claude Code 支持通过 Sentry 捕获运行时异常并上报到你自己指定的 Sentry 实例。
10+
11+
- **配置了 `SENTRY_DSN`**:自动初始化 Sentry SDK,捕获未处理异常和关键错误
12+
- **未配置**:所有 Sentry 调用均为 no-op,零开销
13+
14+
## 环境变量
15+
16+
| 变量 | 必填 | 说明 |
17+
|---|---|---|
18+
| `SENTRY_DSN` || Sentry 项目 DSN,如 `https://xxx@o123456.ingest.sentry.io/789` |
19+
20+
只需要这一个变量,设置后即启用。
21+
22+
## 使用方式
23+
24+
### 自托管 Sentry
25+
26+
```bash
27+
SENTRY_DSN=https://public_key@your-sentry.example.com/123 \
28+
bun run dev
29+
```
30+
31+
### Sentry Cloud (SaaS)
32+
33+
```bash
34+
SENTRY_DSN=https://public_key@o123456.ingest.sentry.io/789 \
35+
bun run dev
36+
```
37+
38+
### 不使用 Sentry(默认行为)
39+
40+
```bash
41+
bun run dev
42+
# SENTRY_DSN 未设置,所有 sentry 函数为 no-op
43+
```
44+
45+
## Sentry 服务端配置
46+
47+
### 步骤
48+
49+
1. **部署 Sentry 实例**(Docker 自托管 或 使用 [sentry.io](https://sentry.io) Cloud)
50+
2. **创建 Project**,选择 **Node.js** 平台
51+
3. 获取项目的 **DSN**(Settings → Projects → Client Keys → DSN)
52+
4. 将 DSN 设置为 `SENTRY_DSN` 环境变量
53+
54+
## 功能详情
55+
56+
### 错误捕获
57+
58+
- **自动捕获**`SentryErrorBoundary` 包裹关键 React 组件,捕获渲染错误
59+
- **手动上报**`errorLogSink` 在写入错误日志时同步上报到 Sentry
60+
- **优雅关闭**:进程退出时 `closeSentry()` 确保事件发送完毕(2s 超时)
61+
62+
### 安全过滤
63+
64+
`beforeSend` 钩子会自动剥离以下敏感 header:
65+
66+
- `authorization`
67+
- `x-api-key`
68+
- `cookie`
69+
- `set-cookie`
70+
71+
### 忽略的错误类型
72+
73+
以下错误模式会被忽略,不会上报:
74+
75+
| 错误 | 原因 |
76+
|---|---|
77+
| `ECONNREFUSED` / `ECONNRESET` / `ENOTFOUND` / `ETIMEDOUT` | 网络不可达,不可操作 |
78+
| `AbortError` / `The user aborted a request` | 用户主动取消 |
79+
| `CancelError` | 交互式取消信号 |
80+
81+
### 其他配置
82+
83+
- **采样率**`sampleRate: 1.0`(捕获全部错误事件)
84+
- **面包屑上限**`maxBreadcrumbs: 20`(控制 payload 体积)
85+
- **性能事务**:已关闭(`beforeSendTransaction` 返回 `null`),仅上报错误
86+
87+
## API
88+
89+
| 函数 | 说明 |
90+
|---|---|
91+
| `initSentry()` | 初始化 SDK,在 `src/entrypoints/init.ts` 中自动调用 |
92+
| `captureException(error, context?)` | 手动上报异常,可附加额外上下文 |
93+
| `setTag(key, value)` | 设置标签,用于 Sentry 面板分组过滤 |
94+
| `setUser({ id, email, username })` | 设置用户上下文,用于错误归因 |
95+
| `closeSentry(timeoutMs?)` | 刷出队列并关闭客户端,进程退出时调用 |
96+
| `isSentryInitialized()` | 检查是否已初始化 |
97+
98+
## 实现文件
99+
100+
| 文件 | 说明 |
101+
|---|---|
102+
| `src/utils/sentry.ts` | 核心 SDK 初始化与封装 |
103+
| `src/components/SentryErrorBoundary.ts` | React Error Boundary 组件 |
104+
| `src/utils/errorLogSink.ts` | 错误日志 sink,集成 `captureException` |
105+
| `src/utils/gracefulShutdown.ts` | 优雅退出,调用 `closeSentry()` |
106+
| `src/entrypoints/init.ts` | 启动时调用 `initSentry()` |

mint.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@
106106
"docs/internals/three-tier-gating",
107107
"docs/internals/feature-flags",
108108
"docs/internals/growthbook-ab-testing",
109+
"docs/internals/growthbook-adapter",
110+
"docs/internals/sentry-setup",
109111
"docs/internals/hidden-features",
110112
"docs/internals/ant-only-world"
111113
]

0 commit comments

Comments
 (0)