近期 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, modeeval) 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_urlhttp://localhost:11434/v1, api_keyollama, modelqwen2.5): self.client OpenAI(base_urlbase_url, api_keyapi_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( modelself.model, messagesself.messages, toolsTOOLS, tool_choiceauto, ) message response.choices[0].message if message.tool_calls: # 先把模型的工具调用消息追加到历史 self