
📅 日期:2026-03-24 📌 定位:向量与检索正式开课前,把 输出可控、工具调用闭环、本地知识 mock、长文 Map-Reduce 和 运行日志 打牢。

在进入 Embedding / RAG 之前,要先承认一件事:检索再准,Prompt 写散了,模型照样会胡编或跑题。CO-STAR 把「长上下文里容易丢的约束」拆成固定维度,适合做成团队模板、评审清单。
维度 | 含义 | 和前端工作的类比 |
|---|---|---|
Context | 任务背景、业务边界 | 需求背景、项目说明 |
Objective | 要完成的单一目标 | 函数返回值 / 验收标准 |
Style | 文体与表述方式 | 设计系统里的文案层级 |
Tone | 情绪与态度 | 对用户说话的语气规范 |
Audience | 读者是谁 | 用户画像、读者技术水平 |
Response | 输出形态(列表、Markdown、字段名) | API 的 Response Schema |
和 RAG 的关系:检索片段只是「动态 Context」的一部分;Objective + Response 决定模型是「严格引用材料」还是「自由发挥」。课前把 CO-STAR 写熟,后面接向量库时少一半扯皮。
工程落地:练习里把 CO-STAR 与「工具分流」写进 唯一一条 system 消息;百炼 Generation.call 若同时传非空的 prompt=,SDK 会再追加一条 user,容易打乱角色边界,故长约束应放在 messages 的 role: system,而不是 prompt=。
这次练习里同时存在几类能力:列目录、读文本、拉监控快照。真实痛点是:模型不会自动知道该先读文档还是先查数,会受——用户措辞(是不是像「告警」)、工具在列表里的顺序、描述里有没有写清触发条件——共同影响。
几条可复用的工程经验:
运维助手工具说明.md),让模型在犹豫时能读到「协议层」说明,而不是只靠参数猜。常见实现 bug 是:只执行第一轮 tool,把结果塞回上下文后又调一次模型,看到第二轮还是 tool_calls(例如要继续 read_local_file)却不再执行,直接回到读下一行用户输入——用户会感觉「没跑完」。


练习里知识被限制在固定根目录内:列举与读取分离——先枚举相对路径,再按路径读文本。实现上要注意:
..、绝对路径),避免被提示词注入带偏。按自然日一个文件、每轮模型响应写一行 JSON,字段里带上:用户原话、第几轮、是否调工具、assistant 的 content、请求的 tool 列表、本地执行结果(可截断)。终端只打印有内容时的最终回答,减少刷屏;真要查「为什么没读某文件」,打开当天 JSONL 一行行看即可。
实现要点(utils.append_practice_jsonl_line):写入后 flush + fsync,避免脚本未结束时在编辑器里看不到追加内容;对嵌套结构做 coerce + allow_nan=False,避免非标准键、NaN 等导致整行写失败;二级摘要 / Map-Reduce 的独立调用可记入同目录下的 YYYY-MM-DD_secondary.jsonl,与主对话日志分离,便于只看预处理链路。
问题:read_local_file 若一次性返回极长正文,主对话上下文与费用都会吃紧;在还没接向量库前,需要 Map-Reduce 思想做预处理。
思路 | 做法 | 代价与风险 |
|---|---|---|
单次摘要 | 长文交给「摘要专用」一次对话,再把摘要给主模型 | 实现简单;摘要易丢条款号、联系人等细节 |
分块 Map + 合并 Reduce | 按固定字符窗口切块(可设 overlap 保留边界上下文),每块单独摘要,再开一轮把多块摘要合成总摘要,最后交给主模型 | 调用次数多(N+1)、延迟高;切块若切断句子,靠 overlap 与合并提示缓解 |
练习代码中的对应关系:
process_long_file(content, chunk_size, overlap):切块 → 逐块 _generation_plain_text(Map)→ 再合并(Reduce)。chunk_size / overlap 可由模块常量或环境变量 MAP_REDUCE_CHUNK_SIZE / MAP_REDUCE_OVERLAP 调整;模型名可用 MAP_REDUCE_MODEL。read_local_file 结果超过 TOOL_OUTPUT_SUMMARY_THRESHOLD_CHARS(可用环境变量覆盖)时,主链路会走 Map-Reduce 预处理,再作为 tool 内容回到多轮对话。需要持续考虑的工程点:

把 CO-STAR 抽成 constants.PROFESSIONAL_SYSTEM_TEMPLATE 时,若沿用「KNOWLEDGE BASE + {retrieved_context}」这类 经典 RAG 注入 写法,在本练习里会失真:事实并不在 system 里预填,而是 tool 消息动态返回。
本次修正方向:
list_doc_files / read_local_file / get_current_status)。CO-STAR_Prompt.py 里删除与模板重复的冗长常量,单一数据源在 constants.py,避免两处改不全。场景 | 规则 | 注意 |
|---|---|---|
str.format() | {name} 由关键字参数替换 | 模板里若要输出字面量花括号,需写成 {{ 与 }} |
RAG 注入 | 有的流水线用 {retrieved_context} 在服务端替换后再发给模型 | 与 tool 流 并存时,要在文案里说清「上下文从哪来」,避免模型盯着空的占位符 |
环境变量调参 | 如 TOOL_OUTPUT_SUMMARY_THRESHOLD_CHARS、CO_STAR_LOG_DIR | 适合测试阶段频繁改阈值而不改代码 |
主题 | 收获 |
|---|---|
CO-STAR | 长任务 Prompt 脚手架;与 RAG 的 Context 注入强相关;宜进 system 而非 SDK 的 prompt= |
Tools | 描述 + 顺序 + System 分流,决定「像不像 Agent」 |
多轮 | 必须闭环到 stop,不能只做单次 tool |
知识边界 | 根目录、列表、防穿越、日志截断 |
长文 | Map-Reduce 预处理 + 失败降级 + 阈值/环境变量可配 |
模板 | 静态 RAG 占位 vs 工具返回事实,文案要与真实数据流一致 |
下一课预告:Embedding、向量空间直觉、与检索怎么接到 CO-STAR 的 Context 位上(届时可能是「向量检索结果注入」与「tool 读全文」的组合,而不是混用空占位)。