AIKnowledge Base

ENGINEERING KNOWLEDGE BASE

AI 知识库

从 Agent 基础到生产治理,把分散知识组织成可检索、可复用、可持续更新的工程体系。

核心主题Agent · MCP · RAG
内容形态原理 · 实现 · 边界
访问范围全部公开

AI / LLM / Agent 知识库#

这是一份面向工程实践的 AI 知识体系,按概念、机制、实现与治理组织。正文默认公开,不再把知识点包装成答题卡或隐藏答案。

阅读方式:先从核心知识建立共同语言,再进入进阶工程与专题。每个主题都尽量保留架构图、代码示例、边界条件与失败模式。

核心知识#

一、AI Agent 基础#

1.1 一句话定义#

Agent = 能够基于环境感知、工具调用和迭代决策完成任务的 LLM 程序。区别于一次性 prompt-response,Agent 具备「循环」——根据中间结果调整下一步动作。

1.2 最小工作循环(ASCII 架构)#

+---------+    感知    +---------+    推理    +---------+    执行    +---------+
| 环境/用户 | ────────→ |  上下文  | ────────→ |  LLM   | ────────→ |  工具   |
| 输入    |           | Context |           |  大脑   |           | Action |
+---------+           +---------+           +---------+           +---------+
                                              ↑                       |
                                              |         反馈           |
                                              └───────────────────────┘
                    感知 → 推理 → 执行 → 反馈 → 感知 → ...

1.3 三个关键能力#

能力 说明 失败模式
工具使用 调用 shell、文件读写、HTTP、MCP server 工具调用格式错乱、循环重试
长上下文管理 在工具结果膨胀时压缩关键信息 context 溢出、信息丢失
自我修正 测试失败后修改方案重试 在错误方向上越走越远

1.4 Agent 与 Workflow / Prompt 的选型链#

Prompt Engineering → Context Engineering → Workflow → Agent
     单轮优化            每步看啥            固定步骤         自主循环

选型不是越高级越好:能 Prompt 解决的不上 Workflow,能 Workflow 解决的不上 Agent。

1.5 代码示例 1:Python 最小 ReAct Agent#

依赖:openai

pip install openai
export OPENAI_API_KEY="sk-xxxxxxxx"
Python 代码示例
# ai_agent_minimal.py
# 实现 Agent 最小循环:感知 → 推理 → 执行 → 反馈
import json
import os
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY", "sk-fake"))

SYSTEM_PROMPT = """你是 ReAct Agent。每一步必须输出 JSON:
{"thought": "当前思考", "action": {"tool": "read_file", "args": {"path": "..."}}}
若已获得足够信息,输出:{"thought": "...", "final_answer": "..."}"""

def read_file(path: str) -> str:
    try:
        with open(path, "r", encoding="utf-8") as f:
            return f.read()
    except Exception as e:
        return f"[error] {e}"

def run_agent(task: str, max_steps: int = 5) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": task},
    ]
    for step in range(max_steps):
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            response_format={"type": "json_object"},
        )
        content = resp.choices[0].message.content
        parsed = json.loads(content)
        print(f"[step {step + 1}] {parsed.get('thought', '')}")

        if "final_answer" in parsed:
            return parsed["final_answer"]

        action = parsed.get("action", {})
        if action.get("tool") == "read_file":
            result = read_file(action["args"]["path"])
            messages.append({"role": "assistant", "content": content})
            messages.append({"role": "user", "content": f"[observation] {result}"})

    return "[timeout] 未能在限定步数内完成"

if __name__ == "__main__":
    # 运行前请在当前目录创建 workspace/note.txt
    print(run_agent("请读取 ./workspace/note.txt 并总结内容"))

1.6 代码示例 2:Python Agent 状态机(限制活动路径)#

1.6 代码示例 2:Python Agent 状态机(限制活动路径)
# ai_agent_state_machine.py
# 用状态机约束 Agent 活动路径,防止乱跑
from enum import Enum, auto

class AgentState(Enum):
    IDLE = auto()
    PERCEIVE = auto()
    REASON = auto()
    EXECUTE = auto()
    FEEDBACK = auto()
    DONE = auto()

class GuardedAgent:
    TRANSITIONS = {
        AgentState.IDLE: [AgentState.PERCEIVE],
        AgentState.PERCEIVE: [AgentState.REASON],
        AgentState.REASON: [AgentState.EXECUTE, AgentState.DONE],
        AgentState.EXECUTE: [AgentState.FEEDBACK],
        AgentState.FEEDBACK: [AgentState.PERCEIVE],
    }

    def __init__(self):
        self.state = AgentState.IDLE
        self.context = []

    def transit(self, target: AgentState):
        if target not in self.TRANSITIONS[self.state]:
            raise ValueError(f"非法状态转移: {self.state.name} -> {target.name}")
        self.state = target
        print(f"state -> {target.name}")

    def run(self, task: str, max_steps: int = 5):
        self.transit(AgentState.PERCEIVE)
        self.context.append(task)
        for _ in range(max_steps):
            self.transit(AgentState.REASON)
            action = "read_file" if "read" in task else "search"
            self.transit(AgentState.EXECUTE)
            obs = f"完成 {action}"
            self.transit(AgentState.FEEDBACK)
            self.context.append(obs)
            if len(self.context) > 3:
                self.transit(AgentState.DONE)
                return "done"
        return "timeout"

if __name__ == "__main__":
    agent = GuardedAgent()
    print(agent.run("读取配置文件并分析"))

1.7 代码示例 3:Go 最小 ReAct Agent(标准库)#

1.7 代码示例 3:Go 最小 ReAct Agent(标准库)
// ai_agent_minimal.go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

const openAIURL = "https://api.openai.com/v1/chat/completions"

var systemPrompt = `你是 ReAct Agent。每一步必须输出 JSON:
{"thought": "...", "action": {"tool": "read_file", "args": {"path": "..."}}}
若已获得足够信息,输出:{"thought": "...", "final_answer": "..."}`

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

func readFile(path string) string {
	b, err := os.ReadFile(path)
	if err != nil {
		return fmt.Sprintf("[error] %v", err)
	}
	return string(b)
}

