# 企业内网本地 LLM + MCP 工具链试点落地指南

- 原文：<https://www.xda-developers.com/added-these-mcp-servers-local-llm-stack-one-replaces-paid-tool/>
- 来源摘要：`/home/lin/.hermes/projects/hermes-gsummary-workflow/runs/outputs/20260525-090836-I-added-these-MCP-servers-to-my-local-LLM-stack,-and-one-of-them-replaces-a-$249-18224-019688960-summary.md`
- 文档性质：基于原文经验改写的企业内网试点技术文档；安全边界、验收标准、样例文件与运维流程为企业落地补充，不代表原文逐字内容。
- 适用对象：研发效能、运维平台、数据平台、安全合规、内部工具团队。
- 目标阶段：4 周内完成小范围、只读、可审计试点，不直接建设“大而全 AI 平台”。

## 1. 改写后的明确需求

将原请求改写为：

> 基于 XDA 文章中“本地 LLM 通过 MCP 接入搜索、文档、记忆、网页抓取和浏览器自动化”的经验，提炼一份适合企业内网小范围试点的落地技术文档。文档需要包含最小架构、部署步骤、样例配置、3 个以上可执行场景、安全边界、验收标准、失败处理和 4 周推广计划。

改写点：

- 把“提炼”明确为“企业内网小范围试点文档”。
- 把“可操作并落地”拆成架构、命令、样例、验收、失败处理和推广计划。
- 限定第一阶段为只读、低风险、可审计试点，避免直接变成生产级自动化平台。

## 2. 一句话结论

企业内网落地本地 LLM + MCP，不应先追求全自动智能体，而应先建设一套“小模型 + 少量高频 MCP 工具 + 只读数据源 + 人工确认 + 审计日志”的低风险试点环境，用 3–5 个内部场景验证价值后再扩展权限。

## 3. 试点边界

### 3.1 第一阶段目标

- 让本地或内网 LLM 能安全读取内部文档、代码样例、运维资料和网页。
- 让模型通过标准 MCP 接口调用有限工具，而不是把所有系统直接暴露给模型。
- 所有输出只写入本地 `outputs/` 目录或内部测试目录。
- 所有生产动作必须人工确认，不允许模型直接改库、发版、删除资源或发送外部消息。

### 3.2 第一阶段不做什么

- 不给模型生产数据库写权限。
- 不允许自动执行 `kubectl delete`、数据库 DDL、CI/CD 发布、邮件群发等高风险动作。
- 不把客户敏感数据、密钥、Token、生产日志原文直接送入模型。
- 不接入互联网闭源 SaaS，除非安全团队明确批准。
- 不把几十个 MCP server 全部常驻进模型上下文。

## 4. 最小可行架构

```text
员工 / 研发 / 运维
  │
  ├─ Web UI / CLI / IDE 插件
  │
  ▼
内网 LLM 客户端
  - OpenAI-compatible API client
  - MCP client
  - 工具白名单
  - 会话日志脱敏
  │
  ├─ 本地/内网推理服务
  │    - Ollama / vLLM / LM Studio / 企业统一推理网关
  │    - 按场景选择模型
  │
  ├─ MCP Server 层
  │    - SearXNG：内网/公开资料搜索
  │    - Context7 或内部文档 MCP：技术文档查询
  │    - Qdrant：向量检索
  │    - OpenMemory/mem0：对话事实记忆
  │    - Crawl4AI：网页正文抓取
  │    - Playwright：需要登录/点击/分页的浏览器操作
  │
  ├─ 受控数据目录
  │    - data/policies/
  │    - data/runbooks/
  │    - data/code_samples/
  │    - data/incidents/
  │
  └─ 输出与审计
       - outputs/
       - logs/redacted_sessions/
       - audit/tool_calls.jsonl
```

## 5. 推荐组件清单

| 能力 | 推荐组件 | 企业内网用途 | 第一阶段权限 |
|---|---|---|---|
| 模型推理 | Ollama / vLLM / OpenAI-compatible gateway | 提供统一模型接口 | 仅内网访问 |
| 搜索 | SearXNG MCP | 搜索内部知识库、公开资料镜像 | 只读 |
| 最新技术文档 | Context7 或内部文档 MCP | 查询框架/API 文档 | 只读 |
| 向量库 | Qdrant | 文档、代码片段、故障案例语义检索 | 只读检索，受控导入 |
| 记忆层 | OpenMemory/mem0 | 保存用户偏好、团队上下文、环境事实 | 低敏事实，人工可清理 |
| 网页抓取 | Crawl4AI MCP | 把网页转成 Markdown | 只读 |
| 浏览器自动化 | Playwright MCP | 登录内网页面、翻页、导出报告 | 测试账号，只读 |

