algo_log

algo_log · Postgres

一次算法运行会写成什么

算法服务把一次任务打进 Logfire。抽取程序按固定规则把它落进 Postgres schema algo_log,用来回放、对账、做训练集。 列名和来源由代码决定,和库里现在有没有数据无关。

workflow_run
一次完整任务。例如用户点一次搭配推荐,这里就一行。
workflow_step
这次任务里的每一步:选人像、推荐、试衣、检索。一步一行。
llm_call
某一步里的一次模型调用:prompt、回复、model、调用地址。没调模型的步骤没有这一行。

01三张表 · 每一列从哪条日志来

抽取先找 Logfire 里名字以 workflow: 开头的根记录,再按同一个 trace_id 拉齐步骤、模型调用和 HTTP。 检视页里的图走 GET /api/media,现场签深圳 CDN,有效期 7 小时,不用为了看图再抽一次。

algo_log.workflow_run

一行对应一次任务。幂等键是 source + logfire_trace_id + logfire_span_id。会话级 distributed tracing 会让多次 workflow 共用一个 trace,不能只按 trace 覆盖。

字段类型从哪读
idvarchar(26)ETL 生成 ULID,不是日志里的
logfire_trace_idtext根 span 的 trace_id
logfire_span_idtext根 span 的 span_id
workflow_nametext根 span 的 span_name,去掉前缀 workflow:。outfit recommend 为 outfit_recommend_submit_task
task_idtext根 span attributes.task_id
user_idtext根 span attributes.user_id
started_attimestamptz根 span start_timestamp
duration_msbigint根 span duration × 1000
statustext根 span is_exception → failed,否则 success
sourcetextLogfire 项目:mirror;server 里 service_name=algorithmserver-aws,其余 → server-shenzhen
paramsjsonb根 span attributes.workflow_params(没有则用其余 attributes)

algo_log.workflow_step

一行对应 Logfire 里名为 step:… 的一步。只有父记录也是 step 时才填 parent_step_id;父是整次任务则为空。

字段类型从哪读
idvarchar(26)ETL 生成 ULID
run_idvarchar(26)所属 workflow_run.id
parent_step_idvarchar(26)父 span 若也是 step:*,指向那行 id;父是 workflow 则为空
logfire_span_idtext该 step span 的 span_id
seqint同一 run 内按 start_timestamp 排序后的序号,从 1 起
step_nametext优先 attributes.step,否则 span_name 去掉 step:
started_attimestamptz该 step span 的 start_timestamp
duration_msbigint该 step span 的 duration × 1000
statustext该 span is_exception
inputjsonbstep span attributes,去掉 steptask_id。常见键:s3_pathqueryimage_urlraw_queryoutfit_index
outputjsonb挂在该 step 下的日志 span_name = step_result 的 attributes.resultrun_timed_step 打的)
timingsjsonb该 step 下 GET/POST/PUT、yolo.* 子 span 的耗时(网络 vs 本地)
eventsjsonb可回放子事件:*.skipped*.knn_hits*.input / *.result;丢掉 embedding 向量

algo_log.llm_call

一行对应一次模型调用。优先读业务日志 *.response;prompt / messages 来自同一次 POST …/chat/completionshttp.request.body.text,完整回复在子 span Reading response bodyhttp.response.body.text。没有 HTTP 体时再看 *.input.context_messages。检视页单独开 trace 区块,按 context_messages 展示 system / human / images / assistant。生图输出是图 URL,不是字节。base64 图换成 URL 或 [image],密钥丢掉。

字段类型从哪读
idvarchar(26)ETL 生成 ULID
run_idvarchar(26)所属 run
step_idvarchar(26)最近父 step 的 id;找不到则空
logfire_span_idtext该日志的 span_id
seqint同一 run 内按时间排序
agent_nametextspan_name 去掉后缀 .response,例如 outfit_recommender
started_attimestamptz该日志 start_timestamp
output_texttextattributes 里第一个有值的 response / content / output
output_jsonjsonb上面那个值若是 object/array,或能 json.loads 的字符串
prompt_texttext兼容旧检视:messages 里的全文拼在一起,图写成 [image],不截断
context_messagesjsonb清洗后的 OpenAI messagessystem / user / assistant 按调用顺序。优先 *.input;没有则从 POST 投影全文,不截断、不拉图片 base64
request_paramsjsonbhttpx 请求体去掉 messages 之后的全部参数:urlmodeltemperatureenable_thinkingresponse_format 等;extra_body 会摊平
token_usagejsonb优先 Reading response body 的 usage:model / prompt_tokens / completion_tokens / cached_tokens。新抽取不再拉 llm.token_usage
duration_msbigint这次调用耗时。优先 HTTP span duration,没有则用 *.response 上的 ms 或父 span