func chat(messages []Message) (map[string]interface{}, error) {
	body, _ := json.Marshal(map[string]interface{}{
		"model":           "gpt-4o-mini",
		"messages":        messages,
		"response_format": map[string]string{"type": "json_object"},
	})
	req, _ := http.NewRequest("POST", openAIURL, bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("OPENAI_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	var result map[string]interface{}
	json.Unmarshal(data, &result)
	return result, nil
}

func runAgent(task string, maxSteps int) string {
	messages := []Message{
		{Role: "system", Content: systemPrompt},
		{Role: "user", Content: task},
	}
	for step := 0; step < maxSteps; step++ {
		resp, err := chat(messages)
		if err != nil {
			return "[error] " + err.Error()
		}
		content := resp["choices"].([]interface{})[0].(map[string]interface{})["message"].(map[string]interface{})["content"].(string)
		var parsed map[string]interface{}
		json.Unmarshal([]byte(content), &parsed)
		fmt.Printf("[step %d] %s\n", step+1, parsed["thought"])
		if final, ok := parsed["final_answer"]; ok {
			return final.(string)
		}
		action := parsed["action"].(map[string]interface{})
		if action["tool"] == "read_file" {
			args := action["args"].(map[string]interface{})
			result := readFile(args["path"].(string))
			messages = append(messages, Message{Role: "assistant", Content: content})
			messages = append(messages, Message{Role: "user", Content: "[observation] " + result})
		}
	}
	return "[timeout] 未能在限定步数内完成"
}

func main() {
	fmt.Println(runAgent("请读取 ./workspace/note.txt 并总结内容", 5))
}

二、Agent 任务规划#

2.1 三要素#

要素 含义 示例
Plan 完成目标的整体步骤序列 调研 → 检索 → 分析 → 报告
Step Plan 中的单个可执行单元 "搜索 2025 年 AI Agent 融资事件"
Type Step 的能力标签 researchcodingwebsearch

2.2 三种经典规划模式(ASCII 对比)#

ReAct:                    Plan-and-Execute:          Reflexion:
Thought → Action → Obs     Planner ──→ Executor       Execute → Evaluate
   ↑          ↓                ↑↓ (replan)              ↓ 反思
   └──────────┘              循环直到完成               Update Policy
模式 核心循环 适用场景
ReAct Thought → Action → Observation 单步决策、工具调用频繁、需要即时反馈
Plan-and-Execute Planner → Executor → (Replan) 目标明确、步骤清晰、可预先规划
Reflexion 执行 → 评估 → 反思 → 更新策略 需要迭代优化、从失败中学习

2.3 设计原则#

  1. 原子性:每个 Step 只做一件事,输出可验证;
  2. 可观测:每个 Step 有状态(todo / running / done / failed);
  3. 可回滚:失败时能重试、跳过或重新规划;
  4. 动态调整:执行中遇到新信息时允许 replan;
  5. 并行友好:无依赖的 Step 可以并发执行。

2.4 代码示例 1:Python Plan / Step / Type 规划器#

2.4 代码示例 1:Python Plan / Step / Type 规划器
# task_planner.py
# 用 Plan / Step / Type 三种模式执行同一研究任务
from dataclasses import dataclass, field
from enum import Enum
from typing import List, Callable

class StepType(Enum):
    WEBSEARCH = "websearch"
    RESEARCH = "research"
    PROCESSING = "processing"

@dataclass
class Step:
    id: str
    step_type: StepType
    description: str
    status: str = "todo"
    result: str = ""

@dataclass
class Plan:
    goal: str
    steps: List[Step] = field(default_factory=list)

class TaskPlanner:
    def __init__(self):
        self.handlers: dict[StepType, Callable[[Step], str]] = {
            StepType.WEBSEARCH: lambda s: f"搜索完成:{s.description}",
            StepType.RESEARCH: lambda s: f"研究完成:{s.description}",
            StepType.PROCESSING: lambda s: f"汇总完成:{s.description}",
        }

    def create_plan(self, goal: str) -> Plan:
        return Plan(goal=goal, steps=[
            Step("s1", StepType.WEBSEARCH, f"搜索 {goal} 的背景信息"),
            Step("s2", StepType.RESEARCH, f"深度分析 {goal}"),
            Step("s3", StepType.PROCESSING, "汇总生成报告"),
        ])

    def run_react(self, goal: str, max_steps: int = 5) -> str:
        steps = []
        for i in range(max_steps):
            thought = f"Step {i+1}: 继续推进目标"
            action = "websearch" if i == 0 else ("research" if i == 1 else "report")
            obs = f"观察到 {action} 的结果"
            steps.append(f"{thought} -> {action} -> {obs}")
            if action == "report":
                break
        return "\n".join(steps)

    def run_plan_execute(self, plan: Plan) -> str:
        for step in plan.steps:
            step.status = "running"
            step.result = self.handlers[step.step_type](step)
            step.status = "done"
        return self._report(plan)

    def run_typed(self, plan: Plan, max_parallel: int = 2) -> str:
        while any(s.status in ("todo", "running") for s in plan.steps):
            batch = [s for s in plan.steps if s.status == "todo"][:max_parallel]
            for step in batch:
                step.status = "running"
                step.result = self.handlers[step.step_type](step)
                step.status = "done"
        return self._report(plan)

    def _report(self, plan: Plan) -> str:
        lines = [f"# {plan.goal}", ""]
        for s in plan.steps:
            lines.append(f"- [{s.status}] {s.result}")
        return "\n".join(lines)

if __name__ == "__main__":
    planner = TaskPlanner()
    goal = "2025 年国内 AI Agent 市场格局"
    plan = planner.create_plan(goal)
    print("=== ReAct ===")
    print(planner.run_react(goal))
    print("\n=== Plan-and-Execute ===")
    print(planner.run_plan_execute(plan))
    print("\n=== Plan/Step/Type ===")
    print(planner.run_typed(planner.create_plan(goal)))

2.5 代码示例 2:Python Replan 触发逻辑#

2.5 代码示例 2:Python Replan 触发逻辑
# replan_trigger.py
# 当 Step 失败或观察到新信息时触发重新规划
import json

def execute_step(step: dict) -> dict:
    # 模拟执行,90% 成功
    import random
    if random.random() < 0.9:
        return {"status": "done", "output": f"完成 {step['name']}"}
    return {"status": "failed", "error": "依赖未就绪"}

def plan_and_execute(goal: str, initial_plan: list, max_replan: int = 3):
    plan = initial_plan
    context = {"goal": goal, "observations": []}
    for attempt in range(max_replan + 1):
        print(f"\n[plan attempt {attempt + 1}] plan={json.dumps(plan, ensure_ascii=False)}")
        for step in plan[:]:
            result = execute_step(step)
            context["observations"].append({step["name"]: result})
            if result["status"] == "failed":
                print(f"  step {step['name']} failed, trigger replan")
                plan = replan(context)
                break
        else:
            return f"成功完成:{goal}"
    return f"失败:{goal}"

def replan(context: dict) -> list:
    # 简化版:把未完成的步骤和新增补偿步骤加入新计划
    remaining = [s for s in context["observations"][-3:] if isinstance(s, dict) and list(s.values())[0].get("status") == "failed"]
    new_plan = [{"name": "fix_dependency"}, {"name": "retry_main_task"}]
    return new_plan

if __name__ == "__main__":
    print(plan_and_execute("部署服务", [{"name": "build"}, {"name": "deploy"}]))

2.6 代码示例 3:Java 任务规划器#

2.6 代码示例 3:Java 任务规划器
// TaskPlanner.java
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

class TaskPlanner {
    enum StepType { WEBSEARCH, RESEARCH, PROCESSING }

    static class Step {
        String id;
        StepType type;
        String description;
        String status = "todo";
        String result = "";
        Step(String id, StepType type, String description) {
            this.id = id; this.type = type; this.description = description;
        }
    }

    static class Plan {
        String goal;
        List<Step> steps = new ArrayList<>();
    }

    private final Map<StepType, Function<Step, String>> handlers = Map.of(
        StepType.WEBSEARCH, s -> "搜索完成:" + s.description,
        StepType.RESEARCH, s -> "研究完成:" + s.description,
        StepType.PROCESSING, s -> "汇总完成:" + s.description
    );

    Plan createPlan(String goal) {
        Plan plan = new Plan();
        plan.goal = goal;
        plan.steps.add(new Step("s1", StepType.WEBSEARCH, "搜索 " + goal + " 的背景信息"));
        plan.steps.add(new Step("s2", StepType.RESEARCH, "深度分析 " + goal));
        plan.steps.add(new Step("s3", StepType.PROCESSING, "汇总生成报告"));
        return plan;
    }

    String runPlanExecute(Plan plan) {
        for (Step step : plan.steps) {
            step.status = "running";
            step.result = handlers.get(step.type).apply(step);
            step.status = "done";
        }
        return report(plan);
    }

    String report(Plan plan) {
        StringBuilder sb = new StringBuilder("# ").append(plan.goal).append("\n\n");
        for (Step s : plan.steps) sb.append("- [").append(s.status).append("] ").append(s.result).append("\n");
        return sb.toString();
    }

    public static void main(String[] args) {
        TaskPlanner planner = new TaskPlanner();
        System.out.println(planner.runPlanExecute(planner.createPlan("2025 年国内 AI Agent 市场格局")));
    }
}

三、Agent 编排#

3.1 多 Agent 协作模式#

Orchestrator + Worker 模式:

       +-----------------+
       |  Orchestrator   |
       |  (编排器/调度器) |
       +--------+--------+
                |
    +-----------+-----------+
    ↓           ↓           ↓
+-------+   +-------+   +-------+
|WorkerA|   |WorkerB|   |WorkerC|
| 检索  |   | 编码  |   | 审查  |
+-------+   +-------+   +-------+
    |           |           |
    +-----------+-----------+
                ↓
         +-------------+
         |  汇总/输出  |
         +-------------+

3.2 多 Agent 状态同步难点#

分布式状态同步:
AgentA --\              /--> AgentB
         |--> State Store <--|
AgentC --/              \--> AgentD
         (Redis / DB / Message Bus)

挑战:状态同步、上下文管理、失败传播、成本爆炸。需要明确的编排协议、状态共享机制和停止条件。

3.3 代码示例 1:Python Orchestrator + Worker#

3.3 代码示例 1:Python Orchestrator + Worker
# orchestrator_worker.py
# Orchestrator 按步骤调度多个 Worker 完成复合任务
import json
import os
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY", "sk-fake-key"))

def query_weather(city: str) -> dict:
    return {"city": city, "temperature": 28, "condition": "sunny"}

def send_email(to: str, subject: str, body: str) -> dict:
    return {"success": True, "message_id": f"msg-{hash(to + subject) & 0xFFFFFF:06x}"}

WORKERS = {
    "query_weather": query_weather,
    "send_email": send_email,
}

SYSTEM_PROMPT = """你是 Orchestrator。用户请求可能需要多个 Worker 协作。
每一步输出 JSON:
{"thought": "...", "step": {"worker": "query_weather", "args": {"city": "..."}}}
若已完成所有步骤,输出:{"thought": "...", "final_answer": "..."}"""

def orchestrate(task: str, max_steps: int = 5) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": task},
    ]
    for step in range(max_steps):
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            response_format={"type": "json_object"},
        )
        content = resp.choices[0].message.content
        parsed = json.loads(content)
        print(f"[step {step + 1}] {parsed.get('thought', '')}")

        if "final_answer" in parsed:
            return parsed["final_answer"]

        step_plan = parsed.get("step", {})
        worker_name = step_plan.get("worker")
        args = step_plan.get("args", {})
        fn = WORKERS.get(worker_name)
        try:
            result = fn(**args) if fn else {"error": f"unknown worker {worker_name}"}
        except Exception as e:
            result = {"error": str(e)}

        messages.append({"role": "assistant", "content": content})
        messages.append({"role": "user", "content": f"[observation] {json.dumps(result, ensure_ascii=False)}"})

    return "[timeout] 未能在限定步数内完成"

if __name__ == "__main__":
    answer = orchestrate("查询北京天气,并将结果通过邮件发送给 bob@example.com")
    print("\n[final]", answer)

3.4 代码示例 2:Python Cross-review 双 Agent 审查#

3.4 代码示例 2:Python Cross-review 双 Agent 审查
# cross_review.py
# 双 Agent 并行审查,互相校验结果
def agent_a_generate(requirement: str) -> str:
    return f"方案 A:基于 {requirement} 设计 REST API"

def agent_b_review(proposal: str) -> dict:
    issues = []
    if "鉴权" not in proposal:
        issues.append("缺少鉴权设计")
    if "限流" not in proposal:
        issues.append("缺少限流设计")
    return {"approved": len(issues) == 0, "issues": issues}

def agent_c_revise(proposal: str, review: dict) -> str:
    if review["approved"]:
        return proposal
    return proposal + "\n补充:增加 JWT 鉴权 + 令牌桶限流"

def cross_review(requirement: str) -> str:
    proposal = agent_a_generate(requirement)
    review = agent_b_review(proposal)
    final = agent_c_revise(proposal, review)
    return final

if __name__ == "__main__":
    print(cross_review("设计用户登录接口"))

3.5 代码示例 3:Go 编排器(标准库)#

3.5 代码示例 3:Go 编排器(标准库)
// orchestrator_worker.go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

const openAIURL = "https://api.openai.com/v1/chat/completions"

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

func queryWeather(city string) map[string]interface{} {
	return map[string]interface{}{"city": city, "temperature": 28, "condition": "sunny"}
}

func sendEmail(to, subject, body string) map[string]interface{} {
	return map[string]interface{}{"success": true, "message_id": fmt.Sprintf("msg-%d", hashStr(to+subject)&0xFFFFFF)}
}

func hashStr(s string) int {
	h := 0
	for _, c := range s {
		h = 31*h + int(c)
	}
	return h
}

var workers = map[string]func(map[string]interface{}) map[string]interface{}{
	"query_weather": func(args map[string]interface{}) map[string]interface{} {
		return queryWeather(args["city"].(string))
	},
	"send_email": func(args map[string]interface{}) map[string]interface{} {
		return sendEmail(args["to"].(string), args["subject"].(string), args["body"].(string))
	},
}

var systemPrompt = `你是 Orchestrator。用户请求可能需要多个 Worker 协作。
每一步输出 JSON:
{"thought": "...", "step": {"worker": "query_weather", "args": {"city": "..."}}}
若已完成所有步骤,输出:{"thought": "...", "final_answer": "..."}`

func chat(messages []Message) (map[string]interface{}, error) {
	body, _ := json.Marshal(map[string]interface{}{
		"model": "gpt-4o-mini", "messages": messages,
		"response_format": map[string]string{"type": "json_object"},
	})
	req, _ := http.NewRequest("POST", openAIURL, bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+os.Getenv("OPENAI_API_KEY"))
	req.Header.Set("Content-Type", "application/json")
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
	var result map[string]interface{}
	json.Unmarshal(data, &result)
	return result, nil
}

func orchestrate(task string, maxSteps int) string {
	messages := []Message{{Role: "system", Content: systemPrompt}, {Role: "user", Content: task}}
	for step := 0; step < maxSteps; step++ {
		resp, err := chat(messages)
		if err != nil {
			return "[error] " + err.Error()
		}
		content := resp["choices"].([]interface{})[0].(map[string]interface{})["message"].(map[string]interface{})["content"].(string)
		var parsed map[string]interface{}
		json.Unmarshal([]byte(content), &parsed)
		fmt.Printf("[step %d] %s\n", step+1, parsed["thought"])
		if final, ok := parsed["final_answer"]; ok {
			return final.(string)
		}
		stepPlan := parsed["step"].(map[string]interface{})
		workerName := stepPlan["worker"].(string)
		args := stepPlan["args"].(map[string]interface{})
		var result map[string]interface{}
		if fn, ok := workers[workerName]; ok {
			result = fn(args)
		} else {
			result = map[string]interface{}{"error": "unknown worker " + workerName}
		}
		messages = append(messages, Message{Role: "assistant", Content: content})
		resJSON, _ := json.Marshal(result)
		messages = append(messages, Message{Role: "user", Content: "[observation] " + string(resJSON)})
	}
	return "[timeout] 未能在限定步数内完成"
}

