L18 本地 Agent:把 Agent 跑在"自家电脑"上
课程目标:理解本地 Agent 的核心价值与权衡,掌握 Foundry Local + Qwen 的本地 Agent 实战,能用 Chroma 做本地 RAG。
一、为什么需要本地 Agent?#
云端 Agent 多好——但有些场景必须跑在本地:
| 场景 | 为什么 |
|---|---|
| 医疗病历分析 | 隐私——病人数据不能上云 |
| 律师审阅合同 | 机密——客户文档不能泄露 |
| 工厂车间巡检 | 离线——没网也得能工作 |
| 学生代码作业 | 成本——每个学生都调用 GPT 烧钱 |
| 个人代码助手 | 隐私——公司代码不能传给 OpenAI |
Creating Local AI Agents 把这种 Agent 叫 Local AI Agents:
把 SLM(小语言模型)跑在你自己的机器上,本地推理、本地 RAG、本地工具、本地 MCP——完全不依赖云。
二、用一个比喻:本地 Agent = "自家厨房"#
把 Agent 比作餐饮:
| 餐饮 | Agent |
|---|---|
| 餐厅(点外卖) | 云端 Agent(API 调用) |
| 自家厨房做饭 | 本地 Agent(跑在自己机器) |
| 食材新鲜可控 | 数据不出本机 |
| 想做什么做什么 | 不受 API 限制 |
| 不需要外卖费 | 零 token 成本 |
| 但要自己买菜买锅 | 需要本地硬件(GPU/RAM) |
自家厨房 = 自由 + 隐私,但前期投入大、做饭慢。
三、SLM:小模型也能做 Agent#
Creating Local AI Agents 强调一个关键反转:
本地 Agent 用的是 SLM(Small Language Model),不是 GPT-5 这种大模型。
3.1 SLM 的强项#
结构化、有限任务:分类、抽取、摘要
工具调用:决定调哪个函数、用什么参数
快速、低成本迭代:私有数据上反复调试
3.2 SLM 的弱项#
开放式多跳推理:跨大 context 的复杂推理弱
广泛世界知识:训练数据少
3.3 关键策略:让 SLM 编排,让工具干活#
用户 ──> 本地 SLM Agent ──> 工具 1(读文件)
├──> 工具 2(搜文档 RAG)
└──> 工具 3(分析代码)
│
▼
答案(全程离线)
核心思想:SLM 做"调度员"——它不需要有所有知识,它只需要知道哪个工具能回答。
四、Foundry Local:微软的本地模型运行时#
Creating Local AI Agents 介绍了 Microsoft Foundry Local——一个轻量级运行时:
# Windows 安装
winget install Microsoft.FoundryLocal
# macOS 安装
brew install microsoft/foundrylocal/foundrylocal
# 下载并运行一个 Qwen 模型
foundry model run qwen2.5-7b-instruct
# 查看本地服务状态
foundry service status
本地端点:通常是 http://localhost:PORT/v1——完全兼容 OpenAI API。
这意味着:你的云端 Agent 代码改一行 base_url 就能跑本地。
五、Qwen Function Calling:本地也能调工具#
Creating Local AI Agents 强调——Qwen 模型是专门为 Function Calling 训练的,本地工具调用也能稳定工作。
5.1 连接本地 Qwen#
from foundry_local import FoundryLocalManager
from openai import OpenAI
# Foundry Local 自动发现/下载模型并提供本地端点
manager = FoundryLocalManager("qwen2.5-7b-instruct")
client = OpenAI(
base_url=manager.endpoint,
api_key=manager.api_key # 本地占位符,无真实密钥
)
5.2 注册工具#
def read_file(path: str) -> str:
"""Read a file, but only inside the sandboxed project directory."""
full = (PROJECT_ROOT / path).resolve()
if PROJECT_ROOT not in full.parents and full != PROJECT_ROOT:
return "Access denied: path is outside the project directory."
return full.read_text(encoding="utf-8")
安全设计:即使本地也要沙箱化——工具不能读取任意路径。
5.3 让 Agent 调用工具#
response = client.chat.completions.create(
model="qwen2.5-7b-instruct",
messages=[{"role": "user", "content": "What does auth.py do?"}],
tools=[{
"type": "function",
"function": {
"name": "read_file",
"description": "Read a file in the project",
"parameters": {
"type": "object",
"properties": {"path": "the file to read"}
}
}
}]
)
# Qwen 会输出结构化的 tool_call
tool_call = response.choices[0].message.tool_calls[0]
print(tool_call.function.name) # "read_file"
print(tool_call.function.arguments) # {"path": "auth.py"}
六、本地 RAG:用 Chroma 做嵌入式向量数据库#
Creating Local AI Agents 用 Chroma 做本地 RAG——它是一个进程内嵌入的向量数据库:
import chromadb
# 初始化(数据存到本地磁盘)
chroma_client = chromadb.PersistentClient(path="./chroma_db")
collection = chroma_client.get_or_create_collection("docs")
# 把文档灌入
collection.add(
documents=["Authentication module uses JWT...", "Database module uses SQLAlchemy..."],
ids=["doc1", "doc2"]
)
# 查询
results = collection.query(
query_texts=["how does auth work?"],
n_results=2
)
完整 RAG 流程:
本地文档 → 本地 embedding → Chroma
↓
用户问题 → 本地 embedding → 相似度搜索 → top-k chunks
↓
本地 SLM 生成答案
七、本地 MCP:协议和云无关#
MCP 是传输协议,不是云服务。MCP Server 可以跑在本地:
# 启动本地 MCP Server(stdio 模式)
python my_local_mcp_server.py
Agent 通过 stdio 连接它——完全本地。
八、云-端混合架构#
Creating Local AI Agents 给出了云-端混合的设计模式:
请求 → {敏感或离线?} ── yes ──> 本地 SLM
{ ── no ──> {需要深度推理?}
{ │
{ ├── no ──> 本地 SLM(便宜快)
not needed └── yes ──> 云端大模型
| 场景 | 跑在哪 |
|---|---|
| 敏感数据 / 离线 | 本地 SLM |
| 简单 / 有界任务 | 本地 SLM(便宜快) |
| 复杂多跳推理(非敏感) | 云端大模型 |
| 云服务宕机时 | 本地 SLM(优雅降级) |
九、动手:本地代码工程助手#
from pathlib import Path
import chromadb
from foundry_local import FoundryLocalManager
from openai import OpenAI
# 1. 设置项目根 + 工具
PROJECT_ROOT = Path("./my_project")
def read_file(path: str) -> str:
full = (PROJECT_ROOT / path).resolve()
if PROJECT_ROOT not in full.parents:
return "Access denied"
return full.read_text(encoding="utf-8")
def list_files(directory: str = ".") -> list[str]:
full = (PROJECT_ROOT / directory).resolve()
if PROJECT_ROOT not in full.parents:
return ["Access denied"]
return [str(p.relative_to(PROJECT_ROOT)) for p in full.iterdir()]
# 2. 本地 RAG(Chroma)
chroma_client = chromadb.PersistentClient(path="./chroma_db")
collection = chroma_client.get_or_create_collection("project_docs")
def search_docs(query: str) -> list[str]:
results = collection.query(query_texts=[query], n_results=3)
return results["documents"][0]
# 3. 连接本地 Qwen
manager = FoundryLocalManager("qwen2.5-7b-instruct")
client = OpenAI(base_url=manager.endpoint, api_key=manager.api_key)
# 4. Agent 循环
def chat(user_input: str) -> str:
response = client.chat.completions.create(
model="qwen2.5-7b-instruct",
messages=[{"role": "user", "content": user_input}],
tools=[
{"type": "function", "function": {"name": "read_file", ...}},
{"type": "function", "function": {"name": "list_files", ...}},
{"type": "function", "function": {"name": "search_docs", ...}}
]
)
# 处理工具调用
tool_calls = response.choices[0].message.tool_calls
if tool_calls:
results = []
for tc in tool_calls:
if tc.function.name == "read_file":
results.append(read_file(**json.loads(tc.function.arguments)))
elif tc.function.name == "list_files":
results.append(list_files(**json.loads(tc.function.arguments)))
elif tc.function.name == "search_docs":
results.append(search_docs(**json.loads(tc.function.arguments)))
# 把工具结果喂回去
response = client.chat.completions.create(
model="qwen2.5-7b-instruct",
messages=[
{"role": "user", "content": user_input},
response.choices[0].message,
*[{"role": "tool", "tool_call_id": tc.id, "content": str(r)}
for tc, r in zip(tool_calls, results)]
]
)
return response.choices[0].message.content
十、本地 Agent 的局限#
Creating Local AI Agents 也诚实地列出了局限:
| 局限 | 缓解 |
|---|---|
| 推理质量 | 选合适的 SLM(Qwen 7B 在工具调用上已不错) |
| 响应速度 | 用 GPU/NPU 加速 |
| 知识范围 | 用 RAG 补充私域知识 |
| 多语言 | 选支持多语言的 SLM |
| 硬件成本 | 8GB RAM 起步,16GB 更舒服 |
十一、关键要点回顾#
| 概念 | 内容 |
|---|---|
| SLM | 小语言模型(本地运行的核心) |
| Foundry Local | 微软的本地推理运行时 |
| Qwen Function Calling | 本地也能调用工具 |
| Chroma | 嵌入式向量数据库(本地 RAG) |
| 本地 MCP | 协议和云无关 |
| 云-端混合 | 敏感/简单本地,复杂/非敏感云端 |
核心策略:让 SLM 编排,让工具干活——SLM 不需要懂所有知识。
十二、参考资源#
Foundry Local 文档:https://learn.microsoft.com/azure/ai-foundry/foundry-local/
Qwen Function Calling:https://qwen.readthedocs.io/
Chroma:https://www.trychroma.com/
十三、下一课预告#
L22 安全的 Agent:审计、加密签名、零信任——怎么让 Agent 的每个动作都可验证、不可抵赖。
为什么重要:当 Agent 帮你执行退款、转账、改数据时,谁来保证它没作弊、没篡改?这就是加密签名的用武之地。
1. 本地 Agent 使用 SLM(小模型)时,「让 SLM 编排,让工具干活」意味着什么?
2. 下列哪类场景是必须把 Agent 跑在本地的典型理由?
3. 即使 Agent 完全跑在本地,为什么工具(如 read_file)仍要做路径沙箱校验?