近期 AI 应用圈有一个比较受关注的方向:Perplexity AI 推出 Portable Computer 本地智能体应用。很多人的第一反应是“又一款 AI 硬件”,但往深了看,它并不仅仅是把大模型塞进一台设备,而是把 AI 搜索、本地推理、智能体任务调度这套能力,整合到随身设备上。对开发者来说,这背后涉及的本地模型部署、工具调用、上下文管理等技术,才是真正值得研究的部分。本文会从概念拆解出发,配合一套可运行的 Python 本地智能体实现,带大家理解这类应用的核心技术链路。
1. 背景与核心概念
1.1 Perplexity AI 与 Portable Computer 是什么
在正式讲技术之前,先把产品方向说清楚。Perplexity AI 是一家以 AI 搜索为核心能力的公司,它的产品特征是直接给用户答案,并且每个答案附带引用来源,而不是像传统搜索引擎那样返回一堆链接。这种“答案引擎”的产品思路,天然适合做成更轻量、更主动的智能设备,所以它转向 Portable Computer 方向并不是偶然。
Portable Computer 可以理解为 Perplexity AI 在硬件与本地化方向上的一次探索。从公开的产品形态和技术方向看,它更像一台面向个人任务的随身 AI 终端:用户可以通过语音或视觉方式与设备交互,设备在本地完成一部分推理,在需要时再调用云端检索能力。它和传统笔记本、手机的区别在于:交互更自然,任务导向更强,且大量计算可以在本地完成。
这样做的好处非常明显。第一,用户的隐私数据不需要全部上传到云端;第二,在弱网环境下,设备仍然可以完成基础问答;第三,智能体可以主动感知场景,替用户完成搜索、记录、提醒等操作。对开发者来说,这套产品形态背后有大量值得学习的工程细节,而不仅仅是关注硬件外观。
1.2 本地智能体应用是什么
“本地智能体应用”这个概念可以拆成三个词来理解:“本地”指模型推理和数据处理发生在设备端,而不是完全依赖云端;“智能体”指程序具备目标拆解、工具调用和反馈循环的能力;“应用”则强调它是一个面向具体任务的软件产品。
举个例子。你对着设备说“帮我查一下明天北京到上海的高铁班次,然后定一个 8 点左右出发的车次”。传统语音助手会把这句话当成一次性的语音识别任务,简单搜索一下关键词。但一个本地智能体应用会把需求拆成两步:第一步,调用搜索工具查询车次;第二步,根据返回结果筛选出符合“8 点左右出发”的车次,再用自然语言汇报结果。如果再接一个订票工具,它还能继续完成后续动作。
所以,本地智能体应用不只是一个“能聊天的模型”,它是一套“感知 → 规划 → 调用工具 → 反馈 → 再规划”的闭环系统。Perplexity AI 的 Portable Computer 之所以值得关注,正是因为它在尝试把这种闭环从网页端搬到设备端,让 AI 能力从“被动回答问题”进化为“主动完成任务”。
1.3 需要区分的几个概念
很多文章把大模型应用、智能体应用和 RAG 混在一起说,这里先做一次边界梳理,后面阅读代码时会轻松很多。
大模型应用:只要程序调用了大模型接口,就可以算大模型应用。比如一个简单的 AI 翻译脚本,输入英文、输出中文,属于大模型应用,但不是智能体。
智能体应用:除了调用模型,还具备工具调用能力。模型可以决定调用哪些外部函数,比如查天气、查数据库、发请求。智能体的关键在于模型拥有“选择工具”的自主性。
RAG(检索增强生成):解决的是“模型不知道最新信息”的问题,通过检索外部知识库,把相关内容拼进提示词里,再让模型回答。RAG 是一种能力模块,不是应用形态。
本地智能体应用则是把“模型 + 工具调用 + 可选检索”都尽量放在本地环境中运行。它们不是互斥的概念,而是一种组合关系:一个本地智能体应用完全可以在内部同时使用 RAG 和外部搜索。
2. 为什么本地智能体成为趋势
2.1 隐私与数据安全
云端大模型虽然能力很强,但用户把问题发送到云端的代价,往往是数据被第三方处理。在很多场景下,比如个人健康数据、企业内部文档、法律材料等,用户并不愿意把内容交给外部 API。把模型部署到本地或私有环境,至少能保证敏感数据不出设备或不出内网,这在合规压力越来越大的背景下尤其重要。
这也是本地智能体应用从“小众玩法”变成“工程方向”的重要原因。Perplexity AI 做便携设备,不可能要求用户把所有问题都依赖云端转发。将一部分推理下沉到设备端,既是体验考虑,也是隐私设计。实际落地时,很多企业也采用类似思路:核心数据留在本地,只有需要更强模型时才把脱敏后的请求发到云端。
2.2 低延迟与弱网可用
如果每一次交互都要经过“设备 → 云端 → 设备”的完整链路,网络抖动会让体验很不稳定。本地推理把模型直接跑在终端上,省去了网络往返时间。在弱网、地铁、地下车库、飞机等场景中,本地智能体的价值会非常明显。
当然,本地模型的能力目前还比不上顶级云端大模型,所以实际产品通常是“本地为主,云端为辅”的混合架构。简单任务本地处理,复杂任务再请求云端的更强模型。这种分层设计不仅适合便携设备,也适合对成本敏感的服务端场景。对普通开发者来说,理解这种混合架构,是设计 AI 产品时的重要思路。
2.3 从“被动问答”到“主动执行”
传统 AI 助手以问答为主,用户问一句,它答一句,本质还是搜索引擎的对话版。而智能体应用更强调“执行”:用户说出目标,智能体负责拆分步骤、调用工具、核对结果。这种形态更适合便携设备,因为设备可以持续感知环境,比如摄像头识别物体、麦克风接收语音指令,然后自动触发任务。
Portable Computer 这类产品如果把智能体能力做透,它可以主动记录用户的使用习惯,预测用户下一步需求,并在用户确认后执行。这种“被动问答 → 主动执行”的进化,正是本地智能体应用被反复提及的根本原因。掌握智能体的设计和开发方式,也会成为 AI 应用开发者的一项核心技能。
3. 本地智能体应用的核心技术模块
要理解 Portable Computer 这样的设备,不能只看外观,要看到它内部的技术模块。下面这四个模块是绝大多数本地智能体应用都绕不开的。
3.1 本地模型推理
本地模型推理是第一步。常见方案有 Ollama、llama.cpp、vLLM、MLC-LLM 等。不同方案侧重点不同:
- Ollama:安装简单,适合个人开发和测试,能快速跑通流程。
- llama.cpp:面向 CPU 和边缘设备优化,显存占用小,适合资源受限场景。
- vLLM:面向高并发服务,适合服务端批量部署。
- MLC-LLM:适合在手机等移动设备上部署,兼顾性能和能耗。
在选择模型时,需要关注模型的参数量、量化等级和上下文长度。设备端通常选用 7B 到 14B 的量化模型,比如 Q4_K_M、Q8_0 这类量化格式,在效果和资源消耗之间取平衡。不要盲目追求大参数模型,对随身设备而言,推理速度、内存占用和功耗往往比单次回答质量更重要。
3.2 工具调用与 Function Calling
智能体与普通聊天程序最大的区别就是工具调用。模型在生成回复时,可以选择“调用某个工具”,并输出结构化参数。程序侧负责执行工具,并把结果返回给模型,让模型基于结果继续生成。
主流实现方式有两类。一类是使用 OpenAI 兼容的 Function Calling API,模型原生支持工具描述与结构化输出,开发体验好;另一类是让模型输出 JSON 格式的指令,然后由程序解析执行。后者实现灵活,但对模型的指令遵循能力要求更高,出错时排错也更麻烦。
关于 Function Calling 的实现原理,可以这样理解:请求时把工具参数以 JSON Schema 的形式传给模型,模型在需要时返回 tool_calls 字段,其中包含工具名和参数。程序收到后执行对应工具,再把结果以 tool 角色的消息追加到对话里,继续请求模型。这个“请求-执行-回填”的循环,就是智能体与普通聊天程序的分水岭。
3.3 上下文管理与多轮对话
本地智能体应用要连续执行任务,上下文管理很关键。每次对话都会增加 token 消耗,而本地模型的上下文窗口通常比云端模型小,所以需要设计滑动窗口、历史摘要、关键信息抽取等机制。
一个简单思路是:保留最近 N 轮对话作为完整内容,更早的内容压缩成摘要。工程上可以使用 LangChain、LlamaIndex 这类框架,也可以自己实现一个 MessageBuffer 类来管理消息列表。对 Portable Computer 这类设备来说,内存和算力有限,上下文管理直接影响响应速度和电池续航,属于必须提前设计的模块。
3.4 检索增强生成(RAG)
本地智能体经常需要回答超出模型知识范围的问题。此时可以在本地维护一个知识库,把文档切片、向量化以后存入向量数据库,比如 Chroma、Milvus Lite、FAISS。用户提问时,先用向量检索找到相关片段,再拼进提示词让模型回答。
RAG 的好处是:模型不需要记住所有细节,只要有检索能力就能回答最新、最具体的问题。对便携设备来说,这意味着即使云端断连,设备也能基于本地知识库工作。结合 Perplexity AI 的搜索基因,可以推测它的 Portable Computer 一定不是单纯靠模型记忆,而是把搜索和 RAG 作为基础能力。
4. 实战:从零实现一个本地智能体应用
下面进入动手环节。我们会基于 Python 实现一个最小可运行的本地智能体应用。它具备两个能力:查系统时间和安全计算。这个架构可以扩展到搜索、知识库查询、HTTP 请求等任意工具。
4.1 环境准备
需要准备的运行环境如下:
- Python 3.10 或以上版本。
- 本地推理服务:推荐安装 Ollama,并拉取一个支持工具调用的模型,比如 qwen2.5。
- 安装 OpenAI Python SDK 和 requests。
版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。安装命令如下:
pip install openai requests如果你使用 Ollama,还需要提前启动服务。在终端执行:
ollama pull qwen2.5 ollama serve注意:这里拉取的模型名称只是示例,不同版本的 Ollama 索引模型名可能不同,请以你本机实际下载的模型为准。如果本地没有支持工具调用的模型,后面的 tool_calls 判断会不生效,这也是后面常见问题章节要讨论的内容。
4.2 创建项目结构
我们按最简单的单文件加工具模块的方式组织项目。这样做的好处是职责清晰:tools.py 负责工具描述和执行逻辑,agent.py 负责智能体对话循环,main.py 是命令行入口。后续如果项目变大,再按工具类型拆目录也不迟。
local-agent/ ├── agent.py # 智能体主循环 ├── tools.py # 工具定义与执行 ├── main.py # 命令行入口 └── requirements.txt # 依赖列表为了方便演示,下面先展示 tools.py,再展示 agent.py 和 main.py。每个文件的路径都会在注释中标注,可以直接复制到对应位置。
4.3 定义工具集
在 tools.py 中定义两个工具:获取系统时间和安全计算。工具描述采用 JSON Schema 格式,这样模型才能生成符合要求的调用参数。
# 文件路径:local-agent/tools.py import ast import operator from datetime import datetime # 工具描述,按照 JSON Schema 格式提供给模型 TOOLS = [ { "type": "function", "function": { "name": "get_current_time", "description": "获取当前系统时间", "parameters": { "type": "object", "properties": {}, "required": [] } } }, { "type": "function", "function": { "name": "safe_calculate", "description": "进行数学计算,支持数字、括号和加减乘除运算", "parameters": { "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式,例如 (1 + 2) * 3" } }, "required": ["expression"] } } } ] _OPERATORS = { ast.Add: operator.add, ast.Sub: operator.sub, ast.Mult: operator.mul, ast.Div: operator.truediv, } def _safe_eval(expression): """基于 AST 的安全计算,避免使用 eval 执行任意代码。""" tree = ast.parse(expression, mode="eval") def _eval(node): if isinstance(node, ast.Constant): if isinstance(node.value, (int, float)): return node.value raise ValueError("仅支持数字常量") if isinstance(node, ast.BinOp): op = _OPERATORS.get(type(node.op)) if op is None: raise ValueError("不支持的运算符") return op(_eval(node.left), _eval(node.right)) if isinstance(node, ast.UnaryOp): if isinstance(node.op, ast.USub): return -_eval(node.operand) if isinstance(node.op, ast.UAdd): return +_eval(node.operand) raise ValueError("不支持的运算符") raise ValueError("不支持的表达式") return _eval(tree.body) def execute_tool(name, arguments): """根据工具名称执行并返回结果。""" if name == "get_current_time": return {"result": datetime.now().strftime("%Y-%m-%d %H:%M:%S")} if name == "safe_calculate": try: value = _safe_eval(arguments["expression"]) return {"result": value} except Exception as exc: return {"error": str(exc)} return {"error": f"未知工具: {name}"}这里重点说明两个设计。第一,工具描述用结构化声明的形式提供给模型,模型才知道有哪些工具、参数怎么填。第二,计算功能没有直接使用 eval,因为 eval 可以执行任意 Python 代码,风险极高。生产环境即使只是工具内部逻辑,也要养成不用 eval 的习惯,改用 AST 或表达式解析库。
4.4 实现智能体主循环
agent.py 是智能体的核心,它负责组织对话、调用模型、处理工具执行结果,并且在一个循环里完成多次工具调用。
# 文件路径:local-agent/agent.py import json from openai import OpenAI from tools import TOOLS, execute_tool class LocalAgent: def __init__(self, base_url="http://localhost:11434/v1", api_key="ollama", model="qwen2.5"): self.client = OpenAI(base_url=base_url, api_key=api_key) self.model = model self.messages = [] self.max_tool_rounds = 5 def chat(self, user_input): """接收用户输入,返回最终回复。""" self.messages.append({"role": "user", "content": user_input}) for _ in range(self.max_tool_rounds): response = self.client.chat.completions.create( model=self.model, messages=self.messages, tools=TOOLS, tool_choice="auto", ) message = response.choices[0].message if message.tool_calls: # 先把模型的工具调用消息追加到历史 self