func main() {
	fmt.Println(orchestrate("查询北京天气,并将结果通过邮件发送给 bob@example.com", 5))
}

四、MCP / 工具协议#

4.1 一句话定义#

MCP(Model Context Protocol)是 AI Agent 与外部世界交互的「USB-C 接口」:把本地文件、数据库、浏览器、GitHub、内部 API 等能力封装成模型可调用的工具集。

4.2 协议生命周期与架构(ASCII)#

Host / Agent              MCP Client             MCP Server          外部系统
     |                        |                       |                  |
     | 1. 发现能力 / 版本       |                       |                  |
     |----------------------->| server/discover      |                  |
     |                        |--------------------->|                  |
     | 2. tools/list           |<--------------------|                  |
     |<-----------------------|  tool schema         |                  |
     |                        |                       |                  |
     | 3. 将可用 tool schema 交给模型(tool calling)                |
     | 4. 模型返回 tool call(name + arguments)                     |
     |                        | 5. tools/call         |                  |
     |                        |---------------------->| 调用真实能力      |
     |                        | 6. structured result  |<-----------------|
     | 7. runtime 把结果回填给模型,继续循环或输出最终答案            |

重点不是某个固定 API 名,而是发现 → 取 schema → 模型选择工具 → 宿主执行 → 结果回填。旧版 MCP 客户端常见 initialize;新版规范以能力/版本发现与 tools/listtools/call 为核心。MCP 只标准化 Host 与 Server 的上下文和能力交换,模型如何选择工具仍由宿主接入的模型 API 决定。

4.3 Tool / Resource / Prompt 的区别#

类型 定位 副作用
Tool 让模型"做一件事" 可产生副作用
Resource 让模型"读一份上下文" 只读
Prompt 暴露可复用的 prompt 模板

4.4 当前传输方式对比#

维度 stdio Streamable HTTP
通信模型 本地子进程 stdin/stdout HTTP POST;需要流式时可用 SSE
适用场景 本地工具、CLI 插件、强隔离 远端部署、团队共享、Gateway 聚合
网络依赖 标准 HTTP + 鉴权
认证建议 进程环境/本机权限 OAuth 或服务端令牌策略

SSE 现在应理解为 Streamable HTTP 可选的流式承载方式,不再把它当成与 stdio、HTTP 并列的独立主传输协议。

4.5 代码示例 1:Python MCP Server(官方 SDK / FastMCP)#

4.5 代码示例 1:Python MCP Server(官方 SDK / FastMCP)
# mcp_server_stdio.py
import os
from pathlib import Path

from mcp.server.fastmcp import FastMCP

# pip install "mcp[cli]"
# SDK 负责协议协商、tools/list、tools/call 与 JSON Schema 生成;
# 业务代码只声明可用工具和自己的安全边界。
mcp = FastMCP("workspace-tools", json_response=True)

WORKSPACE = Path(os.environ.get("WORKSPACE_ROOT", ".")).resolve()

def resolve_inside_workspace(raw_path: str) -> Path:
    path = (WORKSPACE / raw_path).resolve()
    if path != WORKSPACE and WORKSPACE not in path.parents:
        raise ValueError("path escapes workspace")
    return path

@mcp.tool()
def read_file(path: str) -> dict:
    """Read a UTF-8 text file inside the configured workspace."""
    target = resolve_inside_workspace(path)
    return {"content": target.read_text(encoding="utf-8")}

@mcp.tool()
def list_files(directory: str = ".") -> dict:
    """List direct children inside the configured workspace."""
    target = resolve_inside_workspace(directory)
    return {"files": sorted(item.name for item in target.iterdir())}

if __name__ == "__main__":
    mcp.run(transport="stdio")

不建议在业务代码中手写旧版 initialize/JSON-RPC 分发器。使用官方 SDK,随后把路径边界、权限、审计、幂等与人审放在业务层;协议字段和版本协商由 SDK 跟进。

4.6 代码示例 2:Python MCP Client(官方 SDK / stdio)#

4.6 代码示例 2:Python MCP Client(官方 SDK / stdio)
# mcp_client_stdio.py
import asyncio

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    params = StdioServerParameters(
        command="python3",
        args=["mcp_server_stdio.py"],
    )
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([tool.name for tool in tools.tools])
            result = await session.call_tool("list_files", {"directory": "."})
            print(result.content)

if __name__ == "__main__":
    asyncio.run(main())

4.8 权限模型设计要点#

  1. 按任务动态授予权限,不继承用户全部权限;
  2. 工具 Schema 严格,参数做校验;
  3. 高风险操作(写库、发邮件、删文件)走审批;
  4. 密钥隔离,禁止模型回显。

五、RAG / Agentic RAG#

5.1 传统 RAG 的局限#

5.2 Agentic RAG 的升级#

让 Agent 自主决定:是否需要检索、从哪检索、检索几次、如何验证结果、什么时候停止。

5.3 传统 RAG vs Agentic RAG(ASCII)#

传统 RAG:                          Agentic RAG:

  Query                             Query
    |                                 |
    ↓                                 ↓
+-------+                         +---------+
|Embedding|                       | Planner |
+-------+                         +---------+
    |                                 |
    ↓                                 ↓
+-------+     +-----+             +---------+     +-----+
|Vector | --> | LLM |             |Retriever| --> | LLM |
|Search |     |Generate|          |  (多轮)  |     |Reflect|
+-------+     +-----+             +---------+     +-----+
                                      ↑  ↓
                                  可迭代、可验证
维度 传统 RAG Agentic RAG
检索策略 单次向量相似度 Agent 自主决定检索次数、来源、查询改写
查询理解 静态嵌入 动态分解复杂查询为子问题
信息整合 直接拼接上下文 Agent 评估质量、补全缺失、交叉验证
失败处理 无,检索不到就硬答 自动扩展来源或切换策略

5.4 典型模式#

5.5 代码示例 1:Python 最小向量检索(纯 Python)#

5.5 代码示例 1:Python 最小向量检索(纯 Python)
# minimal_vector_search.py
# 不依赖外部向量库,用纯 Python 实现 cosine similarity 检索
import math

def tokenize(text: str) -> set:
    return set(text.lower().split())

def vectorize(text: str, vocab: list) -> list:
    tokens = tokenize(text)
    return [1 if w in tokens else 0 for w in vocab]

def cosine(a: list, b: list) -> float:
    dot = sum(x * y for x, y in zip(a, b))
    norm_a = math.sqrt(sum(x * x for x in a))
    norm_b = math.sqrt(sum(x * x for x in b))
    return dot / (norm_a * norm_b) if norm_a and norm_b else 0.0

class MinimalVectorStore:
    def __init__(self, docs: list):
        self.docs = docs
        self.vocab = sorted(set(w for d in docs for w in tokenize(d)))
        self.vectors = [vectorize(d, self.vocab) for d in docs]

    def search(self, query: str, top_k: int = 2) -> list:
        qv = vectorize(query, self.vocab)
        scored = [(cosine(qv, dv), doc) for dv, doc in zip(self.vectors, self.docs)]
        scored.sort(reverse=True)
        return scored[:top_k]

if __name__ == "__main__":
    docs = [
        "AI Agent 能调用工具完成复杂任务",
        "RAG 通过检索增强生成质量",
        "LLM 是大语言模型",
    ]
    store = MinimalVectorStore(docs)
    results = store.search("Agent 怎么调用工具", top_k=2)
    for score, doc in results:
        print(f"{score:.3f}  {doc}")

5.6 代码示例 2:Python Agentic RAG(ReAct 循环)#

5.6 代码示例 2:Python Agentic RAG(ReAct 循环)
# agentic_rag.py
# Agent 根据问题决定检索次数和生成答案
import json
import os
from openai import OpenAI

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY", "sk-fake"))

DOCS = {
    "agent_basics": "AI Agent 能感知环境、调用工具、迭代决策。",
    "rag_intro": "RAG 通过向量检索把相关知识注入上下文。",
    "mcp_intro": "MCP 是 Agent 调用外部工具的标准协议。",
}

def retrieve(query: str) -> str:
    # 简化检索:关键词匹配
    results = []
    for k, v in DOCS.items():
        if any(w in v for w in query.lower().split()):
            results.append(f"[{k}] {v}")
    return "\n".join(results) if results else "未检索到相关内容"

SYSTEM_PROMPT = """你是 Agentic RAG。每一步输出 JSON:
{"thought": "...", "action": {"tool": "retrieve", "args": {"query": "..."}}}
若信息足够,输出:{"thought": "...", "final_answer": "..."}"""

def agentic_rag(question: str, max_steps: int = 3) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": question},
    ]
    for step in range(max_steps):
        resp = client.chat.completions.create(
            model="gpt-4o-mini",
            messages=messages,
            response_format={"type": "json_object"},
        )
        content = resp.choices[0].message.content
        parsed = json.loads(content)
        print(f"[step {step + 1}] {parsed.get('thought', '')}")
        if "final_answer" in parsed:
            return parsed["final_answer"]
        action = parsed.get("action", {})
        if action.get("tool") == "retrieve":
            result = retrieve(action["args"]["query"])
            messages.append({"role": "assistant", "content": content})
            messages.append({"role": "user", "content": f"[observation] {result}"})
    return "[timeout]"

if __name__ == "__main__":
    print(agentic_rag("MCP 和 Agent 有什么关系?"))

5.7 代码示例 3:Python Corrective RAG(CRAG)#

5.7 代码示例 3:Python Corrective RAG(CRAG)

六、Harness Engineering / ETCLOVG#

6.1 范式演进#

Prompt Engineering → Context Engineering → Harness Engineering
     2022-2024            2025                2026 至今
   单次输入优化         每步看到什么          模型如何在系统里可靠运行

6.2 Harness Engineering 核心构成#

  1. 意图解构:FDQ 分析、模块拆分、设计稿对齐;
  2. 过程约束:Skill 驱动、Hard Gate 人工审核、双层 TDD;
  3. 评测体系:拦截率、采纳率、缺陷逃逸率,闭环自纠错。

6.3 ETCLOVG 七层架构(ASCII)#

用户任务 → L 生命周期与编排
              ↓
        ┌─────┼─────┐
        ↓     ↓     ↓
        C 上下文  T 工具  →  E 执行环境/沙箱 → 外部世界
        ↑           ↑
        └───────────┘
              ↑
    O 可观测性 ─┴─ V 验证评估 ─┴─ G 治理安全
层级 全称 中文 核心问题
E Execution Environment & Sandbox 执行环境与沙箱 动作在哪里安全执行?
T Tool Interface & Protocol 工具接口与协议 能力如何被发现和调用?
C Context & Memory Management 上下文与记忆管理 模型每一步看到什么?
L Lifecycle & Orchestration 生命周期与编排 多步任务如何持续推进?
O Observability & Operations 可观测性与运维 运行过程如何被看见?
V Verification & Evaluation 验证与评估 怎么判断任务真的完成?
G Governance & Security 治理与安全 Agent 在什么约束下行动?

前四层(E/T/C/L)是运行底座,后三层(O/V/G)是管控平面

6.4 代码示例 1:Python ETCLOVG 配置分层#

6.4 代码示例 1:Python ETCLOVG 配置分层
# etclovg_config.py
# 用配置对象表达 ETCLOVG 七层约束
from dataclasses import dataclass, field

