当前位置: 首页 > news >正文

【大模型】如何写一个简单的agent

这里写目录标题

    • 条件
    • 实现原理
    • 代码
    • 特别说明
      • 1. 安全地“拆快递”(防御性编程)
      • 2. 核心动作:拼接文本

条件

需要能支持tool calling的模型,比如我用的qwen3.5

实现原理

  1. 发请求:把 messages 发给 AI。
  2. 看返回:
  • 如果返回里有 tool_calls 说明 AI 需要帮忙,代码去执行真实函数,把结果追加到 messages,继续下一次循环。
  • 如果返回里只有 content(没有 tool_calls)说明 AI 已经拿到了所有信息并整理好了答案,退出循环,打印结果。

代码

importrequestsimportjson# ==========================================# 1. 预制“真家伙”:真实的工具函数# ==========================================defget_weather(city:str)->str:""" 这是一个模拟查询天气的函数。 在真实场景中,这里会是调用第三方天气API的代码。 """# 实际开发中这里可以替换为真实的 HTTP 请求returnf"{city}现在的天气是晴天,气温 25℃。"# 工具注册表:把函数名和函数本身关联起来,方便后面通过名字调用TOOL_FUNCS={"get_weather":get_weather}# ==========================================# 2. 预制“说明书”:Ollama 格式的工具 Schema# ==========================================# 这是给 AI 看的“工具使用手册”,告诉它有哪些工具可以用,以及怎么用。OLLAMA_TOOLS=[{"type":"function",# 工具类型:函数"function":{"name":"get_weather",# 函数名,必须和 TOOL_FUNCS 里的键一致"description":"获取指定城市的当前天气情况",# 函数功能描述,AI 会根据这个决定何时调用"parameters":{# 函数的参数定义,遵循 JSON Schema 规范"type":"object","properties":{"city":{"type":"string","description":"需要查询天气的城市名称"}},"required":["city"]# 指明哪些参数是必须的}}}]# ==========================================# 3. 核心:Agent 循环逻辑# ==========================================defrun_ollama_agent(user_query:str):# 初始化对话上下文(messages)# 这是 Agent 的“记忆”,它会带着这份记忆和 AI 进行多轮对话messages=[{"role":"system","content":"你是一个有用的助手,需要时使用工具获取信息。"},{"role":"user","content":user_query}]# Ollama API 地址url="http://xxxxx/api/chat"# 开启 Agent Loop(防止死循环,最多循环10次)forstepinrange(10):print(f"\n--- 第{step+1}步:调用 Ollama 模型 ---")# 构建发送给 Ollama 的请求体payload={"model":"qwen3:8b",# 指定使用的模型"messages":messages,# 把完整的对话历史发给模型,让它基于上下文思考"keep_alive":"1h","tools":OLLAMA_TOOLS,# 把“工具说明书”也发给模型"stream":False# 设置为 False,让 API 一次性返回完整结果(但 Ollama 底层仍是流式)}# 发送请求给 Ollamatry:response=requests.post(url,json=payload,timeout=(60,1800))response.raise_for_status()# 检查 HTTP 请求是否成功# --- 开始处理 Ollama 的流式响应 ---# 即使 stream=False,Ollama 的 /api/chat 接口返回的仍是一个 JSON 流# 我们需要逐行读取并拼接,直到收到最后一块数据full_content=""assistant_msg={"role":"assistant","content":""}forlineinresponse.iter_lines():ifline:try:# 将每一行字节流解码为 JSON 对象json_line=json.loads(line.decode('utf-8'))# 拼接文本内容(content 字段在每个数据块里都有)if"message"injson_lineand"content"injson_line["message"]:full_content+=json_line["message"]["content"]# 检查是否是最后一个数据块 (done=True)ifjson_line.get("done"):assistant_msg["content"]=full_content# 保存最终拼接的完整文本# 关键逻辑:只在最后一个数据块中检查是否有工具调用请求# tool_calls 字段只会出现在流式响应的最后一个 JSON 对象中if"message"injson_lineand"tool_calls"injson_line["message"]:assistant_msg["tool_calls"]=json_line["message"]["tool_calls"]break# 收到最后一块数据,跳出循环exceptjson.JSONDecodeError:continue# 跳过无法解析的行# 将模型的完整回复(可能包含 tool_calls)追加到对话历史中messages.append(assistant_msg)exceptrequests.exceptions.ConnectionError:print("❌ 连接失败,请检查网络或Ollama服务")breakexceptrequests.exceptions.Timeout:print("❌ 请求超时")breakexceptExceptionase:print(f"❌ 发生未知错误:{e}")break# --- Agent 决策逻辑 ---# 判定1:如果回复中没有 tool_calls,说明 AI 认为任务已完成,可以直接回答用户if"tool_calls"notinassistant_msg:print("✅ 模型完成思考,输出最终回复。")returnassistant_msg["content"]# 判定2:如果回复中有 tool_calls,说明 AI 需要调用工具来获取信息fortool_callinassistant_msg["tool_calls"]:func_name=tool_call["function"]["name"]# 获取要调用的函数名func_args=tool_call["function"]["arguments"]# 获取函数参数print(f"🔧 执行工具:{func_name},参数:{func_args}")# 在工具注册表中找到对应的真实函数并执行result=TOOL_FUNCS[func_name](**func_args)print(f"📦 工具返回结果:{result}")# 将工具的执行结果以 "tool" 角色追加到对话历史中# 这样,在下一次循环时,AI 就能看到工具返回的数据了messages.append({"role":"tool","content":result})# ==========================================# 4. 启动 Agent# ==========================================if__name__=="__main__":# 启动 Agent 并传入用户的问题final_answer=run_ollama_agent("南京和成都的天气怎么样?")print("\n最终答案:",final_answer)

