fix: 多Agent协作 JSON 截断导致主管规划解析失败

- 根因: deepseek-v4-flash 规划输出超 max_tokens=2000 被截断,JSON 不完整解析失败
- agents.py: 新增 _chat_json() 统一处理 JSON 类调用,解析失败自动加大 max_tokens 重试(×1/×2/×3)
- 主管规划/辩论质询/辩论裁决/评审 全部改用 _chat_json 并提高初始上限(3000/1200/2500/2000)
- 实测: 主管(计算器网页版4子任务) 评审(92分) 辩论(指定阵容)
This commit is contained in:
2026-08-12 17:08:16 +08:00
parent dc49864af4
commit f28d73f1bb
+35 -16
View File
@@ -92,6 +92,23 @@ def _extract_score(text):
return 60.0, text
def _chat_json(worker, messages, max_tokens=2000, temperature=None):
"""调用 LLM 并解析 JSON;解析失败自动加大 max_tokens 重试(防止长文截断)。
返回 (data, raw_text, usage);全部失败抛最后异常。"""
last_err = None
for attempt in range(3):
mt = max_tokens * (attempt + 1) # 2000 → 4000 → 6000
text, usage = _chat_worker(worker, messages, temperature=temperature, max_tokens=mt)
try:
data = _extract_json(text)
if isinstance(data, dict) and not data:
raise ValueError('空 JSON')
return data, text, usage
except Exception as e:
last_err = e
raise last_err or ValueError('JSON 解析失败')
# ---------------------------------------------------------------------------
# 主管模式
# ---------------------------------------------------------------------------
@@ -114,17 +131,17 @@ SUPERVISOR_SYNTH_PROMPT = (
def run_supervisor(run_id, run, workers):
_log(run_id, 'supervisor', None, 'plan',
f'主管开始规划:{run["topic"][:200]}')
# 1) 主管拆解
plan_text, u1 = _chat_worker(
# 1) 主管拆解(JSON 解析失败自动加大 max_tokens 重试)
try:
data, _, u1 = _chat_json(
workers[0],
[{'role': 'system', 'content': '你只输出 JSON。'},
{'role': 'user', 'content': SUPERVISOR_PLAN_PROMPT.format(
topic=run['topic'], context=run['context'] or '')}],
temperature=0.3, max_tokens=2000)
try:
subtasks = _extract_json(plan_text)
if isinstance(subtasks, dict):
subtasks = subtasks.get('subtasks') or subtasks.get('tasks') or []
max_tokens=3000, temperature=0.3)
subtasks = data.get('subtasks') or data.get('tasks') or []
if isinstance(data, list):
subtasks = data
except Exception as e:
_update_run(run_id, status='failed', error=f'主管规划解析失败: {e}')
_log(run_id, 'supervisor', None, 'plan', f'❌ 规划解析失败: {e}')
@@ -216,15 +233,19 @@ def run_review(run_id, run, workers):
# 评审
review_usage = None
try:
review_text, u2 = _chat_worker(
review_data, review_text, u2 = _chat_json(
reviewer_w,
[{'role': 'system', 'content': '你只输出 JSON。'},
{'role': 'user', 'content': REVIEWER_PROMPT.format(
topic=run['topic'], output=final_output, rubric=rubric)}],
temperature=0.2, max_tokens=1500)
score, judgment = _extract_score(review_text)
max_tokens=2000, temperature=0.2)
review_usage = u2
total_tokens += u2['total_tokens']; total_cost += u2['cost']
if isinstance(review_data, dict):
score = float(review_data.get('score', review_data.get('总分', 60)))
judgment = review_data.get('judgment') or review_data.get('意见') or review_text
else:
score, judgment = _extract_score(review_text)
except Exception as e:
score, judgment = 0, f'评审调用失败: {e}'
final_score = score
@@ -325,14 +346,13 @@ def run_debate(run_id, run, workers):
f"{v['stance']}】(#{k}){v['view'][:500]}"
for k, v in views.items() if k != w['id'])
try:
text, u = _chat_worker(
data, _, u = _chat_json(
w,
[{'role': 'system', 'content': '你只输出 JSON。'},
{'role': 'user', 'content': DEBATE_REBUT_PROMPT.format(
stance=mine['stance'], topic=run['topic'],
my_view=mine['view'], others=others)}],
temperature=0.7, max_tokens=800)
data = _extract_json(text)
max_tokens=1200, temperature=0.7)
rebut = data.get('rebuttal') if isinstance(data, dict) else text
mine['view'] = mine['view'] + '\n\n【质询回应】' + str(rebut)
total_tokens += u['total_tokens']; total_cost += u['cost']
@@ -346,12 +366,11 @@ def run_debate(run_id, run, workers):
f"{v['stance']}{v['view']}" for v in views.values())
_log(run_id, 'judge', judge_w['id'], 'verdict', '裁判综合裁决中…')
try:
verdict, u4 = _chat_worker(
data, verdict, u4 = _chat_json(
judge_w,
[{'role': 'system', 'content': '你只输出 JSON。你是公正严明的首席裁判。'},
{'role': 'user', 'content': JUDGE_PROMPT.format(topic=run['topic'], transcript=transcript)}],
temperature=0.3, max_tokens=1500)
data = _extract_json(verdict)
max_tokens=2500, temperature=0.3)
if isinstance(data, dict):
consensus = data.get('consensus') or data.get('结论') or verdict
summary = data.get('summary') or data.get('摘要') or ''