> 注意：Context7 默认依赖外部托管文档库；如果内网不允许访问外部服务，应替换为内部文档 MCP 或把官方文档离线镜像后导入向量库。

## 6. 目录结构建议

```text
intranet-llm-mcp-pilot/
├── README.md
├── .env.example
├── docker-compose.yml
├── config/
│   ├── mcp.servers.json
│   ├── model-routing.yaml
│   └── security-policy.yaml
├── data/
│   ├── policies/
│   ├── runbooks/
│   ├── code_samples/
│   └── incidents/
├── scripts/
│   ├── ingest_docs.py
│   ├── run_readonly_agent.py
│   └── redact_logs.py
├── outputs/
└── audit/
    └── tool_calls.jsonl
```

## 7. 基础部署步骤

### 7.1 准备 `.env`

```bash
cp .env.example .env
```

`.env.example`：

```bash
LLM_BASE_URL=http://127.0.0.1:11434/v1
LLM_API_KEY=local-not-required
LLM_MODEL=qwen2.5:14b-instruct
QDRANT_URL=http://127.0.0.1:6333
SEARXNG_URL=http://127.0.0.1:8080
AUDIT_LOG=./audit/tool_calls.jsonl
OUTPUT_DIR=./outputs
DATA_ROOT=./data
```

要求：

- `.env` 不提交 Git。
- 生产 Token 只能放企业密钥管理系统或运行时环境变量。
- 测试阶段默认使用本机或内网地址，不暴露公网端口。

### 7.2 启动基础服务

`docker-compose.yml` 示例：

```yaml
services:
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "127.0.0.1:6333:6333"
    volumes:
      - ./qdrant_storage:/qdrant/storage

  searxng:
    image: searxng/searxng:latest
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - ./searxng:/etc/searxng

  crawl4ai:
    image: unclecode/crawl4ai:latest
    ports:
      - "127.0.0.1:11235:11235"
```

启动：

```bash
docker compose up -d
curl http://127.0.0.1:6333/collections
curl http://127.0.0.1:8080
curl http://127.0.0.1:11235/health
```

验收标准：

- 三个服务均只能在本机或内网访问。
- 健康检查返回正常。
- 防火墙未暴露到公网。

失败处理：

- 端口冲突：修改 Compose 端口并同步 `.env`。
- 镜像无法拉取：使用企业私有镜像仓库同步镜像。
- 健康检查失败：先查看容器日志，不进入业务联调。

### 7.3 配置 MCP Server 白名单

`config/mcp.servers.json` 示例：

```json
{
  "servers": {
    "search": {
      "command": "python",
      "args": ["-m", "mcp_searxng"],
      "env": {
        "SEARXNG_URL": "http://127.0.0.1:8080"
      }
    },
    "crawl4ai": {
      "command": "python",
      "args": ["-m", "mcp_crawl4ai"],
      "env": {
        "CRAWL4AI_URL": "http://127.0.0.1:11235"
      }
    },
    "qdrant_readonly": {
      "command": "python",
      "args": ["-m", "mcp_qdrant_readonly"],
      "env": {
        "QDRANT_URL": "http://127.0.0.1:6333"
      }
    }
  },
  "policy": {
    "default": "deny",
    "allowed_tools": [
      "search.query",
      "crawl4ai.fetch_markdown",
      "qdrant_readonly.search"
    ],
    "blocked_tools": [
      "shell.run",
      "database.write",
      "kubernetes.delete",
      "email.send"
    ]
  }
}
```

验收标准：

- 默认拒绝未知工具。
- 工具调用有审计日志。
- MCP server 配置变更需要代码评审或运维审批。

## 8. 场景一：企业制度 / 运维手册问答

### 8.1 目标

让模型基于内网制度、运维手册、故障复盘文档回答问题，并返回引用来源。

### 8.2 样例文件

`data/policies/change_policy.md`：

```markdown
# 变更管理制度

- 生产变更必须提前 1 个工作日提交审批。
- 数据库 DDL 必须提供回滚方案。
- 高峰期禁止执行高风险变更。
- 紧急变更必须在 24 小时内补充复盘。
```

`data/runbooks/postgres_lock_runbook.md`：

```markdown
# PostgreSQL 锁等待处理手册

1. 查询锁等待会话。
2. 联系业务确认影响范围。
3. 禁止直接 kill 生产会话，除非获得 DBA 确认。
4. 处理完成后记录 SQL、时间、影响表和审批人。
```

