作为开发人员与法学硕士合作时,您通常希望接收结构化格式的数据。虽然这在早期的 GPT-3 时代并不总是有效,但当前支持函数调用的模型在这方面做得更好。
在这篇文章中,我想分享一个名为 CallClaudeForceTool 的简单函数,您可以使用它来调用 Claude 来接收结构化数据/JSON 输出。就我而言,它是用 Golang 编写的,并返回我需要的任何类型的结构。
首先,您需要定义结构并将其以 JSON 模式的格式提供给 LLM。在我的示例中,我定义了一个结构类型,用作查询某些数据的函数的输入。
这是查询函数的输入的结构:
type QueryArgs struct { SearchQuery string `json:"searchQuery,omitempty"` TypeFilter string `json:"typeFilter,omitempty"` }
以下是 JSON Schema 定义。添加好的名称和描述在这里实际上很重要,因为 LLM 在生成 JSON 数据时将使用此信息:
var QueryFnSchema = JSONSchema{ Type: ai.DataTypeObject, Properties: map[string]ai.JSONSchema{ "searchQuery": { Type: ai.DataTypeString, Description: "The search text query to use. Any data that contains this text will be searched. If not provided, then no text comparison will be performed.", }, "typeFilter": { Type: ai.DataTypeString, Description: `The type to filter on. If not provided, data of any type will be searched. Can only be one of: "location", "event", "person", "organization".`, Enum: []string{"location", "event", "person", "organization"}, }, }, } type JSONSchema struct { Type DataType `json:"type,omitempty"` Description string `json:"description,omitempty"` Enum []string `json:"enum,omitempty"` Properties map[string]JSONSchema `json:"properties,omitempty"` Required []string `json:"required,omitempty"` Items *JSONSchema `json:"items,omitempty"` } func (d JSONSchema) MarshalJSON() ([]byte, error) { if d.Properties == nil { d.Properties = make(map[string]JSONSchema) } type Alias JSONSchema return json.Marshal(struct { Alias }{ Alias: (Alias)(d), }) } type DataType string const ( DataTypeObject DataType = "object" DataTypeNumber DataType = "number" DataTypeInteger DataType = "integer" DataTypeBoolean DataType = "boolean" DataTypeString DataType = "string" DataTypeArray DataType = "array" DataTypeNull DataType = "null" )
我正在使用一个简单的 JSON Schema 结构。您可以为此使用依赖项,但此结构通常足以满足 LLM 函数调用。它与 Anthropic 的 Claude 和 OpenAI 的 ChatGPT 配合得很好。
您可以根据 QueryArgs 的等效项或您需要并希望 LLM 响应的任何数据来定义自己的架构,而不是 QueryFnSchema。
现在是时候使用 CallClaudeForceTool 函数了(将在帖子末尾解释):
func main() { // ... queryArgs, claudeResponse := CallClaudeForceTool[QueryArgs]([]ClaudeLLMMessage{ {Role: "user", Content: getQueryPrompt(inputMessage)}, }, ToolDefinition{ Name: "search", Description: "Search for data in the database", InputSchema: queryFnSchema, }, ClaudeConfig{ MaxTokens: 4000, Temperature: 0.8, System: SYSTEM_PROMPT, }) result := db.Query(queryArgs) // ... }
您可以根据自己的需要替换 getQueryPrompt(inputMessage) 、 SYSTEM_PROMPT 和任何配置。
queryArgs 变量现在包含您定义的格式的 LLM 生成的数据,在我的例子中是 QueryArgs 结构。此时,我使用它作为实际函数调用的参数来查询我的数据库。但是,您也可以根据某些输入或提示生成任何结构化数据,然后在代码中使用它。
最后,这是 CallClaudeForceTool 函数和任何辅助类型。其目标是调用 Claude,强制其使用提供的单个工具,并结束而不用任何后续工具/函数输出响应 Claude 或接收任何最终响应文本消息。我们只希望 Claude 用结构化数据回复一次。
func CallClaudeForceTool[T any](messages []ClaudeLLMMessage, tool ToolDefinition, config ClaudeConfig) (T, *ClaudeCreateMessageResponse, error) { payload := ClaudeCreateMessagePayload{ Model: "claude-3-5-sonnet-20240620", Messages: messages, Tools: []ToolDefinition{tool}, ToolChoice: ClaudeToolChoice{ Type: ToolChoiceTypeTool, Name: tool.Name, }, MaxTokens: config.MaxTokens, Temperature: config.Temperature, System: config.System, } payloadBytes, err := json.Marshal(payload) if err != nil { return *new(T), nil, fmt.Errorf("impossible to marshal payload: %w", err) } req, err := http.NewRequest( http.MethodPost, "https://api.anthropic.com/v1/messages", bytes.NewReader(payloadBytes), ) if err != nil { return *new(T), nil, fmt.Errorf("impossible to create request: %w", err) } req.Header.Set("x-api-key", os.Getenv("ANTHROPIC_API_KEY")) req.Header.Set("content-type", "application/json") req.Header.Set("anthropic-version", "2023-06-01") // send the request httpClient := http.Client{Timeout: 60 * time.Second} res, err := httpClient.Do(req) if err != nil { return *new(T), nil, fmt.Errorf("impossible to send request: %w", err) } defer res.Body.Close() resBody, err := io.ReadAll(res.Body) if err != nil { return *new(T), nil, fmt.Errorf("impossible to read all body of response: %w", err) } // unmarshal the response var response ClaudeCreateMessageResponse err = json.Unmarshal(resBody, &response) if err != nil { return *new(T), nil, fmt.Errorf("impossible to unmarshal response: %w", err) } // Get the tool response var toolInput T for _, content := range response.Content { if content.Type != ClaudeResponseContentTypeToolUse || content.Name != tool.Name { continue } inputBytes, err := json.Marshal(content.Input) if err != nil { return *new(T), &response, fmt.Errorf("impossible to marshal tool response: %w", err) } err = json.Unmarshal(inputBytes, &toolInput) if err != nil { return *new(T), &response, fmt.Errorf("impossible to unmarshal tool response: %w", err) } return toolInput, &response, nil } return toolInput, &response, fmt.Errorf("impossible to find tool response") } // Auxiliary types type ClaudeLLMMessage struct { Role string `json:"role"` Content string `json:"content"` } type ClaudeConfig struct { MaxTokens int Temperature float64 System string } type ClaudeCreateMessagePayload struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` Messages []ClaudeLLMMessage `json:"messages"` Stream bool `json:"stream,omitempty"` System string `json:"system,omitempty"` Temperature float64 `json:"temperature,omitempty"` ToolChoice ClaudeToolChoice `json:"tool_choice,omitempty"` Tools []ToolDefinition `json:"tools,omitempty"` } type ClaudeCreateMessageResponse struct { Content []struct { Type ClaudeResponseContentType `json:"type"` Text string `json:"text,omitempty"` ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Input map[string]interface{} `json:"input,omitempty"` } `json:"content"` Id string `json:"id"` Model string `json:"model"` Role string `json:"role"` StopReason string `json:"stop_reason"` StopSequence string `json:"stop_sequence"` Type string `json:"type"` Usage struct { InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"` } `json:"usage"` } type ClaudeToolChoice struct { Type ToolChoiceType `json:"type"` Name string `json:"name,omitempty"` } type ToolChoiceType string const ( ToolChoiceTypeAuto ToolChoiceType = "auto" ToolChoiceTypeCode ToolChoiceType = "any" ToolChoiceTypeTool ToolChoiceType = "tool" ) type ToolDefinition struct { Name string `json:"name"` Description string `json:"description"` InputSchema JSONSchema `json:"input_schema"` }
一些可以使用的示例:
调用函数,如我的示例所示,这可能是LLM“函数调用”最明显的用例。
为 API 请求生成结构化数据:您可以使用 LLM 根据用户输入为 API 请求生成 JSON 负载,确保数据符合所需的架构。
数据验证和转换:LLM 可用于验证输入数据并将其转换为结构化格式,然后可用于进一步处理或存储在数据库中。
通常,每当您有一些文本并想要提取或将其转换为特定结构,而不希望训练自己的自定义分类器时。
就是这样!请告诉我这是否有用,或者分享您如何在申请中使用法学硕士。
Bis demnächst!
~马丁
注释:
这最初发布在我的博客上:https://blog.embiem.me/unleash-your-home-hardware-processing-long-running-tasks-at-home
任何代码均在 MIT 许可证下按原样提供。
封面图片是人工智能生成的。
以上是如何在 Go 中一致地从 Claude 检索有效的 JSON的详细内容。更多信息请关注PHP中文网其他相关文章!