大模型会上网
2026年4月15日 · 556 字 · 3 分钟
如何让大模型具备联网能力,搜索并整合互联网信息 大模型虽然知识渊博,但存在一个明显的局限性:知识截止日期。当用户询问最近发生的事情时,大模型往往无能为力。 例如,当我们问大模型"985大学最差食堂前三名是哪些"这样的问题时,大模型可能无法给出准确的答案,因为它需要最新的网络信息。 这时候,我们就需要给大模型装上"翅膀"——联网搜索能力。 要实现一个能上网的AI Agent,我们需要两个核心工具: 整体流程如下: 我们使用 代码解析: 搜索工具只能返回搜索结果的标题和链接,要获取详细内容,还需要爬取工具: 代码解析: 现在我们将搜索和爬取工具组合起来,创建一个能上网的AI Agent: 关键配置说明: 创建一个运行器来执行Agent: 让我们测试一下这个能上网的AI Agent: 运行结果: Agent会自动执行以下步骤:为什么大模型需要联网
架构设计
用户提问 → AI Agent分析问题 → 调用搜索工具 → 获取搜索结果 → 调用爬取工具 → 整合信息 → 返回答案
代码实现
1. 搜索工具实现
openserp 库来实现Google搜索功能:package tools
import (
"context"
"encoding/json"
"fmt"
"sync"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/components/tool/utils"
"github.com/karust/openserp/core"
"github.com/karust/openserp/google"
)
// 搜索参数结构体
type Search struct {
Text string `json:"text" jsonschema:"required" jsonschema_description:"搜索的文本"`
Limit int `json:"limit" jsonschema:"default=3" jsonschema_description:"返回的结果数量"`
}
var browserOnce sync.Once
var browser *core.Browser
// 打开浏览器
func OpenBrowser() *core.Browser {
browserOnce.Do(func() {
var err error
// 会打开电脑上的浏览器,通过浏览器去发起请求
browser, err = core.NewBrowser(core.BrowserOpts{})
if err != nil {
panic(err)
}
})
return browser
}
// 关闭浏览器
func CloseBrowser() {
if browser != nil {
err := browser.Close()
if err != nil {
fmt.Printf("close browser failed: %v", err)
}
}
}
// 搜索网页内容
func SearchWeb(ctx context.Context, search Search) (string, error) {
browser = OpenBrowser()
googleSearch := google.New(
*browser,
core.SearchEngineOptions{},
)
// 限制搜索结果数量,最多3个
if search.Limit <= 0 {
search.Limit = 3
} else if search.Limit > 3 {
search.Limit = 3
}
// 执行搜索
results, err := googleSearch.Search(core.Query{
Text: search.Text,
Limit: search.Limit,
})
if err != nil {
return "", fmt.Errorf("search failed: %w", err)
}
// 将结果序列化为JSON
jsonResults, err := json.Marshal(results)
if err != nil {
return "", fmt.Errorf("marshal search results failed: %w", err)
}
return string(jsonResults), nil
}
// 创建搜索工具
func SearchTool() tool.InvokableTool {
tool, err := utils.InferTool("SearchTool",
`通过这个工具可以搜索网络内容,最多返回3个结果。`,
SearchWeb)
if err != nil {
panic(err)
}
return tool
}
2. 爬取工具实现
// URL参数结构体
type Url struct {
Url string `json:"url" jsonschema:"required" jsonschema_description:"要爬取的URL"`
}
// 爬取网页内容
func Crawl(ctx context.Context, url *Url) (string, error) {
if url.Url == "" {
return "", fmt.Errorf("url is empty")
}
// 打开网页
page, _ := OpenBrowser().Navigate(url.Url)
// 提取标题
element, _ := page.Element("title")
title, _ := element.Text()
// 提取正文内容
bodyElement, _ := page.Element("body")
body, _ := bodyElement.Text()
return fmt.Sprintf("title=%s,body=%s", title, body), nil
}
// 创建爬取工具
func CrawlTool() tool.InvokableTool {
tool, err := utils.InferTool("CrawlTool",
`通过这个工具爬取url对应的网页内容`,
Crawl)
if err != nil {
panic(err)
}
return tool
}
3. 创建联网Agent
package agent
import (
"context"
"eino_study/config"
"eino_study/model"
"eino_study/tools"
"github.com/cloudwego/eino/adk"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
)
func NewSearchAgent() adk.Agent {
ctx := context.Background()
model := model.NewChatModel(config.NewConfig("../config/config.json"))
// 创建agent
a, err := adk.NewChatModelAgent(ctx, &adk.ChatModelAgentConfig{
Name: "WebGetAgent",
Description: "互联网信息整合大师",
Instruction: `请你充分利用搜索引擎和爬虫技术,整合互联网上的信息来回答我的问题,每个问题爬取的网页数目不超过3个`,
Model: model.OpenaiChatModel,
ToolsConfig: adk.ToolsConfig{
ToolsNodeConfig: compose.ToolsNodeConfig{
Tools: []tool.BaseTool{
tools.SearchTool(),
tools.CrawlTool(),
},
},
},
})
if err != nil {
panic(err)
}
return a
}
4. Agent运行器
func SARunner(ctx context.Context, messages []adk.Message, callback func(msg string)) adk.Message {
agent := NewSearchAgent()
// 创建runner
runner := adk.NewRunner(ctx, adk.RunnerConfig{
Agent: agent,
})
// 运行agent
iter := runner.Run(ctx, messages)
var lastmsg adk.Message
// 迭代处理事件
for {
event, ok := iter.Next()
if !ok {
break
}
// 处理消息输出
if event.Output.MessageOutput != nil {
msg, err := event.Output.MessageOutput.GetMessage()
if err != nil {
panic(err)
}
lastmsg = msg
}
}
return lastmsg
}
实际测试
测试案例1: 查询985大学最差食堂
func TestSearchAgent(t *testing.T) {
ctx := context.Background()
messages := []adk.Message{
{
Role: schema.User,
Content: "985大学最差食堂前三名是哪些?请搜索并整理相关信息。",
},
}
result := SARunner(ctx, messages, func(msg string) {
fmt.Printf("回调消息: %s\n", msg)
})
fmt.Println("最终结果:", result.Content)
}
测试案例2: 查询人物信息
func TestSearchAgentWithFollowUp(t *testing.T) {
ctx := context.Background()
messages := []adk.Message{
{
Role: schema.User,
Content: "特朗普是谁,并告诉我来源网站",
},
}
result := SARunner(ctx, messages, func(msg string) {
fmt.Printf("%s\n", msg)
})
fmt.Println("回答:", result.Content)
}