安装 GO

1
2
$ go version
go version go1.22.0 linux/amd64

安装 Ollma

快速安装

1
$ curl -fsSL https://ollama.com/install.sh | sh

手动安装

  1. 创建安装目录
1
2
$ mkdir -p ~/ollama
$ cd ~/ollama
  1. 下载并解压
1
2
$ curl -L https://ollama.com/download/ollama-linux-amd64.tgz -o ollama-linux-amd64.tgz
$ tar -xzf ollama-linux-amd64.tgz
  1. 设置环境变量(添加到 ~/.bashrc)
1
2
$ echo 'export PATH=$HOME/ollama/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc

检验安装

1
$ ollama -v

运行服务

1
$ ollama serve

下载模型

1
$ ollama run deepseek-r1:1.5b

代码实现

准备工作:

1
2
3
4
$ mkdir langchain-go-ollama-deepseek
$ cd langchain-go-ollama-deepseek
$ go mod init github.com/yourusername/langchain-go-ollama-deepseek
$ touch main.go

在 main.go 中添加代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"context"
"fmt"
"log"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/ollama"
)

func main() {
llm, err := ollama.New(ollama.WithModel("deepseek-r1:1.5b"))
if err != nil {
log.Fatal(err)
}
query := "Write a simple function to calculate fibonacci sequence in Python"
ctx := context.Background()
completion, err := llms.GenerateFromSinglePrompt(ctx, llm, query)
if err != nil {
log.Fatal(err)
}
fmt.Println(completion)
}

下载依赖:

1
$ go mod tidy

运行程序:

1
$ go run main.go

Dubbo-Go 示例

流式模式选择:最匹配的是 ServerStream(服务端流式),即客户端发送单个请求,服务端返回流式响应。

greet.proto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
syntax = "proto3";

package greet;

option go_package = "github.com/apache/dubbo-go-samples/llm/proto;greet";

message GreetRequest {
string prompt = 1;
}

message GreetResponse {
string content = 1;
}

service GreetService {
rpc Greet(GreetRequest) returns (stream GreetResponse) {}
}
1
2
$ go install github.com/dubbogo/protoc-gen-go-triple/v3@v3.0.3
$ protoc --go_out=. --go_opt=paths=source_relative --go-triple_out=. greet.proto

生成 go 代码 greet.pb.go 和 greet.triple.go。

Dubbo 服务端

服务端提供流式 RPC 接口,并在服务端调用 langchaingo 的流式生成。

llm/go-server/cmd/server.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package main

import (
"context"
"fmt"
)

import (
_ "dubbo.apache.org/dubbo-go/v3/imports"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
)

import (
greet "github.com/apache/dubbo-go-samples/llm/proto"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/ollama"
)

type GreetServer struct {
llm *ollama.LLM
}

func NewGreetServer() (*GreetServer, error) {
llm, err := ollama.New(ollama.WithModel("deepseek-r1:1.5b"))
if err != nil {
return nil, err
}
return &GreetServer{llm: llm}, nil
}

func (s *GreetServer) Greet(ctx context.Context, req *greet.GreetRequest, stream greet.GreetService_GreetServer) error {
callback := func(ctx context.Context, chunk []byte) error {
return stream.Send(&greet.GreetResponse{
Content: string(chunk),
})
}
_, err := s.llm.GenerateContent(
ctx,
[]llms.MessageContent{
llms.TextParts(llms.ChatMessageTypeHuman, req.Prompt),
},
llms.WithStreamingFunc(callback),
)
return err
}

func main() {
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithPort(20000),
),
)
if err != nil {
fmt.Printf("Error creating server: %v\n", err)
return
}

greetServer, err := NewGreetServer()
if err != nil {
fmt.Printf("Error creating greet server: %v\n", err)
return
}

if err := greet.RegisterGreetServiceHandler(srv, greetServer); err != nil {
fmt.Printf("Error registering handler: %v\n", err)
return
}

if err := srv.Serve(); err != nil {
fmt.Printf("Error starting server: %v\n", err)
return
}
}

Dubbo 客户端

调用流式接口并逐步接收生成结果。

llm/go-client/cmd/client.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

import (
"context"
"fmt"
)

import (
"dubbo.apache.org/dubbo-go/v3/client"
_ "dubbo.apache.org/dubbo-go/v3/imports"
)

import (
greet "github.com/apache/dubbo-go-samples/llm/proto"
)

func main() {
cli, err := client.NewClient(
client.WithClientURL("tri://127.0.0.1:20000"),
)
if err != nil {
fmt.Printf("Error creating client: %v\n", err)
return
}

svc, err := greet.NewGreetService(cli)
if err != nil {
fmt.Printf("Error creating service: %v\n", err)
return
}

stream, err := svc.Greet(context.Background(), &greet.GreetRequest{
Prompt: "Write a simple function to calculate fibonacci sequence in Go",
})
if err != nil {
fmt.Printf("Error calling service: %v\n", err)
return
}

for stream.Recv() {
fmt.Print(stream.Msg().Content)
}

if err := stream.Err(); err != nil {
fmt.Printf("Stream error: %v\n", err)
return
}

stream.Close()
}

演示