qwen function call 提示词模板

通义千问 function call 提示词模板

提示词出处: Qwen-Agent

提示词如下:

  • 英文
 Tools

## You have access to the following tools:

{tool_descs}

## When you need to call a tool, please insert the following command in your reply, which can be called zero or multiple times according to your needs:

✿FUNCTION✿: The tool to use, should be one of [{tool_names}]
✿ARGS✿: The input of the tool
✿RESULT✿: The result returned by the tool. The image needs to be rendered as ![](url)
✿RETURN✿: Reply based on tool result
  • 中文
# 工具

## 你拥有如下工具:

{tool_descs}

## 你可以在回复中插入零次、一次或多次以下命令以调用工具:

✿FUNCTION✿: 工具名称,必须是[{tool_names}]之一。
✿ARGS✿: 工具输入
✿RESULT✿: 工具结果,需将图片用![](url)渲染出来。
✿RETURN✿: 根据工具结果进行回复

这里我用 golang 写一个示例,实测表现稳定,支持本地 qwen模型

 `(ollama 的API 需要指定好终止词 ✿RESULT✿)`
 温馨提示: 演示代码 实现的是一个终端工具,测试请慎重,以免AI给你删除了不该删的文件😄
package main

import (
	"bufio"
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"
)

// QWEN_API_KEY 通义千问的 API Key
// (这里获取 https://help.aliyun.com/document_detail/2579563.html
const QWEN_API_KEY = "sk-yourapi-key"

/* 先封装一个Agent Tool 的 interface */

type ToolArgs struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Type        string `json:"type"`
}

// Tool 封装一个 Agent Tool 的 interface
type Tool interface {
	// GetArgs 获取工具参数
	GetArgs() []ToolArgs
	// GetName 获取工具名称
	GetName() string
	// GetDescription 获取工具描述
	GetDescription() string
	// Invoke 调用工具
	Invoke(args string) (string, error)
}

/* 这里实现一个可以调用终端的工具 */

type BashTool struct {
}
type BashToolArgs struct {
	Cmd string `json:"cmd"`
}

func (b BashTool) GetArgs() []ToolArgs {
	return []ToolArgs{
		{
			Name:        "cmd",
			Description: "The command to run",
			Type:        "string",
		},
	}
}

func (b BashTool) GetName() string {
	return "bash"
}

func (b BashTool) GetDescription() string {
	return "A bash tool that can run any bash command"
}

func (b BashTool) Invoke(args string) (string, error) {
	param := BashToolArgs{}
	err := json.Unmarshal([]byte(args), &param)
	if err != nil {
		return "", err
	}
	command := exec.Command("bash", "-c", param.Cmd)
	// 捕获命令的输出
	output, err := command.CombinedOutput()

	if err != nil {
		// 如果有错误,打印错误信息
		fmt.Printf("Error: %s\n", err)
		return "", err
	}
	return string(output), nil
}

/* 这里是提示词生成与解析部分代码 */

const fnTag = "✿FUNCTION✿"
const argsTag = "✿ARGS✿"
const resultTag = "✿RESULT✿"
const returnTag = "✿RETURN✿"

var __prompt = `
# 运维专员
你是一个运维专员,你的任务是帮助用户解决各种运维问题。你可以使用如下工具:
%s
## 你可以在回复中插入零次、一次或多次以下命令以调用工具:

%s: 工具名称,必须是[%s]之一。
%s: 工具输入
%s: 工具结果,需将图片用![](url)渲染出来。
%s: 根据工具结果进行回复

`

// 声明一个全局变量来保存工具列表
var globalTools = []Tool{
	BashTool{},
}

// 构造工具的提示词
func getToolPrompt() string {
	var str = ""
	for _, tool := range globalTools {
		args := tool.GetArgs()
		argsStr, _ := json.Marshal(args)
		str += fmt.Sprintf("%s: %s 输入参数: %s   \n", tool.GetName(), tool.GetDescription(), string(argsStr))
	}
	return str
}
func getToolNames() []string {
	var arr = []string{}
	for _, tool := range globalTools {
		arr = append(arr, tool.GetName())
	}
	return arr
}

// ExecFn 执行工具
func ExecFn(fnName string, args string) string {
	for _, tool := range globalTools {
		if tool.GetName() == fnName {
			res, err := tool.Invoke(args)
			if err != nil {
				fmt.Printf("Error: %v", err)
				res = fmt.Sprintf("%v", err)
			}
			return fmt.Sprintf("%s:%s", resultTag, res)
		}
	}
	return fmt.Sprintf("%s:%s", resultTag, "tool not found")
}

var prompt = ""

func init() {
	// 初始化提示词
	prompt = fmt.Sprintf(__prompt, getToolPrompt(), fnTag, strings.Join(getToolNames(), ","), argsTag, resultTag, returnTag)
}

func GetPrompt() string {
	return prompt
}

type ToolAiResult struct {
	FnName string
	Args   string
}

// ParseResult 解析llm结果,有工具调用需求则会返回 ToolAiResult
func ParseResult(result string) *ToolAiResult {
	f := strings.Index(result, fnTag)
	i := strings.Index(result, argsTag)
	j := strings.Index(result, resultTag)
	k := strings.Index(result, returnTag)
	if f == -1 {
		return nil
	}
	if j == -1 {
		j = k
		if j == -1 {
			j = len(result)
		}
	}
	return &ToolAiResult{
		FnName: strings.Trim(result[f+len(fnTag):i], ":\n "),
		Args:   strings.Trim(result[i+len(argsTag):j], ":\n "),
	}
}

