\documentclass[11pt,fontset=none]{ctexart}
\usepackage[a4paper,margin=2.35cm]{geometry}
\usepackage{array}
\usepackage{amsmath}
\usepackage{booktabs}
\usepackage{enumitem}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{tikz}
\usepackage{xcolor}
\usepackage{listings}
\usetikzlibrary{arrows.meta,positioning,shapes.geometric}

\setCJKmainfont{Songti SC}
\setCJKsansfont{Songti SC}
\setCJKmonofont{Songti SC}
\hypersetup{colorlinks=true,linkcolor=blue,urlcolor=blue}
\emergencystretch=3em
\setlist[itemize]{leftmargin=2em}
\setlist[enumerate]{leftmargin=2em}
\lstset{
  basicstyle=\ttfamily\small,
  breaklines=true,
  frame=single,
  columns=fullflexible
}

\title{通用算法问题求解 Agent 系统设计与运行报告}
\author{高级算法设计与应用课程大作业}
\date{\today}

\begin{document}
\maketitle

\section{问题背景}

高级算法设计与应用课程中的算法题通常不是单纯的数值计算题，而是要求学生完成完整的算法设计过程：
理解自然语言题意，抽取输入、约束和目标，判断问题属于哪一类算法范式，给出算法步骤或伪代码，
证明正确性，分析复杂度，并在有样例时验证结果。传统大模型问答可以直接生成文字答案，但如果缺少
过程约束，容易出现三个问题：第一，模型可能跳过建模和算法选择理由；第二，模型生成的答案难以复查
中间依据；第三，样例计算结果可能没有经过工具验证。

因此，本项目设计了一个面向通用算法问题的可解释 Agent 系统。系统不为某一个题型单独写死求解器，
而是把一次求解拆成多个可观察模块：会话记忆、任务规划、算法范式检索、公开大模型执行、轻量脚本
验证、答案评估、循环控制、反思整理和报告生成。用户在网页中输入任意算法问题后，前端会以对话方式
展示每个模块的输出，并在最后生成 PDF 和 LaTeX 源码。系统使用公开可访问的 OpenAI-compatible API
接入 Agnes AI 的 \texttt{agnes-2.0-flash} 模型，不依赖内部未公开模型。

本系统的课程价值在于：它不是只给出最终答案，而是展示“算法问题如何被优化为可求解任务”的全过程。
对于课程大作业，网页可用于公开演示，PDF 可作为一次求解轨迹的归档，MP4 录屏可说明系统设计和运行
效果。

\section{Agent 系统总体设计}

系统由前端、后端、Agent 核心和报告生成四部分组成。前端采用自定义 HTML/CSS/JavaScript，实现类似
对话式应用的界面：左侧是历史记录，右侧顶部显示模型和配置状态，中间滚动展示 Agent 工作流，底部
固定输入框。后端使用 FastAPI 提供静态页面、状态接口、SSE 流式运行接口和报告下载接口。Agent 核心
由 \texttt{agent\_core.py} 实现，工具层由 \texttt{tools.py} 实现，报告由 \texttt{reporting.py}
写出 LaTeX 并编译为 PDF。

\begin{table}[htbp]
\centering
\caption{系统主要文件与职责}
\label{tab:files}
\begin{tabular}{p{4cm}p{9cm}}
\toprule
文件 & 职责 \\
\midrule
\texttt{server.py} & FastAPI 服务入口，读取 \texttt{AGNES\_API\_KEY}，提供页面、SSE 运行流和报告下载。 \\
\texttt{agent\_core.py} & 定义 Agent 状态和 Memory、Planner、Retriever、Executor 等模块。 \\
\texttt{tools.py} & 提供算法范式知识检索、Python 代码提取、安全脚本运行和离线降级框架。 \\
\texttt{reporting.py} & 根据一次求解轨迹生成 LaTeX 源码和 PDF 报告。 \\
\texttt{static/index.html} & 前端页面结构。 \\
\texttt{static/app.js} & SSE 接收、对话渲染、Markdown 与 LaTeX 公式渲染、历史记录管理。 \\
\texttt{static/styles.css} & 页面布局和视觉样式。 \\
\bottomrule
\end{tabular}
\end{table}

