mirror of
https://github.com/primedigitaltech/market-assistant.git
synced 2026-07-21 23:41:39 +08:00
fix(llm): 策略稿润色预留 completion 预算,缓解输出截断
generate_strategy_draft_markdown_llm 按摘要/第九章节选/规则底稿分级收紧,满足最小 completion token 后再调用;底稿与 JSON 超限仍截断并提示。系统提示补充收束全文要求。 Made-with: Cursor
This commit is contained in:
parent
1229e49dbe
commit
f06fd12f1f
@ -18,7 +18,8 @@ STRATEGY_SYSTEM = """你是市场策略顾问,根据**结构化监测摘要**
|
||||
- **不得编造**输入中不存在的销量、占比、价格数字;若底稿与摘要中有数字,须保持一致;表述集中度时用「第一大……份额」「前三家合计」等中文,**不要用** CR1、CR3 等缩写;
|
||||
- 若 `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 = {
|
||||
"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,
|
||||
}
|
||||
raw = json.dumps(payload, ensure_ascii=False)
|
||||
if len(raw) > 500_000:
|
||||
payload["rules_draft_markdown"] = rules_md[:200_000] + "\n\n…(底稿过长已截断,请勿编造截断后内容)\n"
|
||||
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": rd,
|
||||
"report_strategy_excerpt": ex,
|
||||
}
|
||||
raw = json.dumps(payload, ensure_ascii=False)
|
||||
user = STRATEGY_USER_PREFIX + raw
|
||||
return call_llm(STRATEGY_SYSTEM, user)
|
||||
if len(raw) > 500_000:
|
||||
payload["rules_draft_markdown"] = _truncate_rules_draft_md(rd, 200_000)
|
||||
raw = json.dumps(payload, ensure_ascii=False)
|
||||
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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user