在这里插入图片描述

0. 前言

过去一段时间,AI Agent 成为大模型应用开发中最热门的方向之一。

很多入门文章会把 Agent 概括为:

Agent = LLM + Tool Calling + Memory + Planning

这个定义适合入门,但远远不够工程化。

真正进入企业级应用、复杂业务系统、AI coding agent、医疗问答 agent、企业知识库 agent、自动化运维 agent 之后,Agent 开发的核心问题并不是“模型是否支持 function calling”,而是:

  • 参数缺失时是否能正确处理?
  • 工具失败时是否会幻觉回答?
  • 工具返回超长结果时如何压缩?
  • 多个工具结果冲突时如何仲裁?
  • 权限不足时是否会绕过系统?
  • 长期记忆是否会污染?
  • 上下文是否会被日志、网页、历史对话撑爆?
  • 长任务执行是否有状态机、checkpoint、trace?
  • 每一次 tool call 是否可以审计、复现和评测?

这些问题才是 Agent 工程化的核心。

本文围绕 Agent 开发中的几个核心主题展开:

  1. Harness Engineering 是什么;
  2. Tool Calling 的完整执行状态机;
  3. 参数缺失、工具失败、权限不足、结果冲突的处理策略;
  4. 工具输出过长时的结构化提取与 Observation Compression;
  5. Reference Pointer / Evidence Pointer 的工程实现;
  6. Memory System 的分层设计;
  7. Context Management 与 Context Pollution;
  8. Hooks、Skills、MCP、CLI、Plugin 的关系;
  9. Claude Code、Hermes、OpenClaw 的框架对比;
  10. 面试级回答框架与工程实现思路。

1. Agent 不是 Prompt,而是受控执行系统

1.1 常见误解

很多 Agent 教程会停留在下面这个层面:

用户输入
  ↓
LLM 思考
  ↓
调用工具
  ↓
返回答案

这种理解太粗糙。

真实工程中的 Agent 更接近下面这种结构:

User Input
  ↓
Intent / Task Understanding
  ↓
Context Builder
  ↓
LLM Reasoning
  ↓
Tool Selection
  ↓
Argument Generation
  ↓
Schema Validation
  ↓
Permission Check
  ↓
Tool Execution
  ↓
Observation Compression
  ↓
Memory Update
  ↓
Loop Controller
  ↓
Final Answer / Next Action

这已经不是一个简单的 prompt chain,而是一个带状态、带权限、带工具、带审计、带失败恢复的软件系统。


2. Harness Engineering:Agent 的工程外壳

2.1 Harness 的定义

Harness 可以理解为包裹 LLM 的运行时外壳。

更精确地说:

Harness 是围绕大模型构建的工程执行系统,负责上下文构造、工具暴露、参数校验、权限控制、工具执行、结果压缩、记忆管理、失败恢复、轨迹记录和循环终止。

LLM 只负责语言理解和推理建议。
Harness 决定这个建议能不能真正安全、稳定、可控地执行。


2.2 Harness 的核心模块

Agent Harness
├── Context Builder
├── Tool Registry
├── Tool Runtime
├── Schema Validator
├── Permission Manager
├── Memory Manager
├── Observation Compressor
├── Event Logger
├── Loop Controller
├── Error Handler
└── Evaluator

2.3 为什么 Harness 重要?

因为大模型天然是不确定的。

大模型可能:

  • 编造参数;
  • 选择错误工具;
  • 忽略权限;
  • 在工具失败后继续生成自信答案;
  • 把过长结果塞进上下文;
  • 被旧记忆误导;
  • 在长任务里忘记原始目标。

Harness 的任务就是把这种不确定性纳入确定性的工程约束。


3. Tool Calling 不等于 Function Calling

3.1 表层 function calling

表层 function calling 通常是这样的:

{
  "name": "weather_api",
  "arguments": {
    "city": "Hangzhou",
    "date": "tomorrow"
  }
}

这只是工具调用链路中的一个中间产物。

3.2 工程级 Tool Calling

工程级 tool calling 至少包含以下步骤:

1. Tool Discovery
2. Tool Selection
3. Argument Generation
4. Schema Validation
5. Argument Completion
6. Permission Check
7. Tool Execution
8. Result Validation
9. Result Compression
10. Conflict Resolution
11. Context Write-back
12. Trace Logging

