ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ForgeCode 动态系统上下文渲染机制:变量注入与逐轮重渲染实现解析

ForgeCode 动态系统上下文渲染机制:变量注入与逐轮重渲染实现解析 人工智能AI Agent代码智能体AI 应用CLI开发工具【免费下载链接】forgecodeAI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300 models项目地址https://gitcode.com/gh_mirrors/forge39/forgecode点击查看免费下载导读ForgeCodeAI 结对编程工具中系统上下文System Context是模型感知工作区环境、工具能力与项目信息的核心输入。传统实现只在 Agent 初始化时渲染一次系统上下文导致current_time等时效性信息长期过期且无法引用会话状态中的动态变量。本文以仓库内实现方案文档 plans/2025-04-02-system-context-rendering-final.md 为主体完整解析其设计目标、TemplateService签名演进、SystemContext变量扩展、编排器逐轮重渲染流程与测试策略并结合当前仓库源码给出落地印证。读完你将掌握如何让系统提示词每轮对话保持新鲜、如何将会话变量注入系统模板以及该机制与事件渲染Event Rendering如何保持一致的变量处理范式。一、问题背景静态系统上下文的三大缺陷ForgeCode 的系统上下文渲染机制在设计之初存在明显的功能缺口系统上下文仅在 Agent 初始化时渲染一次整个会话期间不再刷新像current_time这类强时效信息在长对话中会逐渐失真。无法使用会话状态中的动态变量会话中产生的变量无法进入系统模板模板只能引用固定的上下文数据。与事件渲染机制不一致事件渲染render_event已经支持传递变量而系统渲染render_system却没有对等的变量通道。方案文档明确给出两个改造目标在**每一轮对话conversation turn**都重新渲染系统上下文向系统上下文渲染器传递变量与事件渲染保持一致。二、接口演进为render_system增加变量参数2.1 方案中的签名设计方案第一步是修改TemplateServicetrait在render_system方法签名中引入变量参数async fn render_system( self, agent: Agent, prompt: TemplateSystemContext, variables: HashMapString, Value, ) - anyhow::ResultString;关键点在于variables使用HashMapString, Value与事件渲染共用同一套serde_json::Value类型天然支持字符串、数字、布尔、数组、嵌套对象等任意 JSON 值返回类型保持anyhow::ResultString渲染失败时错误沿调用链向上传播由编排器统一处理签名接受完整的Agent使渲染器可以直接读取agent.max_walker_depth、agent.tool_supported、agent.custom_rules等配置。2.2 当前仓库中的 trait 落点方案文档写明的路径是crates/forge_domain/src/services.rs在当前仓库中该 trait 实际定义于 crates/forge_app/src/services.rs#[async_trait::async_trait] pub trait TemplateService: Send Sync { async fn register_template(self, path: PathBuf) - anyhow::Result(); async fn render_templateV: serde::Serialize Send Sync( self, template: TemplateV, object: V, ) - anyhow::ResultString; }从源码结构看当前 trait 仅暴露了通用渲染入口render_template而方案中的render_system属于演进目标——这正是计划文档的价值所在它记录了向 trait 增加专门系统渲染方法的完整设计。trait 约束Send Sync与#[async_trait]保证渲染服务可在线程池与异步编排器中安全共享。三、SystemContext结构体扩展把变量变成一等公民3.1 方案中的完整结构体方案第三步扩展了SystemContext新增variables字段并同步维护Default实现#[derive(Debug, Setters, Clone, Serialize, Deserialize)] #[setters(strip_option)] pub struct SystemContext { // Current date and time at the time of context creation pub current_time: String, // Environment information to be included in the system context #[serde(skip_serializing_if Option::is_none)] pub env: OptionEnvironment, // Information about available tools that can be used by the agent #[serde(skip_serializing_if Option::is_none)] pub tool_information: OptionString, /// Indicates whether the agent supports tools. /// This value is populated directly from the Agent configuration. #[serde(default)] pub tool_supported: bool, // List of file paths that are relevant for the agent context #[serde(skip_serializing_if Vec::is_empty)] pub files: VecString, // README content to provide project context to the agent pub readme: String, #[serde(skip_serializing_if String::is_empty)] pub custom_rules: String, // Variables to pass to the system context #[serde(default, skip_serializing_if HashMap::is_empty)] pub variables: HashMapString, Value, } impl Default for SystemContext { fn default() - Self { Self { current_time: String::new(), env: None, tool_information: None, tool_supported: false, files: Vec::new(), readme: String::new(), custom_rules: String::new(), variables: HashMap::new(), } } }设计要点#[serde(default, skip_serializing_if HashMap::is_empty)]变量为空时不参与序列化避免向模型上下文注入无意义的空映射各字段普遍使用skip_serializing_if控制序列化体积保证系统上下文精简所有字段均有明确的注释语义current_time标注为上下文创建时刻的日期时间强调其时效性本质。3.2 当前仓库中的实际形态当前仓库的SystemContext定义于 crates/forge_domain/src/system_context.rs同样使用derive_setters::Setters与#[setters(strip_option)]并在env、tool_information、files、custom_rules等字段上延续了相同的skip_serializing_if序列化策略。可以推断方案文档所描述的variables字段扩展是沿用了这套既有的可选字段 精简序列化惯例。此外该文件还定义了TemplateConfig映射ForgeConfig的maxReadSize、maxLineLength等配置说明SystemContext的职责是作为 Handlebars 模板的数据源隔离ForgeConfig与模板层的耦合。四、ForgeTemplateService实现变量注入与工作区快照方案第二步给出了render_system的完整实现它承担三个任务构建文件列表、生成时间戳、组装上下文并渲染。4.1 文件遍历与排序let env self.infra.environment_service().get_environment(); // Build the walker, only setting max_depth if a value was provided let mut walker Walker::max_all(); // Only set max_depth if the value is provided walker walker.max_depth(_agent.max_walker_depth.unwrap_or(1)); let mut files walker .cwd(env.cwd.clone()) .get() .await? .iter() .map(|f| f.path.to_string()) .collect::Vec_(); // Sort the files alphabetically to ensure consistent ordering files.sort();遍历深度由agent.max_walker_depth控制未配置时默认深度为 1遍历根目录取env.cwd当前工作目录结果强制按字母序排序保证系统上下文中的文件列表在每次重渲染时顺序稳定——这对模板 diff 与上下文压缩的稳定性至关重要。4.2 时间戳与上下文组装// Get current date and time with timezone let current_time Local::now().format(%Y-%m-%d %H:%M:%S %:z).to_string(); let ctx SystemContext { current_time, env: Some(env), tool_information: Some(self.tool_service.usage_prompt()), tool_supported: _agent.tool_supported.unwrap_or_default(), files, readme: README_CONTENT.to_string(), custom_rules: _agent.custom_rules.as_ref().cloned().unwrap_or_default(), variables: variables.clone(), // Add the variables }; // Render the template with the context let result self.hb.render_template(prompt.template.as_str(), ctx)?; Ok(result)时间格式为%Y-%m-%d %H:%M:%S %:z即日期 时间 带冒号的 UTC 偏移例如2026-09-27 01:48:05 08:00让模型感知本地时区tool_information通过self.tool_service.usage_prompt()注入工具使用说明README_CONTENT为所有 Agent 提供项目级上下文variables通过variables.clone()拷贝进上下文避免渲染期间引用被修改。4.3 当前仓库中的渲染实现印证当前仓库的ForgeTemplateService位于 crates/forge_services/src/template.rs内部使用ArcOnceCellRwLockHandlebarsstatic实现 Handlebars 引擎的惰性初始化与并发安全并提供了read_all并行读取模板文件与register_template按目录注册模板等基础设施。方案中render_system最终调用的self.hb.render_template即对应这里的render_template方法crates/forge_services/src/template.rsasync fn render_templateV: serde::Serialize Send Sync( self, template: TemplateV, object: V, ) - anyhow::ResultString { let rendered self .get_hb() .await .read() .await .render_template(template.template, object)?; Ok(rendered) }模板编译还有一个值得注意的细节crates/forge_services/src/template.rs以.md结尾的文件按 Handlebars 语法编译其余文件作为原始字符串模板处理。这意味着系统提示词模板必须使用.md后缀才能获得变量替换能力。五、编排器改造每轮对话重渲染系统上下文5.1 主循环中的重渲染时机方案第四步在crates/forge_domain/src/orch.rs的编排器对话循环init_agent中加入了系统上下文重渲染逻辑。核心代码片段loop { // Get the latest conversation variables let variables self.conversation.read().await.variables.clone(); // Re-render system prompt if present if let Some(system_prompt) agent.system_prompt { let system_message self .services .template_service() .render_system(agent, system_prompt, variables) .await?; context context.set_first_system_message(system_message); } // Set context for the current loop iteration self.set_context(agent.id, context.clone()).await?; let response self .services .provider_service() .chat( agent .model .as_ref() .ok_or(Error::MissingModel(agent.id.clone()))?, context.clone(), ) .await?; let ChatCompletionResult { tool_calls, content } self.collect_messages(agent, response).await?; // Get all tool results using the helper function let tool_results self.get_all_tool_results(agent, tool_calls).await?; context context .add_message(ContextMessage::assistant(content, Some(tool_calls))) .add_tool_results(tool_results.clone()); // Check if context requires compression context self.compactor.compact_context(agent, context).await?; self.set_context(agent.id, context.clone()).await?; self.sync_conversation().await?; if tool_results.is_empty() { break; } }每次迭代的流程是从会话读取最新变量self.conversation.read().await.variables.clone()确保后续轮次能感知会话中新增或变更的变量若 Agent 配置了system_prompt调用render_system重新渲染并通过context.set_first_system_message(system_message)将新结果替换系统消息——注意是替换而非追加避免系统提示词随轮次累积膨胀调用 Provider 发起chat请求收集工具调用结果并追加进上下文需要时触发上下文压缩compact_context若本轮没有工具调用循环结束完成一轮完整对话。这个循环意味着只要会话中有工具调用如run_command、fs_write等改变状态的操作系统上下文就会在进入下一轮模型请求前重新渲染时间戳、文件列表、会话变量得以持续刷新。5.2 用户提示词的事件渲染与变量一致性方案还展示了render_event的调用方式说明用户提示词与系统提示词共享同一份会话变量if let Some(user_prompt) agent.user_prompt { // Get conversation variables from the conversation let variables conversation.variables; // Use the consolidated render_event method which handles suggestions and // variables self.services .template_service() .render_event(agent, user_prompt, event, variables) .await? }这印证了方案设计说明第 3 条与事件渲染的变量处理方式保持一致。当前仓库中事件渲染的数据结构EventContext定义于 crates/forge_domain/src/event.rs其中确实包含variables: HashMapString, Value字段并且会在构造时注入current_datechrono::Local::now().format(%Y-%m-%d)#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Setters)] pub struct EventContext { event: EventContextValue, suggestions: VecString, variables: HashMapString, Value, current_date: String, /// Structured terminal context injected by [TerminalContextService], /// or None when terminal context is unavailable or disabled. #[serde(default, skip_serializing_if Option::is_none)] terminal_context: OptionTerminalContext, }这说明事件渲染支持变量在仓库中已是既成事实方案的目标正是把这一能力对齐到系统上下文渲染。5.3 初始渲染init_agent_context使用空变量方案第六步要求更新init_agent_context在初始化阶段以空变量渲染一次系统提示词async fn init_agent_context(self, agent: Agent) - anyhow::ResultContext { let tool_defs self.init_tool_definitions(agent); // Use the agents tool_supported flag directly instead of querying the provider let tool_supported agent.tool_supported.unwrap_or_default(); let mut context Context::default(); if let Some(system_prompt) agent.system_prompt { // Create empty variables for initial rendering let empty_variables HashMap::new(); let system_message self .services .template_service() .render_system(agent, system_prompt, empty_variables) .await?; context context.set_first_system_message(system_message); } Ok(context.extend_tools(if tool_supported { tool_defs } else { Vec::new() })) }设计意图明确初始化时会话变量尚未产生先以空映射渲染一个静态基线系统提示词进入对话循环后再以实时变量逐步刷新。同时tool_supported直接读取 Agent 配置agent.tool_supported.unwrap_or_default()不再回查 Provider减少了初始化阶段的依赖调用。六、模板层变量如何在系统提示词中生效方案第五步给出了针对新机制的测试用例同时揭示了模板语法约定// In forge_services/src/template.rs or a test file #[tokio::test] async fn test_render_system_with_variables() { // Create a test agent let agent Agent::new(test-agent) .system_prompt(Template::from_string({{current_time}} - {{variables.test_var}})); // Create test variables let mut variables HashMap::new(); variables.insert(test_var.to_string(), json!(test_value)); // Render the system prompt with variables let result template_service .render_system(agent, agent.system_prompt.as_ref().unwrap(), variables) .await .unwrap(); // Verify the result contains both the current time and the variable assert!(result.contains(test_value)); }模板语法要点{{current_time}}直接引用SystemContext.current_time字段这是渲染时刻的时间戳{{variables.test_var}}通过variables命名空间访问会话变量句点.表示嵌套字段访问断言result.contains(test_value)验证了变量确实进入了渲染输出。当前仓库的 Handlebars 模板实际应用中templates/forge-partial-system-info.md 展示了同款语法例如{{env.os}}、{{env.cwd}}、{{#each files}}、{{#if extensions}}等并支持{{#each extensions.extension_stats}} - .{{extension}}: {{count}} files ({{percentage}}%){{/each}}这类列表渲染与条件分支。因此{{variables.xxx}}的写法与仓库现有模板体系完全兼容Agent 开发者可以零成本上手。七、验证标准与设计取舍7.1 验收条件方案文档定义了实现成功的五条标准系统上下文在每个对话轮次重新渲染保证信息实时性会话状态中的变量被正确传递给系统上下文渲染器系统上下文模板能够访问并展示这些变量模板侧已同步修改全部测试通过代码干净不保留不必要的向后兼容层。7.2 技术设计说明方案文档给出的四条设计原则值得深入理解干净实现Clean Approach不引入向后兼容层直接修改 trait 签名、结构体与调用方减少过渡代码的维护成本性能每轮重渲染不应造成性能问题。从实现看重渲染涉及文件遍历深度默认 1与 Handlebars 渲染均在毫秒级文件列表排序进一步保证了缓存/压缩的稳定性一致性与事件渲染的变量处理范式保持一致降低理解与维护心智负担错误处理anyhow::Result贯穿全链路渲染失败会中断本轮对话并在上层统一处理不会产生半渲染的脏上下文。八、当前仓库中的演进落地需要说明的是方案文档记录的是该功能的实现计划final 版本当前仓库代码已经历后续演进。对照现状可以看出该设计思路在仓库中的延续与落点系统上下文构建当前由 crates/forge_app/src/system_prompt.rs 中的SystemPrompt::add_system_message承担它同样构建SystemContext包含env、tool_information、tool_supported、files、custom_rules、skills、tool_names、extensions等字段并渲染两块内容——Agent 自定义系统提示词与forge-custom-agent-template.md模板上下文类型SystemContext已扩展出skills、model、tool_names、extensions、agents、config等字段见 crates/forge_domain/src/system_context.rs说明系统上下文的数据源覆盖面持续扩大编排层文档所述crates/forge_domain/src/orch.rs中的编排逻辑在当前仓库中已迁移至 crates/forge_app/src/orch.rs 等模块orch_spec/、orch_runner.rs等架构分层发生了调整但系统上下文需随会话状态刷新这一核心设计原则始终延续。因此阅读本文档时可以将其视为该机制的设计蓝本render_system的变量参数化、SystemContext.variables字段、编排循环中的set_first_system_message替换式刷新共同构成了 ForgeCode 动态系统上下文渲染的完整方案也为后续维护者扩展新的上下文变量提供了清晰的接入点。结语动态系统上下文渲染是提升 AI 结对编程体验的关键机制它让模型在每个对话轮次都能看到最新时间、最新工作区快照与最新会话变量避免模型以为还是昨天的上下文失真。本文完整还原了方案文档的六步实现路径接口签名 → 服务实现 → 结构体扩展 → 编排器改造 → 测试 → 初始化方法并对照当前仓库源码给出了每步的落点证据。开发者若要在 ForgeCode 中自定义系统提示词可参考 templates/forge-partial-system-info.md 的 Handlebars 语法与 crates/forge_app/src/system_prompt.rs 的上下文组装逻辑快速上手。赞分享人工智能AI Agent代码智能体AI 应用CLI开发工具【免费下载链接】forgecodeAI enabled pair programmer for Claude, GPT, O Series, Grok, Deepseek, Gemini and 300 models项目地址https://gitcode.com/gh_mirrors/forge39/forgecode点击查看免费下载相关推荐自定义 SaTokenContext 指南为任意 Web 框架接入 Sa-Token 权限认证自定义 SaTokenContext 指南为任意 Web 框架接入 Sa Token 权限认证 本篇指南讲解 Sa Token 中最核心的框架适配层—— Sa人工智能AI Agent代码智能体AI 应用CLI开发工具如何用 Cua Driver 在不将 Chromium 窗口置于前台的情况下导航、点击和输入网页如何用 Cua Driver 在不将 Chromium 窗口置于前台的情况下导航、点击和输入网页 当你让 Agent 操作本机浏览器时默认的做法是激活浏览器人工智能AI Agent代码智能体AI 应用CLI开发工具深入解析 forgecode 的 forge-partial-system-info.mdAgent 运行时系统环境上下文的渲染机制深入解析 forgecode 的 forge partial system info.md Agent 运行时系统环境上下文的渲染机制 导读 template人工智能AI Agent代码智能体AI 应用CLI开发工具上一篇PhotoPicker图片加载优化Glide集成与内存管理的完整解决方案下一篇IOPaint基于AI的智能图像修复与内容编辑解决方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表