输入关键词开始搜索

DeepSeek API 本地 Agent 搭建 — V4 Pro + 工具调用实战

为什么选择 DeepSeek

模型                   上下文    价格(输入/输出)       工具调用
deepseek-chat(V3)      128K     ¥1/¥4 每百万 token    ✅
deepseek-reasoner(R1)  128K     ¥4/¥16                ❌ 不支持 Function Calling
deepseek-v4-pro        128K     ¥2/¥8                 ✅ 支持 + 推理链

Agent 场景核心需求:工具调用(Function Calling) + 长上下文 + 推理能力。deepseek-v4-pro 三项都满足。

第一步:获取 API Key

  1. 打开 platform.deepseek.com
  2. 注册 → API Keys → 创建
  3. 复制 sk-xxxxxxxx 保存好

第二步:最小调用验证

curl https://api.deepseek.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-your-key-here" \
  -d '{
    "model": "deepseek-v4-pro",
    "messages": [{"role": "user", "content": "Hello"}],
    "temperature": 0.7
  }'

第三步:Function Calling — Agent 的灵魂

Agent 区别于普通聊天机器人的核心:模型决定”该调哪个工具”,代码执行工具 + 把结果塞回对话

import json
import requests

API_KEY = "sk-your-key-here"
API_URL = "https://api.deepseek.com/v1/chat/completions"

# ── 定义工具 ──
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "read_file",
            "description": "读取本地文件内容",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string", "description": "文件路径"}
                },
                "required": ["path"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "write_file",
            "description": "写入内容到本地文件",
            "parameters": {
                "type": "object",
                "properties": {
                    "path": {"type": "string"},
                    "content": {"type": "string"}
                },
                "required": ["path", "content"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "run_command",
            "description": "执行 shell 命令并返回输出",
            "parameters": {
                "type": "object",
                "properties": {
                    "command": {"type": "string"}
                },
                "required": ["command"]
            }
        }
    }
]

# ── 工具执行器 ──
def execute_tool(name: str, args: dict) -> str:
    if name == "read_file":
        with open(args["path"], "r") as f:
            return f.read()
    elif name == "write_file":
        with open(args["path"], "w") as f:
            f.write(args["content"])
        return f"已写入 {args['path']}"
    elif name == "run_command":
        import subprocess
        result = subprocess.run(
            args["command"], shell=True,
            capture_output=True, text=True, timeout=30
        )
        return result.stdout or result.stderr
    return f"未知工具: {name}"

# ── Agent 循环 ──
SYSTEM_PROMPT = """你是一个本地编程助手,可以读写文件和执行命令。
需要操作文件或执行命令时,调用对应工具。完成用户任务后直接总结结果。"""

def agent_loop(user_input: str, max_turns: int = 10):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_input}
    ]

    for turn in range(max_turns):
        resp = requests.post(API_URL, headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        }, json={
            "model": "deepseek-v4-pro",
            "messages": messages,
            "tools": TOOLS,
            "temperature": 0.3
        }).json()

        msg = resp["choices"][0]["message"]

        # 模型决定回复文本 → 结束
        if msg.get("content") and not msg.get("tool_calls"):
            return msg["content"]

        # 模型决定调工具 → 执行 + 塞回结果
        if msg.get("tool_calls"):
            messages.append(msg)  # 助手消息(含工具调用)

            for tc in msg["tool_calls"]:
                fn_name = tc["function"]["name"]
                fn_args = json.loads(tc["function"]["arguments"])
                print(f"  🔧 调用工具: {fn_name}({fn_args})")

                result = execute_tool(fn_name, fn_args)

                messages.append({
                    "role": "tool",
                    "tool_call_id": tc["id"],
                    "content": result[:2000]  # 截断过长结果
                })

    return "达到最大轮次"

第四步:实战 — 让 Agent 分析项目

result = agent_loop("""
请分析 /home/user/myproject 目录:
1. 列出所有 .cpp 和 .h 文件
2. 统计代码总行数
3. 把结果写入 project_stats.txt
""")
print(result)

执行过程:

  🔧 调用工具: run_command({"command": "find /home/user/myproject -name '*.cpp' -o -name '*.h'"})
  🔧 调用工具: run_command({"command": "find /home/user/myproject ... | xargs wc -l"})
  🔧 调用工具: write_file({"path": "project_stats.txt", "content": "..."})

Agent 回复:
  项目分析完成:
  - C++ 源文件: 15 个
  - 头文件: 8 个
  - 总行数: 4,230 行
  - 结果已写入 project_stats.txt

第五步:扩展更多工具

# 网页搜索
{
    "name": "web_search",
    "description": "搜索网页",
    "parameters": {
        "properties": {
            "query": {"type": "string"}
        }
    }
}

# 执行 Python 代码
{
    "name": "run_python",
    "description": "执行 Python 代码并返回结果",
    "parameters": {
        "properties": {
            "code": {"type": "string"}
        }
    }
}

# Git 操作
{
    "name": "git_commit",
    "description": "提交当前变更",
    "parameters": {
        "properties": {
            "message": {"type": "string"}
        }
    }
}

第六步:安全边界 — 白名单 + 确认机制

DANGEROUS_COMMANDS = ["rm -rf", "sudo", "chmod 777", "> /dev/sda"]

def execute_tool_safe(name: str, args: dict) -> str:
    if name == "run_command":
        cmd = args["command"]
        for dangerous in DANGEROUS_COMMANDS:
            if dangerous in cmd:
                return f"❌ 拒绝执行危险命令: '{dangerous}'"
    return execute_tool(name, args)

# 高风险操作要求确认
HIGH_RISK_TOOLS = {"write_file", "run_command"}

# 在 agent_loop 中:
if fn_name in HIGH_RISK_TOOLS:
    print(f"  ⚠️ 高风险操作: {fn_name}({fn_args})")
    confirm = input("  执行? (y/n): ")
    if confirm.lower() != 'y':
        messages.append({
            "role": "tool",
            "tool_call_id": tc["id"],
            "content": "用户拒绝了此操作"
        })
        continue

关键参数调优

参数推荐值原因
temperature0.3Agent 场景需要确定性,不要创意
top_p0.9配合低 temperature
max_tokens4096足够返回工具参数 + 总结
System Prompt精简省 token,把空间留给工具结果

常见问题

“为何不用 deepseek-reasoner?” → R1 不支持 Function Calling,无法调工具。

“工具结果太长怎么办?”content[:2000] 截断。或让工具返回摘要而非全文。

“如何让 Agent 记住之前的操作?” → messages 数组天然是对话历史,工具结果塞回去模型就能引用。

“如何对接到 Webhook / Discord Bot?” → 把 agent_loop(input) 包装成 HTTP 端点即可:

from flask import Flask, request
app = Flask(__name__)

@app.post("/chat")
def chat():
    user_input = request.json["message"]
    reply = agent_loop(user_input)
    return {"reply": reply}

完整文件结构

my-agent/
├── agent.py          # 核心循环 + 工具定义
├── config.py         # API_KEY + 模型参数
├── tools/
│   ├── files.py      # 文件读写工具
│   ├── shell.py      # 命令执行工具
│   └── web.py        # 搜索工具
└── requirements.txt  # requests, flask