YAOTU INSIGHTS

Repowise Codebase Chat 技术指南:基于 MCP 工具集的 SSE 流式 Agent 对话架构

Repowise Codebase Chat 技术指南:基于 MCP 工具集的 SSE 流式 Agent 对话架构
【免费下载链接】repowiseCodebase intelligence for AI and humans: code health scores, auto-generated docs, git analytics, dead code detection, and architectural decisions via MCP.项目地址https://gitcode.com/gh_mirrors/re/repowise点击查看免费下载本文是 Repowise「代码库聊天Codebase Chat」功能的技术参考与实战解析。该功能让用户以自然语言直接与代码库交互Agent 使用用户配置的任意 LLM Provider从 MCP 工具面MCP surface精选7 个工具而非完整的 11 个默认 MCP 工具通过 SSE 把流式响应实时推送到浏览器边生成边展示工具调用过程并在 Artifact 面板中渲染工具结果。读完本文你将掌握其端到端调用链数据库 → ChatProvider 协议 → Agentic Loop → SSE → 前端状态机、Provider 配置解析规则、SSE 事件协议以及各 ProviderAnthropic / OpenAI / Gemini / Ollama / LiteLLM的差异化实现。文中所有实现细节均可在当前仓库源码中找到对应依据。1. 架构总览一次提问的完整旅程用户在聊天框输入问题后请求按如下链路流转流程图引自 docs/architecture/chat.md并对照 聊天路由实现 做了细化User types question | v POST /api/repos/{repo_id}/chat/messages | v ------ Chat Router (SSE stream) ------ | | | 1. Create/load conversation | | 2. Save user message to DB | | 3. Build LLM message history | | 4. Call provider.stream_chat() -------- tool_executor callback | | | | v | | 5. Stream text_delta events ------- SSE to browser | 6. On tool_start: | | - Execute tool (or provider | | executes internally) | | - Emit tool_result event ------- SSE to browser | 7. If tool calls found: | | - Append to history, loop to 4 | | 8. If no tool calls: | | - Save assistant message to DB | | - Emit done event | ---------------------------------------一个关键设计决策是Agentic Loop 默认跑在 Chat Router 中OpenAI、Anthropic、Ollama、LiteLLM 皆是如此只有 Gemini 例外——它的循环在stream_chat()内部执行。原因在于 Gemini 的 API 要求在回放对话历史中的函数调用时携带thought_signature签名而通过 Router 做 OpenAI 格式的往返转换会丢失该签名因此 Router 向 Gemini 传入一个tool_executor回调由 Gemini 在内部循环中直接调用。这一点在第 10 节会详细展开。2. 数据库 Schema会话与消息的持久化两张表由迁移0005_chat_conversations.py引入迁移文件位于 packages/core/alembic/versions/0005_chat_conversations.py其中upgrade()与文档描述完全对应。conversations表ColumnTypeNotesidString(32)PKUUID hexrepository_idString(32)FKrepositories.idondeleteCASCADEtitleText自动生成新会话默认New conversation首轮回答后由前 6 个词提炼created_atDateTime(tz)server_defaultsa.func.now()updated_atDateTime(tz)收到新消息时自动更新索引ix_conversations_repo_updated建立在(repository_id, updated_at)上——按仓库列出会话并按更新时间排序是主要查询路径。chat_messages表ColumnTypeNotesidString(32)PKUUID hexconversation_idString(32)FKconversations.idondeleteCASCADEroleString(32)user或assistantcontent_jsonTextJSON 负载见下created_atDateTime(tz)索引ix_chat_messages_conv_created建立在(conversation_id, created_at)上。消息内容格式用户消息{text: What does the auth module do?}助手消息除了正文文本还会把本轮所有工具调用及其结果作为持久化负载一起存储tool_calls数组这样历史会话回放时能完整还原当时的工具证据{ text: The auth module handles..., tool_calls: [ { id: call_abc123, name: get_context, arguments: {targets: [src/auth]}, result: { ... } } ] }从源码看存储的tool_calls条目实际结构为{id, name, arguments, summary, artifact}即还包含工具结果摘要与 Artifact 信封见 routers/chat.py 中_stored_tool_callArtifact 数据随消息持久化历史会话无需重新调用工具即可回放结果面板。3. ChatProvider 协议可选接入的流式聊天能力协议定义在 packages/core/src/repowise/core/providers/llm/base.py。设计要点既有的BaseProvider.generate()完全不动新增一个基于typing.Protocolruntime_checkable的ChatProvider协议类把「流式聊天 工具调用」作为 Provider 的可选能力——实现stream_chat()即视为支持。runtime_checkable class ChatProvider(Protocol): def stream_chat( self, messages: list[dict], # OpenAI-format message list tools: list[dict], # OpenAI-format tool definitions system_prompt: str, max_tokens: int 8192, temperature: float 0.7, request_id: str | None None, tool_executor: Any | None None, # async callable(name, args) - dict ) - AsyncIterator[ChatStreamEvent]: ...配套数据类ChatToolCall(id, name, arguments)— LLM 想执行的工具调用ChatStreamEvent(type, text?, tool_call?, tool_result_data?, stop_reason?, input_tokens, output_tokens)— 流中的一个事件。事件类型type填充字段含义text_deltatext增量文本 tokentool_starttool_callLLM 请求调用某个工具tool_resulttool_call,tool_result_data工具已执行由 Provider 内部执行时usageinput_tokens,output_tokenstoken 用量更新stopstop_reason生成结束end_turn/tool_use/max_tokens消息统一使用OpenAI 格式的 dict 列表作为协议输入各 Provider 在stream_chat()内部自行转换为自家原生格式。stop_reason采用 Provider 无关的中性取值base.py中的normalize_stop_reason()会把各厂商枚举stop、length、tool_calls、function_call等归一化为end_turn、max_tokens、tool_use未知值原样保留以便诊断。现有实现Anthropic、OpenAI、Gemini、Ollama、LiteLLM。它们都接受tool_executor参数但只有 Gemini 真正使用它用于thought_signature处理见第 10 节。4. Tool Registry聊天工具的唯一事实来源定义在 packages/server/src/repowise/server/chat_tools.py。它是聊天工具 schema 与执行的单一来源从 MCP 注册表投影出「请求作用域」的工具面向 LLM 暴露 OpenAI 格式的 function 定义。核心函数函数用途get_tool_catalog(repo_path)返回该仓库配置的 MCP 工具面含生成的 schemaget_tool_schemas_for_llm()转成 OpenAI 格式的 tool definitions 交给 LLMexecute_tool(name, args)仅当工具属于该仓库配置面时执行并保证输出可 JSON 序列化get_artifact_type(name)/get_artifact_presentation(name)/get_artifact_evidence_basis(name)把工具名映射为前端 Artifact 类型 / 呈现方式 / 证据依据init_tool_state(...)把 FastAPI app state 桥接到 MCP 模块全局session 工厂、FTS、向量库、决策存储、repo 路径实现细节值得注意工具面由仓库配置决定get_tool_catalog调用selected_tool_entries(repo_path)即工具集来自仓库的 MCP 配置而非写死的列表双重安全约束execute_entry遵循ToolEntry.safety合约——mutating工具在未显式确认时直接返回confirmation_required错误执行失败不会抛异常中断流而是返回{error: ..., error_code: tool_failed}工作区别名兜底_scope_repo_arg会拦截模型擅自填写的repo参数用当前工作区别名覆盖确定性序列化_make_json_serializable递归把 dict/list/dataclass 转成纯 JSON 类型避免 SSE 序列化失败。7 个聊天工具与 Artifact 类型映射聊天 Agent 只暴露精选子集不包含完整的 MCP 默认面没有get_answer、get_symbol、get_health、list_reposToolArtifact Typeget_overviewoverviewget_contextwiki_pageget_riskrisk_reportget_change_riskrisk_reportget_whydecisionssearch_codebasesearch_resultsget_dead_codedead_code前端 ArtifactPanel 依据artifact.type决定渲染方式Markdown、Mermaid 图、搜索结果、原始 JSON。5. Provider 配置API Key 与活动 Provider 的解析链实现在 packages/server/src/repowise/server/provider_config.py。API Key 与活动 Provider/模型选择存储在服务端provider_config.json中路径为$REPOWISE_CONFIG_DIR/provider_config.json未设置时位于~/.repowise/provider_config.json环境变量优先于存储的 Key。文件采用「先写临时文件再os.replace原子替换」的方式落盘并尽力设置0600权限保护 Key 材料日志输出会用_redact_key对sk-前缀的 Key 打码。API Key 解析顺序源码为三层比文档更细进程环境变量如GEMINI_API_KEY、ANTHROPIC_API_KEY、OPENAI_API_KEY目标仓库的.repowise/.env——以 dict 形式读取不会注入os.environ因此工作区模式下某个仓库的 Key 不会泄漏到另一个仓库服务端存储的 Keyprovider_config.json中keys段通过set_api_key写入。另外UI 里新增的 Key 会被_mirror_key_to_repo_env同步镜像到该仓库的.repowise/.env以 Provider 目录中的第一个规范环境变量名为准这样之后在该仓库跑 CLI 也能读到同一把 Key——CLI 只读.env不读服务端存储。活动 Provider 解析顺序源码为五层覆盖文档的两步每仓库持久化的 UI 选择provider_config.json的repos[repo_id]——这是用户对该仓库的显式覆盖按仓库隔离避免「为一个仓库选模型却影响其他仓库」的历史 bug仓库自身config.yamlprovidermodel由repowise init写入——无缝默认服务端全局active_providerPATCH /api/providers/active写入的旧式单仓库状态REPOWISE_PROVIDER/REPOWISE_MODEL环境变量自动探测——遍历目录取第一个有可用 Key 或无需 Key 的 Provider。Provider 目录文档第 5 节列出的核心目录为 Gemini、Anthropic、OpenAI、Ollama本地、无需 Key、LiteLLM对照当前源码中的PROVIDER_CATALOG实际还包含 OpenRouter、DeepSeek、Kimi、Eden AI、Claude Code本地 CLI、Codex本地 CLI、OpenCode本地 CLI等。每个条目声明default_model、可选的models列表、env_keys与requires_key。示例源码原文{ id: gemini, name: Google Gemini, default_model: gemini-3.5-flash-lite, models: [gemini-3.5-flash-lite, gemini-3.1-flash-lite, gemini-3.1-pro-preview], env_keys: [GEMINI_API_KEY, GOOGLE_API_KEY], requires_key: True, },base_url同样有三层解析进程环境变量名目来自PROVIDER_BASE_URL_ENVS与 CLI/MCP 解析共用同一张表保证不漂移→ 仓库.env→ 仓库config.yaml中按 Provider 分段的openai: {base_url: http://localhost:4000/v1}这类写法。这使得本地 LiteLLM / OpenAI 兼容端点可以在init时配置并被聊天复用。6. SSE 流式协议浏览器实时看到生成过程聊天端点返回Content-Type: text/event-stream。每个事件格式event: data data: {type: ..., ...}事件形状逐条解析// LLM 的增量文本 {type: text_delta, text: The auth module...} // LLM 要调用工具 {type: tool_start, tool_id: call_123, tool_name: get_context, input: {targets: [src/auth]}} // 工具执行完成 {type: tool_result, tool_id: call_123, tool_name: get_context, summary: Context for 1 target(s), artifact: {type: wiki_page, data: {...}}} // 流结束 {type: done, conversation_id: abc123, message_id: def456} // 错误 {type: error, message: Provider error: ...}HeadersCache-Control: no-cache, no-transform、X-Accel-Buffering: no、Connection: keep-alive。其中no-transform是为了防止 Next.js rewrite 代理的压缩中间件对流做 gzip 缓冲与 jobs 流同一处理策略。Retry流开始时发送retry: 3000。终端事件纪律每条流必须以done或error在data通道上收尾。前端useChat只按type字段分派事件因此发送到其他通道、或缺少type的事件会被丢弃——客户端只会看到「回答到一半流停了」。客户端在 reader 结束时若未收到终端事件会自行结算本地状态把进行中的工具标为错误态但服务端仍然欠它一个终端事件。源码中该格式由_sse_event(event, data)统一生成见 routers/chat.py。此外源码还会在特定场景发出文档未列出的补充事件{type: suggestions, suggestions: [...]}—— 回答完成、且本轮有可跟进问题时附带建议追问{type: truncated, loops: 10}—— 10 轮循环全部以工具调用结束、始终未产出最终答案时发出。7. Agentic Loop最多 10 轮的模型-工具交替循环循环每请求最多运行10 次迭代源码常量_MAX_AGENTIC_LOOPS 10见 routers/chat.pyfor each iteration: 1. Call provider.stream_chat(messages, tools, system_prompt, tool_executor) 2. Collect text_delta events - stream to client 3. Collect tool_start events - stream to client 4. Collect tool_result events (from internal execution) - stream to client 5. If there are pending tool calls (not internally executed): a. Execute each tool b. Emit tool_result to client c. Append assistant tool results to message history d. Continue loop 6. If no tool calls: break循环结束后助手消息文本 全部工具调用及其结果保存到数据库并发出done事件。对照源码_AgentTurn的实现有几个值得说明的细节两阶段工具执行模型_model_turn收集tool_start事件进入pending列表若 Provider 已内部执行tool_result事件则从pending中移除对应项——这正是 Gemini 内部循环与 Router 循环的协作点客户端断连即中止每轮都检查request.is_disconnected()断连置aborted并停止不保存任何内容源码注释明确客户端断连或 Provider 失败时不落库避免半截回答污染历史grounding 预读在模型第一轮之前Router 会基于页面上下文ChatPageContext用plan_groundingrun_grounding做一次「该页面已获授权的读取」例如打开 Wiki 页先拉一次get_context并把结果注入消息历史、以origingrounding标记存储重复的调用会被历史去重跳过ProviderError 即 error 事件模型调用抛出ProviderError时置aborted并立即yield一个errorSSE 事件截断标记10 轮全是工具调用时置truncatedTrue回复内容中带truncated标记并发出truncated事件。历史构建方面_db_messages_to_llm_format把数据库消息转成 LLM 格式_with_navigation_context再注入页面导航上下文工具结果以tool_result_message回填历史助手侧用assistant_tool_call_message记录本轮文本与工具调用。8. REST API 端点Chat 端点MethodPathDescriptionPOST/api/repos/{repo_id}/chat/messagesSSE 流——发送消息并获取流式响应GET/api/repos/{repo_id}/chat/conversations列出仓库的会话GET/api/repos/{repo_id}/chat/conversations/{id}获取会话及其全部消息DELETE/api/repos/{repo_id}/chat/conversations/{id}删除会话POST body{ message: What does the auth module do?, conversation_id: null, provider: null, model: null }conversation_id— 省略或null表示开启新会话provider/model— 可选的单请求覆盖即 UI 的模型选择器仅对本次请求生效不带覆盖时按第 5 节的仓库级解析链确定 Provider 与模型。从源码看POST还接受一个context字段kind/label/target/target_kind用于把页面上下文传给 grounding 与系统提示词且所有聊天端点都受verify_api_key依赖保护Router 级依赖。补充端点源码中的完整面超出文档表格源码中的聊天路由还提供了会话生命周期管理的更多端点均带verify_api_key依赖GET /api/repos/{repo_id}/chat/suggestions?kind...target...— 基于页面已获授权读取生成建议问题不产生模型调用与主聊天共用同一套 grounding 函数保证两处对页面类型的映射永不漂移POST /api/repos/{repo_id}/chat/conversations/{id}/restore— 恢复软删除的会话PATCH /api/repos/{repo_id}/chat/conversations/{id}— 更新标题或固定pinned状态标题不可为空POST /api/repos/{repo_id}/chat/conversations/{id}/fork— 从某个消息节点派生新会话through_message_id与before_message_id二选一GET /api/repos/{repo_id}/chat/conversations/{id}/artifacts/{artifact_id}与PATCH .../artifacts/{artifact_id}— 读取历史消息中持久化的 Artifact、切换其固定状态。Providers 端点MethodPathDescriptionGET/api/providers列出全部 Provider 及其状态与活动选择PATCH/api/providers/active设置活动 Provider 与模型POST/api/providers/{id}/key存储 API KeyDELETE/api/providers/{id}/key移除 API Key9. 前端架构API 层packages/api-client/src/chat.ts—listConversations、getConversation、deleteConversation、getChatSuggestions、restoreConversation、updateConversation、forkConversation、getConversationArtifact、setConversationArtifactPinned核心的postChatMessage返回原始Response而非解析后的 JSON供调用方读取response.body作为ReadableStream逐帧消费 SSE。它还会透传AbortSignal不仅中止读循环连 POST 请求本身一起中止否则服务端 Agentic Loop 会继续空转、DB 会话直到 socket 坍塌才被回收Provider 管理封装getProviders/setActiveProvider/addProviderKey/removeProviderKey同样在 api-client 层。HooksuseChat(repoId)实现见 packages/web/src/lib/hooks/use-chat.ts—— 完整的聊天状态机。使用fetchReadableStream读取而非EventSource后者仅支持 GET。管理消息列表、流式状态、会话 ID、错误处理与中止控制。实现上的细节用AbortController在中止/切换仓库/组件卸载时打断流文本增量通过requestAnimationFrame批处理queueText/flushText避免高频 text_delta 触发过量 React 重渲染流异常终止时用stopRunningTools把进行中的工具调用标记为错误态并附上说明。对外暴露sendMessage、loadConversation、resetuseProviders()—— SWR 包装的 Provider 管理暴露providers、activeProvider、activeModel、activate、saveKey、removeKey。组件packages/ui/src/chat/与packages/web/src/components/chat/组件用途ChatInterface主容器——空态问候语 建议问题 模型选择器与活跃态消息列表 输入框ChatMessage渲染用户气泡或助手消息工具块 MarkdownChatMarkdown客户端 Markdown 渲染react-markdownremark-gfm使用设计 token 样式ToolCallBlock/ToolCallGroup内联工具调用可视化——运行中spinner、已完成折叠勾选 摘要、已完成展开输入/输出 JSONArtifactPanel右侧滑入面板多 Artifact 分 Tab按类型渲染Markdown、Mermaid 图、搜索结果、原始 JSONModelSelector紧凑 popover切换 Provider/模型并内联添加 API KeyConversationHistory下拉列出历史会话支持删除、新建、恢复、fork、固定等操作页面结构仓库落地页/repos/[id]就是聊天界面紧凑头部仓库名 commit 徽标 分支徽标、ChatInterface占满剩余视口高度、侧边栏导航项由 Overview 改为 Chat。仓库的其他子页graph、wiki、coverage 等保持不变。10. Provider 专属说明Anthropic使用client.messages.stream()原生 Anthropic 消息格式。OpenAI 格式消息需转换工具结果转为user角色的tool_resultcontent block工具调用转为tool_usecontent block。Agentic Loop 跑在 Chat Router。OpenAI使用client.chat.completions.create(streamTrue)。原生 OpenAI 格式几乎无需转换。工具调用片段在流 chunk 中累积完整后一次性以tool_start事件发出。Agentic Loop 跑在 Chat Router。Gemini使用client.models.generate_content()非流式在线程池中执行见 packages/core/src/repowise/core/providers/llm/gemini.py。Agentic Loop 在stream_chat()内部运行Gemini 的 API 在回放对话历史中的 function call 时必须携带thought_signature若经 Router 做 OpenAI 格式往返会丢失该签名因此内部循环全程使用原生Content对象通过tool_executor回调执行工具并产出tool_start/tool_result事件。tool_executor参数对 Gemini 是必需的缺失时它会yield一个stop让调用方接管循环但注释明确这会在下一次往返时因thought_signature缺失而失败。Ollama使用 OpenAI 兼容端点localhost:11434/v1经AsyncOpenAI调用流式模式与 OpenAI 一致无需 API Key。Agentic Loop 跑在 Chat Router。LiteLLM使用litellm.acompletion(streamTrue)OpenAI 兼容流式输出。Agentic Loop 跑在 Chat Router。附注推理类模型的温度参数兼容值得补充的是流式聊天与生成共用 Provider 层的温度兼容策略见 base.py推理时代的部分模型如gpt-5、o1、o3、o4系列Anthropic Opus/Sonnet 5 系列等会拒绝显式temperature并返回 400。temperature_kwargs()对已知前缀直接跳过该参数is_temperature_rejection()/remember_temperature_rejection()则在运行时从第一次拒绝中「学习」新模型并加入进程内缓存避免每次多付一次失败的调用。这是跨生成与聊天两条路径的共享底层逻辑。结语与进一步阅读Repowise Codebase Chat 的核心价值在于「把 MCP 工具面安全地投影给一个带流式的 Agent 循环」仓库级工具配置决定能力边界ChatProvider协议让不同厂商的流式/工具语义归一化SSE 协议与前端useChat状态机保证「生成过程可见、结果可回放」。若想深入源码建议按以下顺序阅读聊天路由与 Agentic Looppackages/server/src/repowise/server/routers/chat.py工具注册表与执行合约packages/server/src/repowise/server/chat_tools.pyProvider 协议与事件类型packages/core/src/repowise/core/providers/llm/base.pyProvider 配置解析链packages/server/src/repowise/server/provider_config.py数据库迁移packages/core/alembic/versions/0005_chat_conversations.py前端 API 层packages/api-client/src/chat.ts、packages/web/src/lib/hooks/use-chat.ts架构级说明docs/architecture/chat.md、docs/architecture/ARCHITECTURE.md赞分享【免费下载链接】repowiseCodebase intelligence for AI and humans: code health scores, auto-generated docs, git analytics, dead code detection, and architectural decisions via MCP.项目地址https://gitcode.com/gh_mirrors/re/repowise点击查看免费下载相关推荐Hoppscotch 快速上手三步部署自托管 API 调试平台的实操指南Hoppscotch 快速上手三步部署自托管 API 调试平台的实操指南 Hoppscotch 是一个开源 API 开发工具支持 REST、GraphQL、开发工具接口测试前端后端CLI基于 n8n 的 Tech Stack Expert 对话式技术选型 Agent工作流架构、系统提示词与实战接入解析基于 n8n 的 Tech Stack Expert 对话式技术选型 Agent工作流架构、系统提示词与实战接入解析 本文以 oTTomator Live A示例工程ruflo 性能优化 Agent 实战指南基于 sublinear 算法的 Performance Optimizer 架构、MCP 工具与集成模式ruflo 性能优化 Agent 实战指南基于 sublinear 算法的 Performance Optimizer 架构、MCP 工具与集成模式 本指南以人工智能AI Agent多智能体Agent 编排Agent 记忆工具调用代码智能体MCP 服务AI 评测上一篇【免费下载】 探索Windows驱动存储库的利器Driver Store ExplorerRAPR下一篇终极指南如何使用Go-libp2p构建去中心化网络应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考