### 8.3 导入向量库

`scripts/ingest_docs.py` 示例逻辑：

```python
from pathlib import Path

DATA_ROOT = Path("data")

for path in DATA_ROOT.rglob("*.md"):
    text = path.read_text(encoding="utf-8")
    print({
        "path": str(path),
        "chars": len(text),
        "collection": "internal_docs"
    })
    # 实际项目中：分块、生成 embedding、写入 Qdrant
```

运行：

```bash
python scripts/ingest_docs.py
```

### 8.4 测试问题

```text
如果我需要在生产库执行 DDL，需要准备什么？
```

期望输出形态：

```text
结论：生产 DDL 至少需要提前审批、回滚方案和风险窗口确认。
依据：
- data/policies/change_policy.md：数据库 DDL 必须提供回滚方案。
- data/policies/change_policy.md：生产变更必须提前 1 个工作日提交审批。
注意：如果是紧急变更，需 24 小时内补充复盘。
```

验收标准：

- 回答必须包含引用文件路径。
- 找不到依据时必须回答“未在知识库中找到明确依据”。
- 不允许把模型常识伪装成公司制度。

失败处理：

- 引用缺失：降低回答置信度，强制返回“需要人工确认”。
- 答案和制度冲突：优先制度原文，并记录 bad case。
- 文档过旧：在输出中标记“来源日期未知/可能过期”。

## 9. 场景二：本地代码审查助手

### 9.1 目标

让模型对代码样例做只读审查，输出风险点、证据行、修改建议，不直接改代码。

### 9.2 样例代码

`data/code_samples/user_api.py`：

```python
import sqlite3


def get_user(user_id):
    conn = sqlite3.connect("users.db")
    sql = f"SELECT * FROM users WHERE id = {user_id}"
    return conn.execute(sql).fetchone()
```

### 9.3 审查提示词

```text
你是企业内部代码审查助手。请只读审查下面代码：
1. 找出安全、可靠性、可维护性问题。
2. 每个问题必须指出证据行或代码片段。
3. 给出最小修改建议。
4. 不要直接写文件，不要执行命令。
```

### 9.4 期望输出形态

```text
风险等级：高
问题 1：SQL 注入风险
证据：f"SELECT * FROM users WHERE id = {user_id}"
建议：改为参数化查询。

问题 2：SELECT * 可维护性差
证据：SELECT *
建议：显式列出需要字段。

问题 3：连接未关闭
证据：sqlite3.connect 后未使用上下文管理器
建议：使用 with sqlite3.connect(...) as conn。
```

验收标准：

- 能识别 SQL 注入。
- 能指出具体证据。
- 只输出建议，不改文件。
- 不把样例数据库当成真实生产库访问。

失败处理：

- 如果模型直接生成大段重构：要求重新输出“最小修改建议”。
- 如果模型要求访问生产数据库：拦截并记录工具调用。
- 如果模型误报过多：建立 bad case 集，加入 few-shot 示例或规则过滤。

## 10. 场景三：网页抓取 + 内部研究简报

### 10.1 目标

让模型通过 SearXNG 搜索主题，再用 Crawl4AI 抓取网页正文，生成带来源的内部简报。

### 10.2 输入样例

```text
请调研最近 MCP 生态中适合企业内网使用的工具，输出 5 条以内简报，每条附来源 URL。
```

### 10.3 工具调用策略

```yaml
search:
  max_results: 5
  allow_domains:
    - github.com
    - modelcontextprotocol.io
    - docs.anthropic.com
    - microsoft.github.io
crawl:
  max_pages: 5
  output_format: markdown
  timeout_seconds: 20
summary:
  require_source_url: true
  max_bullets: 5
```

### 10.4 期望输出形态

```text
主题：MCP 企业内网工具简报

1. Playwright MCP 适合需要浏览器会话的自动化场景。
   来源：https://...
   内网适配建议：第一阶段只读账号、禁止提交表单。

2. Context7 可用于开发文档查询，但如果内网禁止外连，需要离线替代。
   来源：https://...
   内网适配建议：用内部文档索引替代外部托管服务。
```

验收标准：

- 每条结论必须有 URL。
- 抓取失败不能编造内容。
- 搜索与抓取结果进入审计日志。
- 只生成简报，不自动发送邮件或群消息。

失败处理：

