Skip to content

Latest commit

 

History

History
43 lines (38 loc) · 5.57 KB

File metadata and controls

43 lines (38 loc) · 5.57 KB

🤖 Agent Context: agent-api

Go 异步任务 + Worker Pool + LLM Agent 后端(DeepSeek)。客户端提交 prompt → worker 以 think → call tool → observe 多步循环执行 agent → 轮询取结果。仅标准库,零第三方依赖。

1. 🛠️ Ground Truth Verification Baseline

  • 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,别归因到后续改动。

2. 🗺️ Core Symbol & Architecture Map

  • 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.go go: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。

3. 🚨 Local Engineering Red Lines & Gotchas

  • 路由分两组:业务路由 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 失败。
  • 请求/响应体积护栏:/run body MaxBytesReader 32KB;LLM 响应 io.LimitReader 1MB。
  • 依赖纪律(已被 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。
  • 代码注释为中文,解释「为什么」而非「是什么」。

4. 📚 Coding Vault Linkage

  • 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