chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
# Zero-to-hero support desk
|
||||
|
||||
A maintained 0-to-hero reference for the Go Micro lifecycle: scaffold a few
|
||||
typed services, run them in one process, let an agent chat with those services
|
||||
as tools, then inspect the durable flow that triggered the work. It is one
|
||||
runnable file and one CI smoke test, so the reference path stays honest as the
|
||||
framework evolves.
|
||||
|
||||
## The path
|
||||
|
||||
1. **Scaffold services** — `customers`, `tickets`, and `notify` are ordinary
|
||||
typed Go Micro services. Their request/response structs and method comments
|
||||
become the tool contract the agent sees.
|
||||
2. **Run the harness** — the example starts an in-memory registry, broker,
|
||||
client, store, services, agent, and flow in one process; no external
|
||||
dependencies or API key are required for the default run.
|
||||
3. **Chat through an agent** — the `support` agent receives the ticket event as
|
||||
a prompt and calls service tools to look up the customer, triage the ticket,
|
||||
and draft a reply.
|
||||
4. **Inspect the workflow** — the `intake` flow records the event-driven run and
|
||||
prints the agent result, showing the service → agent → workflow lifecycle as
|
||||
one runtime.
|
||||
|
||||
## The scenario
|
||||
|
||||
A customer files a ticket. A `ticket.created` event triggers the support
|
||||
agent, which:
|
||||
|
||||
1. looks the customer up (`customers` service),
|
||||
2. sets the ticket's priority (`tickets` service),
|
||||
3. drafts a reply and emails it (`notify` service) — **but only after passing
|
||||
the approval gate.**
|
||||
|
||||
```
|
||||
> event: events.ticket.created {"id":"ticket-1","customer":"alice@acme.com",...}
|
||||
|
||||
[customers] looked up Alice (pro plan)
|
||||
[tickets] ticket-1 → priority=high status=in_progress
|
||||
▣ approval gate notify_NotifyService_Send(alice@acme.com) — approved
|
||||
[notify] 📨 to=alice@acme.com: "Hi Alice — thanks for reaching out..."
|
||||
|
||||
✓ ticket triaged and the customer was replied to — triggered by an event
|
||||
```
|
||||
|
||||
## The pieces
|
||||
|
||||
- **Services** (`customers`, `tickets`, `notify`) — plain Go Micro services. The
|
||||
agent discovers their endpoints as tools automatically.
|
||||
- **Agent** (`support`) — `micro.NewAgent` with those three services. It reasons
|
||||
over the ticket and calls the tools.
|
||||
- **Flow** (`intake`) — triggers on `events.ticket.created` and hands the event to
|
||||
the agent: *the event is the prompt*. No human types anything.
|
||||
- **Guardrail** (`ApproveTool`) — the agent can read and triage freely, but
|
||||
emailing a customer (`notify.Send`) passes through the gate first. Return
|
||||
`false` to hold it for a person or a policy; the example approves and logs.
|
||||
|
||||
## Expected inspect transcript
|
||||
|
||||
The provider-free run prints the same visible checkpoints a new developer should
|
||||
compare against after chat and flow execution. The transcript includes service
|
||||
tool calls, the approval gate, and the inspect/run-history commands that prove
|
||||
the workflow run was recorded.
|
||||
|
||||
```text
|
||||
> event: events.ticket.created {"customer":"alice@acme.com","id":"ticket-1","subject":"Can't log in"}
|
||||
|
||||
[customers] looked up Alice (pro plan)
|
||||
[tickets] ticket-1 → priority=high status=in_progress
|
||||
▣ approval gate notify_NotifyService_Send(alice@acme.com) — approved
|
||||
[notify] 📨 to=alice@acme.com: "Hi Alice — thanks for reaching out. We've bumped this to high priority and are on it."
|
||||
|
||||
support agent: Triaged ticket-1 for Alice and sent a reply.
|
||||
|
||||
inspect transcript:
|
||||
micro inspect flow intake
|
||||
flow: intake runs=1 latest.reply="Triaged ticket-1 for Alice and sent a reply."
|
||||
micro agent history support
|
||||
agent: support runs=1 latest.status=completed
|
||||
|
||||
✓ ticket triaged and the customer was replied to — triggered by an event
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
go run main.go # mock model — deterministic, no API key
|
||||
```
|
||||
|
||||
The maintained check is the same deterministic path:
|
||||
|
||||
```bash
|
||||
go test ./examples/support
|
||||
```
|
||||
|
||||
Against a live model, the agent reasons about the ticket itself instead of
|
||||
following the script:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_API_KEY=sk-ant-... # or OPENAI_API_KEY, GEMINI_API_KEY, ...
|
||||
go run main.go -provider anthropic
|
||||
```
|
||||
|
||||
## What to change next
|
||||
|
||||
- Make the gate real: return `false` from `ApproveTool` for billing actions, or
|
||||
route the decision to a human.
|
||||
- Expose the agent over A2A so another team's agent can file tickets — add
|
||||
`agent.WithA2A(":4000")`.
|
||||
- Add a `kb` (knowledge base) service and watch the agent search it before
|
||||
replying.
|
||||
Reference in New Issue
Block a user