- 搜索结果质量低：限制可信域名或改用内部镜像源。
- 页面抓取失败：返回失败原因和 URL，不用搜索摘要冒充全文。
- 多来源冲突：标记冲突并要求人工确认。

## 11. 场景四：只读运维日报 Agent

### 11.1 目标

让模型读取脱敏后的日志摘要、巡检结果和变更记录，生成每日运维日报。

### 11.2 输入文件

`data/incidents/daily_check_2026-05-25.md`：

```markdown
# 每日巡检

- PostgreSQL 主库连接数峰值：75%
- Redis 内存使用率峰值：68%
- Kubernetes 异常重启 Pod：2 个
- 当日生产变更：1 个，已审批
- 未关闭告警：3 个
```

### 11.3 只读 Agent 脚本骨架

`scripts/run_readonly_agent.py`：

```python
from pathlib import Path

DATA_ROOT = Path("data/incidents").resolve()
OUTPUT_DIR = Path("outputs").resolve()
OUTPUT_DIR.mkdir(exist_ok=True)


def safe_read(relative_path: str) -> str:
    path = (DATA_ROOT / relative_path).resolve()
    if not str(path).startswith(str(DATA_ROOT)):
        raise ValueError("path outside allowed data root")
    return path.read_text(encoding="utf-8")


def write_report(name: str, content: str) -> Path:
    path = (OUTPUT_DIR / name).resolve()
    if not str(path).startswith(str(OUTPUT_DIR)):
        raise ValueError("path outside output dir")
    path.write_text(content, encoding="utf-8")
    return path


source = safe_read("daily_check_2026-05-25.md")
report = f"""# 运维日报\n\n## 原始巡检摘要\n\n{source}\n\n## 待人工确认\n\n- 异常重启 Pod 的业务影响范围\n- 未关闭告警是否需要升级\n"""
print(write_report("ops_daily_2026-05-25.md", report))
```

运行：

```bash
python scripts/run_readonly_agent.py
```

验收标准：

- 只能读取 `data/incidents/`。
- 只能写入 `outputs/`。
- 报告中必须列出待人工确认事项。
- 不允许自动重启服务、关闭告警或修改监控规则。

失败处理：

- 路径越界：直接失败并写审计日志。
- 输入文件缺失：生成“缺少数据”报告，而不是猜测。
- 模型建议生产动作：标记为“需要人工审批”，不执行。

## 12. 记忆层设计：OpenMemory/mem0 与 Qdrant 分工

### 12.1 适合进入 OpenMemory/mem0 的内容

- 用户偏好：例如“DBA 团队要求 SQL 审查必须包含回滚建议”。
- 环境事实：例如“测试集群 kubeconfig 路径为内部跳板机只读配置”。
- 团队约定：例如“日报默认按业务线聚合”。

### 12.2 不适合进入 OpenMemory/mem0 的内容

- 大量制度全文。
- 代码仓库全部文件。
- 原始日志。
- 客户数据、密钥、Token。
- 会快速过期的工单状态。

### 12.3 适合进入 Qdrant 的内容

- 内部文档、SOP、Runbook。
- 脱敏故障复盘。
- 代码片段和设计文档。
- FAQ 和历史问题库。

### 12.4 清理策略

```text
每周：抽样检查新增记忆是否包含敏感信息。
每两周：清理重复、冲突、过期记忆。
每月：导出记忆审计报告，由系统负责人确认。
```

验收标准：

- 记忆写入必须可追踪来源会话。
- 用户可查看、修改、删除自己的记忆。
- 敏感字段进入记忆层时必须拦截。

## 13. 企业安全与合规边界

### 13.1 数据边界

- 只允许读取试点目录和批准的数据源。
- 敏感日志必须先脱敏再进入向量库。
- 输出报告不得包含密钥、Token、身份证号、手机号、客户原始数据。
- 所有工具调用写入 `audit/tool_calls.jsonl`。

### 13.2 权限边界

- 第一阶段只读。
- 第二阶段最多允许生成变更建议，不允许自动执行。
- 第三阶段如需执行动作，必须接审批系统和回滚方案。
- 高风险工具默认禁用：shell、数据库写入、Kubernetes 删除、发邮件、发群消息。

### 13.3 模型边界

- 内网敏感数据只能使用企业批准模型。
- 小模型输出必须有人审查，不能作为最终生产决策。
- 对没有来源引用的结论降低置信度。
- 不把外部文章经验直接当作企业制度。

## 14. 审计日志格式

`audit/tool_calls.jsonl` 示例：