@dataclass
class ExecutionConfig:
    sandbox_type: str = "docker"
    timeout_seconds: int = 120
    max_memory_mb: int = 2048
    network_default: str = "deny"
    allow_domains: list = field(default_factory=lambda: ["api.github.com"])

@dataclass
class ToolingConfig:
    tools: list = field(default_factory=list)
    dynamic_visibility: bool = True
    schema_validation: bool = True

@dataclass
class ContextConfig:
    max_tokens: int = 8000
    system_prompt_budget: int = 1000
    tool_output_budget: int = 4000
    summary_interval: int = 5

@dataclass
class LifecycleConfig:
    max_steps: int = 10
    max_tool_calls: int = 20
    replan_enabled: bool = True

@dataclass
class ObservabilityConfig:
    trace_enabled: bool = True
    cost_tracking: bool = True
    log_level: str = "INFO"

@dataclass
class VerificationConfig:
    auto_test: bool = True
    failure_taxonomy: list = field(default_factory=lambda: [
        "planning", "tool_use", "context", "environment", "safety", "eval"
    ])

@dataclass
class GovernanceConfig:
    input_guard: bool = True
    action_guard: bool = True
    human_in_the_loop: bool = True
    audit_retention_days: int = 180

@dataclass
class HarnessConfig:
    execution: ExecutionConfig = field(default_factory=ExecutionConfig)
    tooling: ToolingConfig = field(default_factory=ToolingConfig)
    context: ContextConfig = field(default_factory=ContextConfig)
    lifecycle: LifecycleConfig = field(default_factory=LifecycleConfig)
    observability: ObservabilityConfig = field(default_factory=ObservabilityConfig)
    verification: VerificationConfig = field(default_factory=VerificationConfig)
    governance: GovernanceConfig = field(default_factory=GovernanceConfig)

if __name__ == "__main__":
    cfg = HarnessConfig()
    print(f"沙箱超时: {cfg.execution.timeout_seconds}s, 最大步数: {cfg.lifecycle.max_steps}")

6.5 代码示例 2:Python Harness 主循环骨架#

6.5 代码示例 2:Python Harness 主循环骨架
# harness_loop.py
# 把 ETCLOVG 七层约束整合进 Agent 主循环
import time

class Harness:
    def __init__(self, config: dict):
        self.max_steps = config.get("max_steps", 10)
        self.timeout = config.get("timeout", 60)
        self.tool_whitelist = config.get("tools", [])
        self.start_time = time.time()

    def check_governance(self, action: dict) -> bool:
        if action["tool"] not in self.tool_whitelist:
            print(f"[G] 拦截未授权工具: {action['tool']}")
            return False
        return True

    def check_budget(self, step: int) -> bool:
        if step >= self.max_steps:
            print("[G] 超过最大步数")
            return False
        if time.time() - self.start_time > self.timeout:
            print("[G] 超过超时时间")
            return False
        return True

    def run(self, agent, task: str):
        step = 0
        while self.check_budget(step):
            step += 1
            print(f"\n[L] step {step}")
            action = agent.reason(task)
            print(f"[O] 计划动作: {action}")
            if not self.check_governance(action):
                break
            result = agent.execute(action)
            print(f"[V] 执行结果: {result}")
            if agent.is_done(result):
                return "success"
        return "failure"

class DummyAgent:
    def reason(self, task: str) -> dict:
        return {"tool": "read_file", "args": {"path": "note.txt"}}

    def execute(self, action: dict) -> str:
        return f"executed {action['tool']}"

    def is_done(self, result: str) -> bool:
        return "executed" in result

if __name__ == "__main__":
    harness = Harness({"max_steps": 3, "timeout": 10, "tools": ["read_file"]})
    print(harness.run(DummyAgent(), "读取文件"))

6.6 代码示例 3:Java Harness 配置校验#

6.6 代码示例 3:Java Harness 配置校验
// HarnessConfig.java
public class HarnessConfig {
    public static class Execution {
        int timeoutSeconds = 120;
        int maxMemoryMb = 2048;
        String networkDefault = "deny";
    }

    public static class Lifecycle {
        int maxSteps = 10;
        int maxToolCalls = 20;
    }

    public static class Governance {
        boolean inputGuard = true;
        boolean actionGuard = true;
    }

    Execution execution = new Execution();
    Lifecycle lifecycle = new Lifecycle();
    Governance governance = new Governance();

    public boolean validate() {
        return execution.timeoutSeconds > 0
            && lifecycle.maxSteps > 0
            && lifecycle.maxToolCalls >= lifecycle.maxSteps;
    }

    public static void main(String[] args) {
        HarnessConfig cfg = new HarnessConfig();
        System.out.println("config valid: " + cfg.validate());
    }
}

6.7 论文实证#

"只换 Harness、不换模型"可带来远超模型升级的增益:

七、Agent 沙箱、评估与治理#

7.1 Agent Sandbox(Execution 层)#

三大价值

  1. 安全隔离:容器/虚拟机/权限限制,控制文件、网络、进程、密钥访问;
  2. 可复现性:环境能重置到已知基线,保证评测一致;
  3. 提升自主性:划定合法范围后,低风险动作自动执行,无需每一步授权。

沙箱架构:

+----------------------------------+
|           Agent Host             |
|  +----------------------------+  |
|  |        Sandbox             |  |
|  |  +------+  +------+        |  |
|  |  | File |  | Net  | deny   |  |
|  |  | R/W  |  | whitelist    |  |
|  |  +------+  +------+        |  |
|  |  | CPU/Memory/Timeout      |  |
|  |  +----------------------+  |  |
|  |  | Readonly Secrets      |  |
|  |  +----------------------+  |  |
|  +----------------------------+  |
+----------------------------------+

7.2 Agent Evaluation(Verification 层)#

Task-to-Feedback 五阶段

任务与基准锚定 → 执行前就绪验证 → 受控执行与追踪捕获 → 多级判断与故障归因 → 持续回归与部署反馈

失败分类

失败类型 典型表现 改进方向
规划失败 任务拆解错误 改编排策略、补充任务约束
工具选择失败 选错工具或参数错误 优化工具描述、减少工具菜单
上下文失败 忘记目标或遗漏关键文件 改检索、压缩和记忆策略
环境失败 依赖缺失、权限不足 强化沙箱和就绪检查
安全失败 越权动作或泄露敏感信息 加权限模型、护栏和审计
评估失败 评估器误判 校准评估器、人工抽检

7.3 Agent Governance(Governance 层)#

三层治理视角

子层 关注点 典型机制
模型层 输入输出内容安全 护栏、内容过滤、输出审计
系统层 工具、沙箱、网络、密钥访问控制 网关、代理、权限模型
组织层 合规、审计、人审流程 审计日志、合规报告

工具调用四个治理点(H1–H4)

用户输入/环境观察
    ↓
 H1 输入护栏 —— 检查 Prompt 注入、敏感内容、政策违规
    ↓
模型生成动作
    ↓
 H2 动作护栏 —— 权限判断、策略匹配、风险分级
    ↓
┌──────────────┐
│ 高风险动作 → H4 人在回路审批 │
└──────────────┘
    ↓
工具执行
    ↓
 H3 后执行信息流控制 —— 防止敏感结果继续传播
    ↓
结果进入上下文

7.4 代码示例 1:Python 沙箱配置与校验#

7.4 代码示例 1:Python 沙箱配置与校验
# sandbox_config.py
# 声明式沙箱配置:最小权限 + 资源预算 + 网络白名单
import fnmatch

class Sandbox:
    def __init__(self, config: dict):
        self.fs_rules = config.get("filesystem", {})
        self.network = config.get("network", {})
        self.process = config.get("process", {})

    def allowed_file_action(self, path: str, action: str) -> bool:
        for deny in self.fs_rules.get("deny", []):
            if fnmatch.fnmatch(path, deny.get("path", "")) and action in deny.get("actions", []):
                return False
        for allow in self.fs_rules.get("allow", []):
            if fnmatch.fnmatch(path, allow.get("path", "")) and action in allow.get("actions", []):
                return True
        return self.fs_rules.get("default", "deny") == "allow"

    def allowed_domain(self, domain: str) -> bool:
        if self.network.get("default", "deny") == "allow":
            return True
        return domain in self.network.get("allowlist", [])

if __name__ == "__main__":
    cfg = {
        "filesystem": {
            "default": "deny",
            "allow": [{"path": "./src/**", "actions": ["read", "write"]}],
            "deny": [{"path": "./.env", "actions": ["read", "write"]}],
        },
        "network": {"default": "deny", "allowlist": ["api.github.com"]},
        "process": {"timeout_seconds": 120, "max_memory_mb": 2048},
    }
    sb = Sandbox(cfg)
    print(sb.allowed_file_action("./src/main.py", "read"))  # True
    print(sb.allowed_file_action("./.env", "read"))         # False
    print(sb.allowed_domain("api.github.com"))              # True
    print(sb.allowed_domain("evil.com"))                    # False

7.5 代码示例 2:Python 治理 Hook(H1-H4)#

7.5 代码示例 2:Python 治理 Hook(H1-H4)
# governance_hooks.py
# 模拟 H1-H4 四个治理点
import re

class Governance:
    HIGH_RISK_TOOLS = {"delete_file", "send_email", "git_push"}

    def h1_input_guard(self, user_input: str) -> tuple:
        # 检查 prompt 注入
        if re.search(r"忽略.*指令|ignore previous", user_input, re.I):
            return False, "H1 拦截:疑似 prompt 注入"
        return True, "H1 通过"

    def h2_action_guard(self, tool: str, args: dict, risk_whitelist: set) -> tuple:
        if tool in self.HIGH_RISK_TOOLS and tool not in risk_whitelist:
            return False, f"H2 高风险:{tool} 需要审批"
        return True, "H2 通过"

    def h3_output_filter(self, output: str, secrets: list) -> str:
        for s in secrets:
            output = output.replace(s, "***")
        return output

    def h4_human_approval(self, tool: str, args: dict) -> bool:
        # 简化:模拟人审通过
        print(f"[H4] 请审批 {tool}({args}),模拟通过")
        return True

def run_with_governance(user_input: str, tool: str, args: dict, output: str, secrets: list):
    g = Governance()
    ok, msg = g.h1_input_guard(user_input)
    print(msg)
    if not ok:
        return
    ok, msg = g.h2_action_guard(tool, args, set())
    print(msg)
    if not ok:
        if not g.h4_human_approval(tool, args):
            return
    safe_output = g.h3_output_filter(output, secrets)
    print(f"执行结果(已脱敏): {safe_output}")

if __name__ == "__main__":
    run_with_governance(
        "帮我发邮件", "send_email",
        {"to": "bob@example.com"},
        "邮件已发送,token: sk-abc123",
        ["sk-abc123"],
    )

7.6 代码示例 3:Python 评估器与失败分类#

