更新 README
This commit is contained in:
parent
6bd0544f45
commit
456498d5ce
18
README.md
18
README.md
@ -1,15 +1,27 @@
|
||||
#### 1. AI agent
|
||||
|
||||
Agent模块负责处理request工作流,生成prompt与LLM交互。
|
||||
|
||||
> Agent代码:
|
||||
> <http://ishangsf.com:8100/ken/agent>
|
||||
>
|
||||
> websocket接口地址:
|
||||
> <ws://112.74.39.99:8080/ws>
|
||||
>
|
||||
> 接口文档:
|
||||
> <http://ishangsf.com:8100/ken/agent/src/branch/main/README.md>
|
||||
>
|
||||
> 前端接口测试案例代码:
|
||||
> <http://ishangsf.com:8100/ken/agent/src/branch/main/examples>
|
||||
>
|
||||
> 测试api_key: “***simpletest2025***_xxxx” xxxx为任意字符串
|
||||
|
||||
Agent 有三模式运行,由plugin在request的model参数决定,运行模式在交互过程对agent是透明的。
|
||||
|
||||
> 1. agent local model:就是agent调用本地部署的LLM模型
|
||||
> 2. agent remote api:就是agent调用openai或deepseek等外部服务的api
|
||||
> 3. plugin remote api:由plugin调用openai或deepseek等外部服务的api
|
||||
|
||||
agent网页版测试 <https://host:port/simpletest2025>
|
||||
前端测试代码 repo/examples
|
||||
测试api_key: “***simpletest2025***_xxxx” xxxx为任意字符串
|
||||
|
||||
##### 1.1 系统框架
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import ws from 'ws';
|
||||
|
||||
process.env["NODE_TLS_REJECT_UNAUTHORIZED"]="0"
|
||||
const ADDR = "wss://127.0.0.1:8080/ws"
|
||||
const ADDR = "ws://112.74.39.99:8080/ws"
|
||||
// const ADDR = "ws://127.0.0.1:8080/ws"
|
||||
|
||||
export function makeClient(
|
||||
onMessage: ((data: ws.RawData) => void) | any = undefined,
|
||||
|
@ -9,7 +9,7 @@ async function run() {
|
||||
"request_id" : 123, // 随机生成 (必填)
|
||||
"language" : "en", // 语言, en-英文, cn-中文,默认为 en (可选)
|
||||
"msg" : "What would be a good company name a company that makes colorful socks?",
|
||||
"model" : "local deepseek-r1:32b", //(必填)
|
||||
"model" : "local qwen2.5-coder:7b", //(必填)
|
||||
"stream" : false // stream方式返回,目前未支持 (可选)
|
||||
}
|
||||
|
||||
@ -21,8 +21,7 @@ async function run_stream() {
|
||||
let client = await makeClient((data: ws.RawData)=>{
|
||||
let d = JSON.parse(data.toString())
|
||||
process.stdout.write(d.msg)
|
||||
console.log(data.toString())
|
||||
return
|
||||
// console.log(data.toString())
|
||||
})
|
||||
|
||||
let req = {
|
||||
@ -30,7 +29,7 @@ async function run_stream() {
|
||||
"request_id" : 123, // 随机生成 (必填)
|
||||
"language" : "en", // 语言, en-英文, cn-中文,默认为 en (可选)
|
||||
"msg" : "What would be a good company name a company that makes colorful socks?",
|
||||
"model" : "local deepseek-r1:32b", //(必填)
|
||||
"model" : "local qwen2.5-coder:7b", //(必填)
|
||||
"stream" : true // stream方式返回,目前未支持 (可选)
|
||||
}
|
||||
|
||||
|
@ -16,15 +16,13 @@ type Agent struct {
|
||||
ctx context.Context
|
||||
apicheck *keyChecker
|
||||
clients *common.ThreadSafeMap[*client, struct{}]
|
||||
docPath string
|
||||
}
|
||||
var once sync.Once
|
||||
|
||||
func NewAgent(ctx context.Context, docPath string) *Agent {
|
||||
func NewAgent(ctx context.Context) *Agent {
|
||||
return &Agent{
|
||||
ctx : ctx,
|
||||
clients: new(common.ThreadSafeMap[*client, struct{}]).Init(nil, false),
|
||||
docPath: docPath,
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,11 +32,10 @@ func (a *Agent) Start(port uint64, crtDir *string) {
|
||||
go func(){
|
||||
sm := http.NewServeMux()
|
||||
// sm.HandleFunc("/simpletest2025", a.serveTestPage)
|
||||
// sm.HandleFunc("/doc", a.serveDoc)
|
||||
// sm.HandleFunc("/assets/", a.serveAssets)
|
||||
sm.HandleFunc("/ws", a.serveWs)
|
||||
|
||||
sm.HandleFunc("/doc", a.serveDoc)
|
||||
sm.HandleFunc("/assets/", a.serveAssets)
|
||||
|
||||
addr := fmt.Sprintf(":%d", port)
|
||||
log.Info("[agent] start websocket server", "addr", addr)
|
||||
|
||||
@ -70,7 +67,7 @@ func (a *Agent) serveDoc(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
http.ServeFile(w, r, a.docPath+"/agent服务协议.html")
|
||||
http.ServeFile(w, r, "./assets/agent服务协议.html")
|
||||
}
|
||||
|
||||
func (a *Agent) serveAssets(w http.ResponseWriter, r *http.Request) {
|
||||
@ -80,8 +77,7 @@ func (a *Agent) serveAssets(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
fmt.Println(r.URL.Path[1:])
|
||||
http.ServeFile(w, r, a.docPath+"/" +r.URL.Path[1:])
|
||||
|
||||
http.ServeFile(w, r, "./"+r.URL.Path[1:])
|
||||
}
|
||||
|
||||
func (a *Agent) serveWs(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -1,36 +1,15 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"agent/src/prompt"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAgent(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
a := NewAgent(ctx, "../../docs")
|
||||
crt := "../../config"
|
||||
a.Start(8080, &crt)
|
||||
a := NewAgent(ctx)
|
||||
// crt := "../../config"
|
||||
a.Start(8080, nil)
|
||||
<-make(chan bool)
|
||||
}
|
||||
|
||||
|
||||
func Test001(t *testing.T) {
|
||||
s := "\u003cthink\u003e\nOkay, so I'm trying to understand this code that someone provided. It's about creating a snake game using Python and Tkinter. The selected portion is the __init__ method of the Food class. Let me break it down step by step.\n\nFirst, I see that the Food class has an __init__ method. That means when a new instance of Food is created, this method runs automatically. Inside this method, there are some variables being set: x and y. They're using random.randint to generate these values. \n\nLooking at how x is calculated: it's random.randint(0, (WIDTH / SPACE_SIZE) - 1) multiplied by SPACE_SIZE. Similarly for y. I think WIDTH and HEIGHT are the dimensions of the game window, and SPACE_SIZE is probably the size of each grid block or the size of the food item. So, dividing WIDTH by SPACE_SIZE gives the number of possible positions along the width. Subtracting 1 ensures that the random number doesn't go out of bounds when multiplied back by SPACE_SIZE.\n\nSo x and y are being set to a position within the game window, in increments of SPACE_SIZE. That makes sense because it keeps the food aligned with the grid where the snake moves.\n\nNext, self.coordinates is assigned as a list [x, y]. This probably holds the current position of the food so that other parts of the code can check if the snake has eaten it.\n\nThen there's a call to canvas.create_oval. The parameters are x, y for the top-left corner and x + SPACE_SIZE, y + SPACE_SIZE for the bottom-right corner. The fill color is FOOD, which I assume is defined earlier as a constant. The tag \"food\" might be used later to identify this object on the canvas.\n\nPutting it all together, when a Food object is created, it randomly places an oval-shaped food item somewhere on the game canvas. This position is stored so that the snake can check for collisions or eating events in subsequent turns.\n\nI'm a bit confused about why they use create_oval instead of another shape, but I guess it's just a design choice. Also, I wonder how the food respawns when eaten. Maybe there's another part of the code that deletes the old food and creates a new instance of Food, which would call this __init__ method again.\n\nAnother thing to note is that the coordinates are stored as [x, y], which will be used in collision detection with the snake's head. If the snake's head position matches the food's coordinates, then the snake grows, and the score increases.\n\nI think I've got a good grasp of what this code does now. It initializes the food at a random valid position on the canvas and sets up its visual representation.\n\u003c/think\u003e\n\nThe selected code is from the `__init__` method of the `Food` class in a Python-based snake game. Here's an explanation of its logic:\n\n1. **Random Position Calculation**:\n - The `x` and `y` coordinates for the food are determined using `random.randint()`.\n - `(WIDTH / SPACE_SIZE) - 1` calculates the maximum index along the width, ensuring the food stays within bounds.\n - Multiplying by `SPACE_SIZE` converts this index to pixel coordinates.\n\n2. **Storing Coordinates**:\n - The food's position is stored in `self.coordinates` as `[x, y]`, allowing other parts of the code to check for collisions with the snake.\n\n3. **Visual Representation**:\n - An oval is drawn on the canvas using `create_oval()`, positioned at `(x, y)` with dimensions based on `SPACE_SIZE`.\n - The color is set to `FOOD` (defined earlier), and tagged as \"food\" for easy reference.\n\nThis method initializes the food's position and appearance each time a new `Food` object is created."
|
||||
fmt.Println(prompt.RemoveThinkPart(s))
|
||||
}
|
||||
|
||||
func Test002(t *testing.T) {
|
||||
prompt_docstring := strings.Replace(
|
||||
`a
|
||||
file: {%s}
|
||||
"""
|
||||
{%s}
|
||||
"""
|
||||
`,
|
||||
"\"\"\"", "```", 2)
|
||||
fmt.Println(prompt_docstring)
|
||||
// fmt.Println(fmt.Sprintf(prompt_docstring, "a", "b"))
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"agent/src/utils"
|
||||
"context"
|
||||
|
||||
"github.com/tmc/langchaingo/llms"
|
||||
@ -11,33 +10,29 @@ import (
|
||||
var LLMGroups map[string]*LLMGroup
|
||||
var LLMNames []string
|
||||
|
||||
// const ollamaurl = "http://192.168.1.5:11434"
|
||||
const ollamaurl = "http://ishangsf.com:11434"
|
||||
|
||||
func init() {
|
||||
LLMGroups = make(map[string]*LLMGroup)
|
||||
llm, err := ollama.New(ollama.WithServerURL("http://192.168.1.5:11434"), ollama.WithModel("deepseek-r1:32b"))
|
||||
models := []string{"qwen2.5-coder:7b", "deepseek-coder-v2:16b"}
|
||||
LLMNames = make([]string, len(models))
|
||||
LLMGroups = make(map[string]*LLMGroup, len(models))
|
||||
|
||||
for i, model := range models {
|
||||
llm, err := ollama.New(ollama.WithServerURL(ollamaurl), ollama.WithModel(model))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
model = "local " + model
|
||||
g := &LLMGroup{
|
||||
name: "local deepseek-r1:32b",
|
||||
name: model,
|
||||
llm: make(chan llms.Model, 1),
|
||||
local: true,
|
||||
}
|
||||
g.llm <- llm
|
||||
LLMGroups["local deepseek-r1:32b"] = g
|
||||
|
||||
llm, err = ollama.New(ollama.WithServerURL("http://192.168.1.5:11434"), ollama.WithModel("deepseek-coder-v2:16b"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
LLMGroups[model] = g
|
||||
LLMNames[i] = model
|
||||
}
|
||||
g = &LLMGroup{
|
||||
name: "local deepseek-coder-v2:16b",
|
||||
llm: make(chan llms.Model, 1),
|
||||
local: true,
|
||||
}
|
||||
g.llm <- llm
|
||||
LLMGroups["local deepseek-coder-v2:16b"] = g
|
||||
|
||||
LLMNames = utils.MapKeys(LLMGroups)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"agent/src/utils"
|
||||
"context"
|
||||
"sync"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/tmc/langchaingo/llms"
|
||||
@ -12,33 +11,19 @@ import (
|
||||
|
||||
func TestOllama(t *testing.T) {
|
||||
// llm, err := ollama.New(ollama.WithServerURL("http://192.168.1.5:11434"), ollama.WithModel("deepseek-r1:32b"))
|
||||
llm, err := ollama.New(ollama.WithServerURL("http://ishangsf.com:11434"), ollama.WithModel("deepseek-r1:32b"))
|
||||
llm, err := ollama.New(ollama.WithServerURL("http://ishangsf.com:11434"), ollama.WithModel("qwen2.5-coder:7b"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
for range 1 {
|
||||
go func() {
|
||||
content := []llms.MessageContent{
|
||||
llms.TextParts(llms.ChatMessageTypeSystem, "You are a company branding design wizard."),
|
||||
llms.TextParts(llms.ChatMessageTypeHuman, "What would be a good company name a company that makes colorful socks?"),
|
||||
}
|
||||
// completion, err := llm.GenerateContent(ctx, content, llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
|
||||
// fmt.Print(string(chunk))
|
||||
// return nil
|
||||
// }))
|
||||
completion, err := llm.GenerateContent(ctx, content)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_ = completion
|
||||
utils.PrintJson(completion.Choices)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
llm.GenerateContent(ctx, content, llms.WithStreamingFunc(func(ctx context.Context, chunk []byte) error {
|
||||
fmt.Print(string(chunk))
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ var (
|
||||
main_data = struct {
|
||||
port uint64
|
||||
crtDir string
|
||||
docs string
|
||||
serveDocs string
|
||||
saveRequestTo string
|
||||
}{}
|
||||
|
||||
@ -29,7 +29,6 @@ func init() {
|
||||
f := cmd_run.Flags()
|
||||
f.Uint64Var(&main_data.port, "port", 8080, "http port")
|
||||
f.StringVar(&main_data.crtDir, "crt", "", "where is server.crt, server.key (default no https)")
|
||||
f.StringVar(&main_data.docs, "docs", "./docs", "where is the document directory")
|
||||
|
||||
// TODO
|
||||
// f.StringVar(&main_data.saveRequestTo, "save_request_to", "", "save request to dir (default not save)")
|
||||
@ -53,7 +52,7 @@ var cmd_run = &cobra.Command{
|
||||
crtDir = &main_data.crtDir
|
||||
}
|
||||
fmt.Println(main_data.saveRequestTo)
|
||||
a := agent.NewAgent(cmd.Context(), main_data.docs)
|
||||
a := agent.NewAgent(cmd.Context())
|
||||
a.Start(main_data.port, crtDir)
|
||||
|
||||
_ = saveRequestTo
|
||||
|
@ -41,7 +41,7 @@ type RequestExec struct {
|
||||
RequestId uint64 `json:"request_id"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Model string `json:"model"`
|
||||
Stream bool `json:"stream"`
|
||||
Stream bool `json:"stream,omitempty"`
|
||||
}
|
||||
|
||||
func (m *RequestExec) Check() error {
|
||||
|
@ -85,16 +85,20 @@ func Handle(client common.WsClient, msg []byte) error {
|
||||
var execReq *message.RequestExec
|
||||
if strings.HasPrefix(cmd.Cmd, "exec_") {
|
||||
execReq, err = message.Parse[message.RequestExec](msg)
|
||||
if err != nil || execReq.Check() != nil {
|
||||
if err != nil {
|
||||
errMsg = "invalid request format"
|
||||
return err
|
||||
}
|
||||
if err = execReq.Check(); err != nil {
|
||||
errMsg = err.Error()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cmd.Cmd == "ide_service" {
|
||||
req, err := message.Parse[message.MsgIDEService](msg)
|
||||
if err != nil {
|
||||
errMsg = "invalid request format"
|
||||
errMsg = "invalid ide request format"
|
||||
return err
|
||||
}
|
||||
err = client.NewWantResponse(req.RequestId, msg)
|
||||
@ -134,7 +138,7 @@ func Handle(client common.WsClient, msg []byte) error {
|
||||
task.request = req
|
||||
task.handler = task.exec_chat
|
||||
default:
|
||||
errMsg = "invalid request format"
|
||||
errMsg = "unknown cmd"
|
||||
return err
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user