跳到主要内容

CozeLoop — 架构与原理

30 秒导读: CozeLoop 是字节开源的「AI Agent 全生命周期平台」。把它想成 AI 应用的 CI + APM:一边像 CI——你定义"被测对象(target)+ 评分员(evaluator)+ 数据集",平台自动把每条数据跑一遍、打分、出报表(这叫一次 experiment / 实验);一边像 APM——你的线上 Agent 通过 SDK 把每一次模型调用、工具调用上报成 trace,平台存进 ClickHouse 供你回放、排障、算指标。两边还能闭环:线上 trace 可以一键导成数据集去做评测,评测分数也能写回 trace 上。

本文档讲的是 backend/modules/ 下的 evaluationobservability 两个模块(即 area = evals-observability),不含 prompt / 前端。

1. 这是什么(零基础也能懂)

一句话定义

CozeLoop 是一个面向开发者的 LLM/Agent 平台,覆盖「开发 → 调试 → 评测 → 监控」全流程;本文聚焦其中**评测(Evaluation)可观测(Observability)**两块。

解决什么问题 / 给谁用

假设你做了一个客服 Agent,想回答两个问题:

  • "它到底答得好不好?" —— 你需要一批测试问答(数据集),逐条让 Agent 回答,再让另一个"裁判"给每条答案打分。手工做几千条不现实。→ 评测子系统帮你自动化。
  • "线上为什么这条回复出错了?" —— 一次回复背后是 prompt 渲染 → 模型调用 → 工具调用 → 解析,链路很深。出问题时你想像看调用栈一样逐层回放。→ 可观测子系统把每一步记成 span,串成 trace。

用户是 AI 应用的开发者 / 算法工程师 / 平台团队

它能做什么(功能)

  • 管理评测集(带 schema 的多轮问答数据集,支持大字段裁剪/外存)。
  • 定义评估器:4 类——Prompt(用一个 LLM 当裁判)、Code(跑一段 Python/JS 打分)、CustomRPC(调你自己的服务)、Agent(异步智能体评测)。
  • 实验:把 评测对象 × 评估器 × 评测集 三者组合,逐条目逐轮次执行、打分、聚合成报表。
  • trace 上报与查询:SDK 报 span,落 ClickHouse,按 trace_id 拉全链路、按标签筛选、算 token/延迟指标。
  • 在线评测:对线上 trace 自动采样并触发评估器,分数以 annotation 形式回写到 span。

用起来什么样

部署后浏览器访问 http://localhost:8082(见根 README.md)。后端是一个 Go 服务,两个进程角色:

# main.go —— HTTP 服务,承接前端/OpenAPI 请求(建实验、查 trace…)
# consumer.go —— 消费 RocketMQ,跑实验调度、消费上报的 span

评测的核心心智模型,一句话:一次实验 = 对数据集里每一行,先跑被测对象拿到输出,再让每个评估器对(输入,输出)打分,最后把多评估器分数加权成行分、聚合成实验分。

一句话直觉 / 类比

  • 实验(experiment) ≈ 一次「批量考试」:评测集是题库,target 是考生,evaluator 是阅卷老师,实验分是平均分。
  • trace ≈ 分布式调用栈:span 是栈帧,parent_id 把它们串成树,trace_id 是这次请求的身份证。
  • annotation ≈ 给 trace 贴的便利贴:人工反馈、自动评分都贴上去。

本节不出现底层代码。下面进入全景。

2. 顶层全景(它大概怎么转)

一张图:两套子系统 + 闭环

下面这张图怎么读:左半边是评测(离线为主),右半边是可观测(在线为主),中间两条虚线箭头是把二者闭环的集成点。

┌──────────────────── 评测 Evaluation ────────────────────┐
│ │
用户/OpenAPI ─建实验─▶ experiment(target+evaluators+evalset) │
│ │ │
│ 发 MQ 调度事件 │
│ ▼ │
│ consumer:调度器逐 item/turn 执行 │
│ ① 跑 target 得到输出 │
│ ② 每个 evaluator 并发打分 │
│ ③ 多评估器分数 → 加权行分 → 聚合实验分 │
│ │
└───────────▲──────────────────────────┬──────────────────┘
│导成数据集 │评测对象可以是
│(trace_export) │"线上 trace 记录"
┌───────────┴──────────────────────────▼──────────────────┐
│ │
你的 Agent ─SDK上报─▶ OpenAPI/IngestTraces │
│ │ 发 MQ │
│ ▼ │
│ OTel 风格 collector:receiver→processor→exporter │
│ │ │
│ ▼ 落库 │
│ ClickHouse(spans 表) ◀─annotation 回写分数──┐ │
│ │ 查询 │ │
│ 按 trace_id / 标签 / 指标拉取 │ │
│ 在线评测 │
│ 采样线上 span ─触发评估器─▶ AutoEvalCallback ─┘ │
│ │
└──────────────────── 可观测 Observability ────────────────┘