特别说明

forlineinresponse.iter_lines():ifline:try:# 将每一行字节流解码为 JSON 对象json_line=json.loads(line.decode('utf-8'))# 拼接文本内容(content 字段在每个数据块里都有)if"message"injson_lineand"content"injson_line["message"]:full_content+=json_line["message"]["content"]# 检查是否是最后一个数据块 (done=True)ifjson_line.get("done"):assistant_msg["content"]=full_content# 保存最终拼接的完整文本# 关键逻辑:只在最后一个数据块中检查是否有工具调用请求# tool_calls 字段只会出现在流式响应的最后一个 JSON 对象中if"message"injson_lineand"tool_calls"injson_line["message"]:assistant_msg["tool_calls"]=json_line["message"]["tool_calls"]break# 收到最后一块数据,跳出循环exceptjson.JSONDecodeError:continue# 跳过无法解析的行中,详细解释下这个代码 # 拼接文本内容(content 字段在每个数据块里都有)if"message"injson_lineand"content"injson_line["message"]:full_content+=json_line["message"]["content"]

这段代码的核心作用是:在流式(Streaming)响应中,像拼图一样,把 AI 吐出来的一小块一小块的文本,拼凑成一句完整的话。

虽然在请求头里设置了stream: False,但 Ollama 的底层 API 实际上返回的仍然是一个换行符分隔的 JSON 流(JSON Lines)。这意味着 AI 不会一次性把一大段话发给你,而是像打字机一样,一个字或一个词地往外蹦,每蹦出一个片段,就包装成一个 JSON 发给你。

我们来逐行拆解这段代码:

1. 安全地“拆快递”(防御性编程)

if"message"injson_lineand"content"injson_line["message"]:

因为 AI 返回的每一个 JSON 数据块,结构可能不一样。有些数据块里只有进度信息,没有message字段;有些有message,但可能只有thinking(思考过程)而没有content(最终回复)。

2. 核心动作:拼接文本

full_content+=json_line["message"]["content"]
  • 假设 AI 要回复“你好”,它可能会分三次发给你:
    * 第 1 个数据块:{"message": {"content": "你"}}
    * 第 2 个数据块:{"message": {"content": "好"}}
    * 第 3 个数据块:{"message": {"content": "!"}}
  • 作用: 这行代码就是把这三个片段依次累加到full_content变量里。经过三次循环,full_content就会变成完整的"你好!"

这段代码就是一个**“流式文本拼接器”**。它一边接收 AI 吐出的碎片,一边把它们拼成完整的句子,同时还能保证在遇到残缺数据时不崩溃。等最后拼完了,再把完整的full_content交给后面的逻辑去处理。

http://www.jsqmd.com/news/1113622/

相关文章:

  • Linux 内存多维治理:从 cgroup v2 水位线到 DAMON 与 THP 碎片化的企业级调优实战
  • 2026学生党教室网课听课降噪耳机久戴稳佩戴低干扰专注体验
  • AI Agent开发指南:从概念到实战
  • Anybus品牌介绍
  • ClawPro专有云版:数据不出域,Agent不失控
  • Linux-surface没声音:RT5645的解决方法
  • 东莞注塑机数采如何助力精益生产落地见效
  • 采购类标书靠谱服务商
  • 从 Demo 到生产:AI Agent 的可靠性工程
  • Python毕设选题推荐:基于 Python 的图书馆智能荐书服务管理系统的设计与实现 基于 Python 的大数据图书个性化推荐分析系统【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 笔试强训 Day 20:经此一役小红所向无敌、连续子数组最大和、非对称之美
  • DD马达推荐排行榜单
  • <HarmonyOS TechTalk 19> C/C++三方库编译构建 #鸿蒙课程##鸿蒙生态#
  • PCL2启动器架构深度解析:模块化设计与多认证系统实现机制
  • 治理遗留系统中的“生肉 SQL”:一次用多模型协作优化慢查询的实战复盘
  • 终极指南:3分钟学会用AutoRaise实现macOS悬停自动激活窗口
  • Python计算机毕设之基于 Python 的在线图书阅览智能推荐管理系统的设计与实现 基于 Python 的书籍评分溯源智能推荐系统(完整前后端 代码+说明文档+LW,调试定制等)
  • 【提效翻倍】大模型多轮会话上下文管理全实战:滑动窗口 + 摘要记忆 + 持久化,附生产级可运行代码
  • 龙虾人工智能应用场景解析:养殖、聊天、自动化全搞定
  • SQL注入深度解析:从原理到防御的Web安全实战指南
  • 云原生指纹浏览器集群:别只会堆浏览器实例,要先管好隔离和调度
  • 5分钟上手Translumo:终极Windows实时屏幕翻译工具完整指南
  • GanttProject免费项目管理工具实战指南
  • 现场走线太难?试试这种无线温度传感器,省钱又省事
  • 告别 GitOps 翻车!7 招让 ArgoCD 稳如老狗
  • Opencv4.10编译成mingw动态链接库
  • Ethercat设备数据 转 EthernetIP项目案例
  • 如何快速解决网盘限速问题:九大网盘直链下载助手完整指南
  • Nginx 启动报错 nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) 解决方案
  • Etsy店铺被封怎么办?2026年10大封店原因及申诉方案