编排

在大模型应用开发中,我们需要将各种基础组件,比如ChatModel,Embedding,ToolsNode ,Reriever等,按照业务逻辑进行组合串联,最终完成特定的功能,这种就是编排。

Eino框架提供了一套强大的编排功能,包括Chain、Graph和Workflow三种编排方式。

1. Graph

Graph是Eino中最基础、最强大的编排方式,它基于节点(Node)和边(Edge)的模型,可以绘制出复杂的数据流动网络,支持分支、并行、循环等复杂结构。

我们以一个实例来说明。

需求:创建一个房产经纪人Agent。

1.1 案例

package main

import (
	"context"
	"errors"

	"github.com/cloudwego/eino-ext/components/model/ollama"
	"github.com/cloudwego/eino/components/prompt"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/components/tool/utils"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/schema"
)

const (
	ollamaBaseURL   = "http://localhost:11434"
	ollamaModelName = "modelscope.cn/Qwen/Qwen3-32B-GGUF:latest"
)

type userInfoRequest struct {
	Email string `json:"email"`
	Name  string `json:"name"`
}
type userInfoResponse struct {
	Name     string `json:"name"`
	Email    string `json:"email"`
	Company  string `json:"company"`
	Position string `json:"position"`
	Salary   string `json:"salary"`
}

