Skip to content

Commit 6a26d2e

Browse files
committed
feat: add agentic react agent example
1 parent a05f4e9 commit 6a26d2e

8 files changed

Lines changed: 768 additions & 16 deletions

File tree

adk/intro/http-sse-service/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.24.9
55
replace github.com/cloudwego/eino-examples => ../../..
66

77
require (
8-
github.com/cloudwego/eino v0.7.14
8+
github.com/cloudwego/eino v0.7.20-0.20260119125832-603cc7e9c3b2
99
github.com/cloudwego/eino-examples v0.0.0-00010101000000-000000000000
1010
github.com/cloudwego/hertz v0.10.3
1111
github.com/hertz-contrib/sse v0.1.0
@@ -51,7 +51,7 @@ require (
5151
github.com/tidwall/sjson v1.2.5 // indirect
5252
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
5353
github.com/volcengine/volc-sdk-golang v1.0.199 // indirect
54-
github.com/volcengine/volcengine-go-sdk v1.1.44 // indirect
54+
github.com/volcengine/volcengine-go-sdk v1.2.4 // indirect
5555
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
5656
github.com/yargevad/filepathx v1.0.0 // indirect
5757
golang.org/x/arch v0.19.0 // indirect

adk/intro/http-sse-service/go.sum

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ github.com/cloudwego/eino v0.7.8/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyL
109109
github.com/cloudwego/eino v0.7.11/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ=
110110
github.com/cloudwego/eino v0.7.14-0.20251225084034-ff42791c540f/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ=
111111
github.com/cloudwego/eino v0.7.14/go.mod h1:nA8Vacmuqv3pqKBQbTWENBLQ8MmGmPt/WqiyLeB8ohQ=
112+
github.com/cloudwego/eino v0.7.19-0.20260108113617-d04d4b5bda31/go.mod h1:OdDJi17QawFUJRIFrVJRgdc9grjrh3eFDD0k34ZRH8M=
113+
github.com/cloudwego/eino v0.7.19-0.20260113134107-0b72ca9f4030/go.mod h1:2gsj+xyPFnmZfz7jtnbA6VghaaJsThYh2appzPeslRE=
114+
github.com/cloudwego/eino v0.7.20-0.20260115134922-6816c8a31d1a/go.mod h1:2gsj+xyPFnmZfz7jtnbA6VghaaJsThYh2appzPeslRE=
115+
github.com/cloudwego/eino v0.7.20-0.20260119125832-603cc7e9c3b2/go.mod h1:2gsj+xyPFnmZfz7jtnbA6VghaaJsThYh2appzPeslRE=
112116
github.com/cloudwego/eino-ext/components/model/ark v0.1.45 h1:LWvSHJVlvS1S/IxN9XUKNw/MI0I7YPePt3LMNxyCrZ0=
113117
github.com/cloudwego/eino-ext/components/model/ark v0.1.45/go.mod h1:e8P5dGVI/JMQ1FYNgmu5EFRWA8fivBc6NwNJ9g8FBK8=
114118
github.com/cloudwego/eino-ext/components/model/openai v0.1.5 h1:+yvGbTPw93li9GSmdm6Rix88Yy8AXg5NNBcRbWx3CQU=
@@ -517,6 +521,7 @@ github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi
517521
github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ=
518522
github.com/volcengine/volcengine-go-sdk v1.1.44 h1:WLoLlzt67ZlJeow55PPx65/Mh52DewVXqkHcFSodM9w=
519523
github.com/volcengine/volcengine-go-sdk v1.1.44/go.mod h1:oxoVo+A17kvkwPkIeIHPVLjSw7EQAm+l/Vau1YGHN+A=
524+
github.com/volcengine/volcengine-go-sdk v1.2.4/go.mod h1:oxoVo+A17kvkwPkIeIHPVLjSw7EQAm+l/Vau1YGHN+A=
520525
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
521526
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
522527
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright 2026 CloudWeGo Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"io"
24+
"strings"
25+
26+
"github.com/bytedance/sonic"
27+
"github.com/cloudwego/eino-examples/internal/logs"
28+
"github.com/cloudwego/eino-ext/components/model/agenticark"
29+
"github.com/cloudwego/eino/callbacks"
30+
"github.com/cloudwego/eino/components/model"
31+
"github.com/cloudwego/eino/schema"
32+
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model/responses"
33+
)
34+
35+
func newAgenticModelCallback() *agenticModelCallback {
36+
return &agenticModelCallback{}
37+
}
38+
39+
type agenticModelCallback struct{}
40+
41+
func (a *agenticModelCallback) OnEndWithStreamOutput(ctx context.Context, runInfo *callbacks.RunInfo,
42+
output *schema.StreamReader[*model.AgenticCallbackOutput]) context.Context {
43+
44+
printHeader(fmt.Sprintf("AgenticModel Name: %s", runInfo.Name), "\033[36m")
45+
46+
go func() {
47+
var (
48+
lastBlockType schema.ContentBlockType
49+
lineLength int
50+
)
51+
52+
for {
53+
chunk, err := output.Recv()
54+
if err != nil {
55+
if errors.Is(err, io.EOF) {
56+
break
57+
}
58+
logs.Errorf("read streaming output failed, err: %v", err)
59+
return
60+
}
61+
62+
if chunk.Message == nil {
63+
continue
64+
}
65+
66+
for _, block := range chunk.Message.ContentBlocks {
67+
switch block.Type {
68+
case schema.ContentBlockTypeReasoning:
69+
if lastBlockType != block.Type {
70+
printHeader("reasoning", "\033[33m")
71+
lineLength = 0
72+
}
73+
74+
lineLength = printWrapped(block.Reasoning.Text, lineLength)
75+
76+
lastBlockType = block.Type
77+
78+
case schema.ContentBlockTypeFunctionToolCall:
79+
if lastBlockType != block.Type {
80+
printHeader(fmt.Sprintf("call function tool: %s", block.FunctionToolCall.Name), "\033[35m")
81+
lineLength = 0
82+
}
83+
84+
lineLength = printWrapped(block.FunctionToolCall.Arguments, lineLength)
85+
86+
lastBlockType = block.Type
87+
88+
case schema.ContentBlockTypeServerToolCall:
89+
if lastBlockType != block.Type {
90+
printHeader(fmt.Sprintf("call server tool: %s", block.ServerToolCall.Name), "\033[35m")
91+
lineLength = 0
92+
}
93+
94+
status, ok := agenticark.GetItemStatus(block)
95+
if ok && status != responses.ItemStatus_completed.String() {
96+
fmt.Print(status + "...\n\n")
97+
}
98+
if block.ServerToolCall.Arguments != nil {
99+
fmt.Print(responses.ItemStatus_completed.String() + "!\n\n")
100+
args, _ := sonic.MarshalIndent(block.ServerToolCall.Arguments, "", " ")
101+
fmt.Println(string(args))
102+
lineLength = 0
103+
}
104+
105+
lastBlockType = block.Type
106+
107+
case schema.ContentBlockTypeServerToolResult:
108+
if lastBlockType != block.Type {
109+
printHeader(fmt.Sprintf("server tool result: %s", block.ServerToolResult.Name), "\033[32m")
110+
lineLength = 0
111+
}
112+
if block.ServerToolResult.Result != nil {
113+
res, _ := sonic.MarshalIndent(block.ServerToolResult.Result, "", " ")
114+
fmt.Println(string(res))
115+
lineLength = 0
116+
}
117+
118+
lastBlockType = block.Type
119+
120+
case schema.ContentBlockTypeAssistantGenText:
121+
if lastBlockType != block.Type {
122+
printHeader("assistant generated text", "\033[36m")
123+
lineLength = 0
124+
}
125+
126+
lineLength = printWrapped(block.AssistantGenText.Text, lineLength)
127+
128+
lastBlockType = block.Type
129+
}
130+
}
131+
}
132+
}()
133+
134+
return ctx
135+
}
136+
137+
func printWrapped(content string, currentLength int) int {
138+
for _, r := range content {
139+
if r == '\n' {
140+
currentLength = 0
141+
fmt.Print(string(r))
142+
continue
143+
}
144+
145+
w := 1
146+
if r > 127 {
147+
w = 2
148+
}
149+
150+
if currentLength+w > 120 {
151+
fmt.Print("\n")
152+
currentLength = 0
153+
}
154+
155+
fmt.Print(string(r))
156+
currentLength += w
157+
}
158+
return currentLength
159+
}
160+
161+
func printHeader(content string, color string) {
162+
const lineLength = 60
163+
separator := strings.Repeat("=", lineLength)
164+
165+
contentLength := len(content)
166+
if contentLength >= lineLength {
167+
// Content too long, just print it within separators
168+
fmt.Printf("\n\n%s%s\n%s\n%s\033[0m\n\n", color, separator, content, separator)
169+
return
170+
}
171+
172+
padding := lineLength - contentLength
173+
leftPad := padding / 2
174+
175+
// Create padding string with spaces
176+
padStr := strings.Repeat(" ", leftPad)
177+
178+
fmt.Printf("\n\n%s%s\n%s%s\n%s\033[0m\n\n", color, separator, padStr, content, separator)
179+
}

flow/agent/react/agentic/main.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright 2026 CloudWeGo Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
"log"
24+
"os"
25+
26+
"github.com/bytedance/sonic"
27+
"github.com/cloudwego/eino-ext/components/model/agenticark"
28+
"github.com/cloudwego/eino/components/tool"
29+
"github.com/cloudwego/eino/compose"
30+
"github.com/cloudwego/eino/schema"
31+
"github.com/cloudwego/eino/utils/callbacks"
32+
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model/responses"
33+
)
34+
35+
func main() {
36+
ctx := context.Background()
37+
38+
am, err := agenticark.New(ctx, &agenticark.Config{
39+
Model: os.Getenv("ARK_MODEL_ID"),
40+
APIKey: os.Getenv("ARK_API_KEY"),
41+
Thinking: &responses.ResponsesThinking{
42+
Type: responses.ThinkingType_enabled.Enum(),
43+
},
44+
ServerTools: []*agenticark.ServerToolConfig{
45+
{
46+
WebSearch: &responses.ToolWebSearch{
47+
Type: responses.ToolType_web_search,
48+
},
49+
},
50+
},
51+
Reasoning: &responses.ResponsesReasoning{
52+
Effort: responses.ReasoningEffort_low,
53+
},
54+
})
55+
if err != nil {
56+
log.Fatalf("failed to create agentic model, err=%v", err)
57+
}
58+
59+
r, err := NewAgent(ctx, &AgentConfig{
60+
Model: am,
61+
ToolsConfig: compose.ToolsNodeConfig{
62+
Tools: []tool.BaseTool{
63+
&SummarizeNewsTool{},
64+
&GetUserLocationTool{},
65+
},
66+
},
67+
ToolReturnDirectly: map[string]struct{}{
68+
summarizeNewsToolName: {},
69+
},
70+
})
71+
if err != nil {
72+
log.Fatalf("failed to create agent, err=%v", err)
73+
}
74+
75+
input := []*schema.AgenticMessage{
76+
schema.SystemAgenticMessage("You are a news assistant that helps users search for recent news. " +
77+
"Before using the `summarize_news` tool, " +
78+
"You MUST use the `get_user_location` tool to get the user's country." +
79+
"You MUST use the `summarize_news` tool to summarize the news and return the result."),
80+
schema.UserAgenticMessage("What news has been happening in the last three days?"),
81+
}
82+
83+
cb := callbacks.NewHandlerHelper().AgenticModel(&callbacks.AgenticModelCallbackHandler{
84+
OnEndWithStreamOutput: newAgenticModelCallback().OnEndWithStreamOutput,
85+
}).Handler()
86+
87+
sr, err := r.Stream(ctx, input, WithComposeOptions(compose.WithCallbacks(cb)))
88+
if err != nil {
89+
log.Fatalf("failed to stream, err=%v", err)
90+
}
91+
92+
var msgs []*schema.AgenticMessage
93+
for {
94+
msg, err := sr.Recv()
95+
if err != nil {
96+
if err == io.EOF {
97+
break
98+
}
99+
log.Fatalf("failed to recv, err=%v", err)
100+
}
101+
msgs = append(msgs, msg)
102+
}
103+
104+
msg, err := schema.ConcatAgenticMessages(msgs)
105+
if err != nil {
106+
log.Fatalf("failed to concat messages, err=%v", err)
107+
}
108+
109+
printHeader("final output", "\033[36m")
110+
111+
for _, block := range msg.ContentBlocks {
112+
switch block.Type {
113+
case schema.ContentBlockTypeFunctionToolResult:
114+
var res *SummarizeNewsToolInput
115+
if err = sonic.UnmarshalString(block.FunctionToolResult.Result, &res); err != nil {
116+
log.Fatalf("failed to unmarshal function tool result, err=%v", err)
117+
}
118+
119+
b, err := sonic.MarshalIndent(res.News, "", " ")
120+
if err != nil {
121+
log.Fatalf("failed to marshal function tool result, err=%v", err)
122+
}
123+
124+
fmt.Println(string(b))
125+
126+
default:
127+
fmt.Println(fmt.Sprintf("%s", block))
128+
}
129+
}
130+
}

0 commit comments

Comments
 (0)