
1. 从躺平说起为什么我决定把日常流程交给 Agent躺平挖 alpha这个说法第一次听会觉得有点矛盾——躺平了还怎么挖但真正做过一段时间信息筛选和机会捕捉的人会明白这里的躺平不是什么都不干而是把那些重复、机械、消耗注意力的动作交给一套自动化流程自己只保留判断和决策的部分。这个系列写到第三篇前两篇分别聊了信息源的整理和初步的筛选逻辑这一篇要解决的是一个更核心的问题怎么让 Agent 真正嵌入日常工作流而不是变成一个看起来很酷但用两次就吃灰的玩具。关键词里出现了 alpha、Agent、Harness、LLM、CC 这几个词基本勾勒出了这篇要讲的范围。alpha 在这里指的是那些有价值但尚未被广泛注意到的信息差和机会点Agent 是执行主体Harness 是承载 Agent 运行的工程框架LLM 是底层推理能力CC 则涉及到本地代理和端点转发这一层的工程细节。这几个词放在一起其实描述的是一个完整的链路用 LLM 做推理用 Harness 做编排和容错用 Agent 做具体任务的执行最终目标是稳定地产出有价值的 alpha 信号。我自己的场景比较典型每天需要浏览大量分散的信息源从中筛出值得进一步跟进的内容然后做初步的验证和归档。这个过程如果纯手工做一天两三个小时就没了而且注意力会被大量噪音消耗掉。之前试过用脚本做简单的关键词过滤但问题是规则太死遇到新的表达方式就失效。后来转向用 LLM 做语义层面的筛选效果好很多但新的问题又来了——单次调用不稳定遇到长文本会截断遇到格式不规范的内容会解析失败整个流程跑着跑着就断了。这就是 Harness 要解决的问题。Harness 这个词在工程语境里指的是线束或约束框架放到 Agent 场景里它的作用是给 LLM 和 Agent 提供一个稳定的运行环境处理重试、回退、上下文管理、插件加载这些事情。没有 Harness 的 Agent 就像没有安全带的赛车跑得快但随时可能出事。这篇要讲的就是我怎么一步步把这套东西搭起来中间踩了哪些坑以及最终形成的工作流是什么样的。适合读这篇的人已经在用 LLM 做自动化但遇到稳定性问题的想搭 Agent 但不知道从哪下手的以及那些每天花大量时间做信息筛选、想把这个过程半自动化的人。不需要你是资深工程师但至少要对 Python 和基本的命令行操作有概念因为后面会涉及到具体的配置和代码。2. Harness 到底在做什么拆开 Agent 运行时的黑盒2.1 为什么裸调 LLM 的 Agent 跑不长很多人搭 Agent 的第一反应是直接调 LLM 的 API写个循环让它自己决定下一步做什么。这个做法在 demo 阶段没问题但一旦放到日常流程里问题会集中爆发。我总结下来主要是三类第一类是上下文管理失控。Agent 每执行一步都会产生新的上下文如果不做裁剪和压缩很快就会超出模型的上下文窗口。超了之后要么报错要么模型开始遗忘前面的关键信息行为变得不可预测。我遇到过最典型的情况是 Agent 在前面已经确认了某个信息源不可用但因为上下文被截断后面又重新去请求那个源陷入死循环。第二类是错误处理缺失。网络请求会超时API 会限流返回的内容可能不符合预期格式。裸调的 Agent 遇到这些情况通常直接崩掉或者返回一个半成品结果。更麻烦的是有些错误是静默的——比如返回了一个 200 状态码但内容其实是错误信息Agent 如果不做校验就会把错误内容当成正常结果继续处理。第三类是状态不可追溯。Agent 跑了十几步之后出了问题你想知道是哪一步开始偏的但因为没有完整的执行日志和状态快照根本无从查起。这个问题在调试阶段特别致命因为你连复现都做不到。Harness 的价值就在于把这些问题在框架层面解决掉。它相当于给 Agent 套了一层运行时负责上下文的生命周期管理、错误的捕获和重试、状态的持久化和回放。你写 Agent 逻辑的时候只需要关注做什么怎么稳定地做交给 Harness。2.2 Harness 的核心组件拆解不同实现的 Harness 细节不一样但核心组件大同小异。我按自己的理解拆成四块执行调度器负责决定下一步执行什么。它接收 Agent 的决策输出解析成具体的动作然后调用对应的工具或插件。这一层的关键是动作的解析要足够健壮因为 LLM 输出的格式经常有细微偏差比如该用 JSON 的地方用了自然语言描述或者字段名拼写有误。上下文管理器维护对话历史和中间状态。它需要决定哪些内容保留、哪些压缩、哪些丢弃。常见的策略是滑动窗口加摘要近期内容保留原文远期内容压缩成摘要。好的上下文管理器还会做重要性标记把关键决策点单独存下来避免被压缩掉。工具与插件层Agent 能调用的具体能力。比如读取文件、发起网络请求、执行代码、查询数据库。这一层需要做权限控制和输入校验防止 Agent 执行危险操作或者传入非法参数。状态存储与回放把每一步的执行状态持久化支持中断后恢复和事后回放。这一层在调试和容错时特别有用你可以精确地看到 Agent 在每一步看到了什么、决定了什么、执行结果是什么。把这四块理清楚之后再去看各种 Harness 实现就能快速定位它的设计取舍。有的偏重轻量上下文管理做得简单有的偏重企业级状态存储和权限控制做得很重。选哪个取决于你的场景没有绝对的好坏。2.3 插件机制Harness 的扩展点在哪里Harness 的插件机制是我觉得最值得关注的部分因为它决定了这套框架能不能适应你的具体需求。插件本质上就是注册到 Harness 里的工具Agent 在运行时可以动态调用。一个设计良好的插件机制应该具备几个特征注册方式简单最好是通过配置文件或者装饰器就能完成插件的输入输出有明确的 schema 定义方便 LLM 理解怎么调用插件之间可以组合一个插件的输出能作为另一个插件的输入以及插件有独立的错误处理单个插件失败不会拖垮整个流程。我在实际使用中把插件分成三类数据获取类抓取网页、读取文件、查询接口、数据处理类文本清洗、格式转换、摘要生成、动作执行类写入文件、发送通知、更新状态。分类的好处是可以在 Harness 层面针对不同类型做不同的超时和重试策略。数据获取类通常需要较长的超时和多次重试数据处理类可以快速失败动作执行类则需要保证幂等性。插件配置的一个常见坑是参数传递。LLM 生成的参数经常有类型问题比如该传数字的地方传了字符串该传数组的地方传了单个值。Harness 如果不在这一层做类型转换和校验插件内部就要处理这些脏数据很容易出 bug。我的做法是在插件注册时定义严格的参数 schemaHarness 在调用前做一次校验和转换不通过就直接返回错误让 Agent 重新生成。3. 把工作流拆成 Agent 能执行的原子步骤3.1 从我想做什么到Agent 能做什么这一步是很多人卡住的地方。你脑子里想的是帮我找到今天值得关注的 alpha 信号但 Agent 没法直接执行这个指令因为它太抽象了。你需要把它拆成一系列具体的、可验证的步骤。我的拆解逻辑是这样的先定义输入和输出再定义中间的转换过程。输入是一组信息源的原始内容输出是经过筛选和初步验证的候选列表。中间的转换包括内容获取、去重、初步筛选、深度分析、验证、归档。每个步骤都要满足几个条件有明确的输入输出格式可以独立执行和验证失败时有明确的错误信息执行时间可控。不满足这些条件的步骤需要继续拆直到满足为止。举个例子初步筛选这个步骤如果定义成筛掉不相关的内容就太模糊了。我会定义成对每条内容判断它是否包含至少一个预设的关注主题返回布尔值和判断理由。这样 Agent 执行起来就有明确的判断标准输出也容易验证。3.2 步骤之间的依赖关系怎么处理拆完步骤之后下一个问题是它们之间的依赖关系。有些步骤是串行的前一步的输出是后一步的输入有些可以并行还有些有条件分支比如只有通过初步筛选的内容才进入深度分析。Harness 通常提供几种编排方式顺序执行、并行执行、条件分支、循环。我的经验是尽量用简单的编排能用顺序就不用并行能用条件就不用循环。原因是复杂的编排在出错时很难定位而且 LLM 在复杂控制流下的表现往往不如简单流程稳定。一个具体的例子内容获取这一步我原本设计成并行抓取多个源后来发现并行带来的问题比收益大——某个源超时会拖慢整体而且并发请求容易触发限流。改成串行加缓存之后整体耗时反而更短因为大部分源的内容在缓存里实际需要网络请求的很少。条件分支的使用要谨慎。我一开始设计了很多分支比如如果内容长度超过阈值就走摘要流程否则直接分析。后来发现 LLM 在分支判断上经常出错导致该走摘要的没走不该走的走了。简化之后统一走摘要流程只是摘要的长度根据内容长度动态调整稳定性好了很多。3.3 给每个步骤加上可观测性这一步经常被忽略但我觉得是日常使用中最关键的。Agent 跑起来之后你需要知道它现在在做什么、做到哪一步了、有没有异常。没有可观测性出了问题只能靠猜。我的做法是给每个步骤加上结构化的日志输出包括步骤名称、开始时间、结束时间、输入摘要、输出摘要、状态成功/失败/跳过、错误信息如果有。这些日志写到文件里同时关键节点发通知。日志的粒度要适中。太粗了定位不到问题太细了日志量爆炸。我的经验是每个步骤一条主日志步骤内部的循环或重试单独记录。比如内容获取这个步骤主日志记录获取了 N 条内容每条内容的获取结果单独记录。还有一个技巧是给每次运行分配一个唯一的 run_id所有日志都带上这个 id。这样事后排查时可以快速过滤出某一次运行的完整链路。这个做法在同时跑多个任务时特别有用不然日志混在一起根本分不清。4. 本地代理与端点转发CC 这一层的工程细节4.1 为什么需要本地代理这一层直接让 Agent 调用远程 LLM 接口在简单场景下没问题但一旦流程复杂起来就会遇到几个问题请求需要统一管理比如加统一的认证、日志、限流不同模型或不同端点的切换需要改代码以及本地的一些工具和远程接口需要打通。本地代理这一层就是解决这些问题的。它在本地起一个服务Agent 把请求发给本地代理代理再转发到实际的远程端点。这样做的好处是Agent 不需要知道远程端点的具体地址和认证方式只需要知道本地代理的地址切换模型或端点只需要改代理的配置不用动 Agent 代码代理层可以做统一的日志、限流、缓存、重试。CC 在这个语境下通常指的是这类本地代理和端点转发工具。它的核心功能是接收本地的请求按照配置转发到对应的远程端点然后把响应返回给调用方。配置通常包括监听地址和端口、上游端点的地址和认证信息、路由规则什么请求转发到哪个端点、以及各种中间件日志、限流、重试。4.2 配置中的常见坑与排查思路配置本地代理时我踩过的坑主要集中在几个地方端点路径拼接错误。很多代理工具会把本地路径和上游路径做拼接但拼接规则如果不清楚很容易出现路径重复或缺失。比如本地请求/v1/chat上游端点是https://api.example.com/v1最终应该请求https://api.example.com/v1/chat。但如果配置不当可能变成https://api.example.com/v1/v1/chat或者https://api.example.com/chat。排查方法是打开代理的详细日志看实际发出的请求 URL 是什么。认证头丢失或重复。代理转发时需要在请求头里加上游的认证信息同时可能要移除本地请求里带的认证头。如果处理不当会出现认证失败或者认证头重复导致上游拒绝。这个问题的表现通常是 401 或 403 错误排查时重点看请求头。超时设置不合理。代理层和上游层都有超时设置如果代理的超时比上游短会出现代理已经超时返回但上游还在处理的情况。反过来如果代理超时太长上游已经断了代理还在等。我的经验是代理的超时比上游略长一点留出网络传输的余量。404 错误的排查。关键词里提到了unexpected status 404 not found和cc switch local proxy failed while handling codex endpoint /responses这类错误通常是端点路径配置不对或者上游端点不支持请求的方法。排查步骤是先确认本地请求的路径和方法再看代理配置的路由规则最后看上游端点实际支持的路径和方法。三者对不上就会 404。4.3 代理层的日志与调试技巧代理层的日志是排查问题的第一手资料。我建议至少记录这几项请求时间、请求方法、请求路径、请求头脱敏后、请求体大小、上游地址、响应状态码、响应时间、响应体大小。调试时的一个实用技巧是开启请求和响应的完整记录但只在调试期间开因为完整记录会包含敏感信息和大量数据。另一个技巧是给代理加一个透传模式在这个模式下代理只记录不修改用来确认问题是不是代理层引入的。如果遇到间歇性的失败可以在代理层加一个请求 id贯穿整个链路。这样当出现问题时可以通过请求 id 快速定位到具体的请求和响应。这个做法在排查偶发问题时特别有效。5. 容错与回退让 Agent 在出错时还能继续跑5.1 错误分类哪些能重试哪些不能Agent 运行中遇到的错误可以分成几类处理方式完全不同瞬时错误网络抖动、临时限流、上游短暂不可用。这类错误重试通常能解决关键是重试策略要合理——指数退避加随机抖动避免重试风暴。永久错误认证失败、请求格式错误、资源不存在。这类错误重试多少次都没用应该快速失败并记录让 Agent 走其他路径或者上报。逻辑错误LLM 输出格式不对、参数类型错误、状态不一致。这类错误需要 Agent 重新生成或者修正而不是简单重试。资源错误上下文超限、内存不足、磁盘满。这类错误需要先释放资源或者调整策略再继续。我的做法是在 Harness 层给每个插件和每个步骤定义错误类型然后针对不同类型配置不同的处理策略。瞬时错误自动重试永久错误直接失败逻辑错误触发重新生成资源错误触发清理和降级。5.2 回退策略从哪一步重新开始当流程中断时从哪一步重新开始是个关键决策。从头开始最简单但最浪费从断点继续最高效但需要状态保存得好。我的策略是分级回退如果错误发生在某个步骤内部先尝试在该步骤内重试如果重试失败回退到该步骤的开始如果还是失败回退到上一个检查点最后才考虑从头开始。检查点的设置很关键。我在流程的关键节点设置检查点把当前状态持久化。检查点之间的步骤如果失败回退到最近的检查点。检查点的密度需要权衡——太密了状态保存开销大太疏了回退代价高。我的经验是每个阶段设一个检查点比如内容获取完成、初步筛选完成、深度分析完成各设一个。关键词里提到了代码回退这在 Agent 场景里指的是当 Agent 修改了某些状态或文件后如果后续步骤失败需要把这些修改回退掉。实现方式是操作前先备份失败时恢复备份。这个机制在 Agent 会写文件的场景里特别重要不然失败一次可能把之前的数据搞乱。5.3 幂等性让重复执行不出问题回退和重试都意味着某些步骤会被重复执行如果步骤不是幂等的重复执行就会出问题。比如发送通知这个步骤重试一次就发两次通知写入文件重试一次就写两份。保证幂等性的常见做法给每个操作分配唯一 id执行前先检查这个 id 是否已经执行过或者用覆盖写代替追加写或者把操作设计成设置状态而不是改变状态。我在实际使用中对写文件的操作统一用先写临时文件再原子替换的方式这样即使中途失败也不会留下半成品。对发送通知的操作用一个本地的已发送记录来去重。对更新状态的操作用版本号或者时间戳来做乐观锁。6. 提示词与上下文工程让 LLM 稳定输出可用结果6.1 提示词的结构化设计LLM 的输出稳定性很大程度上取决于提示词的设计。我的经验是提示词要结构化包含几个固定部分角色定义、任务描述、输入格式、输出格式、约束条件、示例。角色定义告诉 LLM 它是什么角色比如你是一个信息筛选助手。任务描述说明具体要做什么。输入格式和输出格式要非常明确最好给出 schema。约束条件说明什么不能做。示例给出一个完整的输入输出对。输出格式的约束是最关键的。我通常要求 LLM 输出 JSON并给出严格的 schema。为了处理 LLM 偶尔不按格式输出的情况Harness 层会做一次解析尝试失败的话触发重新生成重新生成时在提示词里强调格式要求。一个实用技巧是在提示词里加入如果你不确定输出一个特定的标记而不是猜测。这样可以把 LLM 的不确定性显式化避免它编造内容。我在筛选场景里用这个技巧让 LLM 对不确定的内容输出UNCERTAIN然后这些内容进入人工复核队列。6.2 上下文压缩的取舍长流程中上下文会不断增长压缩是必须的。但压缩会丢失信息怎么取舍是个问题。我的策略是分层压缩最近的几轮对话保留原文稍早的对话压缩成摘要保留关键决策和结论更早的只保留最终结论。压缩时用 LLM 做摘要提示词里强调保留所有影响后续决策的信息。另一个技巧是把结构化的状态和对话历史分开存。对话历史可以压缩但结构化状态比如已处理的内容 id 列表、当前的筛选结果单独存不参与压缩。这样即使对话历史被压缩关键状态也不会丢。压缩的触发时机也需要注意。我是在上下文达到窗口的 70% 时触发压缩留出 30% 的余量给后续对话。压缩后如果还是超就继续压缩更早的部分。6.3 提示词优化插件的使用关键词里提到了提示词优化插件这类插件的作用是自动优化提示词提升 LLM 的输出质量。常见的方式包括自动补充示例、自动调整措辞、自动添加约束。我的使用经验是这类插件在初期有帮助但不能完全依赖。它们能发现一些明显的问题比如缺少输出格式说明、约束不够明确但对于领域特定的优化还是需要人工介入。使用这类插件的一个技巧是把它当成提示词 linter而不是提示词生成器。让它检查你的提示词有没有明显问题然后你根据建议手动修改。这样既能利用工具的检查能力又能保持对提示词的控制。7. 我实际跑下来的一些体会这套流程跑了一段时间之后有几个体会比较深。第一个是简单优于复杂。我一开始设计了很多分支和并行觉得这样效率高。实际跑下来发现简单的串行流程加缓存稳定性和可维护性都好得多。Agent 的复杂度应该花在任务本身而不是流程编排上。第二个是可观测性不是可选项。没有日志和状态记录出了问题就是黑盒。我现在的做法是宁可多记日志也不要事后靠猜。日志的存储成本远低于排查问题的时间成本。第三个是容错要设计在流程里而不是事后补。一开始我总想着先跑通再说结果每次出错都要临时加处理逻辑。后来改成设计阶段就考虑每步可能出什么错、怎么处理整体稳定性好了很多。第四个是LLM 的输出永远要做校验。不管提示词写得多好LLM 总有概率输出不符合预期的内容。Harness 层的校验和重试机制是必须的不能省。最后一个体会是关于躺平的。这套流程搭好之后确实省了很多重复劳动但躺平不是什么都不管。你需要定期检查流程的运行情况看有没有新的失败模式看筛选结果的质量有没有下降。Agent 是帮你放大能力的工具不是替代你判断的黑盒。真正的 alpha 还是来自于你对信息的理解和判断Agent 只是让你有更多时间和精力去做这部分。