7.6 代码示例 3:Python 评估器与失败分类
# agent_evaluator.py
# 对 Agent episode 做结果评估、轨迹评估、失败分类
class AgentEvaluator:
    FAILURE_TYPES = ["planning", "tool_use", "context", "environment", "safety", "eval"]

    def evaluate_result(self, expected: str, actual: str) -> bool:
        return expected.lower().strip() == actual.lower().strip()

    def evaluate_trace(self, trace: list, max_steps: int) -> dict:
        steps = len(trace)
        loops = len(trace) - len(set(trace))
        return {
            "steps": steps,
            "exceeded_budget": steps > max_steps,
            "suspected_loops": loops,
        }

    def classify_failure(self, trace: list, error: str) -> str:
        if "tool" in error.lower():
            return "tool_use"
        if "timeout" in error.lower() or "memory" in error.lower():
            return "environment"
        if "permission" in error.lower() or "unauthorized" in error.lower():
            return "safety"
        return "planning"

if __name__ == "__main__":
    ev = AgentEvaluator()
    print(ev.evaluate_result("success", "success"))
    print(ev.evaluate_trace(["read", "think", "read", "think"], 5))
    print(ev.classify_failure([], "tool not found"))

八、LLM Wiki#

8.1 定义#

Andrej Karpathy 推广的知识库范式:用 wikilink 互联的 markdown 页面替代向量数据库 RAG。结构化、人类可读、git 友好、不漂移。

8.2 LLM Wiki vs 向量 RAG(ASCII)#

向量 RAG:                          LLM Wiki:
+-----------+                      +-----------+
| 海量文档  |                      | Markdown  |
| chunk+emb |                      | wikilink  |
+-----------+                      +-----------+
     |                                  |
     ↓                                  ↓
+-----------+    +-----+           +-----------+    +-----+
| 向量相似度  | -> | LLM |           | wikilink  | -> | LLM |
| 检索     |    |生成 |           | 全文检索  |    |生成 |
+-----------+    +-----+           +-----------+    +-----+
  黑盒、难版本                       可追溯、git diff
维度 向量 RAG LLM Wiki
存储 向量库(chunk + embedding) 普通 markdown 文件
检索 相似度搜索 wikilink + 全文
可读性 人不可读 人直接读
可版本 git diff
漂移 取决于 chunk 质量 出处链回 + 关键事实标源

8.3 四原则#

  1. 每条知识可追溯 — 链回原始来源;
  2. 关键数字/事实标源 — 防 AI 摘要偏差;
  3. 冲突显式标注 — 新旧矛盾时列出双方来源;
  4. 不编造 — 没有出处写 (?)TODO: verify

8.4 三层 + 三操作#

三层架构raw sources → the wiki → schema

三个核心操作

8.5 代码示例 1:Python Wiki Ingest(解析来源并建页)#

8.5 代码示例 1:Python Wiki Ingest(解析来源并建页)
# wiki_ingest.py
# 模拟 LLM Wiki 的 Ingest 流程:读来源 → 摘要 → 判断关联 → 建页
import json
import re
from datetime import datetime

class WikiIngest:
    def __init__(self, wiki: dict):
        self.wiki = wiki

    def parse_source(self, raw_text: str, source_path: str) -> dict:
        summary = raw_text[:200] + "..." if len(raw_text) > 200 else raw_text
        return {
            "source": source_path,
            "summary": summary,
            "entities": re.findall(r"`([^`]+)`", raw_text),
            "date": datetime.now().isoformat(),
        }

    def find_related(self, entities: list) -> list:
        related = []
        for title, page in self.wiki.items():
            if any(e in page.get("entities", []) for e in entities):
                related.append(title)
        return related

    def create_or_update(self, title: str, ingest: dict):
        if title in self.wiki:
            self.wiki[title]["sources"].append(ingest["source"])
            self.wiki[title]["updated"] = ingest["date"]
        else:
            self.wiki[title] = {
                "title": title,
                "summary": ingest["summary"],
                "entities": ingest["entities"],
                "sources": [ingest["source"]],
                "created": ingest["date"],
                "updated": ingest["date"],
            }

if __name__ == "__main__":
    wiki = {}
    ingest = WikiIngest(wiki)
    raw = "MCP 是 Model Context Protocol 的缩写,Agent 可以通过 MCP 调用外部工具。详见 `mcp-server-design`。"
    parsed = ingest.parse_source(raw, "raw/sessions/mcp-intro.md")
    related = ingest.find_related(parsed["entities"])
    print("相关页面:", related)
    ingest.create_or_update("MCP", parsed)
    print(json.dumps(wiki, ensure_ascii=False, indent=2))

8.6 代码示例 2:Python Wiki Query(链回来源生成回答)#

8.6 代码示例 2:Python Wiki Query(链回来源生成回答)
# wiki_query.py
# 基于 wiki 页面生成带来源的回答
class WikiQuery:
    def __init__(self, wiki: dict):
        self.wiki = wiki

    def search(self, query: str) -> list:
        results = []
        for title, page in self.wiki.items():
            score = 0
            for word in query.lower().split():
                if word in title.lower() or word in page.get("summary", "").lower():
                    score += 1
            if score > 0:
                results.append((score, page))
        results.sort(reverse=True)
        return [p for _, p in results]

    def answer(self, query: str) -> str:
        pages = self.search(query)[:3]
        if not pages:
            return "未找到相关知识 (?)"
        lines = [f"关于 '{query}':"]
        for p in pages:
            lines.append(f"- {p['title']}: {p['summary']}")
            lines.append(f"  来源: {', '.join(p['sources'])}")
        return "\n".join(lines)

if __name__ == "__main__":
    wiki = {
        "MCP": {
            "title": "MCP",
            "summary": "MCP 是 Agent 调用外部工具的标准协议。",
            "sources": ["raw/sessions/mcp-intro.md"],
        },
        "Agent": {
            "title": "Agent",
            "summary": "Agent 是能感知环境、调用工具、迭代决策的 LLM 程序。",
            "sources": ["raw/sessions/agent-basics.md"],
        },
    }
    print(WikiQuery(wiki).answer("MCP 是什么"))

8.7 代码示例 3:Python Wiki Lint(查孤儿/过期/缺来源)#

8.7 代码示例 3:Python Wiki Lint(查孤儿/过期/缺来源)
# wiki_lint.py
# 定期体检:孤儿页、缺来源页、过期页
from datetime import datetime, timedelta

class WikiLint:
    def __init__(self, wiki: dict, links: dict):
        self.wiki = wiki
        self.links = links  # title -> [linked_titles]

    def orphan_pages(self) -> list:
        referenced = set()
        for targets in self.links.values():
            referenced.update(targets)
        return [t for t in self.wiki if t not in referenced and t != "index"]

    def missing_sources(self) -> list:
        return [t for t, p in self.wiki.items() if not p.get("sources")]

    def stale_pages(self, days: int = 90) -> list:
        cutoff = datetime.now() - timedelta(days=days)
        stale = []
        for t, p in self.wiki.items():
            updated = p.get("updated", "")
            if updated:
                try:
                    if datetime.fromisoformat(updated) < cutoff:
                        stale.append(t)
                except ValueError:
                    pass
        return stale

if __name__ == "__main__":
    wiki = {
        "MCP": {"sources": ["s1"], "updated": "2026-07-01"},
        "Orphan": {"sources": ["s2"], "updated": "2026-07-01"},
        "NoSource": {"sources": [], "updated": "2026-07-01"},
        "Old": {"sources": ["s3"], "updated": "2026-01-01"},
    }
    links = {"index": ["MCP"], "MCP": []}
    lint = WikiLint(wiki, links)
    print("孤儿页:", lint.orphan_pages())
    print("缺来源:", lint.missing_sources())
    print("过期页:", lint.stale_pages())

进阶工程#

Agent 开发与 Harness 工程进阶#

12.1 Agent 开发生命周期(AIDC)#

一个工业级 Agent 的开发不是「写个 prompt 调 API」那么简单,建议按 AIDC 生命周期管理:

  1. Assess(评估任务可 Agent 化)

    • 任务是否可被分解为可观测的步骤?
    • 每一步是否有可验证的成功/失败标准?
    • 是否依赖外部实时信息或只读知识?
    • 失败代价有多高?是否需要人工兜底?
  2. Instrument(工具化)

    • 把外部能力封装成工具/MCP Server,输出结构化 Schema。
    • 定义输入校验、幂等性、错误码、超时、重试策略。
    • 给工具打标签(read / write / high-risk / human-approval)。
  3. Develop(编排开发)

    • 选择推理模式:ReAct / Plan-and-Execute / Reflexion / LATS。
    • 设计 Prompt 版本、System Prompt 模板、Few-shot 示例。
    • 搭运行时:状态机(LangGraph)、中间件链、记忆层、护栏、沙箱。
  4. Curate(持续治理)

    • 评估:离线回归 + 在线 shadow。
    • 可观测:trace / span / event / cost。
    • 安全:Guardrails、审计、回滚、灰度。
    • 知识:失败案例回流、负面示例库、prompt 迭代。

口诀:先拆步骤、再固工具、再选模式、最后治理。很多 demo 级 Agent 失败,是因为跳过了「可验证」和「治理」两步。

12.2 高级推理模式对比#

12.3 工具/MCP 设计的工程最佳实践#

工具是 Agent 的「手」,设计好坏直接决定上限。推荐遵守以下规则:

1. Schema 是契约

2. 幂等性

3. 错误契约

4. 权限与最小暴露

5. 版本与兼容

6. 返回大小控制

点击查看:一个高可用 MCP 工具的实现骨架
from pydantic import BaseModel, Field
from typing import Literal
import hashlib

class QueryApprovalInput(BaseModel):
    ticket_id: str = Field(..., description="审批单号,形如 AP-20250710-001")
    fields: list[str] | None = Field(None, description="只返回指定字段,默认 summary")
    idempotency_key: str | None = Field(None, description="幂等键,写操作必填")

class ToolError(Exception):
    def __init__(self, code: str, message: str, retryable: bool, suggestion: str = ""):
        self.code = code
        self.message = message
        self.retryable = retryable
        self.suggestion = suggestion

async def query_approval_ticket(input: QueryApprovalInput):
    """
    查询审批单核心信息。只读工具,低风险。
    """
    if not input.ticket_id.startswith("AP-"):
        raise ToolError(
            code="INVALID_TICKET_ID",
            message=f"ticket_id 格式错误: {input.ticket_id}",
            retryable=False,
            suggestion="请确认单号以 AP- 开头",
        )
    # 实际查询数据库或 RPC
    data = {"ticket_id": input.ticket_id, "status": "pending", "owner": "carly"}
    if input.fields:
        data = {k: v for k, v in data.items() if k in input.fields}
    return {
        "ticket_id": input.ticket_id,
        "data": data,
        "cached": False,
        "cost_ms": 12,
    }

async def approve_ticket(input: QueryApprovalInput):
    """
    审批通过。写操作,需要幂等键 + Guardrails 授权。
    """
    if not input.idempotency_key:
        raise ToolError(
            code="MISSING_IDEMPOTENCY_KEY",
            message="写操作必须提供 idempotency_key",
            retryable=False,
        )
    # 幂等去重 + 权限判断
    # if already_approved(input.idempotency_key): return {"already_applied": True}
    # if not guardrails.allow("approve_ticket", input): raise ToolError(...)
    return {
        "ticket_id": input.ticket_id,
        "action": "approved",
        "idempotency_key": input.idempotency_key,
    }

12.4 记忆模型:三层架构#

