Distribution: Adapting to Multiple Platform Specs, Generating Covers and Titles, Batch Export, and Feeding Data Back
Export the same finished cut in multiple versions per platform's spec, pair it with a clickable cover and title, and feed post-release data back to guide how the next episode gets shot.
Today's Goals
- Automatically export multiple versions per target platform's spec, including resolution, duration, and cover size
- Batch-generate cover copy and titles with a model, and pick the best version against judgeable criteria
- Design a data feedback path that turns playback data into input for the next episode
Yesterday you fitted the brakes; today you touch the accelerator and actually publish an episode. When you finish, scroll back up and tick the three goals off.
Plain-Language Walkthrough
Distribution's first lesson: the most important column is not a number
When a distributor schedules a release, it juggles IMAX screens, ordinary screens and streaming platforms, each wanting a different print format. The most important column on the distribution manager's table has never been "what resolution"; it is "who stated this requirement" — what is written in the exhibition contract and what a projectionist said in passing carry completely different weight.
Multi-platform distribution's first lesson lives exactly there. Open your editor to write a platform spec table and you hit an awkward fact: not all of those numbers are obtainable.
Take this course's three target platforms. TikTok has public content-posting API documentation with clearly stated specs:
| Item | Requirement stated in TikTok's official docs |
|---|---|
| Side length | minimum 360 pixels, maximum 4096 pixels |
| Container | MP4 recommended; WebM and MOV also accepted |
| Video codec | H.264 recommended |
| File size | maximum 4GB |
| Frame rate | 23 to 60 FPS |
| Duration | up to 10 minutes on the developer upload endpoint |
And Douyin and Kuaishou? Their duration, resolution, bitrate and cover dimensions cannot be found on public documentation sites. At that point you have two options.
One is finding a blog post titled "the complete 2026 Douyin video spec guide" and copying its numbers into your code. That is what most people do, and it is what this day most wants to talk you out of. Copied numbers have three problems: they may be out of date, they may apply only to one account type, and — worst of all — they make you doubt your code when an upload fails, instead of doubting the number. You will spend two hours reading ffmpeg parameters when the real cause is that a three-year-old blog post got it wrong.
The other is leaving the table honestly blank and turning that fact into a field in the code. Each platform in the lab carries a sourceOfTruth:
official-doc: written explicitly in the platform's official developer documentation, so code may make decisions from it;check-your-console: not findable in public documentation, so it must be confirmed on the spec page inside your own account console.
For the second class the program's behavior is definite: stream-copy the master as it is and guess no numbers, while adding a line to the publishing checklist reading "before uploading, verify duration, resolution, bitrate and cover size in the console." Leaving it blank is not laziness; it moves the uncertainty out of the code and onto a checklist, handed to the only person who can confirm it — you.
This rule matters far beyond distribution. Any module you write against an external system meets the same problem: some constraints are documented, some are hearsay. Mix them in one table and the whole table's credibility drops to that of its weakest row. Labeled separately, you at least know which rows can go into assertions.
With the spec table settled, the next question is the interesting one: three versions of one episode — does that mean rendering three times?
Render once, package many times
Of course not. But explaining why requires separating two words people conflate.
Transcoding decodes the video and re-encodes it, actually recompressing the picture data. Muxing (or remuxing) merely repacks an already-encoded bitstream into a different container, touching not one byte of picture data. In kitchen terms: transcoding re-cooks the dish, muxing serves the same dish on a different plate.
Why does the difference matter? Two reasons. First, time — re-encoding a vertical clip takes seconds to tens of seconds; changing containers takes tens of milliseconds. Second, and more importantly, lossy encoding loses quality on every pass. Your master is already the result of one encode after generation; transcoding once per platform means the audience sees doubly compressed picture. That shows most in dark areas and fast motion — and short drama is full of rainy nights and tracking shots.
So the correct shape is render once, package many times: encode exactly once when producing the master, and make every platform export a stream copy where possible.
# Join shots into the master: identical encoding parameters, stream copy, no recompression
ffmpeg -f concat -safe 0 -i concat.txt -c copy master.mp4
# Platform export: change the container, add faststart, still no re-encode
ffmpeg -i master.mp4 -c copy -movflags +faststart episode-1.tiktok.mp4
# Over-length needs no re-encode either: -t together with -c copy cuts directly
ffmpeg -i master.mp4 -c copy -t 600 episode-1.trimmed.mp4When is re-encoding actually required? Only when the master's picture data itself fails a requirement: out-of-range resolution needs scaling, an unaccepted codec needs a different encoder, an out-of-range frame rate needs conversion, an oversized file needs a lower bitrate. Everything else — container changes, faststart, truncation by duration — can be stream-copied.
Write that judgment as a function whose return value is not only "re-encode or not" but also the reason for every decision, or nobody will know afterwards where the conclusion came from:
function decidePackaging(master, spec) {
const checks = []
const manualChecks = []
const extraArgs = []
let reencode = false
// Unknown-spec platforms: guess nothing, stream-copy as is, hand verification to a human
if (spec.sourceOfTruth === 'check-your-console') {
checks.push('spec unknown: stream-copying the master as is')
manualChecks.push(`${spec.name}: verify against the console spec page before uploading`)
return { action: 'copy', checks, extraArgs, manualChecks }
}
const long = Math.max(master.width, master.height)
const short = Math.min(master.width, master.height)
if (short < spec.minSidePx || long > spec.maxSidePx) {
checks.push(`side lengths ${short}-${long} out of range, scaling required`)
reencode = true
}
if (master.videoCodec !== spec.videoCodec) {
checks.push(`codec ${master.videoCodec} is not the recommended one, re-encode required`)
reencode = true
}
if (master.durationSec > spec.maxDurationSec) {
// Over-length truncates with -t under stream copy; this is the most misjudged case
checks.push('duration over limit: truncating with -t under stream copy, still no re-encode')
extraArgs.push('-t', String(spec.maxDurationSec))
}
return { action: reencode ? 'reencode' : 'copy', checks, extraArgs, manualChecks }
}def decide_packaging(master, spec):
checks, manual_checks, extra_args = [], [], []
reencode = False
# Unknown-spec platforms: guess nothing, stream-copy as is, hand verification to a human
if spec["source_of_truth"] == "check-your-console":
checks.append("spec unknown: stream-copying the master as is")
manual_checks.append(f"{spec['name']}: verify against the console spec page before uploading")
return {"action": "copy", "checks": checks, "extra_args": extra_args,
"manual_checks": manual_checks}
long_side = max(master["width"], master["height"])
short_side = min(master["width"], master["height"])
if short_side < spec["min_side_px"] or long_side > spec["max_side_px"]:
checks.append(f"side lengths {short_side}-{long_side} out of range, scaling required")
reencode = True
if master["video_codec"] != spec["video_codec"]:
checks.append(f"codec {master['video_codec']} is not recommended, re-encode required")
reencode = True
if master["duration_sec"] > spec["max_duration_sec"]:
# Over-length truncates with -t under stream copy; the most misjudged case
checks.append("duration over limit: truncating with -t under stream copy, no re-encode")
extra_args += ["-t", str(spec["max_duration_sec"])]
return {"action": "reencode" if reencode else "copy", "checks": checks,
"extra_args": extra_args, "manual_checks": manual_checks}The lab prints an encode tally: shots encoded 3 times (the master's raw material), re-encodes 0, stream-copy packagings 4, alongside "the naive approach would re-encode 3 times." Covers follow the same idea: all three platforms want a vertical cover, so grab the frame once and copy it three times rather than grabbing three frames.
Covers and titles: let the model produce candidates in bulk
With the cut ready, the two things that decide the click rate come next: the cover and the title.
They differ fundamentally from video: video is expensive and slow; cover copy is cheap and fast. One video generation costs a few yuan and a few minutes; one text call costs a few cents and a few seconds. Two things three orders of magnitude apart in price deserve completely different strategies — video wants "get it right the first time," copy wants "make several and choose."
So the correct shape here is not "have the model write the best title" but have it write one per angle. The lab fixes five: suspense, contrast, identity, numeric, declarative. Each maps to a different instruction, the model writes one of each, and the next step picks.
The angles are not arbitrary; they come from a plain observation that short-drama titles hook people in only a handful of ways, and enumerating those as templates is steadier than free improvisation. And the angles are a function of the input — each template draws from that episode's storyboard (the lead's name, the first line, the last line), so a different episode changes all five candidates.
One more mandatory step: put a backstop on the model's output. What the model returns may not be printable on a cover — too long, carrying an explanatory preamble, or containing debug markers like brackets. The lab has a usableCoverCopy function for exactly this: reject anything outside the length band, anything containing square, curly or angle brackets, anything hitting a hype word. Rejected output falls back to a local template.
Choose rather than generate: converge with a score
With five candidates, how do you choose?
Not by asking the model which is best. Letting a model grade its own homework has two problems: it is unstable, giving different answers to the same batch on two asks; and it is unexplainable, leaving you unable to justify picking the third one to anyone.
The right approach is a deterministic scoring function. It need not be clever; it only needs to turn your editorial judgment into additive points:
const BANNED = ['best ever', 'number one', 'must watch', 'shocking', 'guaranteed']
function scoreTitle(title, ctx, others) {
const reasons = []
let score = 0
const len = [...title].length
// The sweet zone below is measured in characters; tune it to your language and font
if (len >= 24 && len <= 52) { score += 3; reasons.push(`length ${len} in the sweet zone +3`) }
else { score -= 2; reasons.push(`length ${len} outside the sweet zone -2`) }
if (/[?…]|isn't|turns out|only|still/i.test(title)) { score += 2; reasons.push('carries suspense +2') }
if (title.includes(ctx.protagonist)) { score += 1; reasons.push('names a character +1') }
if (/\d/.test(title)) { score += 1; reasons.push('has a concrete number +1') }
const hit = BANNED.filter((w) => title.toLowerCase().includes(w))
if (hit.length) { score -= 5; reasons.push(`hype wording ${hit.join(', ')} -5`) }
return { score, reasons }
}
// Tie-break by id so two runs pick a byte-identical top three
const top3 = candidates
.sort((a, b) => b.score - a.score || a.id.localeCompare(b.id))
.slice(0, 3)import re
BANNED = ["best ever", "number one", "must watch", "shocking", "guaranteed"]
def score_title(title, ctx, others):
reasons, score = [], 0
length = len(title)
# The sweet zone below is measured in characters; tune it to your language and font
if 24 <= length <= 52:
score += 3
reasons.append(f"length {length} in the sweet zone +3")
else:
score -= 2
reasons.append(f"length {length} outside the sweet zone -2")
if re.search(r"[?…]|isn't|turns out|only|still", title, re.I):
score += 2
reasons.append("carries suspense +2")
if ctx["protagonist"] in title:
score += 1
reasons.append("names a character +1")
if re.search(r"\d", title):
score += 1
reasons.append("has a concrete number +1")
hit = [w for w in BANNED if w in title.lower()]
if hit:
score -= 5
reasons.append(f"hype wording {', '.join(hit)} -5")
return {"score": score, "reasons": reasons}
# Tie-break by id so two runs pick a byte-identical top three
top3 = sorted(candidates, key=lambda c: (-c["score"], c["id"]))[:3]Three details are worth keeping. First, every item writes a sentence into reasons: an unexplained score has no floor, and you cannot tune the rules from it. Second, hype wording is a penalty, not a filter: filtering candidates out can leave you with none in the extreme case, while a heavy penalty puts them last but keeps them in the pool. Third, ties need a tie-breaker — order by id. Without it, tied candidates' relative order depends on the sort implementation, two runs may pick different top threes, and you will blame the model's instability and go tune temperature and prompts in entirely the wrong direction.
The "generate candidates, then score to choose" pattern reaches far beyond titles. It fits any stage whose output is subjective, uncontrollable per call, and cheap: copy, cover composition, cut points, even prompts themselves. Give the uncontrollable part to the model and the floor to the scoring function.
Data feedback: map the retention curve onto specific stages
And after publishing? Most people open the dashboard, look at the numbers, sigh, and shoot the next episode. That is not data feedback; that is spectating.
Data feedback has one criterion: every conclusion must land on a specific stage of the pipeline. A metric that lands on no stage cannot be acted on.
The retention curve suits this naturally, because its x-axis is time and your timeline table records every shot's start and end. Three rules map the curve's three segments onto three stages:
function diagnose(stat, windows) {
const findings = []
const at = (sec) => stat.retention.find((p) => p.atSec === sec)?.ratio ?? 1
// First three seconds: the cover and title got them in; the first shot failed to hold them
const openDrop = 1 - at(3)
if (openDrop > 0.4) {
findings.push({ stage: 'cover+titles+frames',
symptom: `lost ${(openDrop * 100).toFixed(0)}% in the first 3 seconds`,
action: 'repost with the top-scoring cover and title, and check the first frame carries information' })
}
// Middle: find the steepest drop between adjacent points and map it back to a shot
let worst = null
for (let i = 1; i < stat.retention.length; i += 1) {
const [prev, cur] = [stat.retention[i - 1], stat.retention[i]]
if (prev.atSec < 3) continue
const drop = prev.ratio - cur.ratio
if (!worst || drop > worst.drop) worst = { drop, from: prev.atSec }
}
if (worst && worst.drop > 0.15) {
const shot = windows.find((w) => worst.from >= w.startSec && worst.from < w.endSec)
findings.push({ stage: 'clips',
symptom: `lost ${(worst.drop * 100).toFixed(0)}% mid-episode, inside ${shot.shotId}`,
action: `shorten ${shot.shotId} or change its camera move, regenerating only that shot` })
}
// Whole episode: a low completion rate means the ending left no hook, so the script is at fault
if (stat.completionRate < 0.3) {
findings.push({ stage: 'script', symptom: 'completion rate too low',
action: 'give the next episode a cliffhanger ending' })
}
return findings
}def diagnose(stat, windows):
findings = []
ratio_at = {p["at_sec"]: p["ratio"] for p in stat["retention"]}
# First three seconds: the cover and title got them in; the first shot failed to hold them
open_drop = 1 - ratio_at.get(3, 1)
if open_drop > 0.4:
findings.append({"stage": "cover+titles+frames",
"symptom": f"lost {open_drop * 100:.0f}% in the first 3 seconds",
"action": "repost with the top-scoring cover and title, and check the first frame"})
# Middle: find the steepest drop between adjacent points and map it back to a shot
worst = None
points = stat["retention"]
for prev, cur in zip(points, points[1:]):
if prev["at_sec"] < 3:
continue
drop = prev["ratio"] - cur["ratio"]
if worst is None or drop > worst["drop"]:
worst = {"drop": drop, "from": prev["at_sec"]}
if worst and worst["drop"] > 0.15:
shot = next(w for w in windows
if w["start_sec"] <= worst["from"] < w["end_sec"])
findings.append({"stage": "clips",
"symptom": f"lost {worst['drop'] * 100:.0f}% mid-episode, inside {shot['shot_id']}",
"action": f"shorten {shot['shot_id']} or change its camera move"})
# Whole episode: a low completion rate means no hook at the end, so the script is at fault
if stat["completion_rate"] < 0.3:
findings.append({"stage": "script", "symptom": "completion rate too low",
"action": "give the next episode a cliffhanger ending"})
return findingsNote the payoff of the middle rule: it does not say "this episode's pacing is off," it says "shot s02 needs redoing." The scope shrinks from an episode to a shot, and redoing one shot costs a third of redoing an episode — yesterday's arithmetic paying off here. That is the practical value of landing data on a stage: it shrinks both the change and the spend.
The rules are written crudely on purpose. Crude means explainable: you can point at the log and say why this stage is the suggestion. Once you hold real data across dozens of episodes, replacing these hand-written thresholds with learned ones is the next step — but explainability must not be dropped.
The last checklist before publishing
The checklist last. It is not ceremony; its reason is concrete: publishing is the only irreversible action on this line. Every earlier step can be re-run; content that is out cannot be recalled.
The lab prints a tickable checklist in three classes. First, what the machine already verified, listed for human confirmation: all three versions play, the cover orientation matches the cut, the title comes from the top three and contains no hype wording. Second, what the machine cannot verify — the two unknown-spec platforms, verified against the console before uploading. Third, the hard lines, two of them: generated-content labeling (both explicit and implicit; the how is in /en/learn/ai-drama-pipeline/day-11) and traceable provenance for music and material.
Every line must be confirmable by one person in one minute. Writing "ensure the content is compliant" is useless — nobody knows where to look to call it confirmed.
Source Reading
Hands-On Lab
Confirm ffmpeg and ffprobe are both present first. Almost every decision today rests on master information read by ffprobe, and without it the whole decision chain is empty. If you get stuck, look at the encode-tally lines; they tell you directly whether the decision function is inverted.
- Run the starter unmodified, compare the encode tally, the candidate scores and the diagnoses, and note the obviously wrong ones.
- Implement spec decisions: unknown-spec platforms stream-copy and leave a manual verification item, documented platforms compare item by item before deciding; confirm re-encodes drop from 3 to 0.
- Add the scoring function and the tie-breaker, confirm all five candidates print scores with reasons, and that two consecutive runs give an identical top three.
- Add the backstop on model output and confirm the cover-copy line changes from placeholder text to "falling back to the local template."
- Implement the retention-to-stage mapping and confirm the two platforms differ, with the middle rule naming a specific shot id.
Interview Questions
Today's 3 questions are in the question bank below, focused on the trade-offs of render-once-package-many, the generate-then-score selection pattern, and designing a data feedback loop. Read the analysis before the key points — practicing the derivation beats memorizing them. The cn / global labels let you pick by target market.
Checklist and Tomorrow
- I can automatically export multiple versions per target platform's spec, including resolution, duration, and cover size
- I can batch-generate cover copy and titles with a model, and pick the best version against judgeable criteria
- I can design a data feedback path that turns playback data into input for the next episode
- I can explain the difference between transcoding and muxing, and list the cases that truly require re-encoding
- I can explain why every number in a spec table needs a stated source, and what to do when there is none
- All 5 lab acceptance criteria pass
- I can answer at least 2 of the 3 interview questions without looking at the key points
Tomorrow (D14) is the last day: run a five-episode season from one world-bible record, check whether characters and art style drifted across episodes, produce a full-season bill, and gather these fourteen days into a portfolio you can present. Why leave a season for last? Because a season is the final examination of every engineering decision above it — idempotency, concurrency, cost, distribution — and any one of them done sloppily shows up immediately when five episodes run back to back.
Interview questions
The same video has to be published to several platforms with different specs. How do you design the export flow to minimize transcoding?同一条视频要发多个平台,每个平台规格不同,你会怎么设计导出流程才能少转码?
Common in ChinaCommon overseasIntermediate#media-pipeline#ffmpegHow to reason about it · think before answering
- This question tests whether you distinguish transcoding from remuxing. Rendering once per platform is not a coding failure, it is a failure to notice that every lossy re-encode costs quality.
- Separate the two: transcoding decodes and re-encodes, so the picture data is genuinely recompressed; remuxing just moves an already-encoded bitstream into another container without touching a byte. One takes seconds and loses quality, the other takes milliseconds and is lossless.
- Then give the flow: render one master using the most conservative parameters that satisfy every target, then run each platform through a decision function and stream-copy whenever possible. Container changes, faststart and duration trims all stay within stream copy.
- Know the cases that truly require re-encoding: out-of-range resolution, an unaccepted codec, a frame rate outside the allowed band, and a file that exceeds the size cap. Duration is the one people misjudge most, since -t with stream copy already trims it.
- Add an engineering rule: the decision function should return a list of reasons, not just a boolean. When someone asks why a platform got re-encoded, you answer from the log rather than rereading the code.
- Expect the follow-up: how do you prove it? Print an encode counter alongside the count the naive approach would have produced. A number without a baseline convinces nobody.
分析过程 · 先想清楚再作答
- 这题在考你分不分得清转码与封装。答成「按每个平台各渲染一遍」的人不是不会写代码,是没意识到有损编码每转一次就掉一次画质。
- 先把两个词分开:转码是重新解码再编码,画面数据真的被压了一遍;封装只是把已编好的码流换个容器,一个字节都没动。前者要几秒到几十秒并且掉画质,后者几十毫秒且无损。
- 然后给流程:先渲染一份母版,参数取所有目标平台的交集里最保守的一档;之后每个平台走一次判定函数,能流复制就流复制。换容器、加 faststart、按时长截断都属于流复制的范围。
- 必须重编码的情况要能背出来:分辨率越界要缩放、编码格式不被接受、帧率超范围、文件大小超限要降码率。除此之外都不该重编码——尤其时长超限这一条最容易被误判,其实 -t 配流复制就能切。
- 补一条工程判据:判定函数要返回理由列表,不只是布尔值。出片之后有人问「为什么这个平台转了码」,你要能拿日志回答,而不是重新读一遍代码。
- 可预期的追问是「怎么证明真的少转了」。答案是打印一个编码次数计数器,并同时给出朴素做法的次数做对照——没有对照的数字说服不了任何人。
Key points
- Separate transcode from remux: container swaps, faststart and duration trims are all stream copies
- Render one master using the most conservative intersection of all target constraints
- Re-encode only for out-of-range resolution, unaccepted codec, out-of-band frame rate, or oversize files
- Have the decision function return reasons so every re-encode can be explained
- Print an encode counter next to the naive baseline to prove the saving
答题要点
- 分清转码与封装:换容器、加 faststart、按时长截断都可以流复制
- 一次渲染母版,参数取所有目标平台约束的最保守交集
- 只有分辨率越界、编码不被接受、帧率超范围、体积超限才必须重编码
- 判定函数返回理由列表,让每次重编码都能被解释
- 打印编码次数计数器并与朴素做法做对照,才算证明少转了码
When a model generates creative content such as titles and cover copy, how do you guarantee a quality floor?让模型生成标题、封面文案这类创意内容,怎么保证质量下限?
Common in ChinaCommon overseasIntermediate#llm-output-quality#candidate-selectionHow to reason about it · think before answering
- The pivot is the word floor. The question is not how to make output better but how to keep it from being bad, and those two goals need different techniques.
- Start from one criterion: is this stage expensive? Expensive slow stages such as video generation must get it right once by constraining the input. Cheap fast stages such as copywriting should generate several variants and converge. A three-order-of-magnitude price gap justifies opposite strategies.
- So the shape is: generate one candidate per preset angle, then converge with a deterministic scoring function. The floor comes from the scorer, not from the model, because model variance is the normal case and the scorer has none.
- Three requirements for the scorer: emit a reason per rule, since an unexplained score cannot be iterated on; penalize banned wording heavily rather than filtering it, because filtering can leave you with nothing; and always define a tie-breaker, or two runs pick different winners and you will blame the model and start tuning temperature.
- Add a structural guard: model output may be too long, prefixed with explanation, or carry debug markers. Validate the shape and fall back to a local template when it fails. That layer handles uncontrollable structure, which is a different problem from uncontrollable quality.
- Expect the follow-up: why not let the model score itself? Because it is unstable and unexplainable. The same batch can be ranked differently twice, and you cannot justify the choice to anyone. Model judgment can be one input to the scorer, never the only judge.
分析过程 · 先想清楚再作答
- 题眼是「下限」两个字。它问的不是怎么让输出更好,而是怎么保证输出不会太差——这两个目标的手段完全不同,混起来答就散了。
- 先给一条判断依据:这个环节贵不贵。贵而慢的环节(比如视频生成)要「一次做对」,靠约束输入;便宜而快的环节(文案)应该「多做几版再挑」,靠收敛输出。价格差三个数量级,策略就该完全不同。
- 于是形态是:按几个预设角度各生成一版候选,再用一个确定性的打分函数收敛成前几名。下限由打分函数保证,而不是由模型保证——模型不稳定是常态,打分函数不会。
- 打分函数的三条要求:每一项都写出理由(分数不解释就没法迭代规则)、违规词用扣重分而不是过滤(过滤在极端情况下会一条不剩)、同分必须有决胜键(否则两次运行挑出不同结果,你会误以为是模型不稳定去调温度)。
- 还要有一道兜底:模型返回的东西不一定能直接用,可能太长、带解释性前缀、夹着调试符号。加一个格式校验,不通过就回落到本地模板。这一层挡的是「输出结构不可控」,和打分挡的「输出质量不可控」是两件事。
- 可预期的追问是「为什么不让模型自己评分」。答案是不稳定且不可解释:同一批候选问两次可能给出不同答案,而且你无法向任何人说明为什么选了第三条。模型评分可以作为打分函数的一项输入,但不能是唯一的裁判。
Key points
- Pick the strategy by stage cost: constrain input when expensive, converge output when cheap
- Generate one candidate per preset angle, then rank with a deterministic scoring function
- The scorer must emit reasons, penalize banned wording instead of filtering, and define a tie-breaker
- Add a separate structural fallback for malformed output, distinct from quality scoring
- Do not let the model judge itself; use it at most as one signal inside the scorer
答题要点
- 按环节的价格选策略:贵的一次做对靠约束输入,便宜的多做几版靠收敛输出
- 形态是按预设角度批量出候选,再用确定性打分函数挑前几名
- 打分函数必须输出理由、对违规词扣重分而非过滤、同分给决胜键
- 另加一道结构兜底:格式不合格就回落本地模板,与质量打分是两件事
- 不让模型给自己评分,它不稳定也不可解释,最多作为打分的一项输入
How do you feed post-publication metrics back into the production pipeline? Describe a concrete path.内容发布之后的数据要怎么回流到生产流程里?说一条具体可落地的路径。
Common in ChinaCommon overseasDeep dive#feedback-loop#analyticsHow to reason about it · think before answering
- The easy wrong answer is build a dashboard and review it regularly, which is spectating rather than feedback. The discriminator is whether you can map a metric to a concrete action.
- Set the rule first: every conclusion must land on a specific pipeline stage. A metric that maps to no stage cannot be acted on, so it does not belong in the feedback path at all.
- Then give a concrete mapping. Retention curves fit naturally because their x axis is time and your timeline table records the start and end of every shot. Early drop maps to cover, title and the first frame; the steepest mid-curve drop is looked up in the timeline to a specific shot id and maps to that shot's duration and camera move; a low completion rate maps to the script's closing hook.
- Landing on a stage pays twice: it narrows the edit from a whole episode to a single shot, and the regeneration cost narrows with it. Say this out loud, it connects analytics to cost control and is the differentiating point of the answer.
- Keep the rules deliberately dumb and explainable, starting with hand-set thresholds. Replace them with learned ones once you have dozens of episodes, but never give up explainability, because you must be able to justify each recommendation from the log.
- Expect the follow-up: how do you merge data across platforms? You do not. Diagnose each platform separately, because the difference in how the same episode performs is itself the signal, and merging erases it.
分析过程 · 先想清楚再作答
- 这题最容易答成「建个数据看板,定期复盘」——那是看热闹,不是回流。区分度在于你能不能给出一条从指标到具体动作的映射。
- 先立判据:每一条结论必须落到流水线上一个具体的环节上。落不到环节的指标,看了也改不了,所以它根本不该出现在回流路径里。
- 然后给一条真实可落地的映射。留存曲线天然适合,因为横轴是时间,而你的时间轴表里记着每一镜的起止时间:开头几秒的掉幅映射到封面与标题、以及第一镜的首帧;中段掉幅最大的那一段用时间轴反查出具体镜头 id,映射到那一镜的时长与运镜;完播率整体偏低映射到剧本的结尾钩子。
- 落到环节的收益是双份的:修改范围从一整集缩到一个镜头,成本也跟着缩到几分之一。这一点要主动说,它把「数据分析」和「成本控制」连起来了,是这题的加分项。
- 判据要写得笨且可解释,先用手写阈值。等积累了几十集真实数据再换成从数据里学出来的,但可解释这条不能丢——你必须能对着日志说清为什么建议改这一环。
- 可预期的追问是「多平台数据怎么合并」。答案是不要合并,分平台各诊断一次:同一集在不同平台的表现差异本身就是信息,合并会把它抹掉。
Key points
- One rule: every conclusion must land on a concrete stage, otherwise it does not belong in the loop
- Map the retention curve in three segments: opening drop to cover and first frame, steepest mid drop to a shot id via the timeline, low completion to the script hook
- Landing on a stage shrinks both the edit scope and the regeneration cost to a single shot
- Start with hand-set thresholds for explainability and learn them later once data allows
- Diagnose platforms separately; the divergence between them is itself signal
答题要点
- 判据只有一条:每条结论必须落到流水线上一个具体环节,落不到就不该进回流路径
- 留存曲线三段映射:开头掉幅到封面标题与首帧,中段掉幅用时间轴反查到具体镜头,完播率到剧本钩子
- 落到环节同时缩小了修改范围与重做成本,只重生成一镜而不是重跑一集
- 先用手写阈值保证可解释,数据够了再换成学出来的规则
- 多平台数据分别诊断不合并,平台间的差异本身就是信息