Go 异步任务 + Worker Pool + LLM Agent 后端(DeepSeek)。客户端提交 prompt → worker 以 think → call tool → observe 多步循环执行 agent → 轮询取结果。仅标准库,零第三方依赖。
- Primary Test Command:
go test ./... -count=1 - Baseline Test Result: ✅ 32 passed, 0 failed(captured on 2026-09-03;
main无测试文件) - Build / Lint Command:
go build ./...、go vet ./... - Benchmark:
go test -bench=. -benchmem ./worker/ - 参考 README 记录基线:
go test ./... -count=1应全绿;若先跑失败,先排查 Pre-Existing Broken,别归因到后续改动。
- Entrypoint:
main.go→config.Load("")→store.NewStore()→worker.NewPool(3, s, llm.NewClient(cfg))→api.NewHandler→:8080,信号触发 30s 优雅停机(os.Exit(run()),禁log.Fatal)。 - Data Flow:
POST /run→api.HandleRun(先落库再入队)→store.Create→metrics.IncSubmitted→pool.Enqueue(id)→worker.process→store.Update(running)→runAgent(llm.ChatWithTools⇄execTool→calculate/current_time)→store.Complete(done|failed)→metrics.IncDone/IncFailed→GET /tasks/{id}轮询。 - Package 分工:
api/:Handler{store, pool, authKey};Routes()注册路由;middleware.go:Recover/RequestID/rateLimiter.Limit(20/s, burst 40, 按 IP) /Auth(Bearer, 空 key 放行);demo.gogo:embed demo.html。store/:Store{tasks map, mu Mutex, nextID}+Task;状态pending→running→done|failed;Complete统一裁决终态;ErrNotFound用errors.Is判断。worker/:Pool{queue chan string, ctx/cancel, wg, chatter, tools, handlers};agent.go的runAgent(maxAgentSteps=5)+defaultTools()+ 手写递归下降四则求值器evalArithmetic(禁 eval)。llm/:Client(OpenAI 兼容 DeepSeek chat completions);Config/Message/Tool/FunctionSpec/ToolCall/AssistantTurn;ChatWithTools;非 2xx →APIError。metrics/:包级atomic.Int64;IncSubmitted(同时 running+1)/IncDone/IncFailed;Handler()输出 Prometheus 文本:agent_tasks_submitted|running|done|failed。config/:Config{DeepSeekAPIKey, APIAuthKey};环境变量 >config.yaml(仅扁平key: value)。
- Existing Wheels(复用层):
worker.chatter接口使测试可注入假 LLM;httptest集成测试端到端跑POST /run → 轮询 → done;go:embed单页 Demo。
- 路由分两组:业务路由
POST /run、GET /tasks/{id}走全链(Recover→RequestID→Limit→Auth);运维路由GET /healthz、GET /metrics、GET /只走 Recover→RequestID,永远别把探活/指标塞进限流与鉴权。 - store 一律走方法:map 不支持并发写,
Create/Update/Complete/Get整段持锁;Get返回副本,改动副本不落库。禁止外部直接读写s.tasks。 - 终态只能由
store.Complete写:杜绝「done 却带 error」;任务失败时存粗粒度分类(如"upstream error"),错误全文只进 slog,绝不能把上游响应体放进Task.Error(Task会被原样序列化返回给调用方)。也不要在 Task 加任何内部字段。 Pool.Stop用cancel()而非close(queue):避免向已关闭 channel 发送 panic。Enqueue满时阻塞(缓冲=worker 数),负载高时异步退化为同步,属 ADR-0004/0007 已知限制;改造需 select+default 或持久化队列,先走 ADR。runAgent步数上限 5:防模型死循环;工具出错返回字符串让模型自纠。上下文取消透传到每次 LLM 调用(per-task 60s 超时,ctx 派生自 pool ctx)。- config.yaml 只接受扁平子集:
key: value、#注释、可选成对引号;嵌套/未知 key 一律启动失败不静默。合法 key 仅deepseek_api_key、api_auth_key;文件本身 gitignored(含密钥),示例是config.yaml.example。环境变量DEEPSEEK_API_KEY/API_AUTH_KEY优先级更高。缺 API key 以退出码 1 失败。 - 请求/响应体积护栏:
/runbodyMaxBytesReader32KB;LLM 响应io.LimitReader1MB。 - 依赖纪律(已被 ADR-0010 局部放宽):默认零第三方依赖(metrics 用
sync/atomic手写 Prometheus 文本,不引 prometheus client)。唯一例外是 opt-in 持久化引入modernc.org/sqlite(纯 Go 无 CGO),仅当db_path非空时才真正加载;不配则仍是单二进制零存储依赖。引入任何新依赖前先立 ADR。 .githooks/pre-commit秘密扫描:core.hooksPath=.githooks。改该文件后若 git 报cannot spawn ... No such file or directory,是编辑器/工具重加了 UTF-8 BOM 弄坏 shebang,用tail -c +4 .githooks/pre-commit > tmp && mv tmp去 BOM。- Windows/交叉:路径用反斜杠/正斜杠均可;
go:embed路径相对包目录。 - 提交规范:Conventional Commits,按 docs/feat/fix/test/chore 拆分,不混在一坨;不主动 push。
- 代码注释为中文,解释「为什么」而非「是什么」。
- Global Standards:
d:\Obsidian\Coding\AGENTS.md - Vault Search:
python d:\Obsidian\Coding\scripts\search-vault.py "<query>" - 沉淀新坑:写草稿至
d:\Obsidian\Coding\08-Inbox\YYYY-MM-DD-{topic}.md - 项目文档:
docs/GO-STANDARDS.md、docs/GO-CHEATSHEET.md、docs/adr/0001..0009