我们最终要实现的是这样一条完整链路:
用户提出任务
↓
Flask API
↓
Agent Harness
↓
LLM 思考与决策
↓
选择 Tool
↓
执行 Tool
↓
获取 Observation
↓
继续决策
↓
完成任务
最终这个 Agent 不只是能“说”,而是能真正“做”:
- 读取文件;
- 修改代码;
- 执行 Python;
- 执行 Shell;
- 分析报错;
- 自动重试;
- 保存会话;
- 管理上下文;
- 调用多个工具;
- 记录完整执行轨迹;
- 通过 Flask API 对外提供服务。
这里最值得注意的,是我们说的目标并不是“做一个聊天机器人”。
聊天机器人最典型的模式是:
用户输入
↓
LLM
↓
文本输出
而 Agent 的运行过程更像:
用户目标
↓
模型决定先做什么
↓
程序执行动作
↓
环境返回结果
↓
模型根据结果重新判断
↓
继续行动
↓
直到任务完成
这中间多出来的,不只是几次 API 调用,而是一套完整的运行基础设施。
我们把这套基础设施叫做 Agent Harness。
你可以暂时把它理解成“Agent 的身体、神经系统和安全边界”。LLM 更像决策组件,它负责理解任务、分析当前状态、选择下一步;但真正的文件访问、命令执行、权限控制、异常处理、日志记录、会话保存,全部需要 Harness 承担。
把这些角色放回同一套系统里看,分工会清楚很多:
![图片[1]-为什么现在需要自己理解 Agent Harness?-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1380.png)
这张图里最关键的不是模块数量,而是边界。LLM 只负责产生下一步决策,Tool Call 还要经过权限校验,才能由程序真正执行;执行环境返回的 Observation 又会进入下一轮决策。State、Context、Memory、Safety 和 Tracing 则一直托住整个循环,让它不只是“会想”,还能够连续、受控、可追踪地做事。
也正因为如此,这门课不会从“LangChain 某个类怎么调用”开始。
不是因为这些框架不好,而是因为如果我们一开始就把最重要的机制全部藏在框架后面,你很容易学会“怎么调用”,却不知道“为什么这样设计”。后面一旦遇到真实项目里的死循环、工具失败、上下文爆炸、权限风险、服务状态管理,你就会发现单纯记 API 很难帮你定位问题。
我们希望走另一条路线,先把 Agent 拆开,再把它重新装起来。
先用一个最小的 Python while 循环理解 Main Loop;再给它加 State;再让它真正调用 Tool;再处理 Tool Error;再加入 Context 和 Memory;再通过 Flask 暴露 API;然后加入 Streaming、SQLite、Middleware、Human Approval、Sandbox、SubAgent、Tracing 和 Evaluation。
到最后,你看到任何 Agent 框架,都会有一种很不一样的感觉,“哦,这一层其实是在帮我封装 Tool Registry。”
“这里其实是在保存 State。”
“这个 Executor 背后,本质上还是 Main Loop。”
这才是我们想达到的学习效果。
这一节就是整门课的地图。先把终点看清楚,后面的 30 多节才不会学成一堆散落的名词。
知识地图
这一节位于整门课程的最前面,它不追求把某个模块讲深,而是负责建立全局视角:我们最终要造什么、为什么要这样造、后面每一块知识会装到哪里。
![图片[2]-为什么现在需要自己理解 Agent Harness?-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1381.png)
学习路径:
![图片[3]-为什么现在需要自己理解 Agent Harness?-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1382.png)
核心小节一:先看终点 – 我们最后到底要造出怎样一套 Agent 系统?
先别管“Agent Harness”这个词听起来是不是有点抽象,我们直接从一个真实任务开始。
假设用户对系统说,“帮我检查这个 Flask 项目为什么启动失败,并给出修复建议。”
如果这是普通聊天模型,它最多只能根据你粘贴进去的代码做判断。
但我们最终的系统希望它能够自己完成一条工作链:
接收任务
↓
查看项目目录
↓
决定先读哪些文件
↓
读取 requirements.txt / pyproject.toml
↓
运行测试或启动命令
↓
观察报错
↓
根据报错继续搜索代码
↓
必要时修改文件
↓
再次执行验证
↓
输出最终结论与执行轨迹
注意,这里面出现了几个完全不同的角色。
第一类是 用户入口。
用户不应该直接调用一个 Python 函数,而是可以通过网页、CLI、IDE 插件、飞书机器人或其他系统来提交任务。为了让不同入口都能调用同一个 Agent,我们需要一个稳定的服务接口。这就是为什么课程后面会引入 Flask。
第二类是 LLM 决策。
模型需要理解:
- 用户到底想解决什么;
- 当前已经做了哪些事情;
- 刚才的工具执行结果意味着什么;
- 下一步最值得调用哪个工具;
- 当前是否已经可以结束。
第三类是 Tool 执行。
LLM 不会真的打开文件,也不会真的运行 Python。它最多产生一个结构化意图:
{
"name": "read_file",
"arguments": {
"path": "app.py"
}
}
真正打开 app.py 的,是我们的 Python 程序。
第四类是 Observation 回传。
工具执行后可能得到:
FileNotFoundError: app.py
也可能得到文件内容:
from flask import Flask
import missing_package
这些结果要重新放回 Agent 的上下文里,让模型决定下一步。
第五类是 运行控制。
如果模型连续 30 次调用同一个工具怎么办?如果它想读取 ~/.ssh 怎么办?如果 Shell 命令超过 60 秒怎么办?如果要删除文件怎么办?这些都不能依赖模型“自觉”。
所以还需要:
Max Steps
Timeout
Path Validation
Permission
Middleware
Human Approval
Tracing
这就是 Agent Harness 真正开始变得“工程化”的地方。
我们可以先用一个纯 Python、完全不依赖真实 LLM 的程序,模拟最终系统最核心的形状:
from dataclasses import dataclass, field
from typing import Any
@dataclass
class AgentState:
goal: str
step: int = 0
observations: list[str] = field(default_factory=list)
finished: bool = False
def read_file(path: str) -> str:
fake_files = {
"app.py": "from flask import Flask\\nimport missing_package",
"requirements.txt": "Flask==3.0.0",
}
return fake_files.get(path, "FILE_NOT_FOUND")
def decide_next_action(state: AgentState) -> dict[str, Any]:
if state.step == 0:
return {
"action": "read_file",
"arguments": {"path": "app.py"}
}
if state.step == 1:
return {
"action": "read_file",
"arguments": {"path": "requirements.txt"}
}
return {
"action": "finish",
"answer": "app.py 导入了 missing_package,但 requirements.txt 没有声明该依赖。"
}
def run_agent(goal: str) -> str:
state = AgentState(goal=goal)
while not state.finished and state.step < 10:
decision = decide_next_action(state)
if decision["action"] == "read_file":
path = decision["arguments"]["path"]
result = read_file(path)
state.observations.append(f"读取 {path}:{result}")
state.step += 1
continue
if decision["action"] == "finish":
state.finished = True
return decision["answer"]
return "任务没有在最大步数内完成。"
if __name__ == "__main__":
result = run_agent("检查项目为什么启动失败")
print(result)
这段代码一点都“不智能”,因为 decide_next_action() 是我们写死的。
但非常重要,因为它已经让我们看到最终 Agent 的骨架:
State
↓
Decision
↓
Action
↓
Tool
↓
Observation
↓
State 更新
↓
下一轮 Decision
后面真正接入 LLM 时,我们替换的主要是:
decide_next_action(...)
让决策不再由固定 if/else 完成,而是由模型根据 Context 做出。
很多人第一次学 Agent 时,会把注意力全部放在模型上。其实真正难的往往不是“调用模型”,而是模型周围这些看起来不起眼的东西:
- State 怎么保存;
- Tool 怎么注册;
- 参数怎么校验;
- Observation 怎么回传;
- Context 怎么组装;
- 错误怎么恢复;
- 什么时候结束;
- 怎么限制权限;
- 怎么对外提供服务。
这也是为什么我们把课程重点放在 Harness 上。
最终系统可以得到
如果课程最后只是得到:
agent.py
我们最终希望项目结构逐渐长成:
agent-harness/
├── app.py
├── api/
│ ├── chat.py
│ └── runs.py
├── agent/
│ ├── engine.py
│ ├── loop.py
│ ├── state.py
│ └── context.py
├── providers/
│ ├── base.py
│ └── provider.py
├── tools/
│ ├── registry.py
│ ├── file_tools.py
│ ├── shell_tools.py
│ └── python_tools.py
├── memory/
│ ├── working_memory.py
│ └── long_term_memory.py
├── middleware/
│ ├── safety.py
│ └── approval.py
├── tracing/
│ └── tracer.py
└── storage/
└── db.py
这里容易踩一个小坑:这张目录图表达的是职责划分,不是要求最终代码必须逐字使用这些文件名。
学到后面,大家可能会看到 Main Loop 和 Engine 放在同一个文件里,也可能看到 Working Memory 与持久化接口放在同一个包里。判断结构是否合理时,我们看的是职责有没有边界,而不是目录名有没有和这张图一字不差。
配套项目采用“阶段项目 + 可复用源码包”的组织方式,实际会更接近:
agent_harness_course_projects/
├── projects/ # 01~12:每个阶段可以单独运行
├── src/agent_harness/ # 最终系统复用的核心实现
│ ├── core/ # Engine、State、Provider、运行边界
│ ├── tools/ # Tool Registry 与工作区工具
│ ├── context/ # Context、Session、Memory
│ ├── service/ # Flask API 与 Run Service
│ ├── safety/ # Middleware、Approval、Sandbox
│ ├── storage/ # SQLite、Trace 与 Artifact 持久化
│ ├── observability/ # Tracing、Metrics、Evaluation
│ ├── advanced/ # Plan、SubAgent、Skills、AGENTS.md
│ └── agents/ # Coding Agent 与 Data Analysis Agent
├── tests/ # 自动化验证
├── run_demo.py # 统一运行入口
└── start.sh # 一键启动入口
比如概念图里的 agent/loop.py,在配套项目中主要由 core/engine.py 承担;file_tools.py、shell_tools.py、python_tools.py 的职责集中在 tools/workspace.py;api/chat.py 与 api/runs.py 对应的路由则由 service/flask_app.py 提供。后续章节引用某个“层”时,我们会同时关心它的职责和它在配套项目中的真实位置。
这不是为了显得“架构复杂”。
恰恰相反,是因为每个模块都应该有清晰职责。
如果 Flask 路由里直接写 300 行 Agent Loop,以后你想从 CLI 调用同一个 Agent,就会非常痛苦。如果 Tool 和安全检查混在一起,你以后增加新工具就容易漏掉权限控制。如果 Tracing 写死在每个函数里,代码会越来越难维护。
所以从第一天起,我们就会一直问一个问题,这段代码到底属于哪一层?
这也是学习 Harness 比单纯学某个 Agent 框架更有价值的地方。










请登录后查看评论内容