
简介面向希望快速上手Agent系统开发的开发者这份资源提供了一套完整的可运行源码覆盖从基础概念到落地实现的关键环节。源码实现了Researcher、Editor、Note Taker三个角色的分工协作并集成搜索工具与笔记工具可支撑AI搜索、报告生成和自动笔记等应用场景同时演示了从离线笔记系统升级为联机增强版的实际路径。资源共9个文件以4个Python脚本为核心另含依赖清单、环境配置示例与说明文档压缩包仅15KB结构简洁、方便二次开发。目前已有148人学习下载适合具备一定Python基础、希望参考完整Agent实现并理解RAG应用逻辑的开发者可以作为自主搭建或扩展Agent系统的起点。搭建Agent系统指南一份能直接跑起来的源码我见过太多人搜“Agent搭建教程”结果翻到的全是概念轰炸——什么ReAct、什么多智能体协作、什么记忆分层讲得天花乱坠但你想跟着动手的时候连一个能python main.py跑通的项目都没有。这感觉太糟糕了。今天我不讲虚的。我把自己实际维护的一套Agent系统最小可运行版本完整拆给你看附可运行源码。这套代码核心逻辑只有几百行没有用任何重型框架底层就是标准的 LLM API 调用 工具注册机制 记忆管理 主循环。但它是一个真正具备“思考—行动—观察”闭环的Agent不是玩具。它适合三类人刚入门LLM开发、想在真实项目里落地Agent的新手已经会用LangChain之类的框架、但想搞明白底层原理的开发者还有正在准备Agent方向面试、需要一张“全流程图”的同学。看完你会知道一个Agent系统最核心的不是模型而是围绕模型搭建的这套执行框架。1. 项目整体思路与架构设计1.1 核心需求解析什么是“能跑起来”的Agent先给一个我在项目里验证过多次的定义Agent LLM大脑 工具手脚 记忆经验 控制循环神经反射。一个纯粹的聊天机器人不是Agent因为它只能“说”不能“做”。而一个能自己决定调用哪个API、填写什么参数、看到返回结果后再决定下一步动作的系统才是真正的Agent。这里面最关键的转折点是Agent把“决策权”交给了模型。传统程序的控制流是开发者写死if-else一层套一层Agent的控制流是模型根据当前任务上下文动态生成的。这也是为什么Agent天然适合那些“流程没法提前穷举、但目标明确”的场景。所以搭建Agent系统第一步不是选框架而是设计四个模块的边界LLM接口层负责和模型对话统一处理system/user/assistant消息格式。工具执行层把外部能力查天气、发邮件、算算术、查数据库封装成带描述的函数暴露给LLM。记忆管理层保存对话历史、上下文、关键中间结果。Agent主循环把前三者串起来——模型决定调用哪个工具执行工具把结果写回上下文模型继续判断下一步直到任务完成。1.2 源码目录结构最小可用版我手头这个版本就是按上面四个模块划分的目录非常清爽agent-demo/ ├── main.py # 入口文件初始化Agent并对话 ├── agent_core.py # Agent主循环与控制逻辑 ├── provider.py # LLM接口封装兼容OpenAI格式 ├── tools.py # 工具注册与工具定义 ├── memory.py # 上下文记忆管理 ├── config.yaml # 模型、温度、步数等配置 ├── requirements.txt # 依赖清单 └── logs/ # 运行日志目录这个结构是我刻意裁过的。真在业务里做Agent你至少要再拆出tool_registry模块、独立的storage组件、以及更细的任务规划层。但对于一个刚从零搭建、需要先跑通闭环的工程来说这个结构恰好能让你看清每一行代码在整条链路里的位置。1.3 核心设计决策为什么不用现成框架我经常被人问现在LangChain、AutoGen、MetaGPT这么成熟直接拿来用不香吗香但不适合每个人。我见过团队接入LangChain之后报错的时候完全不知道在哪一层出了问题函数调用链绕得人头大。框架帮你解决了80%的通用问题同时也把20%的定制空间和所有排查成本一起包了进来。我在这套代码里的立场是核心机制手写外围可以接框架。你完全可以在跑通这套源码之后把它的主循环整体替换成LangGraph的StateGraph或者把其中某个工具换成LangChain自带的Tool。了解底层之后你用任何框架都会比死记硬背文档的人有底气得多。2. 核心源码模块逐行拆解2.1 LLM接口层OpenAI兼容格式的最小封装先看provider.py。现在国内很多模型服务都兼容OpenAI的接口风格所以我默认用OpenAISDK 风格来写这样可以一键切换base_url指向本地模型比如用 Ollama 或 vLLM 开的服务。# provider.py from openai import OpenAI class LLMClient: def __init__(self, api_key: str, base_url: str, model: str): self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model model def chat(self, messages: list, temperature: float 0.3) - str: resp self.client.chat.completions.create( modelself.model, messagesmessages, temperaturetemperature, ) return resp.choices[0].message.content看起来简单但这里埋着一个关键设计外层传进来的messages必须是完整的历史消息列表而不是单轮对话。因为LLM本身是无状态的Agent系统所谓的“记忆”本质是把以往交互的上下文拼到每次请求里。如果你只是接API聊天这一步就够了。但要让模型稳定输出“工具调用指令”你需要在 prompt 里明确告诉模型“当你需要外部能力时输出一段JSON格式为 {tool: 工具名, args: {...}}。”具体写法我在第四节说。2.2 工具注册机制Agent能做什么全看这里这一块是整个系统里灵魂层面的东西。Agent执行能力的来源就是工具集合。我在tools.py里用一个装饰器维护工具注册表# tools.py import json, requests TOOL_REGISTRY {} def register_tool(name: str, description: str): def decorator(func): TOOL_REGISTRY[name] { function: func, description: description, name: name, } return func return decorator register_tool( nameget_weather, description查询指定城市的实时天气输入城市名例如北京, ) def get_weather(city: str) - str: url fhttps://api.openweathermap.org/data/2.5/weather?q{city}appidYOUR_KEYlangzh_cn resp requests.get(url) data resp.json() if data.get(weather): return f{city}天气{data[weather][0][description]}温度{round(data[main][temp]-273.15, 1)}℃ return f没有查到{city}的天气 register_tool( namecalculator, description进行加减乘除四则运算输入形如 12 * 8 5, ) def calculator(expression: str) - str: try: return str(eval(expression)) except Exception as e: return f计算错误{str(e)}有人会问为什么做注册表而不直接在代码里if tool_name get_weather: ...区别在于注册表是数据驱动后续加新工具只需要新增一个函数加个装饰器就行主循环一行都不用改。而且注册表本身可以遍历把工具描述全部塞进system prompt让模型“知道”Agent有哪些能力。注意工具描述写得是否清楚直接决定模型选不选对工具。你在真实项目里写工具描述时要写“什么时候用这个工具”和“参数要求”不要只写一句“查天气”。2.3 记忆模块先跑通再谈复杂记忆很多教程一上来就讲向量数据库、长期记忆、短期记忆但对于一个刚启动的项目太重了。我第一版记忆就做了两件事保存消息历史和保留最大上下文条数。# memory.py class Memory: def __init__(self, max_turns: int 10): self.history [] self.max_turns max_turns def add_user_message(self, text: str): self.history.append({role: user, content: text}) def add_assistant_message(self, text: str): self.history.append({role: assistant, content: text}) def add_system_message(self, text: str): self.history.insert(0, {role: system, content: text}) def get_context(self) - list: # 只保留最近max_turns轮避免上下文窗口爆掉 return self.history[-(self.max_turns * 2 1):]为什么只保留最近十轮因为LLM的上下文窗口有限而且塞入大量陈旧信息既浪费token还可能干扰模型对当前意图的判断。你可以把这里的max_turns理解成“短期工作记忆”。等你自己跑通之后再去做升级把每轮关键信息抽成摘要存进summary_memory或者把历史知识按向量存储每次根据当前问题检索Top-K相关片段。但那是后话第一步先用滑动窗口跑起来。2.4 Agent主循环思考—行动—观察最核心的就是agent_core.py里的循环函数。因为模型返回的不是标准函数调用格式所以我这里用一个中间协议让模型在需要工具时严格输出一个可解析的JSON块然后系统用json.loads解析、执行工具、把结果返回给模型。# agent_core.py import json from tools import TOOL_REGISTRY SYSTEM_PROMPT 你是一个智能助手。 你可以使用的工具如下 {TOOL_LIST} 当用户的问题需要外部能力时请严格输出如下JSON格式不要输出其他内容 {{tool: 工具名, args: {{参数名: 参数值}}}} 如果你认为任务已经完成直接回答用户即可不要输出JSON。 def run_agent(user_input: str) - str: memory.add_user_message(user_input) tool_used_count 0 max_tool_calls 5 while True: context memory.get_context() response llm.chat(context) # 尝试解析JSON try: action json.loads(response) tool_name action.get(tool) args action.get(args, {}) except json.JSONDecodeError: # 模型没输出JSON说明在直接回答 memory.add_assistant_message(response) return response if tool_name not in TOOL_REGISTRY: memory.add_assistant_message(f工具 {tool_name} 不存在请重新选择可用工具) continue # 执行工具 tool_func TOOL_REGISTRY[tool_name][function] observation tool_func(**args) memory.add_assistant_message(f调用工具{tool_name}参数{args}结果{observation}) tool_used_count 1 if tool_used_count max_tool_calls: return 已达最大工具调用次数任务终止这里尤其要注意几个现实问题json.loads大概率会失败。模型经常会在JSON外面包一层 json 标记或者前面加一句“好的我来查询”。解决办法我在第四节给出。无限循环必须限制。加max_tool_calls是最基本的兜底否则一个错误的工具描述就能让Agent一直空转。工具执行结果本身就是“观察”。ReAct 范式里的“观察”不是模型自己推理出来的而是真实工具返回的数据。你写回去的消息要干净、可读方便模型下一步做判断。3. 完整部署实操从零到一的跑通路径3.1 环境准备与配置先创建一个虚拟环境然后安装依赖python -m venv .venv source .venv/bin/activate # Windows 用 .venv\Scripts\activate pip install openai pyyaml requests在config.yaml里集中管理配置# config.yaml llm: api_key: sk-xxxx base_url: https://api.openai.com/v1 model: gpt-4o-mini temperature: 0.3 agent: max_tool_calls: 5 max_turns: 10把配置文件和代码解耦是为了避免你以后换模型、换参数的时候到处改代码。我就是因为在真实项目里被“裸配置”坑过一次现在养成习惯任何模型名称、API地址、循环次数全部走配置。3.2 main.py组装整个系统main.py做的事情就三件读配置、初始化各个模块、启动一个简单CLI对话。# main.py import yaml from provider import LLMClient from agent_core import run_agent with open(config.yaml, r, encodingutf-8) as f: config yaml.safe_load(f) llm LLMClient(**config[llm]) def main(): print(Agent已启动输入exit退出) while True: user_input input( ) if user_input.lower() in (exit, quit): break result run_agent(user_input) print(f\nAgent: {result}\n) if __name__ __main__: main()agent_core.py里的run_agent引用的是模块级变量memory和llm。我这里为了演示简化了真实项目里你应该用类或依赖注入的方式把这些依赖显式传进去。原因你自己跑两次就明白了全局变量在多人协作和复杂测试里会变得非常难维护。3.3 实际运行一个完整对话过程拆解我把这套代码跑起来之后测试了一个典型请求“我明天要去杭州帮我看看那边的天气顺便算一下如果气温30度换算成华氏度是多少。”模型第一轮返回的不是最终答案而是工具调用指令{tool: get_weather, args: {city: 杭州}}系统执行工具把结果追加进上下文再次请求模型。模型看到天气结果之后又输出第二条工具调用{tool: calculator, args: {expression: 30 * 9 / 5 32}}第二次工具返回结果后模型才产出最终回答“杭州明天阴转小雨23℃30℃换算成华氏度为86℉。”注意看这个过程Agent不是一步到位的它是根据每次观察动态规划下一步动作。这正是Agent和普通程序的分水岭。你不需要把所有分支提前写死只要给模型足够的工具和清晰的边界它会在运行时自己设计执行路径。4. 常见问题与排查技巧实录做Agent调试是最磨人的因为输出的不确定性意味着你今天能跑通的流程明天换几个字就翻车。我把自己踩过的坑整理成一张速查表问题现象根因解决方案json.loads频繁报错模型在JSON里夹带了 json标记加载前先去除代码块标记找第一个{和最后一个}做截取模型总是不调工具工具描述不够明确或system prompt没强调规则在prompt里明确“需要外部能力时必须输出JSON否则任务无法完成”Agent陷入死循环工具结果触发模型不断调用同个工具加最大步数限制或在prompt中要求“如果工具返回异常直接向用户说明情况”上下文迅速膨胀工具调用结果被完整塞入历史多轮后token爆炸对工具结果做截断或只保留最近几轮上下文温度太高导致工具指令不稳定默认温度0.7太高涉及工具调用时建议temperature控制在0.2~0.34.1 JSON解析失败的高效兜底我见过很多Agent项目挂在“模型不按格式输出”这一关。一个稳妥做法是解析失败时不是直接返回错误而是把“刚才的输出无法解析”作为系统消息追加回上下文让模型自己修正。这一步相当于把“犯错—修正”也做成了一次思考回合。4.2 工具执行错误别急着抛异常当工具执行报错时把异常信息以观察结果的形式写回上下文比直接让程序崩溃要好。模型看到 “输入参数格式不对应该传城市名称” 这样的反馈往往可以自己修正后再调一次。这种容错方式一开始看起来不“严谨”但在LLM应用里是常态。你的Agent不是在跟确定代码打交道而是在跟概率模型博弈。4.3 记录完整运行轨迹强烈建议在每次工具调用前后加上日志输出[2025-01-01 10:00:01] 模型输出: {tool: get_weather, args: {city: 杭州}} [2025-01-01 10:00:03] 执行工具 get_weather返回: 杭州小雨22℃日志是Agent调试的生命线。因为模型行为不可完全复现所以没有日志你根本没法定位是“模型决策错误”还是“工具执行错误”。有同事跟我抱怨Agent效果不行我上去第一件事就是翻日志结果发现是工具入参错了——模型传了空字符串工具直接报错。最后分享两个实操心得第一Agent的很多问题不是“模型不够聪明”而是上下文信息不够完整或工具描述不够清楚。我调试过不下十个Agent项目结论惊人的一致在Prompt和工具描述上花力气比盲目换更强力的模型更有效。第二如果你要把这套代码往生产方向引优先补三个能力结构化输出校验比如用Function Calling或者Pydantic做数据校验、任务队列与进度管理异步长任务必备、以及多轮任务下的全局规划。现阶段这个源码是最好的起点——它足够小能让你彻底掌握Agent的运作机制它也足够稳我本地连续跑了一周没有崩过。把这段代码拷贝下来改一行模型名填上你的API Key就可以开始你第一个Agent的实验了。别怕踩坑那些坑都是你真正理解Agent的开始。本文还有配套的精品资源点击获取