from __future__ import annotations import os import shutil import subprocess import textwrap import time from pathlib import Path from typing import Dict, Tuple OUTPUT_DIR = Path("outputs") def latex_escape(value: str) -> str: replacements = { "\\": r"\textbackslash{}", "&": r"\&", "%": r"\%", "$": r"\$", "#": r"\#", "_": r"\_", "{": r"\{", "}": r"\}", "~": r"\textasciitilde{}", "^": r"\textasciicircum{}", } return "".join(replacements.get(ch, ch) for ch in value) def build_solution_tex(data: Dict[str, str]) -> str: original = latex_escape(data.get("original_problem", "")) structured = latex_escape(data.get("structured_problem", "")) strategy = latex_escape(data.get("strategy", "")) result = latex_escape(data.get("tool_result", "")) final_answer = latex_escape(data.get("final_answer", "")) process = latex_escape(data.get("process_log", "")) complexity = latex_escape(data.get("complexity_analysis", "本系统在工具层执行真实计算,再由语言模型或规则化报告模块生成解释,从而降低纯文本推理产生错误答案的风险。")) timestamp = latex_escape(time.strftime("%Y-%m-%d %H:%M:%S")) return rf""" \documentclass[11pt,fontset=none]{{ctexart}} \usepackage[a4paper,margin=2.4cm]{{geometry}} \usepackage{{booktabs}} \usepackage{{enumitem}} \usepackage{{hyperref}} \usepackage{{xcolor}} \usepackage{{listings}} \setCJKmainfont{{Songti SC}} \setCJKsansfont{{Songti SC}} \setCJKmonofont{{Songti SC}} \hypersetup{{colorlinks=true,linkcolor=blue,urlcolor=blue}} \lstset{{basicstyle=\ttfamily\small,breaklines=true,frame=single}} \title{{通用问题优化智能体求解报告}} \author{{Problem Optimization Agent}} \date{{{timestamp}}} \begin{{document}} \maketitle \section{{原始问题}} {original} \section{{优化后的结构化问题}} \begin{{lstlisting}} {structured} \end{{lstlisting}} \section{{求解策略}} {strategy} \section{{工具调用与计算结果}} \begin{{lstlisting}} {result} \end{{lstlisting}} \section{{智能体求解过程记录}} \begin{{lstlisting}} {process} \end{{lstlisting}} \section{{最终答案}} {final_answer} \section{{复杂度与适用性分析}} {complexity} \section{{结论}} 本次求解将自然语言理解、结构化建模和确定性算法工具结合起来。语言模型或规则化模块负责 问题优化、解释和报告化表达,工具层负责关键计算和验证,因此求解过程可以被复查和复现。 \end{{document}} """.strip() def _fallback_pdf(text: str, pdf_path: Path) -> None: from reportlab.lib.pagesizes import A4 from reportlab.lib.styles import getSampleStyleSheet from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.cidfonts import UnicodeCIDFont from reportlab.platypus import Paragraph, SimpleDocTemplate, Spacer font_name = "STSong-Light" pdfmetrics.registerFont(UnicodeCIDFont(font_name)) doc = SimpleDocTemplate(str(pdf_path), pagesize=A4, rightMargin=42, leftMargin=42, topMargin=42, bottomMargin=42) styles = getSampleStyleSheet() styles["BodyText"].fontName = font_name styles["BodyText"].leading = 15 story = [] for para in text.split("\n\n"): clean = para.replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "
") story.append(Paragraph(clean, styles["BodyText"])) story.append(Spacer(1, 10)) doc.build(story) def write_solution_report(data: Dict[str, str], stem: str = "solution_report") -> Tuple[str, str]: OUTPUT_DIR.mkdir(exist_ok=True) tex_path = OUTPUT_DIR / f"{stem}.tex" pdf_path = OUTPUT_DIR / f"{stem}.pdf" tex = build_solution_tex(data) tex_path.write_text(tex, encoding="utf-8") engine = shutil.which("xelatex") or shutil.which("pdflatex") if engine: try: subprocess.run( [engine, "-interaction=nonstopmode", "-halt-on-error", tex_path.name], cwd=OUTPUT_DIR, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60, ) except Exception: fallback_text = "\n\n".join( [ "通用问题优化智能体求解报告", data.get("original_problem", ""), data.get("structured_problem", ""), data.get("strategy", ""), data.get("tool_result", ""), data.get("final_answer", ""), ] ) _fallback_pdf(fallback_text, pdf_path) else: fallback_text = textwrap.dedent( f""" 通用问题优化智能体求解报告 原始问题: {data.get('original_problem', '')} 优化后的结构化问题: {data.get('structured_problem', '')} 求解策略: {data.get('strategy', '')} 工具调用与结果: {data.get('tool_result', '')} 最终答案: {data.get('final_answer', '')} """ ) _fallback_pdf(fallback_text, pdf_path) return str(pdf_path), str(tex_path)