部件一句话职责

部件干什么在哪
Experiment 实体一次评测的聚合根:绑定 target / evalset / evaluators / 配置 / 状态modules/evaluation/domain/entity/expt.go
调度器(SchedulerMode)把实验拆成 item/turn,按模式(提交/重试/追加…)逐条派发执行modules/evaluation/domain/service/expt_run_scheduler_mode_impl.go
TurnEvaluation单行执行核心:跑 target → 跑 evaluatorsmodules/evaluation/domain/service/expt_run_item_turn_impl.go
Evaluator 各 source4 类评估器的执行实现(Prompt/Code/RPC/Agent)modules/evaluation/domain/service/evaluator_source_*_impl.go
行分计算器把一行内多个评估器分数加权(或走 HTTP hook)modules/evaluation/domain/service/expt_score_calculator.go
TraceServicetrace 的上报入口与所有查询modules/observability/domain/trace/service/trace_service.go
Collector 管道OTel 风格的 receiver→processor→exporter,消费上报落库modules/observability/domain/trace/entity/collector/
Span 实体一个 span 的全字段模型(含 tags/对象存储引用)modules/observability/domain/trace/entity/loop_span/span.go
ClickHouse spans DAOspan 批量写入与按条件查询modules/observability/infra/repo/ck/spans.go
TaskCallback在线评测回调:把评分写成 span 的 auto_evaluate annotationmodules/observability/domain/task/service/task_callback.go
TraceExportService把选中的 trace 导成评测数据集(闭环)modules/observability/domain/trace/service/trace_export_service.go

主线走一遍(高层)

评测主线:前端建实验 → 后端持久化 Experiment → 发一条调度事件到 MQ → consumer 里调度器领取 → 扫出待跑 item → 对每个 item 的每个 turn 调 TurnEvaluation.Eval:先 CallTarget 拿被测输出,再 CallEvaluators 并发打分 → 行分计算器加权 → 聚合写回实验统计。

可观测主线:你的 Agent 用 SDK 把 span 批量 POST 到 OpenAPI → TraceService.IngestTraces 过一遍 processor 后丢进 MQ → collector 管道消费 → exporter 批量写 ClickHouse → 你在 UI 按 trace_id 或标签查回来。

3. 架构分层(DDD)

两个模块都是严格的 DDD 四层,依赖方向恒为 application → domain ← infra(domain 定接口,infra 实现,domain 绝不反向依赖 infra,见根 ARCHITECTURE.md)。

modules/<evaluation|observability>/
├── application/ 用例编排 + Wire DI(eval 的 experiment_app、ob 的 trace/ingestion/task)
├── domain/ 领域核心:entity(实体)+ service(业务逻辑)+ repo(仓储接口)
├── infra/ 实现:repo 落库(MySQL/ClickHouse/Redis)、mq、rpc、storage、collector
└── pkg/ 模块内工具(errno、utils…)

技术栈(根 ARCHITECTURE.md):HTTP=Hertz,RPC=Kitex+Thrift,ORM=GORM,DI=Wire,LLM=Eino;存储 MySQL(主)+ ClickHouse(trace/分析)+ Redis(缓存/锁/计数)+ RocketMQ(异步)。

4. 阅读地图(建议顺序)

章节读它你会懂难度
01-evaluation-core.md实验执行引擎:状态机、item/turn 拆分、并发模型、加权聚合★★☆
02-evaluator-llm-judge.md4 类评估器,重点是 LLM-as-judge 的输出四级降级解析、Code 沙箱★★☆
03-observability-trace.mdtrace 上报到落库到查询,OTel 风格 collector 管道,CK 存储★★☆
04-integration-and-design.md两子系统闭环、巧妙之处、边界、横向对比、总代码地图★☆☆

建议路径:先 01 建立"实验怎么转"的骨架 → 02 看清"裁判内部" → 03 切到可观测 → 04 把两边接起来并收口。


引用约定:所有 file:line 相对克隆根 backend/,as-of commit 3f01307。行号可能随上游漂移,符号名(函数/类型)更稳,定位优先用符号名 grep。