02outfit_recommend:一次推荐会落哪些行

入口 services/workflow_outfit_recommend_submit_task.py。 根日志名叫 workflow:outfit_recommend_submit_task。 有没有 query 理解、扩写、试衣,取决于这次请求带了什么。

step_name 何时有这一行 workflow_step.input 读自 workflow_step.output 读自 llm_call
body_model 每次都有 span step:body_modelcount 该 step 下 step_result.result(人像列表)
recommend_query_understand raw_query 且 layout 带锚点单品 span 的 raw_query step_result.result(解析后的 outfits) 有则写:recommend_query_understand.input + .response
recommend_query_expand enable_query_expand=true span 的 outfit_count step_result.result 有则写:recommend_query_expand.input + .response
outfit_thinking 当前代码会跳过,一般没有这一行 outfit_thinker.input + .response(当前代码常跳过)
outfit_recommend 每次都有(后台 task,仍挂在同一 trace) span 的 outfit_counttrigger step 成功时的业务输出(outfits) 核心:outfit_recommender.input + outfit_recommender.response,每套 / 每次 retry 一条
outfit_reason 每套有衣服时一条 span 的 outfit_index step_result.result(reason 文案) outfit_reason.response;prompt 在 outfit_reason.input
tryon_tips 准备试衣前一条 span 的 outfit_index step_result.result(穿着说明) tryon_tips.response;prompt 与衣图 URL 在 tryon_tips.input
vton_image 每套能渲染时一条 body_model_idoutfit_index step_result.result(含 image_url vton_image.input + vton_image.response(prompt / 输入图 / 输出图 URL)
outfit_check vton 出图后一条 span 的 image_url step_result.result outfit_check.response;prompt 在 outfit_check.input
outfit_scene_describer vton 出图后一条 span 的 image_url step_result.result(一级场景) outfit_scene_describer.response;prompt 在 *.input

03衣橱检索 closet_search

v1 / v2 步骤几乎相同,检索那一步分别叫 closet_searchcloset_search_v2

step_nameworkflow_stepllm_call
search_query_understand raw_query 时。input.raw_query;output = 解析后的 query/category search_query_understand.input + .response;展示标签是 search_display_keywords.input/.response
closet_searchcloset_search_v2 每次都有。input.query;output = 检索结果

search_display_keywords 是后台另起的任务,不一定和这次检索在同一条 trace,抽不到是预期行为。

04穿搭解析 ootd_v2

入口 services/workflow_ootd_v2_submit_task.py。人像分割、描边识别、合并、去重、描述会按件展开。

step_nameworkflow_step.input / outputllm_call
segment_personinput.s3_path;output 分割结果
outfit_describeinput.s3_path;output 穿搭描述outfit_describer.input + .response
separate_person / human_parseCV 结果在 output
cloth_outline_name_describe输入图 + 识别文本;描边图算输入每轮 cloth_outline_multi_describe.{turn}.input/.response,以及 Reading response body / chat POST 标量
cloth_items_merge输入候选图;输出单品名类别冲突时 cloth_items_merge.input/.response
cloth_embedding_deduplicate查询图 + 候选图;output 为判定结果cloth_embedding_dedup_verify.input/.response
cloth_regenerate / cloth_describe / cloth_soul / cloth_brand_describe按件;input 含图 URL对应 *.input + *.response
outfit_detail_describeoutput 细节描述outfit_detail_describer.input + .response
generate_cover_image / generate_explosion_image输入方图 URL;输出海报 URL*.input + *.response(prompt / 输入图 / 输出图 URL,不是字节)