mirror of
https://github.com/vxcontrol/langchaingo.git
synced 2026-07-21 08:55:25 -04:00
c3c94c54f8
* openai: clean up tool call handling * examples: clean up output in completion example --------- Co-authored-by: Konstantin Trunin <konstantin@trunin.com>
150 lines
4.2 KiB
Go
150 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/tmc/langchaingo/llms"
|
|
"github.com/tmc/langchaingo/llms/openai"
|
|
)
|
|
|
|
func main() {
|
|
llm, err := openai.New(openai.WithModel("gpt-3.5-turbo-0125"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Sending initial message to the model, with a list of available tools.
|
|
ctx := context.Background()
|
|
messageHistory := []llms.MessageContent{
|
|
llms.TextParts(llms.ChatMessageTypeHuman, "What is the weather like in Boston and Chicago?"),
|
|
}
|
|
|
|
fmt.Println("Querying for weather in Boston and Chicago..")
|
|
resp, err := llm.GenerateContent(ctx, messageHistory, llms.WithTools(availableTools))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
messageHistory = updateMessageHistory(messageHistory, resp)
|
|
|
|
// Execute tool calls requested by the model
|
|
messageHistory = executeToolCalls(ctx, llm, messageHistory, resp)
|
|
messageHistory = append(messageHistory, llms.TextParts(llms.ChatMessageTypeHuman, "Can you compare the two?"))
|
|
|
|
// Send query to the model again, this time with a history containing its
|
|
// request to invoke a tool and our response to the tool call.
|
|
fmt.Println("Querying with tool response...")
|
|
resp, err = llm.GenerateContent(ctx, messageHistory, llms.WithTools(availableTools))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
fmt.Println(resp.Choices[0].Content)
|
|
}
|
|
|
|
// updateMessageHistory updates the message history with the assistant's
|
|
// response and requested tool calls.
|
|
func updateMessageHistory(messageHistory []llms.MessageContent, resp *llms.ContentResponse) []llms.MessageContent {
|
|
respchoice := resp.Choices[0]
|
|
|
|
assistantResponse := llms.TextParts(llms.ChatMessageTypeAI, respchoice.Content)
|
|
for _, tc := range respchoice.ToolCalls {
|
|
assistantResponse.Parts = append(assistantResponse.Parts, tc)
|
|
}
|
|
return append(messageHistory, assistantResponse)
|
|
}
|
|
|
|
// executeToolCalls executes the tool calls in the response and returns the
|
|
// updated message history.
|
|
func executeToolCalls(ctx context.Context, llm llms.Model, messageHistory []llms.MessageContent, resp *llms.ContentResponse) []llms.MessageContent {
|
|
fmt.Println("Executing", len(resp.Choices[0].ToolCalls), "tool calls")
|
|
for _, toolCall := range resp.Choices[0].ToolCalls {
|
|
switch toolCall.FunctionCall.Name {
|
|
case "getCurrentWeather":
|
|
var args struct {
|
|
Location string `json:"location"`
|
|
Unit string `json:"unit"`
|
|
}
|
|
if err := json.Unmarshal([]byte(toolCall.FunctionCall.Arguments), &args); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
response, err := getCurrentWeather(args.Location, args.Unit)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
weatherCallResponse := llms.MessageContent{
|
|
Role: llms.ChatMessageTypeTool,
|
|
Parts: []llms.ContentPart{
|
|
llms.ToolCallResponse{
|
|
ToolCallID: toolCall.ID,
|
|
Name: toolCall.FunctionCall.Name,
|
|
Content: response,
|
|
},
|
|
},
|
|
}
|
|
messageHistory = append(messageHistory, weatherCallResponse)
|
|
default:
|
|
log.Fatalf("Unsupported tool: %s", toolCall.FunctionCall.Name)
|
|
}
|
|
}
|
|
|
|
return messageHistory
|
|
}
|
|
|
|
func getCurrentWeather(location string, unit string) (string, error) {
|
|
weatherResponses := map[string]string{
|
|
"boston": "72 and sunny",
|
|
"chicago": "65 and windy",
|
|
}
|
|
|
|
weatherInfo, ok := weatherResponses[strings.ToLower(location)]
|
|
if !ok {
|
|
return "", fmt.Errorf("no weather info for %q", location)
|
|
}
|
|
|
|
b, err := json.Marshal(weatherInfo)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(b), nil
|
|
}
|
|
|
|
// availableTools simulates the tools/functions we're making available for
|
|
// the model.
|
|
var availableTools = []llms.Tool{
|
|
{
|
|
Type: "function",
|
|
Function: &llms.FunctionDefinition{
|
|
Name: "getCurrentWeather",
|
|
Description: "Get the current weather in a given location",
|
|
Parameters: map[string]any{
|
|
"type": "object",
|
|
"properties": map[string]any{
|
|
"location": map[string]any{
|
|
"type": "string",
|
|
"description": "The city and state, e.g. San Francisco, CA",
|
|
},
|
|
"unit": map[string]any{
|
|
"type": "string",
|
|
"enum": []string{"fahrenheit", "celsius"},
|
|
},
|
|
},
|
|
"required": []string{"location"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func showResponse(resp *llms.ContentResponse) string {
|
|
b, err := json.MarshalIndent(resp, "", " ")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
return string(b)
|
|
}
|