diff --git a/README.md b/README.md index 52b546f..b89de43 100644 --- a/README.md +++ b/README.md @@ -13,55 +13,50 @@ ## OneBot 11 协议 > 输入输出均为 JSON 格式的一系列 Object,代表一系列事件或响应,以换行符分隔。 -> 所有可能的字段由对应的 Golang 结构体给出。 +> 所有可能的字段如下,type 由对应的 Golang 类型给出。 ### 输入 事件 (Event) 是输入的基本单位, -```go -type Event struct { - Time int64 `json:"time"` // 事件发生的时间戳 - PostType string `json:"post_type"` // 上报类型: message / notice / request - MessageType string `json:"message_type"` // message 类型: group / private - SubType string `json:"sub_type"` // message 子类型: normal (一般消息) / notice (灰色小字通知) - MessageID int64 `json:"message_id"` // 消息 ID, 唯一标识该事件 - GroupID int64 `json:"group_id"` // QQ群号 - UserID int64 `json:"user_id"` // 事件发送者QQ号 - TargetID int64 `json:"target_id"` - SelfID int64 `json:"self_id"` // 收到事件的QQ号 (你的ID) - NoticeType string `json:"notice_type,omitempty"` - OperatorID int64 `json:"operator_id"` // This field is used for Notice Event - File *File `json:"file,omitempty"` - RequestType string `json:"request_type,omitempty"` - Flag string `json:"flag,omitempty"` - Comment string `json:"comment,omitempty"` // This field is used for Request Event - Sender *User `json:"sender,omitempty"` // 事件发送者个人信息 - Message json.RawMessage `json:"message,omitempty"` // JSON 格式的消息内容 -} -``` +|key|type|说明| +|---|---|---| +|time|int64|事件发生的时间戳| +|post_type|string|上报类型: message / notice / request| +|message_type|string|message 类型: group / private| +|sub_type|string|message 子类型: normal (一般消息) / notice (灰色小字通知)| +|message_id|int64|消息 ID, 唯一标识该事件| +|group_id|int64|QQ群号| +|user_id|int64|事件发送者QQ号| +|target_id|int64|后述| +|self_id|int64|收到事件的QQ号 (你的ID)| +|notice_type|string|后述| +|operator_id|int64|For Notice Event| +|file|*File|后述| +|request_type|string|后述| +|flag|string|后述| +|comment|string|For Request Event| +|sender|*User|事件发送者个人信息| +|message|json.RawMessage|JSON 格式的消息内容| + 其中,文件 (File) 标识一个聊天文件, -```go -type File struct { - ID string `json:"id"` - Name string `json:"name"` - Size int64 `json:"size"` - BusID int64 `json:"busid"` -} -``` +|key|type| +|---|---| +|id|string| +|name|string| +|size|int64| + 用户 (User) 标识一个QQ用户, -```go -type User struct { - ID int64 `json:"user_id"` - NickName string `json:"nickname"` - Sex string `json:"sex"` // "male"、"female"、"unknown" - Age int `json:"age"` - Area string `json:"area"` // 地区 - // 以下为群聊特有字段 - Card string `json:"card"` // 群名片/备注 - Title string `json:"title"` // 专属头衔 - Level string `json:"level"` // 群聊等级 - Role string `json:"role"` // "owner"、"admin"、"member" -} -``` +|key|type|说明| +|---|---|---| +|user_id|int64|用户QQ号| +|nickname|string|昵称| +|sex|string|"male"、"female"、"unknown"| +|age|int|年龄| +|area|string|地区| +|card|string|群名片/备注(群聊特有)| +|title|string|专属头衔(群聊特有)| +|level|string|群聊等级(群聊特有)| +|role|string|"owner"、"admin"、"member"(群聊特有)| + #### 详细事件种类 |类型|post_type|message_type|sub_type|message_id|group_id|user_id|target_id|self_id|notice_type|operator_id|file|request_type|flag|comment|sender|message| @@ -113,4 +108,4 @@ type User struct { ``` 你可以调用的全部 API 如下表。注意:即使之前的记录显示你曾调用过某 API,但如果现在列表中不存在此 API,你就不能调用。 -%v +%v \ No newline at end of file diff --git a/agent.go b/agent.go index 63fab72..914d9e0 100644 --- a/agent.go +++ b/agent.go @@ -4,7 +4,7 @@ package goba import ( "encoding/json" "fmt" - "io" + "strings" "github.com/fumiama/deepinfra" "github.com/fumiama/deepinfra/chat" @@ -26,16 +26,23 @@ type Agent struct { nickname, sex string chars string perm *Perm + manualaddreq bool } -// NewAgent characteristics 最好是 MD 格式, defaultprompt 是上下文为空时的默认项, 建议为 Event JSON +// NewAgent 创建一个 Agent 实例。 +// +// - characteristics 推荐使用 Markdown 格式,描述 Agent 个性。 +// - defaultprompt 为上下文为空时的默认提示,建议为事件的 JSON,一般不会用到,因此也可留空。 +// - manualaddreq 表示是否由用户手动添加请求。 func NewAgent( id int64, batchcap, itemscap int, nickname, sex, characteristics, defaultprompt string, + manualaddreq bool, ) (ag Agent) { ag = Agent{ id: id, nickname: nickname, sex: sex, chars: characteristics, - log: chat.NewLog[fmt.Stringer](batchcap, itemscap, "\n", defaultprompt), + log: chat.NewLog[fmt.Stringer](batchcap, itemscap, "\n", defaultprompt), + manualaddreq: manualaddreq, } _ = ag.LoadPermTable() return @@ -55,16 +62,12 @@ func (ag *Agent) AddRequest(grp int64, req *zero.APIRequest) { // // Note: // -// 1. The response may be empty, meaning that LLM do not want -// to react with these events. In this case, this function will -// return io.EOF and the context will be left no change. -// -// 2. If LLM returns an invalid action, ErrPermissionDenied will be returned -// with complete req, caller may decide whether to use this req by themselves. -// Whatever, this req will not be added into the context. You may call -// AddRequest to add it but it is not recommended. +// - If LLM returns an invalid action, ErrPermissionDenied will be returned +// with complete reqs before invalid call, caller may decide whether to use +// these reqs by themselves. Whatever, invalid req will not be added into +// the context. You may call AddRequest to add it but it is not recommended. func (ag *Agent) GetAction(api deepinfra.API, p model.Protocol, grp int64, role PermRole, isusersystem bool) ( - req zero.APIRequest, err error, + reqs []zero.APIRequest, err error, ) { sysp, err := ag.system(role) if err != nil { @@ -77,15 +80,25 @@ func (ag *Agent) GetAction(api deepinfra.API, p model.Protocol, grp int64, role if err != nil { return } - err = json.Unmarshal([]byte(resp), &req) - if err == nil && req.Action == "" { - err = io.EOF - } - - if !ag.perm.allow(role, req.Action) { - err = errors.Wrap(ErrPermissionDenied, req.Action) - } else { - ag.AddRequest(grp, &req) + reqs = make([]zero.APIRequest, 0, 2) + dec := json.NewDecoder(strings.NewReader(resp)) + for dec.More() { + r := zero.APIRequest{} + err = dec.Decode(&r) + if err != nil { + break + } + if r.Action == "" { + continue + } + switch { + case !ag.perm.allow(role, r.Action): + err = errors.Wrap(ErrPermissionDenied, r.Action) + return + case !ag.manualaddreq: + ag.AddRequest(grp, &r) + } + reqs = append(reqs, r) } return diff --git a/types.go b/types.go index 6cb1979..aa67515 100644 --- a/types.go +++ b/types.go @@ -10,17 +10,17 @@ import ( // Event is the simplified OneBot event that dumped to the agent in JSON format type Event struct { - Time int64 `json:"time"` // 事件发生的时间戳 - PostType string `json:"post_type"` // 上报类型: message / notice / request - MessageType string `json:"message_type"` // message 类型: group / private - SubType string `json:"sub_type"` // message 子类型: normal (一般消息) / notice (灰色小字通知) - MessageID int64 `json:"message_id"` // 消息 ID, 唯一标识该事件 - GroupID int64 `json:"group_id"` // QQ群号 - UserID int64 `json:"user_id"` // 事件发送者QQ号 - TargetID int64 `json:"target_id"` + Time int64 `json:"time"` // 事件发生的时间戳 + PostType string `json:"post_type"` // 上报类型: message / notice / request + MessageType string `json:"message_type"` // message 类型: group / private + SubType string `json:"sub_type,omitempty"` // message 子类型: normal (一般消息) / notice (灰色小字通知) + MessageID int64 `json:"message_id"` // 消息 ID, 唯一标识该事件 + GroupID int64 `json:"group_id,omitempty"` // QQ群号 + UserID int64 `json:"user_id"` // 事件发送者QQ号 + TargetID int64 `json:"target_id,omitempty"` SelfID int64 `json:"self_id"` // 收到事件的QQ号 (你的ID) NoticeType string `json:"notice_type,omitempty"` - OperatorID int64 `json:"operator_id"` // This field is used for Notice Event + OperatorID int64 `json:"operator_id,omitempty"` // This field is used for Notice Event File *zero.File `json:"file,omitempty"` RequestType string `json:"request_type,omitempty"` Flag string `json:"flag,omitempty"`