Agent 的记忆不能是「把所有对话塞进 prompt」。推荐 三层记忆模型

层级 作用 实现方式 生命周期
工作记忆(Working Memory) 当前轮次可用的上下文 System prompt + 最近 N 条消息 单线程
短期记忆(Short-term Memory) 同一线程内的历史状态、todo、产物 ThreadState / Checkpoint / 数据库 线程级
长期记忆(Long-term Memory) 跨线程/跨用户的高价值知识 向量库 + 结构化 Wiki + 知识图谱 持久化

设计要点:

12.5 可观测性:Trace / Span / Event#

生产 Agent 必须有可观测性,否则出问题只能「黑盒猜」。建议按 OpenTelemetry 语义分层:

必看指标:

Trace 示例:

12.6 评估体系:离线 + 在线 + 人工#

Agent 的评估比传统软件复杂,因为输出是非确定性的。推荐三层评估:

1. 离线评估(Offline Evaluation)

2. 在线评估(Online Evaluation)

3. 人工评估与反馈闭环

12.7 ETCLOVG 生产落地清单#

ETCLOVG 是 Harness Engineering 的七层框架,下面是每个维度在生产落地的检查清单:

维度 关键问题 落地项
E - Execution Agent 能不能稳定跑完? 超时、重试、熔断、兜底答案、异常捕获
T - Tooling 工具是否可靠? Schema、幂等、错误契约、版本、权限、限速
C - Context 上下文是否够用、不爆炸? 窗口预算、总结压缩、RAG/Wiki、记忆分层
L - Lifecycle 线程/状态怎么管理? ThreadState、Checkpoint、归档、垃圾回收
O - Observability 能不能看到问题? Trace/Span/Event、Metrics、日志、告警
V - Verification 怎么证明它是对的? 离线测试集、Shadow、A/B、LLM-as-judge、人工复核
G - Governance 怎么防止滥用/出错? Guardrails、审计、沙箱、人工审批、模型访问控制

12.8 部署与灰度策略#

Agent 的部署不能只靠「发版」,因为模型行为有随机性:

1. 模型层灰度

2. Prompt 版本灰度

3. 工具/MCP 版本灰度

4. 熔断与降级

12.9 可运行示例:带反射的 ReAct Agent#

下面是一个 LangGraph 风格 的简化实现,展示了状态机、工具调用、反射/重试。代码不依赖 LangGraph,用纯 Python 写清楚核心逻辑,可直接 py_compile 通过。

点击查看:reflective_react_agent.py
from dataclasses import dataclass, field
from typing import Callable, Literal
import json

# ---------- 数据模型 ----------
@dataclass
class Tool:
    name: str
    description: str
    params_schema: dict
    fn: Callable[[dict], dict]

@dataclass
class AgentState:
    question: str
    messages: list[dict] = field(default_factory=list)
    turns: int = 0
    max_turns: int = 5
    done: bool = False

# ---------- 模拟 LLM 调用 ----------
def call_llm(messages: list[dict]) -> dict:
    """
    真实环境应替换为 OpenAI/Claude/Moonshot 等 SDK。
    这里用一个简单规则模拟:如果问题包含数字就调用 calc,否则直接回答。
    """
    last_user = next((m["content"] for m in reversed(messages) if m["role"] == "user"), "")
    if "35" in last_user or "12" in last_user:
        return {
            "type": "tool_call",
            "tool": "calc",
            "arguments": {"expr": "35 + 12"},
        }
    return {"type": "answer", "content": "这个问题不需要工具,直接回答即可。"}

# ---------- 工具实现 ----------
def calc_tool(args: dict) -> dict:
    expr = args.get("expr", "")
    try:
        result = eval(expr, {"__builtins__": {}}, {})
        return {"ok": True, "result": result, "expr": expr}
    except Exception as e:
        return {"ok": False, "error": str(e), "expr": expr}

TOOLS = {
    "calc": Tool(
        name="calc",
        description="计算数学表达式,例如 '35 + 12'。",
        params_schema={"type": "object", "properties": {"expr": {"type": "string"}}},
        fn=calc_tool,
    )
}

# ---------- Agent 运行时 ----------
def plan(state: AgentState) -> AgentState:
    state.messages.append({"role": "system", "content": "你是一个会调用工具的助手。每步输出 tool_call 或 answer。"})
    state.messages.append({"role": "user", "content": state.question})
    return state

def act(state: AgentState) -> AgentState:
    response = call_llm(state.messages)
    state.messages.append({"role": "assistant", "content": json.dumps(response, ensure_ascii=False)})
    state.turns += 1

    if response["type"] == "answer":
        state.done = True
        return state

    tool_name = response.get("tool")
    args = response.get("arguments", {})
    if tool_name not in TOOLS:
        state.messages.append({
            "role": "tool",
            "content": json.dumps({"error": f"未知工具: {tool_name}"}, ensure_ascii=False),
        })
        return state

    result = TOOLS[tool_name].fn(args)
    state.messages.append({
        "role": "tool",
        "content": json.dumps(result, ensure_ascii=False),
    })
    return state

def reflect(state: AgentState) -> AgentState:
    """
    反射:如果上一轮工具调用失败,让 LLM 自己反思并尝试修正。
    """
    last_tool_msg = next((m for m in reversed(state.messages) if m["role"] == "tool"), None)
    if last_tool_msg is None:
        return state
    payload = json.loads(last_tool_msg["content"])
    if payload.get("ok") is False:
        state.messages.append({
            "role": "system",
            "content": "上一轮工具调用失败,请反思原因并修正参数后重试。",
        })
    return state

def should_continue(state: AgentState) -> Literal["act", "finish"]:
    if state.done or state.turns >= state.max_turns:
        return "finish"
    return "act"

def run_agent(question: str) -> AgentState:
    state = AgentState(question=question)
    state = plan(state)
    while True:
        state = act(state)
        if should_continue(state) == "finish":
            break
        state = reflect(state)
    return state

if __name__ == "__main__":
    final_state = run_agent("35 加 12 等于多少?")
    print(f"turns={final_state.turns}, done={final_state.done}")
    print(json.dumps(final_state.messages, ensure_ascii=False, indent=2))

代码解读:

12.11 开发工具链分层:可观测 / 取证 / 代码地图 / 评估#

时态 回答的问题 代表工具
① 运行时可观测 在线 「这次内部哪一步坏了」 Langfuse、Arize Phoenix、LangSmith
② 事后 transcript 取证 离线复盘 「这次会话到底做了什么」 claude-devtools 类
③ 代码理解 / 代码地图 静态 「代码谁调谁、改哪影响谁」 GitNexus、Serena、Understand Anything
④ 评估 / 回归 批量 「改完整体变好还是变差」 Dataset + LLM-judge(Langfuse / Braintrust)

四层各自的代表与要点:

详见知识库 [[concepts/ai-coding-agent-toolchain-layers]] 与对比页 [[comparisons/code-map-understanding-tools]]。


AI Agent 通识补遗(源自 ai-agent-guide)#

13.1 提示词工程进阶#

三层结构:① Prompt 结构层(Role / Directive / Context / Exemplars / Format / Style 六个单元);② 工程方法层(CoT / ToT / ReAct / Few-shot 等推理引导技术);③ Answer Engineering 层(结构化输出、解析、验证)。第三层存在的原因:Agent 系统里 LLM 的输出是给程序消费的(工具调用、状态更新),输出无法被可靠解析,整个 Agent Loop 就会崩溃。

采样参数怎么选

Few-shot vs Zero-shot:Few-shot 给 1-3 个示例,格式一致、准确率提升 20-40%,但耗 Token 且可能过拟合示例风格。用 Few-shot 的场景:分类任务(意图识别)、格式转换(JSON 提取)、风格模仿、Zero-shot 不达标时;不用:开放问答、创意生成、Token 预算紧张。

PromptOps:把 Prompt 当代码管理——版本管理(SemVer + Changelog)、自动化评测(回归测试 + 指标对比)、灰度发布、可观测性。与 DevOps 的四点区别:① 非确定性(同版本不同输出,无法断言);② 评测靠批量测试集 + 统计指标而非单元测试;③ 回滚原因可能是模型行为漂移而非 bug;④ 改一个词可能影响全局行为,难以局部隔离。

为什么用 Jinja2 模板而不是字符串拼接:f-string 拼接有三个问题——变量超过 3 个可读性差、无法条件渲染("有工具时显示工具列表")、无法复用继承。Jinja2 提供变量注入 {{ var }}、条件 {% if %}、循环 {% for %}、模板继承 {% extends %},让 Prompt 与业务逻辑解耦:模板文件管提示词,代码只准备数据。

C.L.E.A.R 原则:Concise(简洁)、Logical(有逻辑)、Explicit(明确)、Adaptive(可迭代)、Reflective(可复盘)。反例:"帮我写一个好的产品介绍";正例:"为 SaaS 产品写 150 字介绍,突出自动化和节约成本两个卖点,语气正式,第三人称,Markdown 输出"。

13.2 意图识别与决策中枢#

意图识别三方法:关键词匹配(快、便宜、粗糙)、LLM 语义理解(准、贵)、模式识别(越用越准、冷启动差)。生产环境用分层组合:先关键词快速筛选,命中直接路由;未命中走 LLM 语义理解;结果回流模式库优化规则——90% 常见意图走快路径,10% 走贵的 LLM 路径。

Tool RAG:对工具描述做向量检索,只把最相关的 K 个工具 Schema 注入 Prompt(普通 RAG 检索的是知识文档)。工具数量 > 20 个时,比全量注入节省 70%+ Token,同时减少工具选择混淆(工具太多 LLM 反而选不对)。

Agent 主循环五大保护机制

  1. max_turns 防无限循环;
  2. Token Budget 防上下文爆炸(达 80% 阈值触发压缩);
  3. 递减收益检测防死循环(连续 3 轮输出相似度 > 0.9 终止);
  4. 指数退避重试处理瞬时故障(1s→2s→4s→8s→16s);
  5. idle_timeout 防资源占用(5 分钟无进展自动释放)。

决策中枢七步链路:感知 → 意图识别 → 任务分类 → 任务规划 → 工具路由 → 执行调度 → 上下文管理,循环运行即 Agent Loop。

13.3 Skills 体系:工具的组合与复用#

Skill vs Tool:Tool 是单一操作(函数),Skill 是工具的组合 + 使用知识(模块)——包含调用策略、参数约束和使用时机。

三层金字塔:基础 Skill(单工具封装,如 read_file)→ 复合 Skill(多工具组合 + 调用策略,如 code-reviewer)→ 领域 Skill(行业知识包,如 finance-advisor)。自下而上构建。

Skills 太多 Prompt 过长怎么办:三层懒加载——始终只加载名称;触发词匹配时加载完整定义;AI 主动搜索罕见 Skill。

与 MCP 的关系:层次不同。MCP 是工具级标准协议(连 Agent 和工具),Skills 是能力级组织单元(把工具组织成能力包),一个 Skill 可以包含多个 MCP 工具。

从项目实践沉淀 Skill 四步法:模式识别(从日志提取重复模式)→ 抽象提炼(转成 Skill YAML)→ 验证打磨(多项目试用)→ 发布复用(SemVer 版本 + Changelog)。

