feat: converge dynamic plugins onto canonical homeagent-sdk

- Separate built-in plugin interface from external plugin interface
- Route dynamic plugin loading through homeagent-sdk/sdk using reflection
- Turn internal/sdk into an enhanced wrapper over canonical SDK types
- Vendor SDK repo snapshot under third_party/homeagent-sdk for stable builds
- Keep internal constructors/adapters for memory, knowledge, llm, settings
- Align dynamic QQ loading with canonical SDK chain
This commit is contained in:
root
2026-07-06 19:27:51 +08:00
parent a7e06fba77
commit 50e5dec745
37 changed files with 4505 additions and 420 deletions

View File

@ -0,0 +1,94 @@
# web 插件讲解
网络工具插件,提供网页搜索和内容抓取功能。
## 工具清单
| 工具 | 功能 | 源码 |
|------|------|------|
| `web_search` | 通过 DuckDuckGo 搜索网页 | `handleSearch` |
| `web_fetch` | 抓取指定 URL 的内容 | `handleFetch` |
## 核心设计
### 搜索实现
`web_search` 使用 DuckDuckGo 的 HTML 搜索页面(非 API,免注册):
```go
// plugin.go:handleSearch
url := fmt.Sprintf("https://html.duckduckgo.com/html/?q=%s", url.QueryEscape(query))
resp, err := p.httpClient().Get(url)
```
解析策略:扫描 HTML 查找 `<a class="result__a"` 标签提取标题和链接,查找 `<a class="result__snippet"` 提取摘要。搜索最多返回 8 条结果。
由于 DuckDuckGo 在国内被墙,支持通过代理访问(见配置项)。
### 页面抓取
`web_fetch` 直接 HTTP GET 目标 URL 并以文本形式返回内容:
```go
// plugin.go:handleFetch
resp, err := p.httpClient().Get(rawURL)
body, _ := io.ReadAll(resp.Body)
```
返回内容限制最大 100KB,超过截断并提示。
### SSRF 防护
`handleFetch` 检查 URL 是否为内网 IP,防止服务端请求伪造攻击:
```go
// plugin.go:handleFetch — SSRF guard
if !strings.HasPrefix(parsedURL, "http") {
return errorResult("only http/https allowed"), nil
}
// 内网 IP 段检查(127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
```
### HTTP 客户端
`newHTTPClient()` 创建带超时和可选代理的 HTTP 客户端:
```go
// plugin.go:newHTTPClient
func (p *Plugin) newHTTPClient() *http.Client {
transport := &http.Transport{}
if p.proxy != "" {
proxyURL, _ := url.Parse(p.proxy)
transport.Proxy = http.ProxyURL(proxyURL)
}
return &http.Client{
Timeout: time.Duration(p.timeout) * time.Second,
Transport: transport,
}
}
```
超时和代理均在配置中设置。
### 配置读取
```go
// plugin.go:Start
p.timeout = getSetting[float64](s.Settings(), "timeout", 30)
p.proxy = getSetting[string](s.Settings(), "proxy", "")
```
`getSetting` 是泛型辅助函数,支持类型安全的配置读取。
## 配置项
| Key | 默认值 | 说明 |
|-----|--------|------|
| `plugin.web.timeout` | `30` | HTTP 请求超时(秒) |
| `plugin.web.proxy` | 空 | HTTP 代理地址,如 `http://<proxy-host>:<proxy-port>` |
## 注意事项
- DuckDuckGo HTML 格式可能随网站更新变化,如果搜索结果解析失败需调整 `parseSearchResults` 中的 HTML 标记匹配
- SSRF 防护默认阻止内网请求,如需访问内网资源需修改 `isInternalIP` 逻辑
- 搜索结果依赖 DuckDuckGo 可用性,在国内使用建议配置代理