用 Ollama + Python 搭一个本地 tAI Agent 雏形

AI Agent 的核心不是“让大模型聊天”,而是让大模型根据任务判断是否需要调用工具。本文用 Ollama + Python 搭建一个本地 AI Agent 雏形,实现“用户提问 → 模型判断 → 调用工具 → 汇总回答”的完整流程,适合想入门 Agent、MCP 和本地大模型应用开发的程序员。
用 Ollama + Python 搭一个本地 AI Agent 雏形:让大模型学会调用工具
很多开发者第一次接触 AI Agent 时,容易把它理解成“更聪明的 ChatGPT”。
但真正的 Agent,不只是回答问题,而是能根据目标拆解任务、调用工具、读取数据、执行流程,再把结果反馈给用户。
这也是为什么 MCP、Agent、工具调用这些概念最近在开发者社区越来越热。MCP 官方文档把它描述为一种连接 AI 应用与外部数据源、工具和工作流的开放标准,核心目的就是让模型不只停留在对话,而是能和外部系统协作。([Model Context Protocol][1])
这篇文章不做完整 MCP Server 实现,而是先用更容易理解的方式,手写一个“Agent 雏形”。
你会看到一个最小闭环:
用户问题
↓
本地大模型理解
↓
判断是否需要工具
↓
Python 调用工具函数
↓
把工具结果交回模型
↓
生成最终回答
这个流程跑通之后,再去理解 MCP、LangChain、Spring AI、Claude Code 这类工具和框架,就会清楚很多。
1. 环境准备
本文示例使用:
Python 3.10+
Ollama
本地模型:llama3.2 / qwen2.5 / gemma3 均可
Ollama 官方提供了本地 REST API,可以通过 http://localhost:11434/api/chat 与本地模型对话。官方 API 示例也是基于 /api/chat 发送 messages 数组来完成对话。([GitHub][2])
先安装 Ollama,并拉取一个模型:
ollama pull llama3.2
确认 Ollama 是否正常运行:
curl http://localhost:11434/api/chat -d '{
"model": "llama3.2",
"messages": [
{
"role": "user",
"content": "你好,请用一句话介绍你自己"
}
],
"stream": false
}'
如果能返回模型回答,说明本地模型已经可以调用。
2. 项目目录结构
我们先做一个最小可运行版本。
local-agent-demo/
├── agent.py
├── tools.py
└── README.md
其中:
tools.py
负责存放工具函数
agent.py
负责模型对话、意图判断、工具调用和结果汇总
这个结构虽然简单,但已经具备 Agent 的核心雏形。
3. 先写几个工具函数
这里模拟三个常见工具:
- 获取当前时间;
- 计算两个数字之和;
- 查询本地知识库。
新建 tools.py:
# tools.py
from datetime import datetime
def get_current_time() -> str:
"""
获取当前本地时间。
"""
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
def add_numbers(a: float, b: float) -> str:
"""
计算两个数字之和。
"""
return str(a + b)
def search_local_knowledge(keyword: str) -> str:
"""
模拟本地知识库查询。
实际项目中可以替换成数据库、向量库、文档检索或接口查询。
"""
knowledge_base = {
"ollama": "Ollama 是一个可以在本地运行大语言模型的工具,适合开发本地 AI 应用。",
"mcp": "MCP 是 Model Context Protocol,用于连接 AI 应用与外部工具、数据源和工作流。",
"agent": "AI Agent 通常具备任务理解、工具调用、执行反馈和多步骤推理能力。"
}
keyword = keyword.lower()
for key, value in knowledge_base.items():
if key in keyword:
return value
return "本地知识库中没有找到相关信息。"
这三个函数就是我们的“工具库”。
在真实项目中,这些工具可以替换成:
数据库查询
HTTP API 请求
文件读取
向量检索
Shell 命令
订单查询
日志分析
自动化脚本
Agent 的价值就在这里:模型负责理解任务,工具负责执行真实操作。
4. 定义工具注册表
如果工具很多,不能靠 if else 到处散落。
更好的做法是建立一个工具注册表。
在 tools.py 继续加入:
# tools.py
TOOLS = {
"get_current_time": {
"description": "获取当前本地时间",
"function": get_current_time,
"args": []
},
"add_numbers": {
"description": "计算两个数字之和",
"function": add_numbers,
"args": ["a", "b"]
},
"search_local_knowledge": {
"description": "查询本地知识库",
"function": search_local_knowledge,
"args": ["keyword"]
}
}
这样做有两个好处:
第一,工具统一管理。
第二,后续可以把工具说明交给大模型,让模型自己判断该调用哪个工具。
这也是理解 MCP 的关键。MCP 规范里服务端能力包括 tools、resources、prompts 等,工具只是其中一个能力类型。([Model Context Protocol][3])
5. 编写 Ollama 调用函数
新建 agent.py:
# agent.py
import json
import requests
from tools import TOOLS
OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL_NAME = "llama3.2"
def call_ollama(messages):
"""
调用本地 Ollama 模型。
"""
payload = {
"model": MODEL_NAME,
"messages": messages,
"stream": False
}
response = requests.post(OLLAMA_URL, json=payload, timeout=120)
response.raise_for_status()
data = response.json()
return data["message"]["content"]
这里使用的是 Ollama 的 /api/chat 接口。
如果你使用的是其他模型,只需要改:
MODEL_NAME = "llama3.2"
例如:
MODEL_NAME = "qwen2.5"
前提是你已经提前拉取对应模型。
6. 让模型输出结构化工具调用指令
现在的问题是:
模型怎么告诉 Python 它想调用哪个工具?
最简单的方式是让模型输出 JSON。
例如:
{
"need_tool": true,
"tool_name": "get_current_time",
"arguments": {}
}
或者:
{
"need_tool": true,
"tool_name": "add_numbers",
"arguments": {
"a": 12,
"b": 30
}
}
如果不需要工具,就输出:
{
"need_tool": false,
"answer": "直接回答用户的问题"
}
继续在 agent.py 中加入:
def build_tool_prompt(user_input):
"""
构造工具选择提示词。
"""
tool_descriptions = []
for name, info in TOOLS.items():
tool_descriptions.append({
"name": name,
"description": info["description"],
"args": info["args"]
})
return f"""
你是一个本地 AI Agent 的工具选择器。
用户问题:
{user_input}
你可以使用以下工具:
{json.dumps(tool_descriptions, ensure_ascii=False, indent=2)}
请判断是否需要调用工具。
要求:
1. 只能输出 JSON
2. 不要输出 Markdown
3. 不要解释原因
4. 如果需要工具,输出:
{{
"need_tool": true,
"tool_name": "工具名称",
"arguments": {{
"参数名": "参数值"
}}
}}
5. 如果不需要工具,输出:
{{
"need_tool": false,
"answer": "直接回答内容"
}}
"""
这里的重点是限制模型输出格式。
很多 Agent Demo 跑不稳定,核心问题不是模型不会,而是输出格式不可控。
所以必须给它明确约束:
只能输出 JSON
不要 Markdown
不要解释
字段名固定
7. 解析模型输出并调用工具
继续写工具调用逻辑:
def parse_json_safely(text):
"""
尽量安全地解析模型返回的 JSON。
"""
try:
return json.loads(text)
except json.JSONDecodeError:
start = text.find("{")
end = text.rfind("}") + 1
if start != -1 and end != -1:
return json.loads(text[start:end])
raise ValueError(f"模型输出不是合法 JSON:{text}")
def execute_tool(tool_name, arguments):
"""
根据工具名称执行对应函数。
"""
if tool_name not in TOOLS:
raise ValueError(f"未知工具:{tool_name}")
tool_info = TOOLS[tool_name]
func = tool_info["function"]
return func(**arguments)
这里做了一个简单容错。
因为本地模型有时可能会输出多余文字,比如:
好的,以下是 JSON:
{...}
所以我们通过 find("{") 和 rfind("}") 尝试截取 JSON。
生产环境中建议使用更严格的结构化输出方案。
8. 把工具结果交回模型总结
工具执行后,不应该直接把结果原样扔给用户。
更好的方式是让模型基于工具结果生成自然语言回答。
继续加入:
def summarize_with_tool_result(user_input, tool_name, tool_result):
"""
让模型基于工具调用结果生成最终回答。
"""
messages = [
{
"role": "system",
"content": "你是一个严谨的技术助手,请根据工具结果回答用户问题。"
},
{
"role": "user",
"content": f"""
用户原始问题:
{user_input}
调用的工具:
{tool_name}
工具返回结果:
{tool_result}
请给用户一个简洁、准确、自然的回答。
"""
}
]
return call_ollama(messages)
这样 Agent 的链路就完整了:
用户问题
↓
模型判断工具
↓
Python 执行工具
↓
模型总结结果
↓
返回用户
9. 完整 Agent 主函数
继续在 agent.py 中加入:
def run_agent(user_input):
"""
运行本地 Agent。
"""
tool_prompt = build_tool_prompt(user_input)
messages = [
{
"role": "system",
"content": "你是一个只输出 JSON 的工具选择器。"
},
{
"role": "user",
"content": tool_prompt
}
]
decision_text = call_ollama(messages)
decision = parse_json_safely(decision_text)
if not decision.get("need_tool"):
return decision.get("answer", "这个问题不需要调用工具,但模型没有给出回答。")
tool_name = decision["tool_name"]
arguments = decision.get("arguments", {})
tool_result = execute_tool(tool_name, arguments)
final_answer = summarize_with_tool_result(
user_input=user_input,
tool_name=tool_name,
tool_result=tool_result
)
return final_answer
if __name__ == "__main__":
while True:
user_input = input("\n你:").strip()
if user_input.lower() in ["exit", "quit", "q"]:
print("退出 Agent。")
break
try:
answer = run_agent(user_input)
print(f"\nAgent:{answer}")
except Exception as e:
print(f"\nAgent 出错:{e}")
10. 运行测试
安装依赖:
pip install requests
启动:
python agent.py
测试 1:时间工具
你:现在几点?
预期流程:
模型判断需要 get_current_time
↓
Python 调用 get_current_time()
↓
模型基于结果回答
测试 2:计算工具
你:帮我计算 18.5 加 23.7 等于多少
预期工具调用:
{
"need_tool": true,
"tool_name": "add_numbers",
"arguments": {
"a": 18.5,
"b": 23.7
}
}
测试 3:知识库查询
你:MCP 是什么?
预期工具调用:
{
"need_tool": true,
"tool_name": "search_local_knowledge",
"arguments": {
"keyword": "MCP"
}
}
11. 常见问题排查
问题 1:连接不上 Ollama
报错类似:
Connection refused
先检查 Ollama 是否运行:
curl http://localhost:11434/
如果返回:
Ollama is running
说明服务正常。
如果没有返回,先启动 Ollama。
问题 2:模型不存在
报错可能是:
model not found
检查你是否拉取了模型:
ollama list
如果没有,重新拉取:
ollama pull llama3.2
问题 3:模型输出不是 JSON
这是最常见问题。
可以从三方面优化:
第一,强化 system prompt:
你只能输出合法 JSON,不能输出其他任何内容。
第二,降低模型自由发挥空间。
第三,在代码中增加 JSON 截取和校验逻辑。
问题 4:工具参数类型错误
例如模型把数字输出成字符串:
{
"a": "18.5",
"b": "23.7"
}
可以在工具函数里做类型转换:
def add_numbers(a, b) -> str:
return str(float(a) + float(b))
这个在真实项目里非常重要。
因为模型输出永远不能完全信任,所有参数都应该校验。
12. 这个 Demo 和真正 MCP 的区别
本文实现的是一个“类 Agent 工具调用雏形”,不是完整 MCP 实现。
区别主要在于:
| 能力 | 本文 Demo | 完整 MCP |
|---|---|---|
| 工具注册 | Python 字典 | 标准协议 |
| 通信方式 | 函数调用 | JSON-RPC / HTTP / stdio 等 |
| 工具发现 | 手动传给模型 | 客户端可发现 |
| 权限控制 | 无 | 可设计认证与权限 |
| 资源管理 | 简单模拟 | 标准 resources |
| 提示词管理 | 手写 prompt | 标准 prompts |
MCP 规范的核心组件包括基础协议、版本协商、消息模式、授权、服务端能力等,远比本文 Demo 更完整。本文的价值在于先理解底层思想:模型判断任务,外部工具负责执行。([Model Context Protocol][4])
等你理解这个流程后,再去写 MCP Server,会顺很多。
13. 下一步可以怎么扩展?
这个 Demo 只是第一版,可以继续升级。
方向 1:接入真实 API
例如:
天气查询
GitHub Issue 查询
数据库查询
订单系统查询
日志平台查询
工具函数可以变成:
def query_order(order_id: str) -> str:
...
方向 2:加入多轮记忆
现在每次调用都是独立的。
可以把历史消息保存起来:
conversation_history = []
然后每轮传给模型。
方向 3:加入工具调用日志
生产环境必须记录:
用户问题
模型决策
调用工具
工具参数
返回结果
最终回答
否则出现错误很难排查。
方向 4:接入向量数据库
本地知识库可以升级为:
Markdown 文档
PDF 文档
数据库
向量库
RAG 检索系统
这样 Agent 就能回答项目文档、接口说明、业务知识等问题。
方向 5:改造成 MCP Server
等工具稳定后,可以按照 MCP 协议包装成标准工具服务,让支持 MCP 的客户端调用。
14. 写在最后
AI Agent 的核心,不是让大模型说得更像人,而是让它能做事。
这篇文章用 Ollama + Python 搭了一个最小可运行雏形,虽然简单,但已经覆盖了 Agent 最重要的几个环节:
任务理解
工具选择
参数生成
工具执行
结果总结
异常处理
如果你是开发者,建议不要只停留在“哪个 AI 编程工具更强”的讨论里,而是亲手跑一遍这种最小 Agent。
因为只有你真正写过工具调用、参数校验、异常处理和结果汇总,才会明白 Agent 落地最难的地方不是模型,而是工程细节。
如果你长期使用 ChatGPT Plus、Claude Pro、Grok、Gemini Advanced、Cursor、Kiro 等工具,也可以顺手了解一下 gpt108.com。它是 AI会员充值平台,解决的是订阅充值流程问题,不是替代工具本身;使用前建议看清楚套餐说明、账号要求和售后规则。
工具只是入口。
真正让开发者拉开差距的,是能不能把 AI 变成可运行、可维护、可扩展的系统。
更多推荐


所有评论(0)