五层安全架构:声明式权限(YAML 白名单)→ 运行时拦截(permissionGuard 中间件)→ 审批机制(高危操作 /approve)→ 审计日志(调用可追溯)→ 沙箱隔离(限制资源)。按风险等级分层,不是所有 Skill 都要五层。

13.4 CLI Agent:操作本地工具的 Agent#

CLI Agent vs Web Agent:CLI 是纯文本流,支持管道/重定向,直接访问文件系统/进程/网络,可嵌入 Shell 脚本、CI/CD、Cron 做无人值守自动化;Web Agent 受浏览器沙箱限制,只能 API 间接访问系统资源。

  1. 命令白名单:只允许预定义安全命令(前缀匹配 ls/cat/grep/git 等);
  2. 危险模式正则拦截:白名单内也查参数,拦 rm -rf /dd if=、fork bomb;
  3. 沙箱执行环境:Docker 容器(文件系统隔离 + 资源限制)/ Firejail / bubblewrap + 权限降级(非 root、只读挂载关键目录);
  4. 人工审批:部署、删除、网络请求等敏感操作弹确认。层层递进,一层被绕过下一层兜底。

NL2Shell 提准五法:① Few-Shot 精选 20-30 个示例按场景分类(质量 > 数量);② 上下文感知(注入 OS 类型、Shell 版本、已装工具、cwd——macOS 上不生成 apt-get);③ RAG 增强(历史命令库 + man page 向量检索动态注入);④ 多候选 + 规则校验 + 二次评分投票;⑤ 持续学习(记录用户修正回流 Few-Shot 库)。

命令执行失败自愈四步:错误分类(可重试 / 需修正 / 需人工)→ 自动修正(语法错误回传 LLM 重生成、权限加 sudo、路径模糊匹配)→ 重试策略(指数退避最多 3 次,复杂操作回退到稳定态)→ 降级上报(给错误分析 + 建议命令)。每次自愈记录"错误→修正"映射表。

13.5 GUI Agent:截图 + 点击的 Agent#

GUI Agent vs API Agent:API Agent 精确快速但要求目标系统有 API;GUI Agent 通过截图 + 模拟点击操作任意有界面的软件,灵活但可能出错。最佳实践:有 API 优先 API,没 API 才 GUI,两者结合覆盖最全。

工作流程五步循环:截取屏幕 → Vision 模型视觉理解(识别按钮/输入框/文字)→ 决策推理(点哪里、输什么)→ 执行操作(模拟鼠标/键盘/滚动)→ 结果验证(截图检查,决定继续或修正)。

五大挑战:① 准确率(视觉理解误差,当前约 50-70%);② 速度(每步截图 + 推理,5-10 秒/步);③ 安全风险(能操作任意界面,误操作危险大);④ 环境依赖(分辨率、窗口位置变化导致失败);⑤ 成本(Vision 模型调用远高于文本)。

技术栈:视觉理解(GPT-4o Vision / Claude Vision / Gemini Vision)+ 操作执行(Playwright-Web / PyAutoGUI-桌面 / ADB-手机)+ 决策(ReAct + 规划)+ 验证(截图对比 + DOM 分析)+ 状态管理(操作历史 + 当前界面状态)。

代表产品:Claude Computer Use(桌面)、OpenAI Operator(浏览器)、Google Project Mariner、智谱 AutoGLM(手机 App + 网页)、Microsoft Copilot Actions(Office)。

成本控制六策略:截图压缩降分辨率(1920→640 省约 60%)、区域裁剪只截变化区、DOM 辅助(Web 优先 DOM 提取减少截图)、操作缓存(同界面状态复用决策)、模型分级(简单步用小模型)、规划+执行(一次性出计划减少反复推理)。

13.6 低代码平台:Dify 与 Coze#

代码框架 vs 低代码平台:LangGraph/AutoGen 完全灵活、数据自控但门槛高开发慢;Dify/Coze 快速搭建、非技术人员可用但灵活性受限、平台依赖。复杂定制选代码框架,快速验证选低代码。

Dify 四种应用类型:Chatbot(多轮对话,客服/助手)、Text Generator(单次生成,翻译/摘要)、Agent(自主调工具多步推理)、Workflow(可视化固定流程自动化)。

Dify vs Coze 选型:Dify 开源、Docker 自部署、数据可控、面向技术团队,定位 LLMOps 平台(开发→部署→监控全生命周期);Coze 是字节闭源 SaaS Bot 工厂,零代码、内置 100+ 插件、一键发布飞书/微信/抖音,面向业务团队。要数据自控/自部署选 Dify(金融医疗合规唯一选择),要最快上线 + 多端发布选 Coze。

Chatflow vs Workflow:Chatflow 维护会话上下文、节点可访问对话历史,适合多轮交互;Workflow 单次执行输入→处理→输出、不维护状态,适合文档处理/数据抽取/批量翻译。

Coze 插件机制三层:预置插件(100+ 勾选即用)→ 自定义插件(上传 OpenAPI Schema 自动生成工具描述)→ 调用流程(LLM 意图识别 → 并行/串行执行 → 结果回传 LLM)。本质与 Function Calling 相同(LLM 决定调什么、框架执行),只是把工具创建和使用可视化了。

13.7 推理框架与本地部署#

为什么需要推理框架:模型文件只是权重矩阵,推理框架负责加载 GPU、处理 Token、矩阵运算、解码输出、API 接口、并发与显存管理。

维度 Ollama vLLM SGLang
定位 极简部署,Mac/PC 一条命令 工业级引擎,企业生产 结构化生成见长
核心技术 llama.cpp 量化 PagedAttention + 连续批处理 RadixAttention + 压缩 FSM
场景 个人开发/学习/小规模 高并发生产 精确约束输出(JSON/SQL/代码),结构化输出比 vLLM 快 3-7 倍

PagedAttention 解决什么问题:传统推理每个请求的 KV Cache 需连续显存,预分配浪费 + 碎片化,显存利用率只有 20-40%。PagedAttention 把 KV Cache 切成固定大小的页,像 OS 虚拟内存一样按需分配、跨请求共享(Copy-on-Write),利用率提升到 90%+。

量化怎么选:INT4 精度损失约 3-5%,对话场景可用;但数学推理、代码生成、Tool Calling(Function Calling,参数格式错误率升高)、金融/医疗合规不可接受。实用原则:对话型 Agent 用 INT4,工具调用型用 INT8,合规型 FP16 起步并做任务级评测。

为什么从 GPT 切到本地模型只需改一行:主流推理框架(Ollama/vLLM/SGLang/TGI)都兼容 OpenAI API 格式,Agent 代码用 OpenAI SDK,只需把 base_url 改成 localhost:11434(Ollama)或 gpu-server:8000(vLLM),model 改本地模型名。

TP vs PP:Tensor Parallel 同层内按列切分矩阵,GPU 间每步 AllReduce 通信,适合单机多卡(NVLink 高带宽);Pipeline Parallel 按层切分流水线接力,通信少,适合跨机器。大模型常用 TP+PP 混合:先机内 TP,再跨机 PP。

混合部署为什么省 70% 成本:80% 简单任务(FAQ/分类/提取)用本地开源模型,15% 中等任务用本地 INT8 量化,只有 5% 复杂推理调 GPT-4o。总成本 = 5%×GPT 价格 + GPU 费,远低于 100% 用 GPT。关键是 Agent 框架支持动态模型路由。

MoE 为什么 671B 参数推理成本却和 70B Dense 差不多:稀疏激活——每个 Token 只路由到 Top-K 个专家(如 K=8),激活参数仅 ~37B,计算量 ∝ 激活参数量而非总参数量。代价是显存:671B 全量参数都要常驻显存("计算便宜、显存贵")。

补遗 · AI 编码 CLI / Agent 工具生态(hook · subagent · workflow)#

T.1 横向对比总表(先记这张)#