因此,面试或系统设计中讨论 tool calling 时,重点不应该是“模型能否生成 JSON”,而应该是:

当 JSON 不完整、不合法、不安全、不可信、不够用时,系统如何处理?


4. 参数缺失:Schema Validation + Risk-aware Completion

4.1 问题示例

工具 schema:

{
  "name": "weather_api",
  "required": ["city", "date", "region"],
  "properties": {
    "city": {"type": "string"},
    "date": {"type": "string"},
    "region": {"type": "string"}
  }
}

用户问题:

后天天气怎么样?

模型可能生成:

{
  "date": "2026-05-21"
}

这里缺少 cityregion


4.2 错误做法

错误做法是让模型随便猜:

{
  "city": "Hangzhou",
  "date": "2026-05-21",
  "region": "Zhejiang"
}

如果上下文从未出现杭州,这就是幻觉参数。


4.3 正确流程

Generated Arguments
  ↓
Schema Validation
  ↓
Detect Missing Required Fields
  ↓
Context Lookup
  ↓
Session State Lookup
  ↓
User Profile / Permissioned Location Lookup
  ↓
Default Value Policy
  ↓
Clarification Question

4.4 参数类型分级

参数类型 示例 策略
Hard Required patient_id、order_id、city 缺失时不能执行
Soft Required date、language、format 可从上下文或默认值补全
Optional limit、sort_order 可省略
Dangerous Argument file_path、shell_command 必须额外校验

4.5 风险分级

场景 策略
天气查询 可低风险补全,但需说明假设
医疗问诊 不应随便补全症状和病史
金融交易 不能猜账户、金额、交易对象
数据库写入 需要权限和确认
Shell 命令 需要 sandbox、allowlist、denylist

4.6 面试级表达

参数补全不是简单 NLP 问题,而是风险控制问题。
对于 required arguments,系统需要先做 schema validation。
缺失字段优先从上下文、session state、用户授权信息中补全。
如果无法高置信补全,低风险场景可以使用默认值并显式声明假设;
高风险场景必须反问或终止。

5. 工具失败:Error Classification + Policy-based Recovery

5.1 工具失败不能简单 retry

工具失败可能来自完全不同的原因。

timeout
rate limit
permission denied
invalid argument
empty result
internal server error
partial success
business rule violation

如果所有错误都 retry,只会制造更多问题。


5.2 工具失败分类表

失败类型 示例 是否 retry 处理策略
Transient Failure timeout、502 exponential backoff
Rate Limit 429 部分 等待、降级、换 provider
Auth Failure token expired refresh token / request auth
Schema Failure 参数格式错误 参数重生成 / 反问
Business Empty 查无结果 明确返回未找到
Tool Bug exception 部分 fallback + trace
Partial Failure 批量任务部分成功 返回成功/失败明细
Dangerous Failure 修改一半失败 rollback / human review

5.3 结构化错误 Observation

工具失败后,不应该返回一段自然语言给模型,而应该返回结构化 observation:

{
  "tool": "query_order",
  "status": "failed",
  "error_type": "schema_error",
  "retryable": false,
  "message": "order_id must be numeric",
  "trace_id": "tool_run_001",
  "suggested_action": "ask_user_for_order_id"
}

Loop Controller 根据 error_typeretryable 决定下一步。


5.4 失败恢复策略

if error.retryable:
    retry with backoff
elif error.type == "schema_error":
    regenerate arguments or ask clarification
elif error.type == "permission_denied":
    request permission or stop
elif error.type == "empty_result":
    report no result
else:
    fallback or escalate

6. 权限不足:Prompt 不是安全边界

6.1 错误思路

在 system prompt 里写:不要执行危险命令。

这不够。

模型可能仍然输出危险工具调用。
真正的权限控制必须在 tool runtime 层执行。


6.2 权限控制体系

Permission Manager
├── RBAC
├── ABAC
├── Tool Allowlist
├── Tool Denylist
├── Resource Scope
├── Human Confirmation
├── Sandbox
├── Dry Run
├── Audit Log
└── Rollback

6.3 操作风险分级

操作 风险 策略
读取公开文档 直接允许
读取项目文件 检查 workspace scope
修改代码 需要 diff 可追踪
删除文件 人工确认
执行 shell sandbox + denylist
访问生产库 极高 默认禁止
发送邮件 人工确认
转账 / 下单 极高 强制确认

