Agent学习记录-1

阅读学习时间:约10分钟

笔记

Agent的学习记录

背景

我自己通过Vibe coding做了一个产品,主要是用于问卷生成系统和直接导出的,既往在流行病学,临床研究中也会使用到譬如EpiData,问卷星等程序,但我们以结果为导向,我们要的是直接可以用的格式化数据,比如BMI,这个数据我们在问卷里只会使用身高和体重等,但我们内置了一套计算转化的工具,可以最后直接生成;另外,我们还有直接生成基线表的工具,可以直接将格式化的数据导出为一个更直接的数据展现方式。在完成基本功能的实现后,我开始思考,从0生成问卷可能还是太麻烦了,我们可不可以通过设计一个Agent使得问卷生成变得更加简单,我们只需要通过对话的方式就能完成问卷的生成和实现,于是乎,开始有了这个学习的记录。在前段时间 Deepseek Harness 的宣布,我很好奇Harness的意思,后面我大概知道了,我们的模型只能输出文本,但这些个的文本他需要得以转化和实现,通常需要比如Powershell一类的工具得以实现,Harness是一个调度框架,把所有的流程整合在了一起。

1. Prompt and Harness

作为一个Agent,其可以接入不同的智能体模型,但不同的智能体其所产出的文本内容或者格式可能都不一样,通过Prompt来完成这个显然是不足的,因此,我们只需要让Prompt充当一个语义规则的人就可以,用于Agent身份的定义,约束条件,决策逻辑等,而Harness的话则用于工程规则,其是更高一层的管理者,有强制的措施。

比如Prompt只可以做到:

你是 XXX 文件生成 Agent。 --身份设定

你的任务是根据用户需求创建 XXX 配置。

要求: --约束条件
1. 不得猜测不存在的字段。
2. xxx_type 只能是 A/B/C。
3. 每个 node 必须拥有唯一 ID。
4. 如果信息不足,在 warnings 中返回原因。
5. 不直接生成磁盘路径。

而对于Harness:

模型输出
    ↓
JSON Schema Validation
    ↓
semantic validation
    ↓
normalization
    ↓
deterministic serialization
    ↓
atomic file write

而且每次Prompt的生成都可能随着模型的改进其LLM的输出都不一样。

2. 输出

一个很直接的想法是直接使用Prompt然后传入LLM并且直接给予输出,这样的 Demo 非常快,但是生产环境很容易出问题。因此我们只要求模型只产生内部结构,比如一个 json 格式:

{
  "model": {
    "name": "abc",
    "version": 3
  },
  "nodes": [
    {
      "id": "001",
      "input": "...",
      "output": "..."
    }
  ]
}

然后通过脚本,比如python,实现生成一个标准化的格式文件

3. 对话ID

因为在建立Agent的时候,不同的大模型其每轮对话都会有特定的Conversation ID,但此时不能将此作为我们储存对话的Session ID,一方面是切换模型时会产生新的文件夹,后续不好管理,另外一方面也是为了对话安全。因此 Session ID 应该由我们自己独立生成,而将外部模型的 ID 只是作为一个元数据。

3.1 应用层状态管理

我们的对话交给Harness自动保存会更加统一和和谐: 在Harness里,我们:

[
  {
    "role": "user",
    "content": "..."
  },
  {
    "role": "assistant",
    "content": "..."
  }
]

在下一次的时候,我们就可以直接

provider.generate(messages)

来统一重做,然后就可以实现模型之前的任意切换,且仍然处于一个对话里。

3.2 上下文管理

现有大模型上下文最多可以支持1M,但仍然不能支持无限累积,可以将文本分为几个层级

┌─────────────────────┐
│ System instructions │ -- 系统说明
├─────────────────────┤
│ Persistent state    │ -- 持续状态
├─────────────────────┤
│ Conversation summary│ -- 对话总结
├─────────────────────┤
│ Recent N turns      │ -- 短记忆
├─────────────────────┤
│ Relevant artifacts  │ -- 相关内容
├─────────────────────┤
│ Current request     │ -- 当下请求
└─────────────────────┘

在对话维护上,有三个状态很重要:

  • Conversation State:用户和 Agent 说了什么
  • Agent State: Agent 决定了什么
{
  "file_format_version": "2.1",
  "selected_mode": "abc",
  "parameter_x": 5
}
  • Artifact State: 这轮工作创建了什么
{
  "current_artifact": "artifact_0007",
  "revision": 12,
  "checksum": "...",
  "path": "artifacts/main.xxx"
}

3.3 URI管理

我们不要让模型直接决定真实路径,其只应该操作相对路径,比如

{
  "operation": "write_cache",
  "path": "analysis/result.json",
  "content": {}
}

Harness上的映射:

resolve_path(
    session_id,
    "analysis/result.json"
)

最后写入的时候为

/runtime/workspaces/
    01K8.../
        cache/
            analysis/
                result.json

且最好连我们写入文件 write_file 都抽象为 tool,这样子在不同的操作系统上都可以不需要修改 Prompt,因为在这种情况下,所有的文件都会在虚拟文件系统上被读取写入和操作。

3.4 缓存区分

缓存为 cache ,但在 Agent 的运作过程中会不断产生 cache, 其来源也都不一样, 有来自对话的,有来自文件生成的,有来自 Agent 运作的,因此至少要区分:

session/
    conversation/ -- 整个Session
    state/		  -- 整个Session
    cache/        -- 删除后可重新生成
    artifacts/    -- 创建结果
    temp/         -- 单次Agent运行结果

4. 版本

对于所有的Prompt也应该表明版本,其好处在于方便管理,且后续更新的时候更简单迭代,不会因为模型的变化而导致Prompt混乱导致的输出异常。