背景痛点:为什么“跑通”比“跑得快”更难
做 AI Agent 毕业设计,最怕的不是模型不够大,而是“跑不通”。我辅导过 30 多位学弟学妹,最常见的情况有三种:
- 工程化缺失:代码全挤在一个
main.py,Prompt、工具、模型全耦合,一改全崩。 - 依赖混乱:今天装
langchain==0.0.266,明天又装langchain-community==0.0.10,版本冲突到连pip list都看不懂。 - 演示效果差:本地 4090 上跑得好好的,一上答辩笔记本就“CUDA out of memory”,或者因为一次网络抖动,Agent 调用搜索 API 超时,直接卡死,老师眉头一皱,分数 −10。
总结一句话:老师不会关心你用了多少亿参数的模型,他只关心“点一下能跑、跑十次结果一样”。
技术选型:毕业设计场景下的“三选一”
时间只有 3 个月,别玩“全都要”。我把主流框架按“学习曲线 + 文档友好度 + 本地可跑”三维打分(满分 5★):
| 框架 | 学习曲线 | 文档友好 | 本地可跑 | 备注 |
|---|---|---|---|---|
| LangChain | ★★★☆ | ★★★★☆ | ★★★★☆ | 生态最全,社区问答多,出错能搜到 |
| LlamaIndex | ★★☆ | ★★★☆ | ★★★☆ | 专注 RAG,Agent 能力弱,做知识库问答更香 |
| AutoGen | ★☆ | ★★☆ | ★★☆ | 多 Agent 群聊很酷,但调试地狱,毕业设计慎用 |
结论:本科毕设选 LangChain 最稳,理由——GitHub 上能搜到的毕业设计模板 80% 都是它。
核心实现:30 行代码搭一个 ReAct Agent
目标:让 Agent 同时支持“搜索 + 计算器 + 本地知识库”,并且每个工具都能单独开关。下面代码在 Colab / Python 3.9 亲测可跑,只需pip install langchain==0.1.13 openai==110.0 duckduckgo-search==2023.12.3。
# agent.py from langchain.agents import AgentExecutor, create_react_agent from langchain.tools import DuckDuckGoSearchRun, Tool from langchain.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain_community.vectorstores import FAISS from langchain_community.embeddings import HuggingFaceEmbeddings import math, os # 1. 工具箱:搜索、计算器、本地知识库 search = DuckDuckGoSearchRun() def calc(expr: str) -> str: """安全计算器,只支持基本运算""" try: return str(eval(expr, {"__builtins__": None}, {"math": math})) except Exception as e: return f"Calc error: {e}" embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") vectordb = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True) retriever = vectordb.as_retriever(search_kwargs={"k": 3}) tools = [ Tool(name="Search", func=search.run, description="Search the web for current information."), Tool(name="Calculator", func=calc, description="Evaluate math expressions. Input: 2+3*4"), Tool(name="Knowledge", func=lambda q: "\n".join([doc.page_content for doc in retriever.get_relevant_documents(q)]), description="Query local knowledge base.") ] # 2. Prompt 模板:ReAct 格式 template = ChatPromptTemplate.from_messages([ ("system", "You are a helpful assistant. Answer the following questions as best you can. You have access to the following tools:\n\n{tools}\n\nUse the following format:\n\nQuestion: the input question you must answer\nThought: you should always think about what to do\nAction: the action to take, should be one of [{tool_names}]\nAction Input: the input to the action\nObservation: the result of the action\n... (this Thought/Action/Action Input/Observation can repeat N times)\nThought: I now know the final answer\nFinal Answer: the final answer to the original input question\n\nBegin!\n\nQuestion: {input}\nThought:{agent_scratchpad}"), ("human", "{input}") ]) # 3. 组装 Agent llm = ChatOpenAI(base_url="http://localhost:1234/v1", api_key="EMPTY", temperature=0) # 本地 LLM agent = create_react_agent(llm, tools, template) agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, max_iterations=8, handle_parsing_errors=True) if __name__ == "__main__": question = "What is the square root of the year when Python 1.0 was released?" print(agent_executor.invoke({"input": question})["output"])运行效果(截断):
Thought: I need to find the year Python 1.0 was released. Action: Search Action Input: Python 1.0 release year Observation: 1994 Thought: Now I need to compute sqrt(1994) Action: Calculator Action Input: math.sqrt(1994) Observation: 44.654... Final Answer: Approximately 44.65性能与安全:别让“冷启动”毁了答辩
- 冷启动延迟
本地 LLM(如 Llama.cpp)第一次加载 7B 模型要 8-10 s,老师点两下没反应就以为死机。解决:提前写个“预热”脚本,在后台把模型拉起,答辩时直接调 API。 - 非幂等操作
搜索、下单、发邮件都属于“同一条指令执行两次结果不同”。给工具加idempotency_key:
答辩现场网络抽风,重跑也能拿到一致结果。def search_with_cache(query: str, cache: dict): if query in cache: return cache[query] cache[query] = search.run(query) return cache[query] - 本地知识库隔离
把faiss_index文件夹放项目根目录,.gitignore掉,防止把 500 MB 向量文件塞进 GitHub,仓库爆炸。
生产环境避坑指南:日志、校验、防呆
- 日志:用
loguru,一行代码彩色输出,还能自动滚动保存。
答辩后被老师追问“为什么第二次结果不一样”,直接甩日志给他看。from loguru import logger logger.add("agent.log", rotation="10 MB") - 输入校验:别让同学手滑输
rm -rf /。
计算器工具里把eval的builtins清空还不够,再加正则白名单:if not re.fullmatch(r'[\d\.\+\-\*\/\(\)e ]+', expr): return "Illegal character detected." - 防无限循环:ReAct 默认
max_iterations=15,但中文 LLM 偶尔“犯迷糊”会死循环。把early_stopping_method="generate"打开,让模型自己跳出循环,否则答辩现场按 Ctrl+C 很尴尬。
可扩展方向:把“玩具”做成“作品”
- 记忆机制:加
ConversationBufferWindowMemory,让 Agent 记住前三轮对话,老师追问“刚才算的多少”时能接住。 - 前端界面:用 Gradio 三行代码搭个网页:
把 localhost 端口映射到公网,手机扫码就能演示,老师直呼专业。import gradio as gr gr.Interface(fn=lambda q: agent_executor.invoke({"input": q})["output"], inputs="text", outputs="text").launch() - 多 Agent 协作:时间充裕可把“搜索专员”“计算专员”“写作专员”拆成三个 Agent,用
langchain.callbacks做消息总线,PPT 里画个架构图,直接对标 AutoGen,但调试量翻倍,谨慎选择。
结尾:先跑起来,再谈优雅
毕业设计不是 Kaggle 竞赛,先让代码“能跑、能复现、能关机再开”,就已经赢过 70% 的同学。把上面的模板克隆下来,跑通第一行日志,再逐步加功能:今天加记忆,明天加前端,后天写答辩 PPT。等你把“AI Agent 毕业设计从零到一”踩坑记录写成博客,说不定明年就轮到你来辅导学弟学妹了。祝你一次答辩过,周末安心打游戏。