维度 Claude Code OpenAI Codex CLI Kimi Code Gemini CLI Cursor
厂商/形态 Anthropic,Node,闭源 OpenAI,Rust,开源(Apache 2.0) Moonshot,通用 agent 客户端 Google,Node,开源 AI IDE(VS Code fork)
配置入口 ~/.claude/CLAUDE.md+settings.json,MCP 在 ~/.claude.json ~/.codex/config.toml+auth.json ~/.kimi-code/config.toml ~/.gemini/settings.json ~/.cursor/+项目 .cursor/rules/
项目级指令 CLAUDE.md / .claude/rules/*.md AGENTS.md AGENTS.md GEMINI.md@ 可 import) .cursor/rules/*.mdc(MDC)
扩展机制 Skill + Hook + MCP + Plugin(四层) 远程/本地 marketplace + skill 多 provider 路由 + scoped skill + hooks 自定义命令(TOML) + Extensions + MCP Rules + Hooks(1.7+) + MCP
审批/沙箱 permissions.allow 预授权(较粗) --ask-for-approval × --sandbox 双维度(最细) permission 模式分级 Policy Engine(策略文件) IDE 内交互确认
跨会话记忆 MEMORY.md + claude-mem(BM25+向量+KG,唯一强记忆) ~/.codex/memories/(会话级快照) 会话级(resume / cron 持久化) /memory(写入 GEMINI.md) 项目 Rules
协议 Anthropic Messages 原生 Responses / Chat Completions OpenAI + Anthropic 双协议 Gemini 原生 专用 70B 编辑模型

一句话记忆:Claude Code 生态最深(四层扩展),Codex 权限最细(双维度沙箱),Kimi Code 最杂食(双协议多网关),Gemini CLI 最开放(Extensions + GitHub Actions),Cursor 编辑体验最强(专用模型)。

T.2 Claude Code —— Skill / Command / Hook / Plugin 四层#

Anthropic 2025-10 正式提出的可组合架构,从原子到发行单元:

Hook 生命周期事件(按触发节奏记三类 + 专项):

配置写在 settings.jsonhooks 段:先选事件 → 加 matcher(如只对 BashEdit|Write、或 MCP 工具 mcp__memory__.*)→ 定义 handler。Handler 通过 stdin 收 JSON、exit code 控制exit 0 放行、exit 2 阻断(PreToolUse 阻断工具、UserPromptSubmit 拒绝 prompt、Stop 阻止结束继续对话);也可 exit 0 + stdout 输出 JSON 做更细的 permissionDecision(allow/deny/ask)与 additionalContext(注入上下文)。

T.3 OpenAI Codex CLI —— 双维度沙箱(权限最细)#

审批与沙箱是两个正交维度,是三大 CLI 里粒度最细的:

codex --full-auto          # 自动执行 + 锁工作目录 + 禁网(日常推荐)
codex -a never -s workspace-write   # 无人值守但锁目录(CI)
codex --dangerously-bypass-approvals-and-sandbox   # 仅容器/CI,极危险

T.4 Kimi Code —— 双协议 + 多网关路由#

[providers.litellm]
type = "openai"
base_url = "http://host:4000/v1"
[providers."cc-gateway"]
type = "anthropic"
base_url = "http://localhost:8080"
models."cc-gateway/deepseek-v4-pro-1m" = { provider="cc-gateway", model="deepseek-v4-pro-1m", max_tokens=1048576, capabilities=["thinking","image_in","tool_use"] }

T.5 Gemini CLI —— Extensions + 自定义命令 + GitHub Actions#

T.6 Cursor / 其它#

T.8 模型是怎么调用工具的?—— Tool Calling / Function Calling / MCP 三层#

先厘清最常被误解的一点:模型自己不执行任何工具。模型能做的只有生成 token;所谓「调用工具」,是模型按约定格式输出一个结构化的调用意图(调哪个工具、参数是什么),由外部的 runtime / harness 真正去执行,再把结果回填进上下文让模型接着推理。这是「模型提议 → 宿主执行 → 结果回喂」的循环,不是模型直接联网、读文件或删库。

T.8.1 一次完整的 tool-use 循环#

  1. 注入工具定义:请求里带 tools(每个工具一份 JSON Schema:name + description + parameters),runtime 把它拼进上下文。
  2. 模型输出调用:模型决定用工具时不吐自然语言,而是吐一个调用块(Anthropic 叫 tool_use,OpenAI 叫 tool_calls),含 name + 结构化 arguments(JSON)。
  3. runtime 执行:宿主解析 arguments,真正跑代码(读文件、curl、查 DB),拿到结果。
  4. 结果回填:把结果作为 tool_result(OpenAI 里是 role=tool 的消息)追加进对话,再次请求模型。
  5. 循环:模型可能继续调下一个工具(agentic loop),或给出最终自然语言答复。多步任务就是这个循环转很多轮。

一句话:Tool Calling 是总称;Function Calling 是其中最常见的 JSON Schema 函数工具形态。模型表达「下一步动作」,执行权始终在宿主。

  1. 把工具执行结果直接返回给用户——模型没看到结果,无法总结/续推,循环断掉。
  2. 不校验参数就执行——等于把代码执行权交给 LLM,是 prompt injection 的头号入口。
  3. tool_call_id 对不上导致模型「失忆」——回填的 result 必须带上发起时那个 id。
  4. 忽略 finish_reason/stop_reason 就盲目再调一轮——只有值为 tool_calls/tool_use 才该执行工具。
  5. 一个工具做太多事——模型失去组合编排能力,工具要原子化

并行工具调用:模型可能在一轮返回多个 tool_call,但是否并行取决于所选模型与 Provider 能力;只读且无副作用的调用可并发执行,再按调用 ID 回填。写操作默认串行、幂等,并保留审计与人工确认。

Anthropic (Messages) OpenAI(Responses,当前推荐)
工具定义 tools:[{name,description,input_schema}] tools:[{type:"function",name,description,parameters,strict}]
模型发起 content block type:"tool_use"(id/name/input) output item type:"function_call"(call_id/name/arguments)
结果回填 type:"tool_result"(tool_use_id + content) type:"function_call_output"(call_id + output)
强制/约束 tool_choice:{type:"auto"|"any"|"tool"} tool_choice:"auto"|"required"|"none"|{...}

兼容层仍可能遇到 OpenAI Chat Completions 的 message.tool_calls[]role:"tool";新实现优先按所用 provider 的当前 API 建适配层,内部统一为 ToolCall { id, name, arguments }ToolResult { callId, output },不要把某一家字段当成通用协议。

T.8.2 怎么确保模型「能调且调对」工具#

模型不肯调、调错工具、传错参数,是实践里最常见的坑。可靠性从下面几层堆出来:

T.8.3 MCP —— 工具接入的标准协议#

MCP(Model Context Protocol) 是 Anthropic 2024 年底开源的开放协议,把「模型/Agent ↔ 外部工具与数据源」的接入标准化。类比:MCP 之于 AI 工具,如同 USB-C 之于外设、LSP 之于编辑器——一次实现,处处可接,不用给每个 CLI 单独写一遍集成。

T.8.5 本地项目实证(把机制落到真实代码)#

机制(对应上文) 项目里怎么做的 关键位置
双协议工具调用抽象(T.8.1 协议对照) 统一 LLM 客户端手写解析 OpenAI 与 Anthropic 两套工具协议:OpenAI 侧按 delta.tool_callsindex 累积 id/name/arguments 分片,Anthropic 侧按 content_block 累积、input_json_delta.partial_json 拼 JSON;消息转换把 role:tooltool_result block、toolCallstool_use block 互转 swarm-unifiedpackages/core/src/runtime/llm-client.ts
工具循环 + 终止 + 上下文压缩(T.8.1 循环) runWithTools 最多 10 轮,每轮流式收 tool_call→执行→role:tool 回灌;轮次耗尽做一次「不给工具」的强制总结;历史超 20 条压成 1 条 summary+保留 19 条 swarm-unifiedruntime/agent-runner.ts
MCP client / Host(T.8.3) 用官方 @modelcontextprotocol/sdk,支持 stdio / Streamable HTTP;SSE 仅作为 HTTP 的可选流式响应承载。读 mcp.json 连多 server,listTools/callTool,工具名冲突去重为 mcp.<server>.<tool>;内置工具与 MCP 工具 [...AGENT_TOOLS, ...mcpTools] 合流喂模型 swarm-ide-v2backend/src/runtime/mcp.ts + agent-runtime.ts
MCP server 端 + 网关聚合(T.8.3,生产在用) FastMCP 写的真实 MCP server(stdio + streamable-http 双 transport),@list_tools/@call_tool;DB 查询类工具直连 MySQL 自实现(参数化 SQL),必须经 JVM 的保留代理转发下游 Java MCP;工具命名空间 <source>__<name>、代理走白名单 iam-nas-mcp-gatewaygateway.py + downstream.py + tools_iam_db.py
无原生 tool_use 时「模拟」function calling(T.8.2 能力开关) Claude CLI 不吃 OpenAI 工具协议,改用 --schema 结构化输出 + prompt 注入工具清单伪造:把 tools 序列化进 prompt、schema 强制模型把要调的工具写进 tool_calls 字段,再解析回 CompatToolCall;还处理了 tool_choice=required/指定函数 SkillOptskillopt/model/claude_backend.py
工具执行安全边界(T.8.2 权限) bash 工具走 Docker 沙箱(networkDisabled、512m、30s 超时);read/write_file 做路径遍历防护锁在 workspaceRoot 内 swarm-unifiedtools/builtin/exec-tools.ts + tools/sandbox.ts
函数签名自动转 schema + handoff(T.8.2) inspect.signature+get_type_hints 从 Python 函数自动生成 OpenAI function schema;工具返回 {"handoff":...}/{"finish":...} 作为控制指令实现 agent 交接(OpenAI Swarm 复刻) wikicore-backendsrc/wikicore/swarm/tools.py + handoff.py

再往上一层——工具编排(fork-join / DAG)swarm-unified 把工具本身做成编排原语(create_goal/decompose_goal(带 dependsOn 建任务 DAG)/assign_task/report/wake_agent),SwarmOrchestrator 监听 task:assigned/complete/failed 事件、依赖满足才唤醒 assignee,带 maxConcurrency/maxRetries/超时/卡死扫描(tools/builtin/orchestration-tools.ts + orchestrator/swarm-orchestrator.ts)。这把「模型调工具」从单次调用推进到「用工具驱动多 Agent 协作」,是 T.2 提到的 subagent / fork-join 在本地的落地。

层次记忆:SkillOpt(在没有原生工具支持的 CLI 上模拟)→ swarm-unified/swarm-ide-v2原生双协议 + 工具循环)→ iam-nas-mcp-gateway(把内部能力标准化成 MCP server 供任意客户端复用)→ swarm-unified 编排层(用工具编排多 Agent)。一条从「让模型能调工具」到「用工具编排 Agent 系统」的完整实证链。


补遗二 · 2025H2–2026 前沿进展(context engineering · RLVR · 协议生态 · 评测安全)#

F.1 Context Engineering:从「写好 prompt」到「设计 agent 看到的全部上下文」#

2025 年最重要的概念升级。LangChain 归纳为四类操作:Write(写入记忆/草稿板)、Select(按需检索注入)、Compress(压缩/摘要/compaction)、Isolate(多 agent 上下文隔离)。Anthropic 的核心观点是「上下文是有限注意力资源」:长时程任务要靠 compaction、结构化笔记、子 agent 隔离来维持性能。

实证支撑:LOCA-bench(2026)固定任务语义、只膨胀环境状态,发现上下文越长 agentic 成功率掉得越快,且模型间差距在长上下文下反而拉大——超长窗口 ≠ 长时程能力。这解释了为什么 2026 年的模型竞赛同时卷「窗口大小」与「上下文管理」。

一句话记忆:Prompt engineering 优化输入的那句话,context engineering 优化 agent 每一轮看到的整个世界。

F.2 RLVR 与 GRPO 家族:推理模型后训练的事实标准#

F.3 模型格局:万亿开源 agentic 模型 + 闭源分域领先#

F.4 协议生态:MCP 升级、Skills 标准化、A2A 分层#

协议 管什么 形态 关键机制
MCP agent ↔ 工具/数据 client-server 2025-11-25 版:异步操作、无状态传输(解负载均衡限制)、server identity、官方扩展机制;月下载 1.1 亿+;版本号改日期制
Agent Skills agent「怎么做」 能力包分发 三级渐进加载(progressive disclosure);2025-12 开源为开放标准(agentskills.io),与 MCP 互补
A2A agent ↔ agent P2P Agent Card 发现(/.well-known/agent.json)+ 任务六态生命周期;2026 年走向 v1.0(需核实)

F.5 Agent 工程共识与评测安全#


补遗三 · 个人 Agent 系统实战(记忆 · 心跳 · 多 agent 编排 · skill 工程化)#

P.1 Agent 记忆系统:三层架构 + append-only 铁规#

P.2 Proactive Agent:心跳机制 + 数值化主动性#

P.3 Subagent 编排六条铁律(生产踩坑沉淀)#

  1. 两阶段评审,顺序不能反:先 spec 符合性评审(对照原始 spec 查漏项、防 scope creep),通过了才做代码质量评审。
  2. 任务全文直接塞进 subagent 的 context,绝不让 subagent 自己去读计划文件(计划文件只读一次);不派多个实现 subagent 碰同一批文件。
  3. No agent should verify its own work——实现者自查不能替代独立评审,fresh context finds what you miss。
  4. Orchestrator 铁律是 decompose, don't execute;派发前必须先发现本机实际有哪些 profile——对不存在的 assignee 派发会静默失败,任务永远卡住。
  5. 任务依赖用 parent 门控:先建父卡拿到 id 再建子卡,否则 dispatcher 可能在依赖就绪前抢走子任务。
  6. 双车道不信任模型:把外部 CLI(如 Codex)当隔离实现车道时,它的 diff 是「不可信补丁」——必须独立评审 + 自己亲自重跑测试才验收;跨 agent 交接走结构化 metadata(changed_files / tests_run / decisions),下游不翻聊天记录。

P.4 Skill 工程化:三级渐进加载 + 触发率 eval#

P.5 CLI Agent 运维实战(745 行 claude-code 技能沉淀)#

延伸阅读#

面试专项#

需要集中复习、组织回答框架或做临场速览时,请进入 面试专项

企业项目实践#

企业内部项目的架构、实现与复盘包含非公开信息,已独立存放并加密。获得授权后可进入 企业项目实践

更新记录#

v2.1 · 2026-08-01 · Tool Calling / MCP 知识点核验更新#