6.4 PreToolUse Hook 示例

def pre_tool_use_hook(tool_name: str, args: dict) -> dict:
    if tool_name == "bash":
        command = args.get("command", "")

        dangerous_patterns = [
            "rm -rf /",
            "sudo rm",
            "curl | bash",
            "mkfs",
            "dd if=",
            "chmod -R 777"
        ]

        for pattern in dangerous_patterns:
            if pattern in command:
                return {
                    "allow": False,
                    "reason": f"Blocked dangerous command: {pattern}"
                }

    return {"allow": True}

7. 工具结果冲突:Evidence Arbitration

7.1 冲突示例

CRM 系统:用户等级 = VIP
订单系统:用户等级 = Normal

或者:

库存系统 A:库存 = 10
库存系统 B:库存 = 0

Agent 不能随便选一个。


7.2 冲突处理流程

Tool Result A
Tool Result B
  ↓
Normalize Schema
  ↓
Detect Conflict
  ↓
Compare Timestamp
  ↓
Compare Source Authority
  ↓
Check Business Rule
  ↓
Call Authoritative Source
  ↓
Decide / Report Uncertainty

7.3 权威来源规则

数据类型 权威来源
用户等级 CRM 主库
支付状态 支付系统
物流状态 物流系统
医疗诊断 HIS / 医生确认记录
代码状态 当前 git workspace
文档规则 最新 policy document
库存 库存主数据服务

7.4 面试级表达

工具结果应该被看作 evidence,而不是 truth。
当多个 evidence 冲突时,需要根据 source authority、timestamp、freshness、confidence 和业务规则做 evidence arbitration。
如果无法仲裁,需要暴露不确定性,而不是让模型自行编造结论。

8. 工具返回过长:Observation Compression

8.1 为什么不能直接塞上下文?

工具输出可能非常大:

10 万行测试日志
5000 行 SQL 结果
几十篇网页
几万字 PDF
大量 HTML
长时间 shell stdout

直接放入上下文会导致:

1. token 成本暴涨
2. 关键信息被噪声淹没
3. 模型注意力分散
4. 历史上下文被挤掉
5. 后续决策被污染

这就是 context pollution。


8.2 Observation Compression 流程

Raw Tool Output
  ↓
Persist Raw Artifact
  ↓
Classify Output Type
  ↓
Type-specific Extraction
  ↓
Schema Validation
  ↓
Aggregation / Clustering / Ranking
  ↓
Summary Generation
  ↓
Attach Reference Pointers
  ↓
Return Structured Observation

核心原则:

Raw output 不直接进入主上下文。
主 Agent 只接收 bounded structured observation。
原始证据通过 pointer 保留。

9. 结构化提取:Structured Extraction

9.1 定义

结构化提取不是“让模型总结”。

更准确的定义是:

把不可控、非结构化、超长的工具输出转换成固定 schema、可校验、可压缩、可追溯的 observation object。


9.2 日志类输出提取

原始日志示例
2026-05-19 10:12:01 tests/test_auth.py::test_invalid_token FAILED
E AssertionError: expected 401, got 500
E File "/app/src/auth.py", line 88, in validate_token
E KeyError: 'expires_at'
目标结构
{
  "timestamp": "2026-05-19 10:12:01",
  "test_name": "tests/test_auth.py::test_invalid_token",
  "status": "FAILED",
  "error_type": "AssertionError",
  "error_message": "expected 401, got 500",
  "stack_file": "/app/src/auth.py",
  "stack_line": 88,
  "failure_module": "auth",
  "raw_ref": "artifact://logs/pytest_run_001.txt#L120-L135"
}

9.3 Python 示例:提取 pytest failure

import re
from dataclasses import dataclass
from typing import Optional, List


@dataclass
class LogFailure:
    timestamp: Optional[str]
    test_name: Optional[str]
    error_type: Optional[str]
    error_message: Optional[str]
    file: Optional[str]
    line: Optional[int]
    raw_ref: str