```json
{"time":"2026-05-25T09:30:00+08:00","user":"alice","tool":"qdrant.search","input_hash":"sha256:...","allowed":true,"reason":"readonly retrieval"}
{"time":"2026-05-25T09:31:12+08:00","user":"alice","tool":"database.write","input_hash":"sha256:...","allowed":false,"reason":"blocked in phase 1"}
```

最低要求：

- 记录用户、时间、工具名、是否允许、拒绝原因。
- 输入内容只保存 hash 或脱敏摘要。
- 审计日志不可由模型直接修改。

## 15. 4 周推广计划

### 第 1 周：单机验证

目标：跑通模型、MCP client、Qdrant、SearXNG、Crawl4AI。

交付物：

- 本地 Compose 环境。
- 3 份样例文档导入 Qdrant。
- 10 条测试问题与预期答案。
- 工具调用审计日志。

通过标准：

- 80% 以上测试问题能返回带来源答案。
- 所有失败都能明确标记“不确定/未找到”。
- 无越权读取或写入。

### 第 2 周：小团队试点

目标：让 3–5 名内部用户试用文档问答、代码审查、网页简报。

交付物：

- bad case 列表。
- 用户反馈表。
- 敏感信息拦截记录。
- 工具白名单调整记录。

通过标准：

- 用户能独立完成至少 2 个场景。
- 无敏感数据进入日志或记忆层。
- 误导性回答有人工复核机制。

### 第 3 周：策略固化

目标：把工具权限、数据目录、日志保留、审批流程固化为配置和制度。

交付物：

- `security-policy.yaml`。
- MCP server 变更审批流程。
- 记忆清理流程。
- 模型输出免责声明模板。

通过标准：

- 新工具默认拒绝。
- 高风险动作全部需要审批。
- 权限策略可由安全团队审查。

### 第 4 周：有限扩展

目标：扩大到 1–2 个团队，但不增加生产写权限。

交付物：

- 团队级知识库。
- 场景验收报告。
- 成本评估。
- 是否进入第二阶段的决策建议。

通过标准：

- 至少一个场景每周节省明确人工时间。
- 风险事件为 0。
- 有继续投入的业务负责人。

## 16. 管理层摘要

- 这不是一次“买 AI 工具”的项目，而是一次“内网模型工具化能力”的小范围验证。
- 第一阶段重点不是智能，而是安全边界、可审计、可复现。
- 推荐从文档问答、代码审查、内部研究简报、运维日报四个只读场景开始。
- 如果 4 周内无法证明节省时间或降低重复劳动，不建议继续扩大。
- 如果验证通过，再讨论统一模型网关、权限系统、知识库治理和更高阶自动化。

## 17. 最小验收清单

- [ ] 模型服务仅内网访问。
- [ ] MCP server 使用白名单。
- [ ] 默认拒绝未知工具。
- [ ] 试点数据目录明确。
- [ ] 输出目录明确。
- [ ] 工具调用有审计日志。
- [ ] 回答包含来源引用。
- [ ] 找不到依据时不编造。
- [ ] 记忆层不保存敏感数据。
- [ ] 生产动作必须人工确认。
- [ ] 至少完成 3 个可演示场景。
- [ ] 形成 bad case 与改进记录。
- [ ] 安全团队确认第一阶段边界。

## 18. 试点完成后的决策

### 可以继续推进的信号

- 用户每周真实使用，而不是只在演示时使用。
- 输出能稳定引用来源。
- 工具调用没有越权。
- 能发现代码、文档或运维流程中的实际问题。
- 维护成本低于节省的人力成本。

### 应该停止或收缩的信号

- 大量回答无来源或经常编造。
- 用户频繁尝试让模型执行生产动作。
- 日志和记忆层反复出现敏感数据。
- 工具链维护成本过高。
- 场景价值只是“看起来很酷”，没有明确节省时间。

## 19. 附录：第一阶段推荐工具数量

原文作者的经验是：本地小模型不适合常驻过多工具，5–6 个高频工具更稳。企业内网试点可以采用以下初始组合：

```text
必选：
1. 内部文档检索 MCP
2. Qdrant 只读检索 MCP
3. Crawl4AI 网页抓取 MCP
4. SearXNG 搜索 MCP

可选：
5. Playwright MCP，只给测试账号和只读页面
6. OpenMemory/mem0，只保存低敏偏好和环境事实
```

暂不建议第一阶段接入：

- 数据库写入 MCP。
- Kubernetes 管理 MCP。
- 自动发版工具。
- 自动发邮件/群消息工具。
- 能访问全盘文件系统的 shell MCP。
