mirror of
https://github.com/fumiama/go-onebot-agent.git
synced 2026-06-11 21:50:25 +08:00
optimize: use deepinfra.chat
This commit is contained in:
61
agent.go
61
agent.go
@@ -2,60 +2,47 @@ package goba
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/fumiama/deepinfra"
|
"github.com/fumiama/deepinfra"
|
||||||
|
"github.com/fumiama/deepinfra/chat"
|
||||||
"github.com/fumiama/deepinfra/model"
|
"github.com/fumiama/deepinfra/model"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
zero "github.com/wdvxdr1123/ZeroBot"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrZeroOrNegContextCap = errors.New("ctxcap <= 0")
|
ErrPermissionDenied = errors.New("permission denied")
|
||||||
ErrZeroOrNegEventCap = errors.New("evcap <= 0")
|
|
||||||
ErrPermissionDenied = errors.New("permission denied")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Agent is a OneBot event context, it is recommended to create one agent
|
// Agent is a OneBot event context, it is recommended to create one agent
|
||||||
// per group or per user.
|
// per group or per user.
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
|
log chat.Log[fmt.Stringer]
|
||||||
id int64
|
id int64
|
||||||
nickname, sex string
|
nickname, sex string
|
||||||
chars string
|
chars string
|
||||||
ctxcap, evcap int
|
perm *Perm
|
||||||
// 64 bits or 32 bits gap
|
|
||||||
ctx generalctx
|
|
||||||
perm *Perm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAgent characteristics 最好是 MD 格式
|
// NewAgent characteristics 最好是 MD 格式, defaultprompt 是上下文为空时的默认项, 建议为 Event JSON
|
||||||
func NewAgent(id int64, nickname, sex string, characteristics string) Agent {
|
func NewAgent(
|
||||||
|
id int64, batchcap, itemscap int,
|
||||||
|
nickname, sex, characteristics, defaultprompt string,
|
||||||
|
) Agent {
|
||||||
return Agent{
|
return Agent{
|
||||||
id: id, nickname: nickname, sex: sex, chars: characteristics,
|
id: id, nickname: nickname, sex: sex, chars: characteristics,
|
||||||
ctxcap: 16, evcap: 8,
|
log: chat.NewLog[fmt.Stringer](batchcap, itemscap, "\n", defaultprompt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ag *Agent) SetContextCap(n int) {
|
func (ag *Agent) AddEvent(grp int64, ev *Event) {
|
||||||
if n <= 0 {
|
ag.log.Add(grp, ev, false)
|
||||||
panic(ErrZeroOrNegContextCap)
|
|
||||||
}
|
|
||||||
ag.ctxcap = n
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ag *Agent) SetEventCap(n int) {
|
func (ag *Agent) AddRequest(grp int64, req *zero.APIRequest) {
|
||||||
if n <= 0 {
|
ag.log.Add(grp, req, true)
|
||||||
panic(ErrZeroOrNegEventCap)
|
|
||||||
}
|
|
||||||
ag.evcap = n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ag *Agent) AddEvent(ev *Event) {
|
|
||||||
addctx(&ag.ctx, ev, ag.ctxcap, ag.evcap)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ag *Agent) AddRequest(req *zero.APIRequest) {
|
|
||||||
addctx(&ag.ctx, req, ag.ctxcap, ag.evcap)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAction get OneBot CallAction from LLM and add it to context.
|
// GetAction get OneBot CallAction from LLM and add it to context.
|
||||||
@@ -70,24 +57,15 @@ func (ag *Agent) AddRequest(req *zero.APIRequest) {
|
|||||||
// with complete req, caller may decide whether to use this req by themselves.
|
// 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
|
// Whatever, this req will not be added into the context. You may call
|
||||||
// AddRequest to add it but it is not recommended.
|
// AddRequest to add it but it is not recommended.
|
||||||
func (ag *Agent) GetAction(api deepinfra.API, m model.Protocol, role PermRole) (
|
func (ag *Agent) GetAction(api deepinfra.API, p model.Protocol, grp int64, role PermRole, isusersystem bool) (
|
||||||
req zero.APIRequest, err error,
|
req zero.APIRequest, err error,
|
||||||
) {
|
) {
|
||||||
p, err := ag.system(role)
|
sysp, err := ag.system(role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.System(p)
|
|
||||||
|
|
||||||
ag.ctx.mu.Lock()
|
m := ag.log.Modelize(p, grp, sysp, isusersystem)
|
||||||
for i, evs := range ag.ctx.ctx {
|
|
||||||
if i%2 == 0 { // is user input
|
|
||||||
m.User(evs.String())
|
|
||||||
} else { // is agent callback
|
|
||||||
m.Assistant(evs.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ag.ctx.mu.Unlock()
|
|
||||||
|
|
||||||
resp, err := api.Request(m)
|
resp, err := api.Request(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -101,7 +79,8 @@ func (ag *Agent) GetAction(api deepinfra.API, m model.Protocol, role PermRole) (
|
|||||||
if !ag.perm.allow(role, req.Action) {
|
if !ag.perm.allow(role, req.Action) {
|
||||||
err = errors.Wrap(ErrPermissionDenied, req.Action)
|
err = errors.Wrap(ErrPermissionDenied, req.Action)
|
||||||
} else {
|
} else {
|
||||||
ag.AddRequest(&req)
|
ag.AddRequest(grp, &req)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
72
ctx.go
72
ctx.go
@@ -1,72 +0,0 @@
|
|||||||
package goba
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidFirstContextType = errors.New("invalid first context type")
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
eventType = reflect.TypeOf(Event{})
|
|
||||||
// requestType = reflect.TypeOf(zero.APIRequest{})
|
|
||||||
)
|
|
||||||
|
|
||||||
type generalctx struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
ctx []events
|
|
||||||
}
|
|
||||||
|
|
||||||
func addctx[T Event | zero.APIRequest](
|
|
||||||
ctx *generalctx, v *T, ctxcap, evcap int,
|
|
||||||
) {
|
|
||||||
ctx.mu.Lock()
|
|
||||||
defer ctx.mu.Unlock()
|
|
||||||
|
|
||||||
typ := reflect.TypeOf(v).Elem()
|
|
||||||
|
|
||||||
if len(ctx.ctx) == 0 {
|
|
||||||
// must triggered by event
|
|
||||||
if !typ.AssignableTo(eventType) {
|
|
||||||
panic(errors.Wrap(ErrInvalidFirstContextType, typ.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctxcap & evcap must > 0, no need to check
|
|
||||||
evs := make(events, 1, evcap)
|
|
||||||
evs[0] = v
|
|
||||||
ctx.ctx = make([]events, 1, ctxcap)
|
|
||||||
ctx.ctx[0] = evs
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Get the last events slice
|
|
||||||
lastEvents := &ctx.ctx[len(ctx.ctx)-1]
|
|
||||||
|
|
||||||
// Check if the type matches the first element of the last events
|
|
||||||
firstElemType := reflect.TypeOf((*lastEvents)[0]).Elem()
|
|
||||||
if typ.AssignableTo(firstElemType) {
|
|
||||||
// Same type, add to the last events
|
|
||||||
if len(*lastEvents) >= evcap {
|
|
||||||
// Shift elements forward by 1 (discard first element)
|
|
||||||
copy((*lastEvents)[:], (*lastEvents)[1:])
|
|
||||||
(*lastEvents)[len(*lastEvents)-1] = any(v)
|
|
||||||
} else {
|
|
||||||
*lastEvents = append(*lastEvents, any(v))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Different type or empty last events, create new events slice
|
|
||||||
if len(ctx.ctx) >= ctxcap {
|
|
||||||
// Shift elements forward by 2 (user-assistant pair)
|
|
||||||
copy(ctx.ctx[:], ctx.ctx[2:])
|
|
||||||
ctx.ctx = ctx.ctx[:len(ctx.ctx)-2]
|
|
||||||
}
|
|
||||||
evs := make(events, 1, evcap)
|
|
||||||
evs[0] = v
|
|
||||||
ctx.ctx = append(ctx.ctx, evs)
|
|
||||||
}
|
|
||||||
4
go.mod
4
go.mod
@@ -4,9 +4,9 @@ go 1.20
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||||
github.com/fumiama/deepinfra v0.0.0-20250910144855-27a4e697106d
|
github.com/fumiama/deepinfra v0.0.0-20250920170049-e3d1b92cc3a1
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250919145948-e8ffbbc995ac
|
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
8
go.sum
8
go.sum
@@ -5,8 +5,8 @@ github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9o
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fumiama/deepinfra v0.0.0-20250910144855-27a4e697106d h1:iGxnST620IHrJ47DXkjzrZJ2rskBogWze+UyvnAxT6g=
|
github.com/fumiama/deepinfra v0.0.0-20250920170049-e3d1b92cc3a1 h1:6PglFpNVm3DalGyRldacW2/v4jGWwn3v3q1tr2PhbVQ=
|
||||||
github.com/fumiama/deepinfra v0.0.0-20250910144855-27a4e697106d/go.mod h1:wW05PQSn8mo1mZIoa6LBUE+3xIBjkoONvnfPTV5ZOhY=
|
github.com/fumiama/deepinfra v0.0.0-20250920170049-e3d1b92cc3a1/go.mod h1:wW05PQSn8mo1mZIoa6LBUE+3xIBjkoONvnfPTV5ZOhY=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
@@ -22,8 +22,8 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
|||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250919145948-e8ffbbc995ac h1:XWpJQrUg75qUMwyKZX1jjK4ZziS5R4m/yVv5JCee3/s=
|
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444 h1:7aYFXzvVr2zuxBvqrGaJb24Z4W12aXBdW8DuE1mteE4=
|
||||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250919145948-e8ffbbc995ac/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M=
|
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250921063512-13752a73d444/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
|||||||
22
types.go
22
types.go
@@ -8,19 +8,6 @@ import (
|
|||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
zero "github.com/wdvxdr1123/ZeroBot"
|
||||||
)
|
)
|
||||||
|
|
||||||
type events []any
|
|
||||||
|
|
||||||
func (evs events) String() string {
|
|
||||||
sb := strings.Builder{}
|
|
||||||
for _, ev := range evs {
|
|
||||||
err := json.NewEncoder(&sb).Encode(ev) // has been terminated with '\n'
|
|
||||||
if err != nil {
|
|
||||||
panic(errors.Wrap(err, "unexpected"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(sb.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event is the simplified OneBot event that dumped to the agent in JSON format
|
// Event is the simplified OneBot event that dumped to the agent in JSON format
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Time int64 `json:"time"` // 事件发生的时间戳
|
Time int64 `json:"time"` // 事件发生的时间戳
|
||||||
@@ -41,3 +28,12 @@ type Event struct {
|
|||||||
Sender *zero.User `json:"sender,omitempty"` // 事件发送者个人信息
|
Sender *zero.User `json:"sender,omitempty"` // 事件发送者个人信息
|
||||||
Message json.RawMessage `json:"message,omitempty"` // JSON 格式的消息内容
|
Message json.RawMessage `json:"message,omitempty"` // JSON 格式的消息内容
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ev *Event) String() string {
|
||||||
|
sb := strings.Builder{}
|
||||||
|
err := json.NewEncoder(&sb).Encode(ev)
|
||||||
|
if err != nil {
|
||||||
|
panic(errors.Wrap(err, "unexpected"))
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(sb.String())
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user