// CreateRoleMessage 创建角色消息
func CreateRoleMessage(msg string, role string) string {
	return fmt.Sprintf("<|im_start|>%s\n%s<|im_end|>\n", role, msg)
}

// CreateRoleMessageNoEnd 创建角色消息 (不包含 end token)
func CreateRoleMessageNoEnd(msg string, role string) string {
	return fmt.Sprintf("<|im_start|>%s\n%s", role, msg)
}

/* 这里封装一下 http请求 */

// CustomHTTPClient 是一个自定义的HTTP客户端结构
type CustomHTTPClient struct {
	client *http.Client
}

// NewCustomHTTPClient 创建一个新的自定义HTTP客户端实例
func NewCustomHTTPClient(timeout time.Duration) *CustomHTTPClient {
	return &CustomHTTPClient{
		client: &http.Client{
			Timeout: timeout,
		},
	}
}

// Do 发送一个HTTP Post 请求
func (c *CustomHTTPClient) Post(url string, data any, headers map[string]string) ([]byte, error) {
	jsonData, err := json.Marshal(data) // 假设data是可以被json.Marshal序列化的
	if err != nil {
		return nil, fmt.Errorf("marshaling data: %w", err)
	}
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("creating request: %w", err)
	}

	// 设置请求头
	for key, value := range headers {
		req.Header.Add(key, value)
	}

	// 使用上下文以控制超时
	ctx, cancel := context.WithTimeout(context.Background(), c.client.Timeout)
	defer cancel()

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		return nil, fmt.Errorf("executing request: %w", err)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("reading response body: %w", err)
	}

	if resp.StatusCode >= 400 {
		return nil, fmt.Errorf("server returned error status: %d", resp.StatusCode)
	}
	return body, nil
}

/*  对接qwen API */

var client = NewCustomHTTPClient(120 * time.Second)

type QwenParamOptions struct {
	ResultFormat      string `json:"result_format"`
	IncrementalOutput bool   `json:"incremental_output,omitempty"` // true 表示只返回增量数据(用于流式返回
}
type QwenParamInput struct {
	Prompt string `json:"prompt"`
}
type QwenParam struct {
	Model      string           `json:"model"`
	Parameters QwenParamOptions `json:"parameters"`
	Input      QwenParamInput   `json:"input"`
}

type QwenResult struct {
	Output struct {
		Choices []struct {
			FinishReason string `json:"finish_reason"`
			Message      struct {
				Role    string `json:"role"`
				Content string `json:"content"`
			} `json:"message"`
		} `json:"choices"`
	} `json:"output"`
	Usage struct {
		Plugins struct {
		} `json:"plugins"`
		TotalTokens  int `json:"total_tokens"`
		OutputTokens int `json:"output_tokens"`
		InputTokens  int `json:"input_tokens"`
	} `json:"usage"`
	RequestId string `json:"request_id"`
}

// RequestLLM
/**
快速发起 llm 请求
*/
func RequestLLM(message string) (string, error) {
	body := QwenParam{
		Model: "qwen-plus",
		Parameters: QwenParamOptions{
			ResultFormat: "message",
			//IncrementalOutput: true,
		},
		Input: QwenParamInput{
			Prompt: message,
		},
	}
	res, err := client.Post("https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation", body, map[string]string{
		"Content-Type":  "application/json",
		"Accept":        "application/json", // 如果使用流式 text/event-stream
		"Authorization": "Bearer " + QWEN_API_KEY,
		//"X-DashScope-SSE": "enable", // 流式才需要打开
	})
	if err != nil {
		return "", err
	}
	r := QwenResult{}
	err = json.Unmarshal(res, &r)
	if err != nil {
		fmt.Println(string(res))
		return "", err
	}
	if len(r.Output.Choices) > 0 {
		return r.Output.Choices[0].Message.Content, nil
	} else {
		fmt.Println(string(res))
		return "", fmt.Errorf("no choices")
	}
}

func main() {
	fmt.Print("assistant: 我是您终端助理,请告诉我您需要做什么?\nuser: ")
	var prompt = CreateRoleMessage(GetPrompt(), "system")
	for {
		reader := bufio.NewReader(os.Stdin)
		input, _ := reader.ReadString('\n')
		input = strings.TrimRight(input, "\r\n")
		if input == "exit" {
			break
		}
		prompt += CreateRoleMessage(input, "user")
		prompt += CreateRoleMessageNoEnd("", "assistant")
		for {
			fmt.Printf("--------传入的prompt: \n%s\n\n", prompt)
			result, err := RequestLLM(prompt)
			fmt.Printf("--------LLM返回: \n%s\n", result)
			if err != nil {
				fmt.Println("assistant: ", err)
				break
			}
			o := ParseResult(result)
			if o == nil {
				fmt.Println("assistant: ", result)
				break
			}
			res := ExecFn(o.FnName, o.Args)
			prompt += fmt.Sprintf("%s\n%s", result, res)
		}

		fmt.Print("\nuser: ")
	}

}

效果:

在这里插入图片描述

在这里插入图片描述

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值