一.聊聊AI Agent
随着机器学习,深度学习算法的兴起,AI Agent开始从数据中总结概率规律。大语言模型的出现推动了AI Agent的重大变革,如GPT-3,DeepSeek等大参数模型具备了强大的自然语言处理和理解能力。大模型(LLM)是基于海量通用数据训练而成的,就像是一个“通用大脑”,虽然具备强大的自然语言理解和生成能力,但是在面对垂直专业场景时,其精度难以适配。而一个真正能落地的AI应用,不仅是一个可以聊天的大语言模型,而是一个能理解任务,调用工具,访问数据并完成实际目标的系统。
在过去几年里,“大模型”这个词汇异常火爆,很多非专业的朋友可能会认为接入一个ChatGPT,Claude,或者通义千问之类的模型,就等于有了一个AI应用。但是事实上远远没有这么简单,大模型只是AI应用开发中的一个关键组件而非全部。实际上,AI应用是让模型具备了感知世界和执行任务的能力。在企业级的实际开发工作中,如果直接使用大模型处理企业生产数据,会存在数据泄露,隐私侵犯等风险。不同行业有严格的合规要求,AI应用可以根据企业的具体需求和合规要求,采用合理的数据安全和隐私保护措施,可以确保数据合理合规地使用。要让大模型从能聊天变得能办事,我们就要引入一个关键的概念--AI Agent。
AI Agent(即智能体)可以理解为一个具备自主决策能力的系统。它不仅能理解自然语言指令,还能调用外部工具、执行任务、做出决策。它的核心能力是让模型“学会用工具”。比如说对于一个股票助手而言,这个Agent需要理解我们的请求,比如说“帮我分析下今天xx公司的股票走势”,然后调用股票API,抓取数据,分析数据,生成报告。因此,AI Agent并不是要取代大模型,而是让大模型成为“会思考,会行动的智能系统”的关键!本质上AI应用的开发也是后端开发工程师的工作内容。Go作为一门后端语言,其生态中也有相应的框架来支持AI应用的快速开发,其中一个比较具有代表性的就是由字节跳动旗下CloudWeGo团队开源的Eino框架。下面我们就采用Eino框架,学习开发一个简单的股票分析Agent。完善之后我会把这个小项目在GitHub开源,这里只给出部分代码和效果演示,大家主要理解概念和框架的用法。
二.Eino框架实战--基于豆包Seed-1.6开发的股票分析助手
在开发AI Agent之前,我们首先要进行模型的选择。这里推荐大家多多去关注Hugging Face,上面的模型能力评估可供大家参考。我们使用大模型,可以选择本地部署,但是我们的硬件配置比较有限,所以选择调用API。现在各大厂都推出了各种各样的大模型以及衍生产品,也推荐大家去官方平台上多多调研。今天我采用的是字节的豆包Seed-1.6大模型。我们只需要去火山引擎了解对应模型开通服务获取API Key,然后通读文档按照模板接入即可,调用其他模型的方法也大体相同,此处不再过多介绍。
在Eino框架中,AI Agent的智能不仅体现在对自然语言的理解上,更在于它能主动调用外部工具完成任务。这种能力的实现依赖于一个核心机制--Function Calling(函数调用)。简单来说,Function Calling 就是让模型在对话过程中学会“调用函数”,从而让模型具备“动手能力”。这里我详细介绍下,Function Calling这个概念有广义和狭义两个层面。广义的Function Calling是指让大模型能够调用外部工具的一种技术实现,首先向大模型提供工具集以及说明,由大模型在对话过程中智能判断是否需要调用工具。我们今天探讨的Function Calling属于广义层面的范畴。而狭义的Function Calling特指大模型提供商在模型内部与API层面做了支持的一种能力,最早由OpenAI引入,在模型层面:模型提供商需使用海量的函数调用数据对大模型进行专门的微调或强化学习训练,使其具备更强的意图识别,函数选择,参数生成能力。假如我向大模型提问:请给我分析一下茅台公司今天的股价情况,工作流程大致如下图:

可能会有朋友有疑问,传统的由后端程序直接调用工具的方式有什么弊端?首先,是否调用工具,何时调用工具完全交由后端判断,而这部分的逻辑往往是最复杂的,交由后端判断逻辑写不好极容易产生误判。其次调用工具的参数由后端进行构建,难度很大。这两个问题的关键都涉及对自然语言的理解,我们让大模型去干这种智能的活,而不应该让后端以硬编码的方式去做。还有一点需要进行阐明,工具调用是在你的AI应用程序内(后端)执行的,而不是在模型服务器上,大模型本身不直接访问外部API和数据库。工具结果返回后,再由应用程序送回模型整合生成最终回复。
至此,相信朋友们已经对简单AI Agent的搭建以及工作流有了基本的认知。在我们搭建Agent之前,我们需要先构建工具集并喂给大模型,这样大模型才知道有哪些工具可供调用。Eino框架为我们提供了一个ToolsNode组件实现和定义了工具调用,在这个组件下的BaseTool接口提供了Info方法,用于提供工具的基本信息,通过这个方法,大模型通过参数解析就能知晓这个工具的具体用途。InvokableTool接口提供了InvokableRun方法,用于同步执行工具调用。StreamableTool接口提供了StreamableRun方法,用于流式执行工具调用。这里我给出一段构建工具集的demo示例,实例中使用的API是Mock的,在实际开发过程中我们引入真实接口即可。
package stocktool
import (
"context"
"encoding/json"
"fmt"
"net/http"
"io"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/schema"
)
// StockTool 用于获取股票实时信息
type StockTool struct {
apiKey string
}
// 创建工具实例
func NewStockTool(apiKey string) *StockTool {
return &StockTool{apiKey: apiKey}
}
// Info 用于告诉大模型这个工具能做什么
func (s *StockTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
return &schema.ToolInfo{
Name: "get_stock_price",
Desc: "根据股票代码获取当前股价,例如: get_stock_price(symbol='AAPL')",
ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"symbol": {
Type: schema.String,
Required: true,
Desc: "股票代码,例如 600519(贵州茅台)",
},
}),
}, nil
}
// InvokableRun 是 Function Calling 真正调用工具的地方
func (s *StockTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var input map[string]any
if err := json.Unmarshal([]byte(argumentsInJSON), &input); err != nil {
return "", fmt.Errorf("解析输入失败: %w", err)
}
symbol, ok := input["symbol"].(string)
if !ok || symbol == "" {
return "", fmt.Errorf("symbol 参数不能为空")
}
// 示例使用一个公开模拟API--实际上使用真实接口
apiURL := fmt.Sprintf("https://api.example.com/stock?symbol=%s&apikey=%s", symbol, s.apiKey)
resp, err := http.Get(apiURL)
if err != nil {
return "", fmt.Errorf("请求股票API失败: %w", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应失败: %w", err)
}
// 返回JSON字符串给模型
return string(body), nil
}
// 工具的调用名称
func (s *StockTool) InvokableName() string {
return "get_stock_price"
}
经过上面的过程我们便把工具集定义好了,只需要在Agent定义一下就可以让大模型知晓有什么工具,以及工具的属性。接下来我们就需要开始搭建AI Agent,定义工具并创建工具节点后,我们的工具就会被Eino框架所接管。之后我们就进入了构建提示词的部分,这一部分很关键,在大模型应用开发中是一个专业领域--Prompt Engineering(提示词工程)。这里的demo我只给出简单的示例,后面会出专题详细介绍提示词工程。之后的code逻辑实际上就是调用模型,模型理解prompt后开始执行Function Calling。这里我给出的demo仅仅是逻辑示例,实际上还需要一些条件判断,日志输出等,这里大家参考后自行根据需求进行设定。如果感兴趣的话后续可以直接pull我的项目参考我的具体实现。实际上今天开发的只是一个最小可运行系统,后续我们会慢慢介绍并引入其他组件。
package main
import (
"context"
"fmt"
"os"
"EinoLearning/stocktool"
"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
)
func main() {
ctx := context.Background()
// 从环境变量读取豆包 API Key
apiKey := os.Getenv("ARK_API_KEY")
if apiKey == "" {
fmt.Println("请先设置环境变量 ARK_API_KEY")
return
}
// 创建大模型客户端
client := arkruntime.NewClientWithApiKey(apiKey)
// 初始化自定义工具(StockTool)
stockTool := stocktool.NewStockTool("这里填你申请到的腾讯财经API Key")
// 构建工具节点(让 Eino 管理工具调用)
toolsNode, _ := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
Tools: []tool.BaseTool{stockTool},
})
// 将工具元信息注册给模型
info, _ := stockTool.Info(ctx)
toolConfig := []*model.Tool{
{
Type: model.ToolTypeFunction,
Function: &model.FunctionDefinition{
Name: info.Name,
Description: info.Desc,
Parameters: map[string]any{
"type": "object",
"properties": map[string]any{
"symbol": map[string]any{
"type": "string",
},
},
"required": []string{"symbol"},
},
},
},
}
//构造一次对话请求--Promt Engineer
req := model.CreateChatCompletionRequest{
Model: "doubao-seed-1-6-flash-250828", // 使用豆包 Seed 模型
Messages: []*model.ChatCompletionMessage{
{
Role: model.ChatMessageRoleSystem,
Content: &model.ChatCompletionMessageContent{
StringValue: strPtr("你是一个股票分析助手,当用户查询股票时请调用工具获取股价。"),
},
},
{
Role: model.ChatMessageRoleUser,
Content: &model.ChatCompletionMessageContent{
StringValue: strPtr("帮我查一下茅台的股价"),
},
},
},
Tools: toolConfig,
}
// 调用模型
resp, err := client.CreateChatCompletion(ctx, req)
if err != nil {
panic(err)
}
//检查是否触发了 Function 调用
msg := resp.Choices[0].Message
if len(msg.ToolCalls) > 0 {
// 模型发出工具调用请求
call := msg.ToolCalls[0]
fmt.Printf("模型请求调用工具: %s\n", call.Function.Name)
// 执行工具逻辑
result, _ := toolsNode.Invoke(ctx, &schema.Message{
Role: schema.Assistant,
ToolCalls: []schema.ToolCall{
{
Function: schema.FunctionCall{
Name: call.Function.Name,
Arguments: call.Function.Arguments,
},
},
},
})
fmt.Println("工具返回结果:", result[0].Content)
} else {
// 模型直接回复
fmt.Println("模型回答:", *msg.Content.StringValue)
}
}
func strPtr(s string) *string { return &s }
今天我们从“大模型 ≠ AI 应用”的基本概念出发,了解了为什么真正能落地的 AI 系统,往往离不开“工具调用”和“任务执行”这两大能力。我们认识了 Eino 这个基于 Go 的 AI 应用开发框架,并通过一个简化版的 股票分析助手 示例,完成了从模型接入到 Function Calling 的最小可运行版本。通过这个实践,我们可以看到——AI 应用并不是简单调用一个模型接口,而是让模型在理解任务的基础上,调用外部工具、访问真实数据、输出有价值结果的过程。这种“模型 + 工具”的协同方式,也正是未来智能体(AI Agent)发展的核心方向。有不当之处还请朋友们多多批评指正,我们一起成长!
661

被折叠的 条评论
为什么被折叠?



