20 มี.ค. 2026 — ADK Go 1.0: GA
30 มิ.ย. 2026 — ADK Go 2.0: graph workflow, Task API
Pallat Anchaleechamaikorn
(สลับไป terminal — live demo)
go run . console
Framework จาก Google สำหรับสร้าง stateful AI agent
แบบ code-first (ไม่ใช่ low-code / drag-drop)
มีให้เลือก 4 ภาษา: Python · Java · Go · TypeScript
เหมาะกับ: enterprise ที่ flow ซับซ้อนขึ้นเรื่อยๆ — ต้อง testable, version-control ได้, integrate เข้า SDLC เดิม
Agent คนละ process / คนละ vendor คุยกันผ่าน HTTP มาตรฐานเดียวกัน (Google, Apache-2.0, ตอนนี้อยู่ใต้ Linux Foundation)
// ฝั่ง root agent — เรียก remote agent เหมือน local agent
remoteAgent, _ := remoteagent.NewA2A(remoteagent.A2AConfig{
Name: "prime_agent",
AgentCardSource: "http://localhost:8001",
})
llmagent.New(llmagent.Config{
Name: "root_agent",
SubAgents: []agent.Agent{rollAgent, remoteAgent}, // local + remote ปนกันได้
})
ต่างจาก MCP: MCP = agent → tool (data/action), A2A = agent → agent (peer ที่มี capability ของตัวเอง)






type SearchTimeArgs struct {
Timezone string `json:"timezone" jsonschema:"the IANA timezone name"`
}
type SearchTimeResult struct {
Time string `json:"time"`
}
// A plain Go function exposed to the LLM as a callable tool.
func searchTime(ctx agent.Context, args SearchTimeArgs) (SearchTimeResult, error) {
return SearchTimeResult{Time: "10:30 " + args.Timezone}, nil
}
searchTimeTool, err := functiontool.New(functiontool.Config{
Name: "search_time",
Description: "Returns the current time for a given IANA timezone.",
}, searchTime)
geminiModel, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{})
assistant, err := llmagent.New(llmagent.Config{
Name: "assistant_agent",
Model: geminiModel, // model.LLM, ไม่ใช่ string
Instruction: "You are a helpful assistant. Answer concisely in Thai.",
Tools: []tool.Tool{searchTimeTool},
})
Run() เอง — llmagent.New มี LLM loop (reason → call tool → observe) ให้ครบModel เป็น interface model.LLM — สร้างผ่าน gemini.NewModel ก่อน ไม่ใช่ส่ง stringไม่ใช้ LLM ตัดสินใจ flow เลย — deterministic control ล้วน
compose กันได้: เอา LLM Agent ไปเป็น sub-agent ใน Sequential/Parallel/Loop ได้ ผสมเป็น tree
func runDraft(ic agent.InvocationContext) iter.Seq2[*session.Event, error] {
return func(yield func(*session.Event, error) bool) {
yield(emit(ic, "draft: เขียน proposal เวอร์ชันแรกแล้ว"), nil)
}
}
// runReview is identical — only the message differs.
draftAgent, _ := agent.New(agent.Config{Name: "draft_agent", Run: runDraft})
reviewAgent, _ := agent.New(agent.Config{Name: "review_agent", Run: runReview})
// SubAgents go inside the embedded agent.Config; runs them in list order.
pipeline, err := sequentialagent.New(sequentialagent.Config{
AgentConfig: agent.Config{
Name: "proposal_pipeline",
SubAgents: []agent.Agent{draftAgent, reviewAgent},
},
})
// ParallelAgent — runs every sub-agent concurrently.
fetchAll, err := parallelagent.New(parallelagent.Config{
AgentConfig: agent.Config{
Name: "fetch_all_sources",
SubAgents: []agent.Agent{fetchDocs, fetchTickets, fetchLogs},
},
})
// LoopAgent — repeats until escalate or MaxIterations.
retryLoop, err := loopagent.New(loopagent.Config{
AgentConfig: agent.Config{
Name: "retry_loop",
SubAgents: []agent.Agent{attemptAgent},
},
MaxIterations: 5,
})
// Inside attemptAgent: escalate exits the loop immediately.
if n >= 3 {
event.Actions.Escalate = true
}
func runGreeting(ic agent.InvocationContext) iter.Seq2[*session.Event, error] {
steps := []string{
"เริ่มทำงาน...",
"กำลังประมวลผล...",
"เสร็จแล้ว: สวัสดี จาก custom agent ล้วนๆ ไม่มี LLM",
}
return func(yield func(*session.Event, error) bool) {
for _, text := range steps {
event := session.NewEvent(ic, ic.InvocationID())
event.Author = ic.Agent().Name()
event.Branch = ic.Branch()
event.LLMResponse = model.LLMResponse{
Content: genai.NewContentFromText(text, genai.RoleModel),
}
if !yield(event, nil) {
return // caller (Runner) ขอหยุดกลางทาง
}
}
}
}
iter.Seq2[]*Event — ต้องรันจบก่อน return ได้ ขัด streamingchan *Event — spawn goroutine ทุกชั้น agent tree เสี่ยง leakiter.Seq2 — pull-based stack เดียวกัน ไม่ต้อง goroutineiter.Seq2 ทำงานยังไง (compiler desugar)// type จริงใน std lib "iter" (Go 1.23+)
type Seq2[K, V any] func(yield func(K, V) bool)
// สิ่งที่เราเขียน (สไลด์ก่อนหน้า):
for event, err := range runGreeting(ic) {
fmt.Println(event)
if err != nil { break }
}
// สิ่งที่ compiler แปลงให้จริงๆ — แค่ฟังก์ชันเรียกฟังก์ชัน ไม่มี magic:
runGreeting(ic)(func(event *session.Event, err error) bool {
fmt.Println(event)
if err != nil { return false } // break → return false แทน
return true // จบ body ปกติ → return true (ทำต่อ)
})
range บน func value ไม่ได้รันขนาน — เป็นแค่ nested function call บน stack เดียวกัน
iter.Seq2 Execution Flow| Version | วันออก | จุดเด่น |
|---|---|---|
| v0.4.0 | 2026-01-30 | plugin system, HITL, MCP session reconnect |
| v0.5.0 | 2026-02-20 | plugin เต็มรูป, OTel tracing + semconv |
| v1.0.0 | 2026-03-20 | GA แรก — SSE streaming, unify context |
| v1.3.0 | 2026-05-20 | bidirectional streaming, session resumption |
| v1.5.0 | 2026-06-30 | StrictContextMock, AgentSpace compat |
| v2.0.0 | 2026-06-30 | breaking — /v2, graph-based workflow |

go get google.golang.org/adk/v2
ตัวอย่างจริงทั้งหมด: github.com/pallat/adk/tree/main/examples
examples/adk-custom-agent — build ผ่าน, run ผ่านจริง กับ google.golang.org/adk/v2
Q&A 🐹