def extract_pytest_failures(raw_output: str, artifact_id: str) -> List[LogFailure]:
    lines = raw_output.splitlines()
    failures = []

    current = None
    start_line = None

    test_failed_pattern = re.compile(
        r"(?P<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) "
        r"(?P<test_name>.+?) FAILED"
    )

    error_pattern = re.compile(
        r"E (?P<error_type>\w+(Error|Exception|AssertionError))?:?\s*(?P<message>.*)"
    )

    file_pattern = re.compile(
        r'File "(?P<file>.+?)", line (?P<line>\d+)'
    )

    for idx, line in enumerate(lines, start=1):
        test_match = test_failed_pattern.search(line)

        if test_match:
            if current:
                current.raw_ref = f"artifact://logs/{artifact_id}#L{start_line}-L{idx - 1}"
                failures.append(current)

            current = LogFailure(
                timestamp=test_match.group("timestamp"),
                test_name=test_match.group("test_name"),
                error_type=None,
                error_message=None,
                file=None,
                line=None,
                raw_ref=""
            )
            start_line = idx
            continue

        if current:
            error_match = error_pattern.search(line)
            if error_match and not current.error_type:
                current.error_type = error_match.group("error_type") or "UnknownError"
                current.error_message = error_match.group("message")

            file_match = file_pattern.search(line)
            if file_match and not current.file:
                current.file = file_match.group("file")
                current.line = int(file_match.group("line"))

    if current:
        current.raw_ref = f"artifact://logs/{artifact_id}#L{start_line}-L{len(lines)}"
        failures.append(current)

    return failures

9.4 Error Fingerprint 聚类

当日志中有大量失败时,不应该逐条展示。
可以根据 error fingerprint 聚类:

import hashlib


def error_fingerprint(failure: LogFailure) -> str:
    key = (
        f"{failure.error_type}|"
        f"{failure.file}|"
        f"{failure.line}|"
        f"{failure.error_message}"
    )
    return hashlib.md5(key.encode("utf-8")).hexdigest()

聚类后的结果:

{
  "fingerprint": "9ac32...",
  "count": 23,
  "error_type": "KeyError",
  "file": "src/auth.py",
  "line": 88,
  "representative_ref": "artifact://logs/pytest_run_001.txt#L120-L135"
}

这样主 Agent 只需要看到:

{
  "summary": "23 failures share the same KeyError at src/auth.py:88.",
  "top_error_patterns": [
    {
      "count": 23,
      "error_type": "KeyError",
      "location": "src/auth.py:88",
      "message": "missing key: expires_at",
      "raw_ref": "artifact://logs/pytest_run_001.txt#L120-L135"
    }
  ]
}

10. Reference Pointer:证据指针

10.1 定义

Reference Pointer / Evidence Pointer / Artifact Pointer 是用于定位原始证据的地址。

例如:

artifact://logs/pytest_001.txt#L8231-L8260
file://repo/src/auth.py#L80-L120
db://query/q_9283/row/O123
web://search/run_42/result_1
doc://policy.pdf#page=12
git://repo/commit/abc123/src/a.py#L10-L30

10.2 作用

Pointer 解决三个问题:

1. 压缩后的 observation 可以追溯原文
2. 主 Agent 不需要携带全部 raw output
3. 需要细节时可以 drill down

10.3 Drill-down 示例

主 Agent 收到:

{
  "error_type": "KeyError",
  "file": "src/auth.py",
  "line": 88,
  "raw_ref": "artifact://logs/pytest_001.txt#L8231-L8260"
}

如果需要查看原始栈信息,可以调用:

read_artifact("artifact://logs/pytest_001.txt#L8231-L8260")

这就是 drill down。


11. 搜索结果的结构化提取

11.1 Search Observation Schema

{
  "type": "search_observation",
  "query": "Claude Code hooks implementation",
  "result_count": 10,
  "results": [
    {
      "rank": 1,
      "title": "Claude Code Hooks",
      "source": "Anthropic Docs",
      "published_at": null,
      "snippet": "Hooks are shell commands that run at lifecycle events...",
      "credibility": "official",
      "raw_ref": "web://search/run_42/result_1"
    }
  ]
}

11.2 字段说明

字段 作用
title 标题
source 来源
published_at 时间
snippet 摘要
credibility 来源可信度
rank 原始排序
raw_ref 原始结果引用

11.3 Rerank 策略

搜索结果不应直接全部进入上下文。
应该结合以下因素排序:

BM25 relevance
Embedding similarity
Source authority
Freshness
Domain trust
Query-title match
Query-snippet match

12. 数据库结果的结构化提取

12.1 错误做法

SQL 返回 5000 行,然后全部塞给 Agent。

这会严重污染上下文。

12.2 正确做法

先做 profiling:

row_count
columns
data types
categorical distribution
numeric statistics
time distribution
top-k samples
outlier samples
query_ref

12.3 SQL Observation 示例

{
  "type": "sql_observation",
  "query_id": "q_9283",
  "row_count": 5231,
  "columns": ["order_id", "user_id", "amount", "status", "created_at", "region"],
  "aggregations": {
    "status_distribution": {
      "success": 4800,
      "failed": 311,
      "pending": 120
    },
    "region_distribution_top5": {
      "Hangzhou": 1200,
      "Shanghai": 980,
      "Beijing": 700
    },
    "amount_stats": {
      "min": 1.2,
      "max": 9988.0,
      "avg": 231.7,
      "p95": 900.0
    }
  },
  "top_samples": [
    {
      "order_id": "O123",
      "amount": 9988.0,
      "status": "failed",
      "raw_ref": "db://query/q_9283/row/O123"
    }
  ],
  "raw_ref": "db://query/q_9283"
}

12.4 Python 示例

from collections import Counter
import numpy as np


def profile_order_rows(rows: list[dict], query_id: str) -> dict:
    row_count = len(rows)
    columns = list(rows[0].keys()) if rows else []

    status_distribution = Counter(row["status"] for row in rows)
    region_distribution = Counter(row["region"] for row in rows).most_common(5)

    amounts = [
        float(row["amount"])
        for row in rows
        if row.get("amount") is not None
    ]

    amount_stats = {
        "min": min(amounts),
        "max": max(amounts),
        "avg": sum(amounts) / len(amounts),
        "p95": float(np.percentile(amounts, 95))
    }

    top_failed_orders = sorted(
        [row for row in rows if row["status"] == "failed"],
        key=lambda x: float(x["amount"]),
        reverse=True
    )[:5]

    samples = [
        {
            **row,
            "raw_ref": f"db://query/{query_id}/row/{row['order_id']}"
        }
        for row in top_failed_orders
    ]

    return {
        "type": "sql_observation",
        "query_id": query_id,
        "row_count": row_count,
        "columns": columns,
        "aggregations": {
            "status_distribution": dict(status_distribution),
            "region_distribution_top5": dict(region_distribution),
            "amount_stats": amount_stats,
        },
        "top_samples": samples,
        "raw_ref": f"db://query/{query_id}"
    }

13. 为什么必须先结构化提取,再摘要?

错误流程:

Raw Output
  ↓
LLM Summary
  ↓
Agent

问题:

行号丢失
文件路径丢失
error type 丢失
timestamp 丢失
source 丢失
后续无法 drill down

正确流程:

Raw Output
  ↓
Structured Extraction
  ↓
Schema Validation
  ↓
Aggregation / Clustering
  ↓
Summarization
  ↓
Reference Pointer
  ↓
Agent Observation

核心原则:

Extraction preserves evidence.
Summarization compresses meaning.

14. Memory System:记忆系统不是向量库

14.1 记忆分类

Working Memory
  当前任务状态和短期上下文

Episodic Memory
  历史会话、执行轨迹、任务过程

Semantic Memory
  稳定事实、用户偏好、业务规则

Procedural Memory
  Skills、runbook、debug checklist、操作流程

Artifact Memory
  代码、文档、日志、报告、patch、commit

14.2 存储设计

记忆类型 存储方式
当前任务状态 Redis / DB / in-memory state
历史会话 Event Log / JSONL
稳定事实 PostgreSQL
非结构化文本 Vector DB
关系知识 Graph DB
操作流程 Skills / Markdown
文件和日志 Object Storage / File System

14.3 写入 Pipeline

Conversation / Tool Result
  ↓
Memory Candidate Extraction
  ↓
Stability Check
  ↓
Sensitivity Filter
  ↓
Deduplication
  ↓
Conflict Detection
  ↓
Source Attribution
  ↓
Write Memory

14.4 读取 Pipeline

Current Query
  ↓
Query Understanding
  ↓
