@@ -7,30 +7,30 @@ import (
77 "path/filepath"
88 "strings"
99
10- "github.com/cloudwego/eino-ext/adk/backend/local"
1110 "github.com/cloudwego/eino/adk"
12- fsmw "github.com/cloudwego/eino/adk/middlewares/filesystem"
1311 "github.com/go-errors/errors"
12+ "github.com/google/uuid"
1413 einoacp "github.com/strrl/eino-acp"
15- "github.com/strrl/lapp/pkg/tape"
1614 "go.opentelemetry.io/otel"
1715 "go.opentelemetry.io/otel/attribute"
16+
17+ "github.com/strrl/lapp/pkg/tape"
18+ "github.com/strrl/lapp/pkg/tracing"
1819)
1920
2021func buildSystemPrompt (workDir string ) string {
2122 return fmt .Sprintf (`You are a log analysis expert helping developers troubleshoot issues.
2223
23- IMPORTANT: All file operations (read_file, grep, ls, glob, execute) MUST use paths under %s.
24- Do NOT access files outside this workspace directory.
24+ IMPORTANT: Stay within the workspace directory %s for any file or shell work (your runtime provides the tools).
2525
2626Your workspace contains pre-processed log data at %s:
2727- %s/raw.log — the original log file
2828- %s/summary.txt — log templates discovered by automated parsing, with occurrence counts and samples
2929- %s/errors.txt — error and warning patterns extracted from logs
3030
3131Start by reading %s/summary.txt and %s/errors.txt to understand the log patterns.
32- Then use grep and read_file on %s/raw.log to investigate specific patterns in detail .
33- You can also use the execute tool to run shell commands (e.g., awk, sort, wc) for deeper analysis .
32+ Then search and read %s/raw.log for specifics (grep, read, or equivalents your environment exposes) .
33+ Use shell only when it helps (e.g. awk, sort, wc).
3434
3535Provide:
36361. Key findings from the logs
@@ -46,16 +46,15 @@ Be concise and actionable. Focus on what matters.`,
4646type Config struct {
4747 Provider string
4848 Model string
49- // TapePath, when set, enables tape recording to this JSONL file.
49+ // TapePath overrides the default workspace tape file.
5050 TapePath string
5151}
5252
5353// BuildWorkspaceSystemPrompt builds a system prompt for the structured workspace layout.
5454func BuildWorkspaceSystemPrompt (workDir string ) string {
5555 return fmt .Sprintf (`You are a log analysis expert helping developers troubleshoot issues.
5656
57- IMPORTANT: All file operations (read_file, grep, ls, glob, execute) MUST use paths under %s.
58- Do NOT access files outside this workspace directory.
57+ IMPORTANT: Stay within the workspace directory %s for any file or shell work (your runtime provides the tools).
5958
6059Your workspace at %s contains structured log data:
6160- %s/logs/ — original log files
@@ -67,8 +66,8 @@ Your workspace at %s contains structured log data:
6766
6867Start by reading %s/notes/summary.md and %s/notes/errors.md to understand the log patterns.
6968Then drill into specific patterns under %s/patterns/ for details.
70- Use grep on %s/logs/ to search for specific terms across all log files.
71- You can also use the execute tool to run shell commands (e.g., awk, sort, wc) for deeper analysis .
69+ Search %s/logs/ for specific terms across log files.
70+ Use shell only when it helps (e.g., awk, sort, wc).
7271
7372Provide:
74731. Key findings from the logs
@@ -117,30 +116,31 @@ func RunAgentWithPrompt(ctx context.Context, config Config, workDir, question, s
117116 return "" , errors .Errorf ("create chat model: %w" , err )
118117 }
119118
120- backend , err := local .NewBackend (ctx , & local.Config {})
121- if err != nil {
122- return "" , errors .Errorf ("create local backend: %w" , err )
119+ if systemPrompt == "" {
120+ systemPrompt = buildSystemPrompt (absDir )
123121 }
124- backendAdapter := newLocalBackendAdapter (backend )
125122
126- fsHandler , err := fsmw .New (ctx , & fsmw.MiddlewareConfig {
127- Backend : backendAdapter ,
128- StreamingShell : backendAdapter ,
129- })
123+ tapePath := config .TapePath
124+ if tapePath == "" {
125+ tapePath = filepath .Join (absDir , tape .FileName )
126+ }
127+ tapeStore , err := tape .OpenJSONL (tapePath )
130128 if err != nil {
131- return "" , errors .Errorf ("create filesystem middleware : %w" , err )
129+ return "" , errors .Errorf ("open tape store : %w" , err )
132130 }
131+ defer func () { _ = tapeStore .Close () }()
133132
134- if systemPrompt == "" {
135- systemPrompt = buildSystemPrompt (absDir )
136- }
133+ tapeHandler := tape .NewEinoHandler (tapeStore , tape.RunMeta {
134+ RunID : uuid .NewString (),
135+ Provider : provider ,
136+ Model : config .Model ,
137+ })
137138
138139 agent , err := adk .NewChatModelAgent (ctx , & adk.ChatModelAgentConfig {
139140 Name : "log-analyzer" ,
140141 Description : "Analyzes log files to find root causes" ,
141142 Instruction : systemPrompt ,
142143 Model : newACPToolCallingModel (chatModel ),
143- Handlers : []adk.ChatModelAgentMiddleware {fsHandler },
144144 MaxIterations : 15 ,
145145 })
146146 if err != nil {
@@ -153,13 +153,7 @@ func RunAgentWithPrompt(ctx context.Context, config Config, workDir, question, s
153153 }
154154
155155 runner := adk .NewRunner (ctx , adk.RunnerConfig {Agent : agent })
156- var runOptions []adk.AgentRunOption
157- if config .TapePath != "" {
158- jsonlStore := tape .NewJSONLStore (config .TapePath )
159- runOptions = append (runOptions , adk .WithCallbacks (tape .NewHandler (jsonlStore )))
160- slog .Info ("Tape recording enabled" , "path" , config .TapePath )
161- }
162- iter := runner .Query (ctx , userMessage , runOptions ... )
156+ iter := runner .Query (ctx , userMessage , adk .WithCallbacks (tracing .NewSlogEinoHandler (nil ), tapeHandler ))
163157
164158 var result strings.Builder
165159 for {
0 commit comments