用 Python + Ollama 本地模型实现的自主数据分析 Agent,采用标准 ReAct 模式。 完全离线运行,无需 API Key,无需联网。
-
前往 ollama.com 下载并安装 Ollama(支持 macOS / Windows / Linux)
-
拉取模型(gemma4:e4b 约 9.6 GB,首次下载需要等待):
ollama pull gemma4:e4b
-
启动 Ollama 服务(安装后通常会自动运行,也可以手动启动):
ollama serve
-
验证服务正常:
curl http://localhost:11434/api/tags # 应该返回包含 gemma4:e4b 的 JSON
# 进入项目目录
cd agent
# 建议先创建虚拟环境,保持系统 Python 环境整洁
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
# 安装依赖
pip install -r requirements.txt默认配置开箱即用,不需要修改。如果想换模型,才需要:
cp .env.example .env
# 编辑 .env,修改 OLLAMA_MODELpython main.py
# 或直接双击 run.sh(自动创建虚拟环境)| 对比项 | Ollama 本地 | 云端 API(Gemini / Claude) |
|---|---|---|
| 费用 | 免费,无限次数 | 有配额限制或按量付费 |
| 隐私 | 数据不离开本机 | 数据上传到云端 |
| 网络 | 完全离线可用 | 需要稳定的国际网络 |
| 速度 | 取决于本机 GPU/CPU | 通常更快(云端算力) |
| 模型能力 | 7B 参数,够用 | 更强的大模型 |
当前默认模型:gemma4:e4b
- 9.6 GB,中文理解和生成质量更好
- 需要 16GB+ 内存;多轮 Agent 任务负载较高时偶尔可能触发 502
其他可选模型:
ollama pull qwen2.5:7b # 约 4.7 GB,Mac Air 8GB/16GB 更稳定,502 时可切换回此模型
ollama pull qwen2.5:1.5b # 约 1 GB,内存极少时用
ollama pull llama3.1:8b # Meta 出品,英文能力强如果运行中遇到 Ollama 返回 502 错误,编辑
.env,把OLLAMA_MODEL改为qwen2.5:7b即可,代码无需修改。
用户任务
│
▼
┌──────────────────────────────────────────┐
│ Agent 核心循环 │
│ │
│ 历史消息 ──► Ollama HTTP API │
│ (system + (127.0.0.1:11434) │
│ 对话记录) │ │
│ ┌───────┴────────┐ │
│ │ │ │
│ {"tool":"finish"} {"tool":"xxx"} │
│ │ │ │
│ 任务完成 执行 Python 函数│
│ │ │
│ 把结果追加到历史 │
│ │ │
│ 继续下一轮 │
└──────────────────────────────────────────┘
本项目不使用 Ollama 的原生 tools 参数(在 qwen2.5:7b / gemma4:e4b 上都不稳定),改为更可靠的提示词注入方式:
第一步 — 在 system prompt 里描述工具格式:
你有以下工具可以使用,需要调用工具时,只输出一个 JSON 对象:
{"tool": "工具名", "args": {"参数名": "参数值"}}
可用工具:
- fetch_webpage(url: str) → 爬取网页内容
- analyze_data(data: str, question: str) → 分析数据
- save_report(content: str, filename: str) → 保存报告
任务完成后输出:
{"tool": "finish", "result": "最终总结"}
第二步 — 模型回复 JSON,代码解析执行:
# 模型输出(纯文本):
{"tool": "fetch_webpage", "args": {"url": "https://news.ycombinator.com"}}
# 代码解析后执行真正的 Python 函数:
result = fetch_webpage(url="https://news.ycombinator.com")
# 把结果作为 user 消息反馈给模型:
{"role": "user", "content": "fetch_webpage 执行成功,结果如下:\n[页面内容...]"}这种方式与模型 API 格式完全解耦,只要模型能输出 JSON 就能工作。
代码直接调用 Ollama 的 REST API,不依赖任何第三方 SDK:
import requests
resp = requests.post(
"http://127.0.0.1:11434/api/chat",
json={
"model": "gemma4:e4b",
"messages": history,
"stream": False,
"keep_alive": -1, # 防止模型在请求间隔被卸载
},
timeout=120,
)
content = resp.json()["message"]["content"]ReAct = Reasoning(推理)+ Acting(行动)
Thought:我需要先爬取 HN 首页
Action:{"tool": "fetch_webpage", "args": {"url": "https://news.ycombinator.com"}}
Observation:[页面内容返回]
Thought:好的,我现在分析这些数据
Action:{"tool": "analyze_data", "args": {"data": "...", "question": "热门话题规律"}}
Observation:[分析结果返回]
Thought:分析完成,需要保存报告
Action:{"tool": "save_report", "args": {"content": "...", "filename": "hn_report.txt"}}
Observation:报告已保存
Thought:所有任务完成
Action:{"tool": "finish", "result": "今日 HN 热门话题以 AI 和云计算为主..."}
每次工具调用的结果都会成为 LLM 下一次决策的依据,这就是 Agent 能"自主"工作的原因。
两者的核心概念完全相同,只需替换 LLM 调用部分。
| 项目 | 本地 Ollama | Claude API |
|---|---|---|
| 依赖 | requests(内置) |
pip install anthropic |
| 认证 | 无需 | ANTHROPIC_API_KEY |
| 调用方式 | requests.post(...) |
client.messages.create(...) |
| 工具调用 | 提示词注入 + JSON 解析 | 原生 tools 参数 |
import anthropic
client = anthropic.Anthropic(api_key="your-key")
# 工具定义(Claude 原生格式,比提示词注入更精确)
tools = [
{
"name": "fetch_webpage",
"description": "爬取网页内容",
"input_schema": {
"type": "object",
"properties": {"url": {"type": "string"}},
"required": ["url"]
}
}
]
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=4096,
system=system_prompt,
tools=tools,
messages=history
)
# 检测工具调用
for block in response.content:
if block.type == "tool_use":
result = TOOLS[block.name](**block.input)
history.append({
"role": "user",
"content": [{"type": "tool_result", "tool_use_id": block.id, "content": result}]
})云端大模型原生支持结构化工具调用,不需要提示词注入,可靠性更高。
除了上面的通用 ReAct Agent(agent.py),项目里还有一个编排式(Orchestration)Agent,
用于自动生成热点分析文章:agents/hot_agent.py,入口是 hot_main.py。
python hot_main.py| 项目 | agent.py(ReAct) |
agents/hot_agent.py(编排式) |
|---|---|---|
| 工具调用方式 | LLM 自主决定调用哪个工具、何时结束 | 流程固定,代码直接按步骤调度 |
| LLM 的角色 | 全程决策(规划 + 执行) | 只负责"理解和生成"(筛选话题、写文章) |
| 网页抓取 | 单个通用 fetch_webpage(url),按任务需要调用 |
5 个平台专用抓取函数,并发执行 |
步骤1:并发抓取 5 个平台热榜(每个平台一个线程)
├── fetch_weibo_hot() 微博热搜
├── fetch_zhihu_hot() 知乎热榜
├── fetch_toutiao_hot() 今日头条
├── fetch_hackernews_hot() Hacker News
└── fetch_gossip_hot() 娱乐八卦(多来源降级抓取)
│
▼ 合并所有热点,标注来源平台
步骤2:调用 LLM (select_topics) 从所有热点中筛选 Top N 个有价值的话题
│
▼
步骤3:对每个话题调用 LLM (write_article) 生成 800-1200 字深度文章
│
▼
步骤4:保存为 Markdown 文件到 output/ 目录(save_article)
│
▼
步骤5:打印汇总报告(耗时、成功篇数、文件路径)
抓取阶段用 threading 并发执行 5 个平台的请求,互不阻塞,整体耗时取决于最慢的那个平台
(而不是 5 个平台耗时之和)。fetch_gossip_hot() 内部还做了多来源降级:优先从微博热搜里
按关键词过滤娱乐话题,失败则依次尝试抖音热点榜、百度热搜聚合接口。
agent/
├── main.py # 入口:启动检查、模型预热、启动 Agent
├── agent.py # 核心:ReAct 循环、调用 Ollama API、解析 JSON、分发工具
├── tools.py # 工具:fetch_webpage / analyze_data / save_report
├── hot_main.py # 入口:热点文章生成 Agent(HotArticleAgent)
├── agents/
│ ├── hot_agent.py # 编排式 Agent:并发抓取热榜 → 筛选话题 → 生成文章
│ └── hot_tools.py # 工具:各平台热榜抓取、LLM 筛选/生成、文章保存
├── requirements.txt # 依赖列表
├── .env.example # 配置模板(模型名称等)
├── .env # 你的真实配置(不要提交到 Git!)
├── run.sh # 一键运行脚本(自动创建虚拟环境)
├── reports/ # 自动创建,存放 ReAct Agent 生成的报告
└── output/ # 自动创建,存放热点文章生成 Agent 的文章
- Ollama 官方文档(REST API 完整说明)
- ReAct 原论文(2022,提出 ReAct 模式)
- Anthropic Tool Use 文档(换 Claude API 时参考)