func main() {
	ctx := context.Background()
	g := compose.NewGraph[map[string]any, *schema.Message]()
	//1. 创建ChatTemplate节点
	systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的`
	chatTpl := prompt.FromMessages(schema.FString,
		schema.SystemMessage(systemTpl),
		schema.MessagesPlaceholder("histories", true),
		schema.UserMessage("{user_query}"))
	recommendTpl := `
		你是一名房产经纪人,结合工具提供的用户信息,推荐房产
			--- 房产信息 ---
	
		### A. 楼盘列表
	
		**1. 瀚海星辰 (ID: A-01)**
		- **区域**: 海淀区-中关村
		- **特点**: 顶级学区房, 毗邻多所名校, 周围遍布知名科技公司(如字节跳动、腾讯等)。
		- **户型**: 120平米三居室
		- **总价**: 约1500万人民币
		- **适合人群**: 科技公司高管、重视子女教育的家庭。
	
		**2. 国贸天际 (ID: B-02)**
		- **区域**: 朝阳区-国贸CBD
		- **特点**: 城市核心地标, 270度落地窗俯瞰CBD夜景, 奢华精装修,顶级商业配套。
		- **户型**: 280平米大平层
		- **总价**: 约3500万人民币
		- **适合人群**: 企业家、公司创始人(CEO/C-level)、金融精英、追求顶级生活品质人士。
	
		**3. 未来之城 (ID: C-03)**
		- **区域**: 通州区-城市副中心
		- **特点**: 新兴规划区域, 潜力巨大, 环境优美, 配套设施完善, 性价比高。
		- **户型**: 140平米四居室
		- **总价**: 约800万人民币
		- **适合人群**: 在国贸或副中心工作的白领、首次改善型购房家庭。
	
		**4. 文艺 loft (ID: D-04)**
		- **区域**: 朝阳区-798艺术区
		- **特点**: 设计师风格, 挑高5米, 充满艺术气息, 交通便利。
		- **户型**: 60平米复式Loft
		- **总价**: 约450万人民币
		- **适合人群**: 年轻单身贵族、设计师、创意工作者。
	
		### B. 购房建议规则
	
		1.  **预算评估**:
			- 房屋总价建议不超过家庭年收入的10倍。
			- 月供(按30年商业贷款,利率4%估算)不应超过家庭月收入的50%。
		2.  **职住平衡**: 推荐的房产区域应与用户公司所在地有较好的通勤关系。例如,在字节跳动工作的高管,优先推荐海淀区的“瀚海星辰”。
		3.  **身份匹配**: 房产的“适合人群”标签应与用户的职位和身份高度匹配。例如,CEO身份的用户应优先考虑“国贸天际”这类彰显身份的豪宅。
	
	`
	//2. 创建chatModel节点
	modelConf := &ollama.ChatModelConfig{
		Model:   ollamaModelName,
		BaseURL: ollamaBaseURL,
	}
	chatModel, err := ollama.NewChatModel(ctx, modelConf)
	if err != nil {
		panic(err)
	}
	//3. 创建工具节点
	userInfoTool := utils.NewTool(
		&schema.ToolInfo{
			Name: "user_info",
			Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
			ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
				"name": {
					Type: "string",
					Desc: "用户的姓名",
				},
				"email": {
					Type: "string",
					Desc: "用户的邮箱",
				},
			}),
		}, func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
			return &userInfoResponse{
				Name:     input.Name,
				Email:    input.Email,
				Company:  "Bytedance",
				Position: "高级工程师",
				Salary:   "60000",
			}, nil
		})
	//4. 绑定工具到模型
	info, err := userInfoTool.Info(ctx)
	if err != nil {
		panic(err)
	}
	err = chatModel.BindTools([]*schema.ToolInfo{info})
	if err != nil {
		panic(err)
	}
	//5. 创建工具节点
	toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
		Tools: []tool.BaseTool{userInfoTool},
	})
	if err != nil {
		panic(err)
	}
	//6. 创建转换lambda节点
	transformOps := func(ctx context.Context, input *schema.StreamReader[[]*schema.Message]) (output *schema.StreamReader[*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input []*schema.Message) (output *schema.Message, err error) {
			if len(input) > 0 {
				return input[0], nil
			}
			return nil, errors.New("no message")
		}), nil
	}
	lambda := compose.TransformableLambda[[]*schema.Message, *schema.Message](transformOps)
	//7. 创建转换lambda节点 给下一个chatmodel构建提示词
	promptTransformOps := func(ctx context.Context, input *schema.StreamReader[*schema.Message]) (output *schema.StreamReader[[]*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input *schema.Message) (output []*schema.Message, err error) {
			var messages []*schema.Message
			messages = append(messages, schema.SystemMessage(recommendTpl), input)
			return messages, nil
		}), nil
	}
	lambdaPrompt := compose.TransformableLambda[*schema.Message, []*schema.Message](promptTransformOps)
	//8. 创建Graph编排
	const (
		promptNodeKey        = "prompt"
		chatNodeKey          = "chat"
		toolsNodeKey         = "tools"
		recommendChatNodeKey = "chat_recommend"
		lambdaNodeKey        = "lambda"
		lambdaPromptNodeKey  = "lambdaPrompt"
	)
	//9. 添加节点
	_ = g.AddChatTemplateNode(promptNodeKey, chatTpl)
	_ = g.AddChatModelNode(chatNodeKey, chatModel)
	_ = g.AddToolsNode(toolsNodeKey, toolsNode)
	_ = g.AddChatModelNode(recommendChatNodeKey, chatModel)
	_ = g.AddLambdaNode(lambdaNodeKey, lambda)
	_ = g.AddLambdaNode(lambdaPromptNodeKey, lambdaPrompt)
	//10. 添加边 也就是节点之间的依赖关系
	_ = g.AddEdge(compose.START, promptNodeKey)
	_ = g.AddEdge(promptNodeKey, chatNodeKey)
	_ = g.AddEdge(chatNodeKey, toolsNodeKey)
	_ = g.AddEdge(toolsNodeKey, lambdaNodeKey)
	_ = g.AddEdge(lambdaNodeKey, lambdaPromptNodeKey)
	_ = g.AddEdge(lambdaPromptNodeKey, recommendChatNodeKey)
	_ = g.AddEdge(recommendChatNodeKey, compose.END)
	//11. 编译运行
	runnable, err := g.Compile(ctx)
	if err != nil {
		panic(err)
	}
	output, err := runnable.Invoke(ctx, map[string]any{
		"histories":  []*schema.Message{},
		"user_query": "我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产",
	})
	if err != nil {
		panic(err)
	}
	println("=====================思考内容====================")
	if output.ReasoningContent != "" {
		println(output.ReasoningContent)
	}
	println("=========================================")
	if output.Content != "" {
		println(output.Content)
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

2. Chain

Chain是对Graph的封装,适用于简单的线性串联场景。相比Graph,Chain的接口更易于使用,除了不支持环形结构外,几乎拥有Graph的所有能力。

2.1 案例

package main

import (
	"context"
	"errors"

	"github.com/cloudwego/eino-ext/components/model/ollama"
	"github.com/cloudwego/eino/components/prompt"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/components/tool/utils"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/schema"
)

func main() {
	ctx := context.Background()
	//1. 创建ChatTemplate节点
	systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的`
	chatTpl := prompt.FromMessages(schema.FString,
		schema.SystemMessage(systemTpl),
		schema.MessagesPlaceholder("histories", true),
		schema.UserMessage("{user_query}"))
	recommendTpl := `
		你是一名房产经纪人,结合工具提供的用户信息,推荐房产
			--- 房产信息 ---
	
		### A. 楼盘列表
	
		**1. 瀚海星辰 (ID: A-01)**
		- **区域**: 海淀区-中关村
		- **特点**: 顶级学区房, 毗邻多所名校, 周围遍布知名科技公司(如字节跳动、腾讯等)。
		- **户型**: 120平米三居室
		- **总价**: 约1500万人民币
		- **适合人群**: 科技公司高管、重视子女教育的家庭。
	
		**2. 国贸天际 (ID: B-02)**
		- **区域**: 朝阳区-国贸CBD
		- **特点**: 城市核心地标, 270度落地窗俯瞰CBD夜景, 奢华精装修,顶级商业配套。
		- **户型**: 280平米大平层
		- **总价**: 约3500万人民币
		- **适合人群**: 企业家、公司创始人(CEO/C-level)、金融精英、追求顶级生活品质人士。
	
		**3. 未来之城 (ID: C-03)**
		- **区域**: 通州区-城市副中心
		- **特点**: 新兴规划区域, 潜力巨大, 环境优美, 配套设施完善, 性价比高。
		- **户型**: 140平米四居室
		- **总价**: 约800万人民币
		- **适合人群**: 在国贸或副中心工作的白领、首次改善型购房家庭。
	
		**4. 文艺 loft (ID: D-04)**
		- **区域**: 朝阳区-798艺术区
		- **特点**: 设计师风格, 挑高5米, 充满艺术气息, 交通便利。
		- **户型**: 60平米复式Loft
		- **总价**: 约450万人民币
		- **适合人群**: 年轻单身贵族、设计师、创意工作者。
	
		### B. 购房建议规则
	
		1.  **预算评估**:
			- 房屋总价建议不超过家庭年收入的10倍。
			- 月供(按30年商业贷款,利率4%估算)不应超过家庭月收入的50%。
		2.  **职住平衡**: 推荐的房产区域应与用户公司所在地有较好的通勤关系。例如,在字节跳动工作的高管,优先推荐海淀区的“瀚海星辰”。
		3.  **身份匹配**: 房产的“适合人群”标签应与用户的职位和身份高度匹配。例如,CEO身份的用户应优先考虑“国贸天际”这类彰显身份的豪宅。
	
	`
	//2. 创建chatModel节点
	modelConf := &ollama.ChatModelConfig{
		Model:   ollamaModelName,
		BaseURL: ollamaBaseURL,
	}
	chatModel, err := ollama.NewChatModel(ctx, modelConf)
	if err != nil {
		panic(err)
	}
	//3. 创建工具节点
	userInfoTool := utils.NewTool(
		&schema.ToolInfo{
			Name: "user_info",
			Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
			ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
				"name": {
					Type: "string",
					Desc: "用户的姓名",
				},
				"email": {
					Type: "string",
					Desc: "用户的邮箱",
				},
			}),
		}, func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
			return &userInfoResponse{
				Name:     input.Name,
				Email:    input.Email,
				Company:  "Bytedance",
				Position: "高级工程师",
				Salary:   "60000",
			}, nil
		})
	//4. 绑定工具到模型
	info, err := userInfoTool.Info(ctx)
	if err != nil {
		panic(err)
	}
	err = chatModel.BindTools([]*schema.ToolInfo{info})
	if err != nil {
		panic(err)
	}
	//5. 创建工具节点
	toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
		Tools: []tool.BaseTool{userInfoTool},
	})
	if err != nil {
		panic(err)
	}
	//6. 创建转换lambda节点
	transformOps := func(ctx context.Context, input *schema.StreamReader[[]*schema.Message]) (output *schema.StreamReader[*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input []*schema.Message) (output *schema.Message, err error) {
			if len(input) > 0 {
				return input[0], nil
			}
			return nil, errors.New("no message")
		}), nil
	}
	lambda := compose.TransformableLambda[[]*schema.Message, *schema.Message](transformOps)
	//7. 创建转换lambda节点 给下一个chatmodel构建提示词
	promptTransformOps := func(ctx context.Context, input *schema.StreamReader[*schema.Message]) (output *schema.StreamReader[[]*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input *schema.Message) (output []*schema.Message, err error) {
			var messages []*schema.Message
			messages = append(messages, schema.SystemMessage(recommendTpl), input)
			return messages, nil
		}), nil
	}
	lambdaPrompt := compose.TransformableLambda[*schema.Message, []*schema.Message](promptTransformOps)
	//8. 创建Graph编排
	const (
		promptNodeKey        = "prompt"
		chatNodeKey          = "chat"
		toolsNodeKey         = "tools"
		recommendChatNodeKey = "chat_recommend"
		lambdaNodeKey        = "lambda"
		lambdaPromptNodeKey  = "lambdaPrompt"
	)
	// 创建Chain编排
	chain := compose.NewChain[map[string]any, *schema.Message]()
	// 9. 将节点添加到Chain中
	chain.
		AppendChatTemplate(chatTpl).
		AppendChatModel(chatModel).
		AppendToolsNode(toolsNode).
		AppendLambda(lambda).
		AppendLambda(lambdaPrompt).
		AppendChatModel(chatModel)
	//10. 编译运行
	runnable, err := chain.Compile(ctx)
	if err != nil {
		panic(err)
	}
	output, err := runnable.Invoke(ctx, map[string]any{
		"histories":  []*schema.Message{},
		"user_query": "我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产",
	})
	if err != nil {
		panic(err)
	}
	println("=====================思考内容====================")
	if output.ReasoningContent != "" {
		println(output.ReasoningContent)
	}
	println("=========================================")
	if output.Content != "" {
		println(output.Content)
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

3. Workflow

Workflow提供了字段级别的映射能力,可以将不同节点的输出字段灵活地映射到其他节点的输入字段。

3.1 案例

package main

import (
	"context"
	"errors"

	"github.com/cloudwego/eino-ext/components/model/ollama"
	"github.com/cloudwego/eino/components/prompt"
	"github.com/cloudwego/eino/components/tool"
	"github.com/cloudwego/eino/components/tool/utils"
	"github.com/cloudwego/eino/compose"
	"github.com/cloudwego/eino/schema"
)

func main() {
	ctx := context.Background()

	// 创建Workflow编排
	wf := compose.NewWorkflow[map[string]any, *schema.Message]()

	// 1. 创建系统提示词模板
	systemTpl := `你是一名房产经纪人,结合用户的薪酬和工作,使用 user_info API,为其提供相关的房产信息。邮箱是必须的`
	chatTpl := prompt.FromMessages(schema.FString,
		schema.SystemMessage(systemTpl),
		schema.MessagesPlaceholder("histories", true),
		schema.UserMessage("{user_query}"))

	// 2. 创建推荐模板
	recommendTpl := `
你是一名房产经纪人,结合工具提供的用户信息,推荐房产
--- 房产信息 ---

### A. 楼盘列表

**1. 瀚海星辰 (ID: A-01)**
- **区域**: 海淀区-中关村
- **特点**: 顶级学区房, 毗邻多所名校, 周围遍布知名科技公司(如字节跳动、腾讯等)。
- **户型**: 120平米三居室
- **总价**: 约1500万人民币
- **适合人群**: 科技公司高管、重视子女教育的家庭。

**2. 国贸天际 (ID: B-02)**
- **区域**: 朝阳区-国贸CBD
- **特点**: 城市核心地标, 270度落地窗俯瞰CBD夜景, 奢华精装修,顶级商业配套。
- **户型**: 280平米大平层
- **总价**: 约3500万人民币
- **适合人群**: 企业家、公司创始人(CEO/C-level)、金融精英、追求顶级生活品质人士。

**3. 未来之城 (ID: C-03)**
- **区域**: 通州区-城市副中心
- **特点**: 新兴规划区域, 潜力巨大, 环境优美, 配套设施完善, 性价比高。
- **户型**: 140平米四居室
- **总价**: 约800万人民币
- **适合人群**: 在国贸或副中心工作的白领、首次改善型购房家庭。

**4. 文艺 loft (ID: D-04)**
- **区域**: 朝阳区-798艺术区
- **特点**: 设计师风格, 挑高5米, 充满艺术气息, 交通便利。
- **户型**: 60平米复式Loft
- **总价**: 约450万人民币
- **适合人群**: 年轻单身贵族、设计师、创意工作者。

### B. 购房建议规则

1.  **预算评估**:
   - 房屋总价建议不超过家庭年收入的10倍。
   - 月供(按30年商业贷款,利率4%估算)不应超过家庭月收入的50%。
2.  **职住平衡**: 推荐的房产区域应与用户公司所在地有较好的通勤关系。例如,在字节跳动工作的高管,优先推荐海淀区的"瀚海星辰"。
3.  **身份匹配**: 房产的"适合人群"标签应与用户的职位和身份高度匹配。例如,CEO身份的用户应优先考虑"国贸天际"这类彰显身份的豪宅。
`

	// 3. 创建chatModel
	modelConf := &ollama.ChatModelConfig{
		Model:   ollamaModelName,
		BaseURL: ollamaBaseURL,
	}
	chatModel, err := ollama.NewChatModel(ctx, modelConf)
	if err != nil {
		panic(err)
	}

	// 4. 创建工具
	userInfoTool := utils.NewTool(
		&schema.ToolInfo{
			Name: "user_info",
			Desc: "根据用户的姓名和邮箱,查询用户的公司、职位、薪酬信息",
			ParamsOneOf: schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
				"name": {
					Type: "string",
					Desc: "用户的姓名",
				},
				"email": {
					Type: "string",
					Desc: "用户的邮箱",
				},
			}),
		}, func(ctx context.Context, input *userInfoRequest) (output *userInfoResponse, err error) {
			return &userInfoResponse{
				Name:     input.Name,
				Email:    input.Email,
				Company:  "Bytedance",
				Position: "高级工程师",
				Salary:   "60000",
			}, nil
		})

	// 5. 绑定工具到模型
	info, err := userInfoTool.Info(ctx)
	if err != nil {
		panic(err)
	}
	err = chatModel.BindTools([]*schema.ToolInfo{info})
	if err != nil {
		panic(err)
	}

	// 6. 创建工具节点
	toolsNode, err := compose.NewToolNode(ctx, &compose.ToolsNodeConfig{
		Tools: []tool.BaseTool{userInfoTool},
	})
	if err != nil {
		panic(err)
	}

	// 7. 创建转换函数,将 []*schema.Message 转换为 *schema.Message
	transformOps := func(ctx context.Context, input *schema.StreamReader[[]*schema.Message]) (output *schema.StreamReader[*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input []*schema.Message) (output *schema.Message, err error) {
			if len(input) > 0 {
				return input[0], nil
			}
			return nil, errors.New("no message")
		}), nil
	}
	lambda := compose.TransformableLambda[[]*schema.Message, *schema.Message](transformOps)
	// 8. 创建另一个转换函数,将 *schema.Message 转换为 []*schema.Message
	promptTransformOps := func(ctx context.Context, input *schema.StreamReader[*schema.Message]) (output *schema.StreamReader[[]*schema.Message], err error) {
		return schema.StreamReaderWithConvert(input, func(input *schema.Message) (output []*schema.Message, err error) {
			var messages []*schema.Message
			messages = append(messages, schema.SystemMessage(recommendTpl), input)
			return messages, nil
		}), nil
	}
	lambdaPrompt := compose.TransformableLambda[*schema.Message, []*schema.Message](promptTransformOps)

	// 9. 添加节点到Workflow
	// 添加聊天模板节点
	wf.AddChatTemplateNode("prompt", chatTpl).AddInput(compose.START)

	// 添加第一个聊天模型节点
	wf.AddChatModelNode("chat", chatModel).AddInput("prompt")

	// 添加工具节点
	wf.AddToolsNode("tools", toolsNode).AddInput("chat")

	// 添加转换节点(将[]*schema.Message转换为*schema.Message)
	wf.AddLambdaNode("transform", lambda).AddInput("tools")

	// 添加转换节点(将*schema.Message转换为[]*schema.Message)
	wf.AddLambdaNode("prompt_transform", lambdaPrompt).AddInput("transform")

	// 添加第二个聊天模型节点
	wf.AddChatModelNode("chat_recommend", chatModel).AddInput("prompt_transform")

	// 从chat_recommend到END节点
	wf.End().AddInput("chat_recommend")

	// 11. 编译Workflow
	runnable, err := wf.Compile(ctx)
	if err != nil {
		panic(err)
	}

	// 12. 执行调用
	output, err := runnable.Invoke(ctx, map[string]any{
		"histories":  []*schema.Message{},
		"user_query": "我叫 zhangsan, 邮箱是 zhangsan@bytedance.com, 帮我推荐一处房产",
	})
	if err != nil {
		panic(err)
	}

	println("=====================思考内容====================")
	if output.ReasoningContent != "" {
		println(output.ReasoningContent)
	}
	println("=========================================")
	if output.Content != "" {
		println(output.Content)
	}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191