Memory Retrieval
  ↓
Relevance Scoring
  ↓
Recency Scoring
  ↓
Confidence Filtering
  ↓
Permission Filtering
  ↓
Context Injection

14.5 Memory Schema 示例

{
  "memory": "Project uses FastAPI and PostgreSQL for the backend service.",
  "type": "semantic",
  "scope": "project",
  "source": "conversation_2026_05_19",
  "created_at": "2026-05-19",
  "updated_at": "2026-05-19",
  "confidence": 0.91,
  "ttl": null
}

15. Context Management:上下文窗口是预算,不是垃圾桶

15.1 Context Budget Allocation

上下文应该按优先级分配:

P0: System Instruction / Safety Policy
P1: 当前用户任务
P2: 当前 task state
P3: 必要 tool schema
P4: 项目规则 / memory
P5: 检索证据 / 文件片段
P6: 最近对话
P7: 历史摘要
P8: 低相关 memory

15.2 常见污染源

超长工具输出
重复历史对话
低相关 RAG chunk
过期 memory
大段日志
无关网页
subtask 噪声

15.3 处理策略

工具输出压缩
历史对话 checkpoint summary
RAG rerank
Subagent context isolation
Memory confidence filtering
Project rule versioning

16. Hooks:Agent Runtime 的生命周期回调

16.1 Hook 的定义

Hook 是 Agent runtime 在特定生命周期事件触发的回调。

Claude Code 官方 hooks 文档中,hooks 可以是 shell command、HTTP endpoint 或 LLM prompt,并可在 SessionStart、UserPromptSubmit、PreToolUse、PostToolUse、Stop 等事件触发。官方文档还说明 PreToolUse 可以在工具调用执行前触发并阻断调用。
参考:Claude Code Hooks 文档。([Claude][1])

16.2 常见 Hook 事件

SessionStart
UserPromptSubmit
PreToolUse
PostToolUse
Stop
SessionEnd
SubagentStart
SubagentStop

16.3 Hook 用途

Hook 用途
UserPromptSubmit 注入规则、敏感信息检测
PreToolUse 权限检查、危险命令拦截
PostToolUse 日志记录、结果压缩
Stop 保存摘要、更新记忆
SubagentStop 收集子 Agent 输出

17. Skills、MCP、CLI、Plugin 的关系

17.1 Tools

Tool 是 Agent 可以调用的动作。

OpenClaw 文档中定义 tool 是 agent 可调用的 typed function,例如 execbrowserweb_searchmessage,并且 agent 会看到结构化 function definition。([OpenClaw][2])

17.2 Skills

Skill 是过程知识。
OpenClaw 文档中,skill 是 SKILL.md,会被注入 system prompt,提供上下文、约束和 step-by-step guidance。([OpenClaw][2])

Claude Code 的 Skills 文档也使用 SKILL.md 文件定义技能触发条件和执行指令。([Claude API Docs][7])

17.3 MCP

MCP 是外部工具协议。
Hermes MCP 文档说明 MCP 可以把 GitHub、数据库、文件系统、浏览器栈、内部 API 等外部工具服务器连接到 Hermes Agent,并支持启动时自动发现和注册工具。([Hermes Agent][3])

17.4 Plugin

Plugin 是能力包,可以打包:

channels
model providers
tools
skills
speech
web search
media understanding
image generation
video generation

OpenClaw 文档也明确说明 plugin 可以注册多种能力,包括 channels、model providers、tools、skills、speech、web search 等。([OpenClaw][2])

17.5 CLI

CLI 是交互入口,不是 Agent 的全部。

CLI
  ↓
Session Manager
  ↓
Context Loader
  ↓
Agent Loop
  ↓
Tool Runtime
  ↓
Filesystem / Shell / MCP / Browser

18. Claude Code、Hermes、OpenClaw 对比

维度 Claude Code Hermes OpenClaw
主要定位 Coding Agent 长期个人 Agent Self-hosted Agent Gateway
核心入口 CLI / IDE / GitHub CLI / Gateway Gateway / 多渠道
核心能力 代码库理解、hooks、subagents、skills memory、skills、MCP、provider routing tools、plugins、skills、多渠道
记忆重点 CLAUDE.md、session、subagent memory MEMORY.md、USER.md、session search session / gateway 状态
工具扩展 Built-in tools + MCP + Skills Tools + MCP + Toolsets Built-in tools + Plugins
安全重点 permissionMode、hooks、tool restrictions approval、provider routing、config allow/deny tools、sandbox、gateway control
适合讨论 Coding harness Long-running agent memory Agent service / gateway architecture

