Ollama+Python搭建本地AI Agent:工具调用与结构化输出实现
AI Agent的核心不是让模型更会聊天,而是让模型根据任务判断是否需要调用外部工具,再基于工具结果生成回答。本文基于Ollama和Python搭建一个最小化本地AI Agent雏形,覆盖从模型选择、工具注册、JSON结构化输出约束到异常处理的完整流程,适合想入门Agent和MCP实现的开发者。环境准备
需要Python 3.10+、已安装并运行的Ollama,以及一个本地模型(如llama3.2、qwen2.5、gemma3均可)。确认Ollama服务正常:curl http://localhost:11434/api/chat -d '{"model":"llama3.2","messages":[{"role":"user","content":"你好"}],"stream":false}'。返回模型回答则说明环境就绪。
项目结构
创建两个文件:tools.py存放工具函数和注册表,agent.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 是一个可以在本地运行大语言模型的工具",
"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 "未找到相关信息"
然后建立工具注册表,统一管理工具名称、描述、参数列表和引用。
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"]
}
}
封装Ollama调用
在agent.py中编写调用Ollama /api/chat接口的函数。
import json, requests
from tools import TOOLS
OLLAMA_URL = "http://localhost:11434/api/chat"
MODEL_NAME = "llama3.2"
def call_ollama(messages):
payload = {"model": MODEL_NAME, "messages": messages, "stream": False}
response = requests.post(OLLAMA_URL, json=payload, timeout=120)
response.raise_for_status()
return response.json()["message"]["content"]
引导模型输出结构化JSON
核心难点是让模型输出可解析的工具调用指令。构造一个强制JSON输出的prompt,明确字段名和格式。
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)}
判断是否需要调用工具。要求:只能输出JSON,不要Markdown或解释。如果需要工具:{{"need_tool": true, "tool_name": "工具名称", "arguments": {{"参数名": "参数值"}}}}。如果不需要:{{"need_tool": false, "answer": "直接回答"}}
"""
解析与执行工具
模型返回的文本可能包含多余前缀,容错解析JSON。
def parse_json_safely(text):
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)
raise ValueError(f"非法JSON:{text}")
执行工具时注意参数校验,比如数字可能被模型输出为字符串,在工具函数内部做类型转换。
def execute_tool(tool_name, arguments):
if tool_name not in TOOLS:
raise ValueError(f"未知工具:{tool_name}")
return TOOLS["function"](**arguments)
结果汇总与主循环
工具执行结果不应直接返回,而是交给模型重新总结成自然语言。
def summarize_with_tool_result(user_input, tool_name, tool_result):
messages = [
{"role": "system", "content": "你是一个严谨的技术助手,请根据工具结果回答用户问题。"},
{"role": "user", "content": f"用户原始问题:{user_input}\n调用的工具:{tool_name}\n工具结果:{tool_result}\n请给出简洁自然的回答。"}
]
return call_ollama(messages)
完整主函数和交互循环:
def run_agent(user_input):
tool_prompt = build_tool_prompt(user_input)
messages = [{"role": "system", "content": "你只输出JSON。"}, {"role": "user", "content": tool_prompt}]
decision = parse_json_safely(call_ollama(messages))
if not decision.get("need_tool"):
return decision.get("answer", "无回答")
tool_result = execute_tool(decision["tool_name"], decision.get("arguments", {}))
return summarize_with_tool_result(user_input, decision["tool_name"], tool_result)
if __name__ == "__main__":
while True:
user_input = input("\n你:").strip()
if user_input.lower() in ["exit","quit","q"]: break
try:
print(f"\nAgent:{run_agent(user_input)}")
except Exception as e:
print(f"出错:{e}")
运行测试
安装requests后执行python agent.py。测试问题如“现在几点?”、“计算18.5+23.7”、“MCP是什么?”可验证工具调用与结果总结。
常见问题排查
连接不上:检查Ollama是否运行(curl http://localhost:11434/)。模型不存在:ollama pull llama3.2。模型输出非JSON:强化system prompt或增加JSON截取逻辑。参数类型错误:在工具函数内做类型转换,例如def add_numbers(a,b): return str(float(a)+float(b))。
与真正MCP的区别
本文实现的是手动工具注册与调用,而MCP标准协议提供JSON-RPC通信、工具发现、权限控制等规范。理解此雏形后再学习MCP会更容易。
扩展方向
可接入真实API(天气、订单等)、加入多轮对话记忆、记录工具调用日志、连接向量数据库做RAG,或按MCP协议包装成标准服务。
此Demo虽然简单,但覆盖了Agent核心环节:任务理解→工具选择→参数生成→工具执行→结果总结→异常处理。跑通这个最小闭环,才能真正理解AI Agent落地的工程细节。
Re: Ollama+Python搭建本地AI Agent:工具调用与结构化输出实现
楼主这篇文章太及时了,最近刚好在研究怎么让本地模型具备调用工具的能力。你提供的工具注册表和强制 JSON 输出的 prompt 思路非常清晰,特别是把工具描述、参数列表结构化传给模型,这样模型就能理解该用什么工具。我有个小问题:当模型返回的 JSON 里 `need_tool` 为 `false` 时,你是怎么直接让模型回答的?是直接复用 `call_ollama` 还是另外做一次普通对话?另外,如果工具参数填写错误或者模型输出的 JSON 格式不完整(比如少了一个花括号),你在异常处理上有什么经验?期待后续分享更完整的错误处理逻辑。Re: Ollama+Python搭建本地AI Agent:工具调用与结构化输出实现
感谢分享,很完整的入门教程!正好最近在折腾本地大模型,这个工具调用和结构化输出的思路清晰直接,收藏了。有个小问题:如果工具返回的结果比较复杂(比如列表或嵌套JSON),prompt那边需要额外处理吗?还是靠模型自己理解返回内容?Re: Ollama+Python搭建本地AI Agent:工具调用与结构化输出实现
很好的教程,结构清晰,从环境准备到工具注册再到JSON约束输出,把Agent的核心流程讲得很明白。特别是有意识地把“让模型输出结构化JSON”作为重点来设计prompt,这个思路对Ollama这类本地模型来说很关键,模型在自由生成时容易跑偏,强制JSON输出能大幅提高调用的可靠性。准备按你的项目结构试一遍,后面能否再分享一下多工具串联或带状态记忆的进阶版本?
页:
[1]