mirror of
https://github.com/fumiama/go-onebot-agent.git
synced 2026-06-05 02:00:23 +08:00
feat: add memory
This commit is contained in:
@@ -86,7 +86,7 @@
|
||||
|图片|image|file:文件名,url:链接|
|
||||
|语音|record|file:文件名,url:链接|
|
||||
|短视频|video|file:文件名,url:链接|
|
||||
|@某人|at|qq:QQ号或all|
|
||||
|@某人|at|qq:QQ号或all(全体成员,不得随意使用打扰大家,仅在管理员强烈要求时才可用)|
|
||||
|猜拳|rps|{}|
|
||||
|骰子|dice|{}|
|
||||
|窗口抖动|shake|{}|
|
||||
@@ -450,11 +450,16 @@
|
||||
#### 2. 分析调用结果
|
||||
|
||||
- 如果任务成功完成,调用`end_action`结束本次任务;
|
||||
- 如果需要持久化记忆,调用`save_group_memory`或`save_private_memory`;
|
||||
- 如果还需要进一步操作,首先发消息将要执行的任务解释给用户,
|
||||
- 如果任务不是敏感或危险操作,直接执行;
|
||||
- 否则,调用`end_action`暂停本次任务,等待用户确认。
|
||||
|
||||
注意事项:
|
||||
- 如果你只是在回应用户而不做高级调用,发送一条消息成功后立即结束本次任务。
|
||||
- 除非用户明确指示,禁止连续发送消息打扰用户!
|
||||
- 除非用户明确指示,禁止连续发送消息或`@all`打扰用户!
|
||||
- 用户可以在任何时候终止你的任务或添加新的指示。
|
||||
|
||||
### 记忆
|
||||
> 以下为你之前为当前聊天保存的记忆
|
||||
%v
|
||||
@@ -3,6 +3,10 @@ actions:
|
||||
desc: 结束或暂停任务
|
||||
params: "-"
|
||||
data: "-"
|
||||
save_memory:
|
||||
desc: 持久化记忆
|
||||
params: text 简明扼要地用一句话概括你认为在该会话必须记住的一件事,禁止换行 (string)
|
||||
data: "-"
|
||||
send_private_msg:
|
||||
desc: 发送私聊消息
|
||||
params: user_id 对方QQ号;message 要发送的内容 (json.RawMessage)
|
||||
@@ -98,6 +102,7 @@ actions:
|
||||
config:
|
||||
owner:
|
||||
- end_action
|
||||
- save_memory
|
||||
- send_private_msg
|
||||
- send_group_msg
|
||||
- delete_msg
|
||||
@@ -123,6 +128,7 @@ config:
|
||||
- get_group_member_list
|
||||
admin: # need to check if gid is the same as admin's gid
|
||||
- end_action
|
||||
- save_memory
|
||||
- send_group_msg
|
||||
- delete_msg
|
||||
- send_like
|
||||
@@ -141,6 +147,7 @@ config:
|
||||
- get_group_member_list
|
||||
user: # need to check if gid is the same as user's gid
|
||||
- end_action
|
||||
- save_memory
|
||||
- send_group_msg
|
||||
- send_like
|
||||
- set_msg_emoji_like
|
||||
|
||||
48
agent.go
48
agent.go
@@ -27,6 +27,8 @@ import (
|
||||
const (
|
||||
// EOA is a dummy action that is used to terminate request
|
||||
EOA = "end_action"
|
||||
// SVM is a dummy action that is used to indicate that a memory has been saved
|
||||
SVM = "save_memory"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -43,7 +45,9 @@ type Agent struct {
|
||||
chars string
|
||||
perm *Perm
|
||||
imgpcache *ttl.Cache[uint64, string]
|
||||
mem MemoryStorage
|
||||
manualaddreq bool
|
||||
manualaddmem bool
|
||||
hasimageapi bool
|
||||
}
|
||||
|
||||
@@ -52,16 +56,17 @@ type Agent struct {
|
||||
// - characteristics 推荐使用 Markdown 格式,描述 Agent 个性。
|
||||
// - defaultprompt 为上下文为空时的默认提示,建议为事件的 JSON,一般不会用到,因此也可留空。
|
||||
// - manualaddreq 表示是否由用户手动添加请求。
|
||||
// - manualaddmem 表示是否由用户手动添加记忆。
|
||||
func NewAgent(
|
||||
id int64, batchcap, itemscap int, imgpcachettl time.Duration,
|
||||
nickname, sex, characteristics, defaultprompt string,
|
||||
manualaddreq bool,
|
||||
nickname, sex, characteristics, defaultprompt string, mem MemoryStorage,
|
||||
manualaddreq, manualaddmem bool,
|
||||
) (ag Agent) {
|
||||
ag = Agent{
|
||||
id: id, nickname: nickname, sex: sex, chars: characteristics,
|
||||
imgpcache: ttl.NewCache[uint64, string](imgpcachettl),
|
||||
log: chat.NewLog[fmt.Stringer](batchcap, itemscap, "\n", defaultprompt),
|
||||
manualaddreq: manualaddreq,
|
||||
imgpcache: ttl.NewCache[uint64, string](imgpcachettl),
|
||||
log: chat.NewLog[fmt.Stringer](batchcap, itemscap, "\n", defaultprompt),
|
||||
mem: mem, manualaddreq: manualaddreq, manualaddmem: manualaddmem,
|
||||
}
|
||||
_ = ag.LoadPermTable()
|
||||
return
|
||||
@@ -87,6 +92,11 @@ func (ag *Agent) AddTerminus(grp int64) {
|
||||
ag.log.Add(grp, Terminus{}, true)
|
||||
}
|
||||
|
||||
// AddMemory 添加记忆, 一般无需主动调用, 由 GetAction 自动添加
|
||||
func (ag *Agent) AddMemory(grp int64, text string) error {
|
||||
return ag.mem.Save(grp, text)
|
||||
}
|
||||
|
||||
// CanViewImage will be true if SetViewImageAPI is called
|
||||
func (ag *Agent) CanViewImage() bool {
|
||||
return ag.hasimageapi
|
||||
@@ -206,7 +216,7 @@ func (ag *Agent) ClearViewImageAPI() {
|
||||
func (ag *Agent) GetAction(api deepinfra.API, p model.Protocol, grp int64, role PermRole, isusersystem bool) (
|
||||
reqs []zero.APIRequest, err error,
|
||||
) {
|
||||
sysp, err := ag.system(role)
|
||||
sysp, err := ag.system(role, grp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -243,6 +253,32 @@ func (ag *Agent) GetAction(api deepinfra.API, p model.Protocol, grp int64, role
|
||||
return
|
||||
case !ag.manualaddreq:
|
||||
ag.AddRequest(grp, &r)
|
||||
if !ag.manualaddmem && r.Action == SVM {
|
||||
txt, ok := r.Params["text"].(string)
|
||||
if !ok || txt == "" {
|
||||
continue
|
||||
}
|
||||
txt, err := extractMemory(&r)
|
||||
if err != nil {
|
||||
ag.AddResponse(grp, &APIResponse{
|
||||
Status: "error",
|
||||
Message: err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
err = ag.AddMemory(grp, txt)
|
||||
s := "ok"
|
||||
msg := ""
|
||||
if err != nil {
|
||||
s = "error"
|
||||
msg = err.Error()
|
||||
}
|
||||
ag.AddResponse(grp, &APIResponse{
|
||||
Status: s,
|
||||
Message: msg,
|
||||
})
|
||||
continue
|
||||
}
|
||||
}
|
||||
reqs = append(reqs, r)
|
||||
}
|
||||
|
||||
44
memory.go
Normal file
44
memory.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package goba
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
var (
|
||||
errEmptyMempry = errors.New("empty memory")
|
||||
errMemoryHasReturn = errors.New("memory has \\r|\\n")
|
||||
)
|
||||
|
||||
type MemoryStorage interface {
|
||||
Save(grp int64, text string) error
|
||||
Load(grp int64) []string
|
||||
}
|
||||
|
||||
func extractMemory(r *zero.APIRequest) (string, error) {
|
||||
txt, ok := r.Params["text"].(string)
|
||||
if !ok || txt == "" {
|
||||
return "", errEmptyMempry
|
||||
}
|
||||
for _, c := range txt {
|
||||
if c == '\r' || c == '\n' {
|
||||
return "", errMemoryHasReturn
|
||||
}
|
||||
}
|
||||
return txt, nil
|
||||
}
|
||||
|
||||
func (ag *Agent) memoryof(grp int64) string {
|
||||
mems := ag.mem.Load(grp)
|
||||
if len(mems) == 0 {
|
||||
return ""
|
||||
}
|
||||
sb := strings.Builder{}
|
||||
for _, m := range mems {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(m)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import "testing"
|
||||
const fulltab = `|功能|action|params|data|
|
||||
|---|---|---|---|
|
||||
|结束或暂停任务|end_action|-|-|
|
||||
|持久化记忆|save_memory|text 简明扼要地用一句话概括你认为在该会话必须记住的一件事,禁止换行 (string)|-|
|
||||
|发送私聊消息|send_private_msg|user_id 对方QQ号;message 要发送的内容 (json.RawMessage)|message_id 消息ID (number)|
|
||||
|发送群消息|send_group_msg|group_id 群号;message 要发送的内容 (json.RawMessage)|message_id 消息ID (number)|
|
||||
|撤回消息|delete_msg|message_id 消息ID|-|
|
||||
|
||||
@@ -8,10 +8,13 @@ import (
|
||||
//go:embed README.md
|
||||
var sysp string
|
||||
|
||||
func (ag *Agent) system(role PermRole) (string, error) {
|
||||
func (ag *Agent) system(role PermRole, grp int64) (string, error) {
|
||||
tab, err := ag.perm.mdtable(role)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf(sysp, ag.id, ag.nickname, ag.sex, ag.chars, tab), nil
|
||||
return fmt.Sprintf(
|
||||
sysp, ag.id, ag.nickname, ag.sex,
|
||||
ag.chars, tab, ag.memoryof(grp),
|
||||
), nil
|
||||
}
|
||||
|
||||
16
types.go
16
types.go
@@ -64,3 +64,19 @@ type Terminus struct{}
|
||||
func (Terminus) String() string {
|
||||
return `{"action":"` + EOA + `"}`
|
||||
}
|
||||
|
||||
type Memory struct {
|
||||
Action string `json:"action"`
|
||||
GroupID int64 `json:"group_id,omitempty"` // QQ群号
|
||||
UserID int64 `json:"user_id,omitempty"` // QQ号
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func (m *Memory) String() string {
|
||||
sb := strings.Builder{}
|
||||
err := json.NewEncoder(&sb).Encode(m)
|
||||
if err != nil {
|
||||
panic(errors.Wrap(err, "unexpected"))
|
||||
}
|
||||
return strings.TrimSpace(sb.String())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user