Claude Code subagent 支持 toolsdisallowedToolsmodelpermissionModemcpServershooksskillsmemoryisolation 等字段,说明现代 coding agent 已经具备独立工具权限、上下文、记忆和运行策略。([Claude][6])


19. 面试回答模板

19.1 Tool Calling 怎么设计?

Tool calling 不应该被理解成模型生成 JSON。
工程上它是一个受控状态机。

流程包括 tool selection、argument generation、schema validation、
argument completion、permission check、tool execution、observation compression、
conflict resolution 和 trace logging。

参数缺失时,优先从上下文、session state 和授权 profile 中补全;
无法高置信补全时,高风险场景必须反问。
工具失败时,需要先做 error classification,再决定 retry、fallback、降级或终止。
工具输出过长时,先结构化提取,再摘要,并保留 raw reference pointer。
多个工具结果冲突时,需要基于 source authority、timestamp、confidence 和业务规则做 evidence arbitration。

19.2 Memory System 怎么设计?

Agent 记忆不能简单等同于向量库。
应分成 working memory、episodic memory、semantic memory、procedural memory 和 artifact memory。

结构化状态可以放 PostgreSQL;
非结构化历史可以放向量库;
关系型知识可以放图数据库;
完整执行过程需要进入 event log;
操作流程可以沉淀为 skills。

写入记忆时,需要做候选抽取、稳定性判断、敏感信息过滤、去重、冲突检测和 source attribution。
读取记忆时,需要结合 relevance、recency、confidence、scope 和 permission 过滤。
记忆系统最大风险不是记不住,而是记错、记脏、记过期。

19.3 Context Management 怎么设计?

上下文窗口应该被视为有限预算。
优先级最高的是系统规则和当前任务目标,
其次是 task state、必要 tool schema、项目规则、检索证据和最近对话。

长对话需要 checkpoint summary。
工具输出需要 observation compression。
代码库任务需要按文件路径、符号和调用链检索。
高噪声任务可以交给 subagent,主 Agent 只接收结构化摘要。
上下文管理的核心目标是减少 context pollution。

19.4 Harness 是什么?

Harness 是包住 LLM 的工程运行时。
LLM 负责推理和决策建议,Harness 负责上下文构造、工具注册、参数校验、权限控制、工具执行、结果压缩、记忆管理、失败恢复、事件记录和循环控制。
Agent 的稳定性并不只来自模型能力,而来自模型能力和 Harness 工程能力的耦合。

20. AI Coding Agent 的工程坑

20.1 幻觉 API

模型可能编造不存在的函数、参数、配置项。

解决策略:

先查本地代码和依赖文档
再生成修改方案
修改后跑 type check / test

20.2 过度重构

一个小 bug 可能被 Agent 改成大重构。

解决策略:

限制 edit scope
先生成 plan
禁止无关文件修改
通过 git diff 审查

20.3 测试污染

Agent 为了让 CI 变绿,可能修改测试而不是修业务逻辑。

解决策略:

默认保护测试文件
修改测试必须解释业务理由
关键测试改动需要人工 review

20.4 上下文漂移

长任务中模型可能忘记原始目标。

解决策略:

维护 explicit task state
定期 checkpoint summary
关键约束固定在上下文高优先级位置

21. 总结

Agent 开发真正难的地方,不是让模型调用一个函数。

真正难的是构建一个可控执行系统:

参数缺失时不瞎猜
工具失败时不幻觉
返回过长时不污染上下文
权限不足时不绕过
结果冲突时能仲裁
记忆写入时能防污染
上下文管理时能控制预算
长任务执行时能维护状态
所有行为都能审计、回放和评测

一句话总结:

Agent 开发的核心不是 function calling,而是 Harness Engineering。

更进一步:

高质量 Agent 系统,本质上是围绕 LLM 构建的有状态、有权限、有记忆、有工具、有审计、有失败恢复能力的软件工程系统。

这也是 Agent 应用从 demo 走向生产的关键分水岭。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