系统运行时，前端不会让用户填写模型名、接口地址或 API Key。模型固定为 \texttt{agnes-2.0-flash}，
接口固定为 \texttt{https://apihub.agnes-ai.com/v1/chat/completions}，API Key 从后端环境变量或
部署平台 Secrets 中读取。这样老师访问公开页面时可以直接使用，不需要在网页上手动上传或输入 key。

\section{Agent Flow}

本系统的核心是一个可观察的 Agent Flow。一次问题求解不是由单次模型调用直接完成，而是按模块推进：
Memory 记录输入，Planner 结构化问题，Retriever 检索算法范式和工具说明，Executor 调用公开 LLM
生成解答和可选验证脚本，Script Runner 运行轻量脚本验证样例，Evaluator 检查答案质量，
Loop Controller 判断是否需要修订，Reflector 形成最终回答，Artifact Writer 生成报告产物。

\begin{center}
\begin{tikzpicture}[
  node distance=7mm,
  box/.style={draw, rounded corners, align=center, minimum width=38mm, minimum height=8mm, fill=blue!8},
  side/.style={draw, rounded corners, align=center, minimum width=39mm, minimum height=8mm, fill=orange!15},
  outbox/.style={draw, rounded corners, align=center, minimum width=38mm, minimum height=8mm, fill=green!12},
  decision/.style={draw, diamond, aspect=2.25, align=center, inner sep=1mm, fill=orange!8},
  arrow/.style={-Latex, thick}
]
\node[box] (question) {User Question\\用户问题};
\node[box, below=of question] (memory) {Memory\\会话状态};
\node[box, below=of memory] (planner) {Planner\\结构化规划};
\node[box, below=of planner] (retriever) {Retriever\\范式检索};
\node[box, below=of retriever] (executor) {Executor\\LLM 执行};
\node[box, below=of executor] (script) {Script Runner\\脚本验证};
\node[box, below=of script] (evaluator) {Evaluator\\质量评估};
\node[decision, below=8mm of evaluator] (need) {Needs\\Improvement?};
\node[box, below=9mm of need] (reflector) {Reflector\\反思整理};
\node[outbox, below left=8mm and 18mm of reflector] (writer) {Artifact Writer\\报告生成};
\node[outbox, below=of writer] (final) {Final Answer\\最终答案};
\node[side, right=21mm of need] (loop) {Loop Controller\\循环控制};
\node[side, below=of loop] (supp) {Supplemental Retrieval\\补充检索};

\draw[arrow] (question) -- (memory);
\draw[arrow] (memory) -- (planner);
\draw[arrow] (planner) -- (retriever);
\draw[arrow] (retriever) -- (executor);
\draw[arrow] (executor) -- (script);
\draw[arrow] (script) -- (evaluator);
\draw[arrow] (evaluator) -- (need);
\draw[arrow] (need) -- node[right, font=\small] {No} (reflector);
\draw[arrow] (reflector) -- (writer);
\draw[arrow] (writer) -- (final);
\draw[arrow] (need) -- node[above, font=\small] {Yes} (loop);
\draw[arrow] (loop) -- (supp);
\draw[arrow] (supp.west) -- ++(-14mm,0) |- (executor.east);
\end{tikzpicture}
\end{center}

该流程强调两点。第一，中间结果全部可见。前端通过 SSE 接收后端事件，每完成一个模块就把模块名称、
时间和输出内容追加到工作流面板。第二，工具调用是通用能力，而不是固定题解。Retriever 检索的是
课程算法范式知识，包括递归与分治、动态规划、贪心、回溯、分支限界、随机化算法、遗传算法、模拟退火、
图算法以及复杂度与正确性证明；Script Runner 运行的是 LLM 生成的轻量 Python 验证脚本，而不是为某个
题型预置的专用求解器。

\section{模块说明}

\subsection{Memory}

Memory 负责建立本轮求解状态。状态对象保存原始问题、模型配置、中间模块输出、验证脚本结果、评估
结果、最终答案和报告路径。由于系统不使用上传文件，也不做课程资料 RAG，Memory 只记录本轮对话和
Agent 运行轨迹。

\subsection{Planner}

Planner 将自然语言算法题转化为结构化任务。它会要求模型输出 JSON 形式的问题类型、输入变量、约束、
优化目标或证明目标、候选算法范式、是否需要脚本验证以及不超过五步的执行计划。如果公开 LLM 调用失败，
Planner 会基于本地算法范式检索结果生成保底计划，保证前端仍能展示完整流程。

\subsection{Retriever}

Retriever 使用内置算法范式库完成轻量检索。检索逻辑不是上传 PDF 后做向量召回，而是根据问题文本中的
关键词和算法语义匹配课程常见范式。例如，出现“背包、最优、序列”会提高动态规划得分；出现“节点、边、
路径”会提高图算法得分；出现“温度、邻域、接受概率”会提高模拟退火得分。Retriever 输出的内容会被
Executor 用作求解提示，也会展示在前端作为可解释依据。

\subsection{Executor}

Executor 调用公开 LLM 生成算法解答。提示词要求它必须包含问题建模、算法选择理由、核心步骤或伪代码、
正确性说明、复杂度分析；若题目有具体样例，还要求生成一个不读写文件、不导入外部包的 Python 验证脚本。
这一步体现了提示词工程的作用：把“直接回答问题”约束为“按算法设计规范回答问题”。

\subsection{Script Runner}

Script Runner 从 Executor 的回答中提取最后一个 Python 代码块，并在受限环境中执行。它会禁止
\texttt{import}、\texttt{open}、\texttt{eval}、\texttt{exec} 等危险结构，只允许基础内置函数和
\texttt{math}。脚本运行结果会作为工具调用证据返回给 Evaluator 和前端。若 Executor 没有生成脚本，
系统也会明确显示未运行脚本的原因。

\subsection{Evaluator 与 Loop Controller}

Evaluator 根据原始问题、初稿答案和脚本结果判断答案是否合格。检查标准包括：是否回应原问题，是否
说明算法选择，是否给出步骤或伪代码，是否包含正确性说明和复杂度分析，是否合理利用脚本结果。
Loop Controller 根据评估结论决定流程走向：如果答案不足，则要求 Reflector 按评估意见修订；如果
答案合格，则直接进入最终整理。

\subsection{Reflector 与 Artifact Writer}

Reflector 将规划结果、检索依据、执行结果和评估意见整理成最终答案。Artifact Writer 把完整轨迹写入
LaTeX，并生成 PDF 报告。这样一次求解既有网页上的实时过程，也有可提交、可复查的文档产物。

\section{前端运行界面}

图 \ref{fig:app} 展示了系统在样例问题运行过程中的网页界面。页面左侧是历史记录，右侧顶部展示固定
模型和 API 状态，中部正在流式展示 Agent 工作流，底部是固定输入框。截图中可以看到用户问题已经进入
对话区，工作流面板正在展示 Agent 启动、Memory 和 Planner 等模块输出。系统支持 Markdown 和 LaTeX
公式渲染，适合展示算法伪代码、复杂度表达式和数学推导。

\begin{figure}[htbp]
  \centering
  \includegraphics[width=\textwidth]{../screenshots/app_workflow_running.png}
  \caption{通用算法问题求解 Agent 的样例运行过程截图}
  \label{fig:app}
\end{figure}

\section{样例问题求解过程}

本节使用如下样例问题说明 Agent 如何完成一次完整求解：

\begin{lstlisting}
给定一个有向带权图，节点为 A, B, C, D, E。
边为 A->B:4, A->C:2, B->C:1, B->D:5, C->D:8, C->E:10, D->E:2。
请从 A 到 E 找到最短路径，并说明应该使用什么算法。
\end{lstlisting}

\subsection{Memory 输出}

Memory 记录用户原始问题，并初始化本轮任务状态。本轮问题包含明确的节点、边、边权、起点 A、终点 E
和求解目标，因此不需要向用户追问额外输入。

\subsection{Planner 输出}

Planner 将问题结构化为：

\begin{lstlisting}
problem_type: 有向带权图单源到单点最短路径
inputs:
  nodes = [A, B, C, D, E]
  edges = [(A,B,4), (A,C,2), (B,C,1), (B,D,5),
           (C,D,8), (C,E,10), (D,E,2)]
constraints:
  graph is directed
  all edge weights are non-negative
objective:
  minimize total path weight from A to E
output:
  shortest distance, shortest path, algorithm choice and reason
\end{lstlisting}

Planner 的执行计划为：先确认图模型和权重条件，再选择最短路径算法，然后计算距离和前驱，最后给出
路径、复杂度和正确性说明。

\subsection{Retriever 输出}

Retriever 检索到的主要算法范式为“图算法”和“复杂度与正确性证明”。由于题目包含“节点、边、路径、
有向带权图”等关键词，系统判断这是图算法问题。Retriever 同时提醒需要回答以下关键问题：图是否有向、
是否带权、目标是最短路径还是连通性、边权是否允许负数。根据 Planner 抽取的权重集合
\(\{4,2,1,5,8,10,2\}\)，所有边权均为非负，因此适合使用 Dijkstra 算法。如果存在负权边，则应考虑
Bellman-Ford；本例不需要。

\subsection{Executor 与 Script Runner 输出}

Executor 生成的求解思路如下。设 \(\text{dist}[v]\) 表示从 A 到节点 \(v\) 的当前最短距离估计，
\(\text{prev}[v]\) 表示最短路径树中的前驱节点。初始化 \(\text{dist}[A]=0\)，其余节点为无穷大。
每轮从未确定节点中选择 \(\text{dist}\) 最小的节点，并用它的出边更新相邻节点距离。因为本例所有边权
非负，当一个节点被选出时，它的最短距离已经确定。

\begin{table}[htbp]
\centering
\caption{样例问题中 Dijkstra 的距离更新过程}
\label{tab:dijkstra}
\begin{tabular}{p{1.4cm}p{1.6cm}p{7.7cm}p{2.5cm}}
\toprule
轮次 & 确定节点 & 松弛结果 & 当前目标距离 \\
\midrule
1 & A & 更新 \(dist(B)=4\)，\(dist(C)=2\) & \(dist(E)=\infty\) \\
2 & C & 更新 \(dist(D)=10\)，\(dist(E)=12\) & \(dist(E)=12\) \\
3 & B & 由 \(B\to D\) 将 \(dist(D)\) 从 10 改为 9 & \(dist(E)=12\) \\
4 & D & 由 \(D\to E\) 将 \(dist(E)\) 从 12 改为 11 & \(dist(E)=11\) \\
5 & E & 终点 E 被确定，算法结束 & \(dist(E)=11\) \\
\bottomrule
\end{tabular}
\end{table}

Script Runner 可运行的验证脚本逻辑可以概括为：

\begin{lstlisting}
nodes = ["A", "B", "C", "D", "E"]
edges = {
    "A": [("B", 4), ("C", 2)],
    "B": [("C", 1), ("D", 5)],
    "C": [("D", 8), ("E", 10)],
    "D": [("E", 2)],
    "E": [],
}
# Dijkstra: repeatedly select the unvisited node with minimum distance
# and relax its outgoing edges.
\end{lstlisting}

脚本验证结果应输出最短距离 11，路径为 \(A \to B \to D \to E\)。

\subsection{Evaluator、Reflector 与最终答案}

Evaluator 检查答案是否包含算法选择理由、求解步骤、样例结果、正确性说明和复杂度分析。本例中，答案
满足所有必要项，因此 Loop Controller 不需要触发补充检索。Reflector 将各模块结果整理为最终回答：

\begin{quote}
应使用 Dijkstra 算法。因为该有向图所有边权均为非负，Dijkstra 每次选择当前距离最小的未确定节点，
该节点的最短距离不会再被后续路径改进。对给定图运行算法后，从 A 到 E 的最短路径为
\(A \to B \to D \to E\)，总权重为 \(4+5+2=11\)。本例若使用二叉堆优先队列实现，时间复杂度为
\(O((|V|+|E|)\log |V|)\)，空间复杂度为 \(O(|V|+|E|)\)。
\end{quote}

\section{系统特点与局限}

系统的主要特点包括：

\begin{itemize}
  \item \textbf{通用性：}不为每个样例新增专门求解器，而是通过范式检索、LLM 规划和脚本验证处理通用算法问题。
  \item \textbf{可解释性：}Memory、Planner、Retriever、Executor、Script Runner、Evaluator 等模块输出均在前端显示。
  \item \textbf{可复查性：}最终 PDF 保存原始问题、结构化结果、工具调用结果、评估记录和最终答案。
  \item \textbf{可部署性：}项目使用 FastAPI 和 Docker，可部署到 Hugging Face Spaces 并公开访问。
\end{itemize}

系统也存在局限。第一，公开 LLM 的质量会影响 Executor 的初稿质量；第二，Script Runner 只适合小样例
和轻量验证，不适合运行大规模实验；第三，当前 Retriever 使用内置算法范式库，而不是完整课程资料检索，
因此它更适合展示通用算法设计流程，而不是替代教材检索系统。

\section{结论}

本项目实现了一个面向通用算法问题的可解释 Agent 系统。它将自然语言算法题拆解为结构化任务，利用
公开大模型生成算法设计，通过内置算法范式和轻量脚本验证增强可靠性，并把完整求解轨迹展示在网页和
PDF 报告中。样例问题表明，该系统能够从输入图模型出发，自动选择 Dijkstra 算法，给出最短路径
\(A \to B \to D \to E\) 和最短距离 11，同时说明适用条件、正确性依据和复杂度。整体上，系统重点
展示了一个算法求解 Agent 的工作机制，而不是只展示某一道题的固定答案。

\end{document}
