fix(llm): 策略稿润色预留 completion 预算,缓解输出截断

generate_strategy_draft_markdown_llm 按摘要/第九章节选/规则底稿分级收紧,满足最小 completion token 后再调用;底稿与 JSON 超限仍截断并提示。系统提示补充收束全文要求。

Made-with: Cursor
This commit is contained in:
hub-gif 2026-04-17 16:48:05 +08:00
parent 1229e49dbe
commit f06fd12f1f

View File

@ -18,7 +18,8 @@ STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**
- **不得编造**输入中不存在的销量占比价格数字若底稿与摘要中有数字须保持一致表述集中度时用第一大份额前三家合计等中文**不要用** CR1CR3 等缩写
- `structured_brief` `matrix_overview_for_llm` 或矩阵相关字段策略中应**呼应**细分类目分组与竞品矩阵结论不得无故删光矩阵相关建议
- 可调整段落衔接标题层级列表与表格呈现使更易读可补充建议待业务确认类表述但不虚构竞品名称或数据
- **仅输出** Markdown 正文不要 ``` 围栏包裹全文"""
- **仅输出** Markdown 正文不要 ``` 围栏包裹全文
- **输出完整性**须收束各小节与全文勿在句子或列表中途戛然而止"""
STRATEGY_USER_PREFIX = """请基于以下 JSON 输出最终策略稿Markdown\n\n"""
@ -45,24 +46,86 @@ def generate_strategy_draft_markdown_llm(
generated_at_iso=generated_at_iso,
strategy_decisions=strategy_decisions,
)
compact = compact_brief_for_llm(brief, max_chars=80_000)
excerpt = (report_strategy_excerpt or "").strip()
payload = {
excerpt_raw = (report_strategy_excerpt or "").strip()
sys_prompt = STRATEGY_SYSTEM
min_comp = _min_strategy_completion_tokens()
min_comp_relaxed = max(256, min_comp // 2)
def _payload_and_user(
*,
compact_max: int,
excerpt_max: int,
rules_max: int | None,
) -> str:
compact = compact_brief_for_llm(brief, max_chars=compact_max)
ex = (
_truncate_strategy_narrative(excerpt_raw, excerpt_max)
if excerpt_raw
else ""
)
if rules_max is None:
rd = rules_md
else:
rd = _truncate_rules_draft_md(rules_md, rules_max)
payload: dict[str, Any] = {
"job_id": job_id,
"keyword": keyword,
"generated_at_iso": generated_at_iso,
"strategy_decisions": strategy_decisions,
"business_notes": business_notes,
"structured_brief": compact,
"rules_draft_markdown": rules_md,
"report_strategy_excerpt": excerpt,
"rules_draft_markdown": rd,
"report_strategy_excerpt": ex,
}
raw = json.dumps(payload, ensure_ascii=False)
if len(raw) > 500_000:
payload["rules_draft_markdown"] = rules_md[:200_000] + "\n\n…(底稿过长已截断,请勿编造截断后内容)\n"
payload["rules_draft_markdown"] = _truncate_rules_draft_md(rd, 200_000)
raw = json.dumps(payload, ensure_ascii=False)
user = STRATEGY_USER_PREFIX + raw
return call_llm(STRATEGY_SYSTEM, user)
return STRATEGY_USER_PREFIX + raw
for cap_brief, cap_excerpt, cap_rules in (
(80_000, 24_000, None),
(64_000, 20_000, None),
(48_000, 17_000, None),
(36_000, 14_000, None),
(28_000, 11_000, None),
(22_000, 9_000, None),
(18_000, 7_000, None),
(14_000, 5_000, None),
(12_000, 4_000, 220_000),
(10_000, 3_500, 180_000),
(10_000, 3_000, 150_000),
(9_000, 2_500, 120_000),
(8_000, 2_000, 100_000),
(8_000, 2_000, 70_000),
):
user = _payload_and_user(
compact_max=cap_brief,
excerpt_max=cap_excerpt,
rules_max=cap_rules,
)
if _strategy_prompt_ok_for_call(
sys_prompt, user, min_completion_tokens=min_comp
):
return call_llm(sys_prompt, user)
for cap_brief, cap_excerpt, cap_rules in (
(10_000, 2_000, 55_000),
(8_000, 1_500, 45_000),
(7_000, 1_200, 35_000),
):
user = _payload_and_user(
compact_max=cap_brief,
excerpt_max=cap_excerpt,
rules_max=cap_rules,
)
if _strategy_prompt_ok_for_call(
sys_prompt, user, min_completion_tokens=min_comp_relaxed
):
return call_llm(sys_prompt, user)
user = _payload_and_user(compact_max=6_000, excerpt_max=1_000, rules_max=28_000)
return call_llm(sys_prompt, user)
STRATEGY_OPPORTUNITIES_SYSTEM = """你是 B 端市场与增长顾问。输入 JSON 含 ``keyword``、``competitor_brief``(与本任务规则报告同源的结构化摘要,可能经裁剪,并含 ``matrix_overview_for_llm``),以及可选 ``prior_chapter_llm_narratives``(本报告 **第五至第八章** 已生成的大模型归纳节选,与正文**同源**)。
@ -97,6 +160,19 @@ STRATEGY_OPPORTUNITIES_USER_PREFIX = (
)
def _truncate_rules_draft_md(text: str, max_chars: int) -> str:
"""规则策略底稿过长时截断,避免 JSON 与 completion 预算挤占输出。"""
s = (text or "").strip()
if not s:
return ""
if len(s) <= max_chars:
return s
return (
s[: max_chars - 80].rstrip()
+ "\n\n…(规则底稿已截断,请勿编造截断后内容。)\n"
)
def _truncate_strategy_narrative(text: str, max_chars: int) -> str:
s = (text or "").strip()
if not s: