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:固定步骤编排,适合步骤明确、容错低的任务;
- 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 的能力标签 | research、coding、websearch |
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 设计原则#
- 原子性:每个 Step 只做一件事,输出可验证;
- 可观测:每个 Step 有状态(todo / running / done / failed);
- 可回滚:失败时能重试、跳过或重新规划;
- 动态调整:执行中遇到新信息时允许 replan;
- 并行友好:无依赖的 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|
| 检索 | | 编码 | | 审查 |
+-------+ +-------+ +-------+
| | |
+-----------+-----------+
↓
+-------------+
| 汇总/输出 |
+-------------+
- Orchestrator + Worker:一个编排器分发任务给多个 Worker Agent;
- Cross-review:双 Agent 并行审查,互相校验;
- Map-Reduce:并行处理子任务,再汇总结果;
- Factory / Luke 五范式:生成、审查、汇总、竞争、迭代等模式。
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/list、tools/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 权限模型设计要点#
- 按任务动态授予权限,不继承用户全部权限;
- 工具 Schema 严格,参数做校验;
- 高风险操作(写库、发邮件、删文件)走审批;
- 密钥隔离,禁止模型回显。
五、RAG / Agentic RAG#
5.1 传统 RAG 的局限#
- 单次向量检索,参数固定;
- 对复杂、多跳、需要交叉验证的问题力不从心;
- 检索不到就"硬答",容易 hallucination。
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 典型模式#
- ReAct RAG:Question → Thought → Action(检索) → Observation → ... → Answer;
- Self-RAG:生成过程中插入反思 token,判断是否需要检索、检索结果是否相关;
- Corrective RAG (CRAG):检索质量评估 → 高置信直接生成,低置信改写重试或切换外部搜索;
- Multi-Agent RAG:不同 Agent 负责检索、验证、汇总。
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 至今
单次输入优化 每步看到什么 模型如何在系统里可靠运行
- Prompt Engineering:优化单次输入;
- Context Engineering:优化每步看到什么;
- Harness Engineering:工程化驾驭 AI,全链路约束 + 质量闭环。
6.2 Harness Engineering 核心构成#
- 意图解构:FDQ 分析、模块拆分、设计稿对齐;
- 过程约束:Skill 驱动、Hard Gate 人工审核、双层 TDD;
- 评测体系:拦截率、采纳率、缺陷逃逸率,闭环自纠错。
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、不换模型"可带来远超模型升级的增益:
- 编辑工具格式与执行外壳改动,15 个模型不变,编码基准最高提升 10 倍;
- 固定 GPT-5.2-Codex,仅重构系统提示、上下文注入、自验证钩子,Terminal-Bench 2.0 从 52.8% 提升到 66.5%;
- 自动搜索优化 Harness 结构,Terminal-Bench-2 达到 76.4%。
七、Agent 沙箱、评估与治理#
7.1 Agent Sandbox(Execution 层)#
三大价值:
- 安全隔离:容器/虚拟机/权限限制,控制文件、网络、进程、密钥访问;
- 可复现性:环境能重置到已知基线,保证评测一致;
- 提升自主性:划定合法范围后,低风险动作自动执行,无需每一步授权。
沙箱架构:
+----------------------------------+
| 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 四原则#
- 每条知识可追溯 — 链回原始来源;
- 关键数字/事实标源 — 防 AI 摘要偏差;
- 冲突显式标注 — 新旧矛盾时列出双方来源;
- 不编造 — 没有出处写
(?)或TODO: verify。
8.4 三层 + 三操作#
三层架构:raw sources → the wiki → schema
- raw sources:事实锚点(会话、文章、server-wiki);
- the wiki:LLM 整理的知识页;
- schema:维护协议(SCHEMA.md、AGENTS.md)。
三个核心操作:
- Ingest:读新 source → 摘要 → 判断关联 → 更新实体/主题 → 标冲突;
- Query:高质量问答反向沉淀成页面;
- Lint:定期查孤儿/过期/冲突/缺来源。
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 生命周期管理:
Assess(评估任务可 Agent 化)
- 任务是否可被分解为可观测的步骤?
- 每一步是否有可验证的成功/失败标准?
- 是否依赖外部实时信息或只读知识?
- 失败代价有多高?是否需要人工兜底?
Instrument(工具化)
- 把外部能力封装成工具/MCP Server,输出结构化 Schema。
- 定义输入校验、幂等性、错误码、超时、重试策略。
- 给工具打标签(read / write / high-risk / human-approval)。
Develop(编排开发)
- 选择推理模式:ReAct / Plan-and-Execute / Reflexion / LATS。
- 设计 Prompt 版本、System Prompt 模板、Few-shot 示例。
- 搭运行时:状态机(LangGraph)、中间件链、记忆层、护栏、沙箱。
Curate(持续治理)
- 评估:离线回归 + 在线 shadow。
- 可观测:trace / span / event / cost。
- 安全:Guardrails、审计、回滚、灰度。
- 知识:失败案例回流、负面示例库、prompt 迭代。
口诀:先拆步骤、再固工具、再选模式、最后治理。很多 demo 级 Agent 失败,是因为跳过了「可验证」和「治理」两步。
12.2 高级推理模式对比#
12.3 工具/MCP 设计的工程最佳实践#
工具是 Agent 的「手」,设计好坏直接决定上限。推荐遵守以下规则:
1. Schema 是契约
- 每个工具必须有
name、description、input_schema(JSON Schema)。 description要写明:功能、边界、返回格式、异常语义。模型真的会读。- 枚举值用
enum,不要让它自由发挥。
2. 幂等性
- 写操作工具必须支持幂等键(
idempotency_key)或基于业务主键去重。 - 返回结果包含
idempotency_key和实际生效状态,便于 Agent 判断「是否已经做过」。
3. 错误契约
- 统一错误结构:
{ "error_code": "...", "message": "...", "retryable": true/false, "suggestion": "..." }。 - Agent 看到
retryable=true可自动重试;retryable=false则停止并上报。
4. 权限与最小暴露
- 工具按风险分级:
read/write/admin/human_approval。 - Guardrails 在每次 tool call 前做授权判断,失败默认 closed。
5. 版本与兼容
- MCP Server 版本化,工具列表变化时做向后兼容。
- 新增工具不要改旧工具语义;废弃工具保留至少两个版本。
6. 返回大小控制
- 大表查询必须支持分页/字段过滤,避免把 10MB JSON 塞进上下文。
- 推荐返回
summary + 可选 detail_url,让 Agent 决定要不要查详情。
点击查看:一个高可用 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 + 知识图谱 | 持久化 |
设计要点:
ThreadState 要结构化:不要只存字符串,要存
messages、todos、artifacts、metadata、tool_calls、cost。自动总结:消息超过窗口预算时,用 LLM 对更老消息做总结,保留最近完整消息。
去重与冲突:长期记忆写入前做去重,发现冲突时显式标注,不要静默覆盖。
权限隔离:用户 A 的记忆不能被用户 B 读取,团队知识要按空间隔离。
ThreadState扩展了artifacts(产物)、todos(待办)、title(自动标题)。12 步中间件链处理:thread 数据加载 → 沙箱初始化 → Guardrails → 压缩/总结 → 标题生成 → 工具调用 → 状态持久化。
12.5 可观测性:Trace / Span / Event#
生产 Agent 必须有可观测性,否则出问题只能「黑盒猜」。建议按 OpenTelemetry 语义分层:
- Trace:一次用户请求的完整链路。
- Span:链路中的每个阶段(如
plan、tool_call、llm_call、reflect)。 - Event:Span 内的关键事件(如
tool_call_started、tool_result_received、guardrails_blocked)。 - Metric:成功率、延迟、token 消耗、工具调用次数、循环次数。
- Log:结构化日志,包含
trace_id、thread_id、tool_name、model。
必看指标:
agent_turn_count:单请求平均循环轮数,突增说明任务失控。tool_success_rate:工具调用成功率。llm_first_token_latency/llm_total_latency。cost_per_request/cost_per_task。guardrails_block_rate:被护栏拦截的比例。
Trace 示例:
12.6 评估体系:离线 + 在线 + 人工#
Agent 的评估比传统软件复杂,因为输出是非确定性的。推荐三层评估:
1. 离线评估(Offline Evaluation)
- 用固定测试集跑 Agent,对比预期输出。
- 指标:
- Task Success Rate:任务是否完成。
- Tool Accuracy:工具调用参数是否正确。
- Hallucination Rate:是否存在事实幻觉。
- Latency / Cost:延迟和成本是否符合预算。
- 可以用 LLM-as-judge 打分,但必须人工校准分数分布。
2. 在线评估(Online Evaluation)
- Shadow 模式:新模型/新版本并行跑,不返回给用户,只收集指标。
- A/B Test:按流量切分,对比用户满意度、任务完成率。
- 渐进式发布:5% → 20% → 50% → 100%。
3. 人工评估与反馈闭环
- 对失败案例打标签:
工具错误、模型理解错、上下文不足、用户问题不清、护栏误杀。 - 把失败样本沉淀为回归测试用例和负面示例。
- 定期 review top 失败模式,驱动 prompt/工具/护栏迭代。
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. 模型层灰度
- 同一 Agent 支持多个模型后端(如 GPT-4o / Claude 3.5 / 内部模型)。
- 用 feature flag 控制不同用户/流量/场景走不同模型。
- 关键指标:成功率、成本、延迟、用户满意度。
2. Prompt 版本灰度
- Prompt 也要版本化,和代码分开发布。
- 新 prompt 先走 shadow,再小流量 A/B,最后全量。
- 记录每个版本的评估分数和线上指标。
3. 工具/MCP 版本灰度
- MCP Server 独立部署,Agent 通过 registry 发现。
- 新工具默认对灰度用户可见,观察调用成功率后再全量。
4. 熔断与降级
- 模型 API 失败时降级到备用模型或返回兜底答案。
- 工具失败时返回「部分结果 + 说明」,不要让 Agent 空转。
- 循环次数超过阈值直接截断,给出「当前进展 + 未完成项」。
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))
代码解读:
AgentState保存完整消息历史、轮次、终止标志。plan初始化 system prompt 和用户问题。act调用 LLM,如果是tool_call就执行工具并把结果塞回上下文。reflect在工具失败后插入反思提示,让模型下一轮回合修正。should_continue控制循环终止:任务完成或达到max_turns。- 真实环境替换
call_llm为实际模型 SDK,工具注册中心化即可。
12.11 开发工具链分层:可观测 / 取证 / 代码地图 / 评估#
| 层 | 时态 | 回答的问题 | 代表工具 |
|---|---|---|---|
| ① 运行时可观测 | 在线 | 「这次内部哪一步坏了」 | Langfuse、Arize Phoenix、LangSmith |
| ② 事后 transcript 取证 | 离线复盘 | 「这次会话到底做了什么」 | claude-devtools 类 |
| ③ 代码理解 / 代码地图 | 静态 | 「代码谁调谁、改哪影响谁」 | GitNexus、Serena、Understand Anything |
| ④ 评估 / 回归 | 批量 | 「改完整体变好还是变差」 | Dataset + LLM-judge(Langfuse / Braintrust) |
四层各自的代表与要点:
① 运行时可观测(Langfuse 为例):核心数据链
trace → observation(span/generation/tool/retrieval) → score。多步 agent 失败往往在中间某步(工具参数错、检索召回空、reasoning 跑偏),扁平 log 还原不出「谁调谁、第几步坏」,trace 把嵌套调用链完整记录,回放一条失败 trace 即可定位坏 span。Langfuse 开源可自托管、框架无关;LangSmith 是 LangChain 官方但闭源、社区无自托管;Phoenix 最 OTel 原生(注意许可是 Elastic License 2.0)。② 事后 transcript 取证(claude-devtools 为例):关键洞察——CLI coding agent(Claude Code / Codex 等)多数没有运行时埋点钩子,接不进①,所以对它们「事后读日志取证」比「运行时 trace」更现实。claude-devtools 只读
~/.claude/已落盘的 session transcript,零侵入还原被终端折叠的:subagent 执行树、逐轮 token 归因(CLAUDE.md/skills/@文件/tool I/O/thinking/team/user text)、context compaction 可视化。不 wrap、不改被观测对象。③ 代码理解 / 代码地图:正交于①②——前两层看「agent 跑起来的行为」,这层看「agent 要操作的代码本身」。三种流派:静态图谱(GitNexus,Tree-sitter 抽调用-依赖建图 + Graph RAG,答 blast radius)、实时符号操作(Serena,基于 LSP 做 find-refs / rename / symbol editing)、文本打包(Repomix,把库压成省 token 的上下文)。GitNexus 与 Serena 互补:一个画地图看架构,一个在地图上精确改符号。
④ 评估 / 回归:与①常同平台但职责不同——①看单次发生了什么(调试),④批量衡量好不好(回归)。把线上 bad case 攒进 Dataset,每次改 prompt / 换模型都跑 experiment + LLM-as-a-Judge 打分,防「修一个坏三个」。
详见知识库 [[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 就会崩溃。
采样参数怎么选:
- Temperature 控制概率分布尖锐程度;Top-P(核采样)从概率累积达 P 的最小 token 集合里动态截断。
- Agent 工具调用推荐 Temperature=0.1-0.2、Top-P=0.1-0.3——选错工具=任务失败,要最高确定性;ReAct 的 Thought 推理步可放宽到 0.2-0.5。
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 主循环五大保护机制:
max_turns防无限循环;- Token Budget 防上下文爆炸(达 80% 阈值触发压缩);
- 递减收益检测防死循环(连续 3 轮输出相似度 > 0.9 终止);
- 指数退避重试处理瞬时故障(1s→2s→4s→8s→16s);
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 间接访问系统资源。
- 命令白名单:只允许预定义安全命令(前缀匹配 ls/cat/grep/git 等);
- 危险模式正则拦截:白名单内也查参数,拦
rm -rf /、dd if=、fork bomb; - 沙箱执行环境:Docker 容器(文件系统隔离 + 资源限制)/ Firejail / bubblewrap + 权限降级(非 root、只读挂载关键目录);
- 人工审批:部署、删除、网络请求等敏感操作弹确认。层层递进,一层被绕过下一层兜底。
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 正式提出的可组合架构,从原子到发行单元:
- Skill(技能):最小原子能力,
SKILL.md(YAML frontmatter + 指令),放~/.claude/skills/或项目.claude/skills/。 - Command(斜杠命令):把多个 Skill 编排成工作流,
/command触发。 - Hook(钩子):生命周期事件上挂 shell 命令 / HTTP 端点 / LLM prompt / subagent,做强制约束与横切逻辑(权限、压缩、审计),洋葱式组合(before→内层→after)。
- Plugin(插件):面向 marketplace 的发行单元,
/plugin install <name>@<marketplace>,把 Skill+Command+Hook+MCP+LSP 打成一个可版本化的包。经验阈值:某领域 Skill 攒到 >10 个就该升级成 Plugin。
Hook 生命周期事件(按触发节奏记三类 + 专项):
- 会话级:
SessionStart/SessionEnd(每会话一次) - 轮次级:
UserPromptSubmit(提交前,可拒绝 prompt)、Stop/StopFailure(每轮结束;Stop可阻止结束、让对话继续) - 工具调用级:
PreToolUse(执行前,可阻断或改写入参)、PostToolUse/PostToolUseFailure - 专项:子代理
SubagentStart/SubagentStop、上下文压缩PreCompact/PostCompact等
配置写在 settings.json 的 hooks 段:先选事件 → 加 matcher(如只对 Bash、Edit|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(注入上下文)。
- Subagent(子代理):
Task工具派生的独立上下文子 agent(如 Explore / Plan / 自定义 reviewer),有独立 context 窗口,结论回传主 agent——把大批中间文件、并行搜索挡在主 context 之外。SubagentStart/SubagentStop可挂 hook。 - MCP 关键坑:MCP 工具列表在会话进程启动时快照,中途新增/改 MCP server 当前会话不生效,必须重启进程。
T.3 OpenAI Codex CLI —— 双维度沙箱(权限最细)#
审批与沙箱是两个正交维度,是三大 CLI 里粒度最细的:
- 审批
-a/--ask-for-approval:untrusted(仅受信命令免审)/on-request(模型自决)/never(从不问)。 - 沙箱
-s/--sandbox:read-only/workspace-write(锁工作目录、默认禁网)/danger-full-access(无沙箱可联网)。
codex --full-auto # 自动执行 + 锁工作目录 + 禁网(日常推荐)
codex -a never -s workspace-write # 无人值守但锁目录(CI)
codex --dangerously-bypass-approvals-and-sandbox # 仅容器/CI,极危险
- 持久化在
config.toml(approval_policy+sandbox_mode),项目根.codex/config.toml覆盖全局;旧 TS 版的--approval-mode auto/auto-edit/manual已废弃。 - Provider:
[model_providers.*],wire_api支持responses/chat completions,可挂 Azure/Ollama/第三方。 - 记忆:
~/.codex/memories/是会话级快照,非跨会话沉淀(这点弱于 Claude Code)。 - 无状态、可管道:
cat diff | codex exec --sandbox read-only,适合 cross-review、CI headless(--quiet/--json)。
T.4 Kimi Code —— 双协议 + 多网关路由#
- 配置:单文件
~/.kimi-code/config.toml。差异化卖点是自定义 provider 同时支持openai与anthropic两种协议类型:
[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"] }
- capabilities 显式声明:图片识别必须在列表里带
image_in,否则不认多模态;thinking/tool_use同理。 - Skill 体系:原生 scoped skill(project / user / plugin / built-in 多级作用域,
SKILL.md定义),并可用extra_skill_dirs外挂共享目录,与 Claude Code 等共用~/.agents/skills/。 - Agent / Workflow:hooks(
config.toml声明)、subagent 派生与 fork-join 并行、plan mode(先评审计划再执行)、goal 模式(跨多轮自主目标)、cron 定时任务;MCP 经 gateway 接入。 - 权限:permission 模式分级(manual / auto / yolo),敏感与破坏性操作逐次确认;安全亦可由 upstream gateway/provider 兜底。
- 运维:
/reload(TUI 内热加载新 provider)、kimi doctor config <path>(校验配置)。
T.5 Gemini CLI —— Extensions + 自定义命令 + GitHub Actions#
- 配置:
~/.gemini/settings.json(含 hooks、MCP、主题),~/.gemini/skills/常软链到~/.agents/skills/;项目GEMINI.md(支持@其它.mdimport)。 - 自定义斜杠命令:本地
.toml文件或经 MCPlist/prompt自动转成/<command>。 - Extensions:
gemini extensions install <git-url>(HuggingFace / Monday / Terraform 等官方伙伴扩展),本质是打包好的 MCP + 命令。 - MCP 管理:
gemini mcp add|remove|list,支持 HTTP headers、/mcp authOAuth、includeTools/excludeTools白名单。 - Policy Engine(v0.20+):策略文件控制何时/是否弹权限确认;
gemini --sandbox(Docker/macOS Seatbelt profile)。 - GitHub Actions:
@gemini-cli在 issue/PR 里触发 triage/review/改代码(/setup-github配置)。 - Skill Conflict:
~/.gemini/skills/与~/.agents/skills/重名会告警——同名覆盖、Hook 链顺序冲突要靠命名空间隔离 + 启动时冲突检测。
T.6 Cursor / 其它#
- Cursor:项目
.cursor/rules/*.mdc(MDC 格式,frontmatter 可设globs/autoAttach/manual/@file引入),取代旧.cursorrules;MCP 配~/.cursor/mcp.json;1.7(2025-09)新增 Hooks、Team Rules、Agent Autocomplete;编辑走专用训练的 70B 模型而非通用 diff。 - oh-my-pi(omp):Hashline 编辑协议——读文件时每行附内容哈希标签(
22:f1|content),模型只引用标签编辑、文件外部改动即哈希不匹配拒编(并发安全);一等 subagent(独立 worktree+schema 校验)、TTSR 时间旅行流规则(正则触发注入护栏)、自动读 Cursor MDC / Codex AGENTS.md / Copilot 等 8 种配置格式。 - jcode:Rust,极致冷启动(14ms),多 session swarm 协调(可跑上百 session)+ 跨会话 Agent memory + subagent,
jcode login --provider claude/openai/gemini/copilot/...。 - Aider:
repo map(把代码库压成 AI 友好上下文注入),.aider.conf.yml,/architect模式(规划与编辑分离);OpenAI 兼容 HTTP 客户端。 - AGENTS.md(跨工具中立约定):放仓库根、随代码走、团队共享,被 Codex / Kimi Code / Cursor / Gemini 等会话开始自动读取;与 Claude 专属
CLAUDE.md的区别是「中立可被多 harness 复用」。推荐提交进 Git。
T.8 模型是怎么调用工具的?—— Tool Calling / Function Calling / MCP 三层#
先厘清最常被误解的一点:模型自己不执行任何工具。模型能做的只有生成 token;所谓「调用工具」,是模型按约定格式输出一个结构化的调用意图(调哪个工具、参数是什么),由外部的 runtime / harness 真正去执行,再把结果回填进上下文让模型接着推理。这是「模型提议 → 宿主执行 → 结果回喂」的循环,不是模型直接联网、读文件或删库。
T.8.1 一次完整的 tool-use 循环#
- 注入工具定义:请求里带
tools(每个工具一份 JSON Schema:name + description + parameters),runtime 把它拼进上下文。 - 模型输出调用:模型决定用工具时不吐自然语言,而是吐一个调用块(Anthropic 叫
tool_use,OpenAI 叫tool_calls),含 name + 结构化 arguments(JSON)。 - runtime 执行:宿主解析 arguments,真正跑代码(读文件、curl、查 DB),拿到结果。
- 结果回填:把结果作为
tool_result(OpenAI 里是 role=tool 的消息)追加进对话,再次请求模型。 - 循环:模型可能继续调下一个工具(agentic loop),或给出最终自然语言答复。多步任务就是这个循环转很多轮。
一句话:Tool Calling 是总称;Function Calling 是其中最常见的 JSON Schema 函数工具形态。模型表达「下一步动作」,执行权始终在宿主。
- 把工具执行结果直接返回给用户——模型没看到结果,无法总结/续推,循环断掉。
- 不校验参数就执行——等于把代码执行权交给 LLM,是 prompt injection 的头号入口。
tool_call_id对不上导致模型「失忆」——回填的 result 必须带上发起时那个 id。- 忽略
finish_reason/stop_reason就盲目再调一轮——只有值为tool_calls/tool_use才该执行工具。 - 一个工具做太多事——模型失去组合编排能力,工具要原子化。
并行工具调用:模型可能在一轮返回多个 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 怎么确保模型「能调且调对」工具#
模型不肯调、调错工具、传错参数,是实践里最常见的坑。可靠性从下面几层堆出来:
- 能力开关:模型/端点得先支持 tool use。自建网关(如 Kimi Code 的 provider)要在
capabilities里显式声明tool_use,否则请求里的tools会被忽略——「模型不调工具」十有八九是这里没开或走了不支持的协议路由(见 T.4)。 - Schema 即契约:
description写清楚「什么时候用」,参数用 JSON Schema 严格约束(enum/required/type/pattern),能大幅降低乱传参。参数越自由,幻觉越多。 - 强制调用与严格 schema:需要模型必调工具时用
tool_choice(Anthropicany/指定 tool、OpenAIrequired);OpenAI function tools 优先开strict: true,并把additionalProperties:false与字段 required 写完整。严格 schema 减少格式错误,但不能替代运行时权限、业务校验与幂等设计。 - 参数校验 + 回错重试:runtime 收到 arguments 先按 schema 校验,非法就把错误信息当
tool_result回喂,模型会自我纠正重调(agentic self-repair)。 - 结果必须回填:发起了 tool_call 却不把 tool_result 塞回去,下一轮请求就会因「有 tool_call 无对应 result」直接报错——这是新手最常见的协议错误。
- 控制工具数量:几十上百个工具塞进上下文会稀释注意力、涨 token、降准确率。对策:Tool RAG——对工具 description 做向量检索,只注入最相关 K 个工具 schema,工具 >20 个时省 70%+ token 并减少选择混淆;配合命名空间隔离(
mcp__<server>__<tool>)。 - 采样参数:工具选错=任务失败,工具调用应走低温(Temperature 0.1-0.2、Top-P 0.1-0.3)拿最高确定性;ReAct 的 Thought 步可放宽到 0.2-0.5。
- 权限与安全:执行权在宿主,所以危险动作的闸门也在宿主——
PreToolUsehook 拦截、沙箱、审批模式(见 T.2/T.3)。把不可信来源(文件内容、网页、工具输出)当数据而非指令,防 prompt injection 借工具结果提权。
T.8.3 MCP —— 工具接入的标准协议#
MCP(Model Context Protocol) 是 Anthropic 2024 年底开源的开放协议,把「模型/Agent ↔ 外部工具与数据源」的接入标准化。类比:MCP 之于 AI 工具,如同 USB-C 之于外设、LSP 之于编辑器——一次实现,处处可接,不用给每个 CLI 单独写一遍集成。
- 架构:
Host(Claude Code / Cursor 等 Agent)内含Client,每个 Client 一对一连一个Server;Server 对接真实能力(GitHub、DB、文件系统、内网 API)。 - 协议层:基于 JSON-RPC 2.0。当前实现应围绕能力/版本发现、
tools/list(拉工具 schema)、tools/call(执行)、resources/*、prompts/*与变更/进度通知设计;initialize是旧版客户端常见握手,不要把它写成所有 MCP 实现唯一且不变的生命周期。 - 传输:
stdio(本地子进程,凭据走环境变量、暴露面更小)/Streamable HTTP(远程聚合、易水平扩展)。SSE 是 Streamable HTTP 的可选流式响应形式,不作为独立并列 transport;远程 Server 常配 OAuth 或服务端 Token,并应遵守客户端与服务端约定的请求方法、Accept头和会话语义。 - Server 暴露三类原语:Tools(可执行动作,模型会主动调)、Resources(可读数据/文件,按 URI 取)、Prompts(预置提示词模板,常映射成斜杠命令)。记忆句:Tool 是动作,Resource 是上下文,Prompt 是模板。
- 和 tool calling 的关系:MCP 不替代 tool/function calling,而是工具发现与接入的标准层。Host 可以把
tools/list得到的 schema 适配为模型的 function tools,也可以在 provider 支持时让其直接连接 MCP server;无论哪种,仍要有“模型选择 → 受控执行 → 结果回填”的 runtime 循环。即:MCP 负责「工具从哪来、如何互联」,Tool Calling 负责「模型如何表达调用」。 - 关键坑(见 T.2):不要假设工具列表永远静态。新版 MCP 支持能力发现、动态工具列举和变更通知;Host 是否立即刷新取决于实现。生产上应为工具 schema 做版本、缓存失效、白名单和回归测试,而不是依赖重启来“刷新一切”。工具命名通常带前缀(如
mcp__<server>__<tool>),hook 的 matcher、白/黑名单都按这个匹配。
T.8.5 本地项目实证(把机制落到真实代码)#
| 机制(对应上文) | 项目里怎么做的 | 关键位置 |
|---|---|---|
| 双协议工具调用抽象(T.8.1 协议对照) | 统一 LLM 客户端手写解析 OpenAI 与 Anthropic 两套工具协议:OpenAI 侧按 delta.tool_calls 的 index 累积 id/name/arguments 分片,Anthropic 侧按 content_block 累积、input_json_delta.partial_json 拼 JSON;消息转换把 role:tool ↔ tool_result block、toolCalls ↔ tool_use block 互转 |
swarm-unified — packages/core/src/runtime/llm-client.ts |
| 工具循环 + 终止 + 上下文压缩(T.8.1 循环) | runWithTools 最多 10 轮,每轮流式收 tool_call→执行→role:tool 回灌;轮次耗尽做一次「不给工具」的强制总结;历史超 20 条压成 1 条 summary+保留 19 条 |
swarm-unified — runtime/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-v2 — backend/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-gateway — gateway.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/指定函数 |
SkillOpt — skillopt/model/claude_backend.py |
| 工具执行安全边界(T.8.2 权限) | bash 工具走 Docker 沙箱(networkDisabled、512m、30s 超时);read/write_file 做路径遍历防护锁在 workspaceRoot 内 |
swarm-unified — tools/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-backend — src/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 家族:推理模型后训练的事实标准#
- 路线演进:RLHF(PPO + 人工偏好)→ RLVR(用可自动验证的结果奖励——数学答案、代码测试——替代偏好模型,无 critic,组内相对优势归一)。
- GRPO 变体各治一个坑:DAPO(解耦 clip + 动态采样,治熵崩溃与长 CoT 大规模训练);GSPO(重要性比率从 token 级改到序列级,稳住 MoE 的 RL 训练);Dr.GRPO(修正 R1-Zero 式训练的方差缩减偏差);长度控制类(GFPO/DLER)抑制推理长度爆炸。
- Test-time compute 精细化:s1 的 budget forcing(追加 "Wait" token 控制推理预算)成经典手段;生成式过程奖励(GenPRM)显式扩展 CoT;2026 年出现 test-time training/RL 方向。
F.3 模型格局:万亿开源 agentic 模型 + 闭源分域领先#
- Kimi K2(Moonshot,2025-07 开源):1.04T 总参 / 32B 激活 MoE,MLA 架构;训练创新 MuonClip 优化器(qk-clip 钳制 attention logit 爆炸),15.5T token 全程零 loss spike;后训练用大规模 agentic 数据合成(含 MCP 工具)+ 可验证与自评判 RL,主打非思考模式的 agentic 能力。
- DeepSeek V3.2(2025 末):引入 DSA 稀疏注意力,长上下文下大幅降注意力计算量;配套大规模 agent 任务合成管线做后训练(竞赛级成绩等表述需核实)。
- 闭源侧 2025-11 三周内旗舰连发:Gemini 3(1M–2M 上下文 + 原生多模态)、GPT-5.1(Instant/Thinking 双模式路由)、Claude Opus 4.5(首个 SWE-bench Verified 破 80%,主打长时程 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(需核实) |
- 治理:2025-12 Anthropic 将 MCP 捐赠给 Linux 基金会旗下 Agentic AI Foundation(AAIF)(与 Block、OpenAI 联合创立,AWS/Google/Microsoft 等支持)——治理中立化,复制「成为行业标准」路径(需核实)。
F.5 Agent 工程共识与评测安全#
- 多 agent 收敛于 Orchestrator-Subagent:生产上唯一可靠形态 = 编排者把只读、窄范围任务委派给隔离子 agent 再聚合结果(Anthropic 多 agent 研究系统:lead agent 规划 + 并行 subagent 搜索 + 压缩回传,解上下文爆炸/串行慢/路径依赖);共享状态并行写入因误差累积而失败(Cognition《Don't Build Multi-Agents》)。代价:token 消耗约为单 agent 的 15 倍量级。
- Computer Use / GUI agent:OSWorld 从 2024 末的 ~15% 涨到 2026 年的 80% 量级(具体产品数字需核实);OSWorld 2.0 转向长时程真实任务,并指出 benchmark 仍高估真实办公完成率。
- Prompt injection 走向架构级防御:AgentDojo(97 真实任务 + 629 注入用例)成动态攻防基准事实标准;MCPSecBench 梳理 MCP 4 个攻击面 17 种攻击类型;防御思路从「检测恶意输入」转向权限分离双 agent、上下文压缩残差隔离;Skills 文件内嵌恶意指令成为新攻击面。
- 评测范式批评:只看成功率的榜单会掩盖过程违规(用 SQL 注入完成任务也算成功)→ 细粒度轨迹分析 + 动态换题基准(防 Goodhart)。设计生产 agent 的 eval 要看:任务成功率之外的轨迹质量、成本、延迟、安全性。
补遗三 · 个人 Agent 系统实战(记忆 · 心跳 · 多 agent 编排 · skill 工程化)#
P.1 Agent 记忆系统:三层架构 + append-only 铁规#
- 三层记忆:原始层(
memory/YYYY-MM-DD.md每日日志,session 结束蒸馏写入)→ 提炼层(MEMORY.md,决策/教训/约定)→ 专题层。后两层不全量塞进 context,而是 FTS + 向量检索按需注入——记忆是可检索存储,不是 prompt 填充物。 - 写入前先分维度:「用户是谁」→ USER.md(L0 常驻每轮注入);「我们知道了什么」→ MEMORY.md;「今天发生了什么」→ 每日文件。一句话含多个维度要拆开分别写。
- 防 agent 搞坏记忆库的铁规:写日期必须先调工具拿真实时间(禁止猜日期);memory 文件只准 append/edit,绝对禁止 write 覆盖;设容量上限(100 文件 / 单文件 50KB),接近上限时整合进长期记忆或显式遗忘(先 query 确认、再按 chunk 精确删)。
- 睡眠整理(sleep-time compute 的朴素版):凌晨 cron 跑 Harvest(采集 24h 会话)→ Distill(识别偏好声明/环境变化/技术知识)→ Archive → Report,模仿人类睡眠巩固记忆;心跳窗口顺手做记忆「垃圾回收」。
P.2 Proactive Agent:心跳机制 + 数值化主动性#
- 心跳模型:agent 周期性被唤醒,读 HEARTBEAT.md 清单逐条判断「到点了吗」再执行;空文件 = 不触发心跳 API 调用(每一行都在烧 token);心跳只做轻量检查,避免巡检触发深度分析。
- 心跳 vs cron 的分工:需要对话上下文、时间可浮动的打包检查 → 心跳;精确时刻的独立任务 → cron。
- 主动性数值化:
activity_level(0.01.0)持久化存储,用户说「太吵了」立即降 0.150.3;搭讪 ≥3 次没回应则显著降频。另有独立的 LLM 活跃度分析器:把当前 level、未回复数、记忆摘要喂给模型,输出{new_level, reason},reason 留作审计。
P.3 Subagent 编排六条铁律(生产踩坑沉淀)#
- 两阶段评审,顺序不能反:先 spec 符合性评审(对照原始 spec 查漏项、防 scope creep),通过了才做代码质量评审。
- 任务全文直接塞进 subagent 的 context,绝不让 subagent 自己去读计划文件(计划文件只读一次);不派多个实现 subagent 碰同一批文件。
- No agent should verify its own work——实现者自查不能替代独立评审,fresh context finds what you miss。
- Orchestrator 铁律是 decompose, don't execute;派发前必须先发现本机实际有哪些 profile——对不存在的 assignee 派发会静默失败,任务永远卡住。
- 任务依赖用 parent 门控:先建父卡拿到 id 再建子卡,否则 dispatcher 可能在依赖就绪前抢走子任务。
- 双车道不信任模型:把外部 CLI(如 Codex)当隔离实现车道时,它的 diff 是「不可信补丁」——必须独立评审 + 自己亲自重跑测试才验收;跨 agent 交接走结构化 metadata(changed_files / tests_run / decisions),下游不翻聊天记录。
P.4 Skill 工程化:三级渐进加载 + 触发率 eval#
- 三级渐进加载(Progressive Disclosure):① metadata(name+description,~100 词常驻 context)→ ② SKILL.md 正文(触发时加载,理想 <500 行)→ ③ 打包脚本/资源(按需,可执行不加载)。本质是用描述匹配做能力路由,避免把所有工具说明塞进 system prompt。
- 触发准确率可以做 eval 优化:description 是触发的唯一依据;造 20 条 trigger query(should-trigger + near-miss 型 should-not-trigger——共享关键词但其实不需要该技能的查询才有测试价值),每条跑 3 次取触发率,LLM 迭代改写 description,按留出集(test)分数而非训练集选最优,防过拟合。
- 反直觉点:模型对「自己一步就能搞定」的简单请求不查 skill——eval query 必须复杂到「值得查技能」才有意义。
P.5 CLI Agent 运维实战(745 行 claude-code 技能沉淀)#
- 编排 TUI 类 agent 的唯一可靠方式:tmux 包 PTY(send-keys 输入、capture-pane 监控);一次性任务用 print 模式(
claude -p)+--output-format json拿 session_id/cost/num_turns 结构化结果。 - 实测结论:「context 使用超过 70% 后,AI 输出质量可测量地下降」——用
/context监控、主动 compact;不同任务开新 session 比续旧 session 更高效。 - Hook 实战:
PreToolUse钩子里 exit 2 = 阻断工具调用(做安全门拦rm -rf、force push);PostToolUse配Write(*.py)matcher 自动跑 linter;权限支持 glob 如Bash(git push*)。 - 成本三板斧:
--max-turns防跑飞、--max-budget-usd卡预算、简单任务--effort low/ 换小模型。
延伸阅读#
面试专项#
需要集中复习、组织回答框架或做临场速览时,请进入 面试专项。
企业项目实践#
企业内部项目的架构、实现与复盘包含非公开信息,已独立存放并加密。获得授权后可进入 企业项目实践。
更新记录#
v2.1 · 2026-08-01 · Tool Calling / MCP 知识点核验更新#
- 更正“Function Calling 已没人用”的误解:当前常统称 Tool Calling,它仍是模型表达工具调用的核心机制;MCP 负责工具发现与接入,两者互补。
- MCP 传输说明统一为 stdio / Streamable HTTP;SSE 标注为 HTTP 的可选流式承载,而非独立并列 transport。
- OpenAI 示例升级为 Responses API 的
function_call/function_call_output;MCP Server/Client 示例升级为官方 FastMCP SDK,并同步面试专项、速览与项目架构说明。