refactor: Agent驱动重构,整章写入替代场景拼接
This commit is contained in:
116
tools/read_chapter.go
Normal file
116
tools/read_chapter.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/voocel/agentcore/schema"
|
||||
"github.com/voocel/ainovel-cli/state"
|
||||
)
|
||||
|
||||
// ReadChapterTool 读取章节原文,让 Agent 能回读自己和前文的文字。
|
||||
type ReadChapterTool struct {
|
||||
store *state.Store
|
||||
}
|
||||
|
||||
func NewReadChapterTool(store *state.Store) *ReadChapterTool {
|
||||
return &ReadChapterTool{store: store}
|
||||
}
|
||||
|
||||
func (t *ReadChapterTool) Name() string { return "read_chapter" }
|
||||
func (t *ReadChapterTool) Description() string { return "读取章节原文。可读终稿、草稿,或提取角色对话片段" }
|
||||
func (t *ReadChapterTool) Label() string { return "读取章节" }
|
||||
|
||||
func (t *ReadChapterTool) Schema() map[string]any {
|
||||
return schema.Object(
|
||||
schema.Property("chapter", schema.Int("章节号(读单章时必填)")),
|
||||
schema.Property("from", schema.Int("起始章节号(读范围时使用)")),
|
||||
schema.Property("to", schema.Int("结束章节号(读范围时使用)")),
|
||||
schema.Property("source", schema.Enum("来源", "final", "draft")).Required(),
|
||||
schema.Property("character", schema.String("角色名(提取对话片段时使用)")),
|
||||
schema.Property("max_runes", schema.Int("每章最大字符数(范围读取时截取,默认 2000)")),
|
||||
)
|
||||
}
|
||||
|
||||
func (t *ReadChapterTool) Execute(_ context.Context, args json.RawMessage) (json.RawMessage, error) {
|
||||
var a struct {
|
||||
Chapter int `json:"chapter"`
|
||||
From int `json:"from"`
|
||||
To int `json:"to"`
|
||||
Source string `json:"source"`
|
||||
Character string `json:"character"`
|
||||
MaxRunes int `json:"max_runes"`
|
||||
}
|
||||
if err := json.Unmarshal(args, &a); err != nil {
|
||||
return nil, fmt.Errorf("invalid args: %w", err)
|
||||
}
|
||||
|
||||
// 模式 1:提取角色对话
|
||||
if a.Character != "" {
|
||||
chars, _ := t.store.LoadCharacters()
|
||||
var aliases []string
|
||||
for _, c := range chars {
|
||||
if c.Name == a.Character {
|
||||
aliases = c.Aliases
|
||||
break
|
||||
}
|
||||
}
|
||||
samples := t.store.ExtractDialogue(a.Character, aliases, 8)
|
||||
return json.Marshal(map[string]any{
|
||||
"character": a.Character,
|
||||
"samples": samples,
|
||||
})
|
||||
}
|
||||
|
||||
// 模式 2:范围读取
|
||||
if a.From > 0 && a.To > 0 {
|
||||
maxRunes := a.MaxRunes
|
||||
if maxRunes <= 0 {
|
||||
maxRunes = 2000
|
||||
}
|
||||
texts, err := t.store.LoadChapterRange(a.From, a.To, maxRunes)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load chapter range: %w", err)
|
||||
}
|
||||
return json.Marshal(map[string]any{
|
||||
"chapters": texts,
|
||||
"from": a.From,
|
||||
"to": a.To,
|
||||
})
|
||||
}
|
||||
|
||||
// 模式 3:单章读取
|
||||
if a.Chapter <= 0 {
|
||||
return nil, fmt.Errorf("chapter is required")
|
||||
}
|
||||
|
||||
var content string
|
||||
var err error
|
||||
switch a.Source {
|
||||
case "draft":
|
||||
content, err = t.store.LoadDraft(a.Chapter)
|
||||
default: // final
|
||||
content, err = t.store.LoadChapterText(a.Chapter)
|
||||
if (err == nil && content == "") {
|
||||
// 回退到草稿
|
||||
content, err = t.store.LoadDraft(a.Chapter)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read chapter %d: %w", a.Chapter, err)
|
||||
}
|
||||
if content == "" {
|
||||
return json.Marshal(map[string]any{
|
||||
"chapter": a.Chapter,
|
||||
"content": "",
|
||||
"note": "章节不存在",
|
||||
})
|
||||
}
|
||||
|
||||
return json.Marshal(map[string]any{
|
||||
"chapter": a.Chapter,
|
||||
"content": content,
|
||||
"word_count": len([]rune(content)),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user