\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}