news 2026/7/5 2:24:16

将 Pi Agent 接入 HagiCode 的实践之路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
将 Pi Agent 接入 HagiCode 的实践之路

其实一切都要从那个问题说起:在 HagiCode Mono 项目中,虽然repos/Hagicode.Libs已经实现了可复用的PiProvider,可是repos/hagicode-corerepos/web还没有将 Pi 提升为项目级一等 Agent CLI。这就像你有了一双好鞋,却还没系好鞋带,虽然能走路,但总觉得差点什么。

现有的PiProvider位于HagiCode.Libs/src/HagiCode.Libs.Providers/Pi/PiProvider.cs,它实现了基于行分隔 JSON(--mode json --print)的 CLI 通信协议,支持会话管理、工具调用和流式输出。这部分代码写得很好,只是它还躺在那里,没有被唤醒。

这种状况造成了一个断层:Pi 的底层能力是完整的,但项目级的接入链路(从用户配置到执行监控)是缺失的。这就像画了一幅好画,却没挂在墙上让人欣赏。所以需要将 Pi 作为新的活跃 providerPiCli接入整个系统,使其成为与其他 Agent CLI 一等的工作流入口。

毕竟,我们想要的是完整的体验,而不是零散的片段。

关于 HagiCode

本文分享的方案来自我们在 HagiCode 项目中的实践经验。HagiCode 是一个智能代码助手项目,在开发过程中我们需要整合多种 AI 能力提供商。本文所述的 Pi agent 接入方案,正是我们在实践中总结出来的一套可复用的集成模式,对于类似的多 provider 集成场景具有参考价值。项目的 GitHub 地址是 github.com/HagiCode-org/site。

架构设计

Pi agent 对接采用了thin adapter 模式,而不是在 core 层重新实现 Pi 进程协议。这个设计决策其实也没什么复杂的,就是想了想,何必重复造轮子呢?

毕竟:

  1. 避免重复实现PiProvider中的参数构建、进程启动、JSON 解析和错误处理逻辑已经很完整,重复实现会制造两套行为源和两套测试矩阵。这也罢了,但维护起来会很麻烦。
  2. 保持一致性:与 Kimi、Gemini、Reasonix、DeepAgents 等现有 CLI 的接入方式保持一致,都是通过 thin adapter 委托给 libs 层的实现。大家都这么做,跟着走就是了。
  3. 关注点分离hagicode-core专注于运行时契约和业务逻辑,进程细节交给HagiCode.Libs处理。各司其职,岂不美哉?

这种设计让 HagiCode 在保持核心层简洁的同时,能够快速集成新的 AI 能力提供商。毕竟,简洁是美,效率是钱。

核心组件实现

Provider 枚举和 Factory

hagicode-core/src/PCode.Models/AIProviderType.cs中新增了PiCli = 13枚举值:

public enum AIProviderType
{
ClaudeCodeCli = 0,
// ... 其他 provider
PiCli = 13,
}

这个枚举值是 provider 身份的根源,会影响 OpenAPI 生成的PCode_Models_AIProviderType.ts前端枚举、AIProviderFactory的创建分支,以及 Provider 解析和活跃 provider 判断逻辑。在AIProviderFactory中注册新的创建分支:

case AIProviderType.PiCli:
provider = new PiCliProvider(
logger,
configuration,
providerFactory.GetRequiredService<ICliProvider<PiOptions>>(),
providerFactory.GetService<IAgentCliRuntimeEnvironmentResolver>());
break;

其实这些代码也没什么特别的,就是一个枚举加一个 switch case 而已。只是它们很重要,就像乐谱上的音符,一个个加起来才能演奏出完整的曲子。

Thin Adapter 实现

PiCliProvider.cs是核心的 thin adapter,它实现了IAIProviderIVersionedAIProviderIAsyncDisposable接口。通过构造函数接收ICliProvider<PiOptions>(来自HagiCode.Libs),将AIRequest/ProviderConfiguration映射到PiOptions,然后委托执行、ping、版本查询等操作给底层 provider。

关键是要处理 Pi 特有的 JSON 事件流,包括assistant.thoughtassistantterminal.completed等事件。这些事件在流式输出过程中需要被正确解析和转换为系统的标准格式。这有点像翻译,把一种语言翻译成另一种语言,意思要传达到位才行。

主职业预设

main-professions.yaml中增加了profession-pi主职业条目:

- Id: "profession-pi"
Name: "Pi"
Family: "pi"
Summary: "hero.professionCopy.primary.pi.summary"
Icon: "executor-avatar:Pi"
SourceLabel: "hero.professionCopy.sources.aiProvidersPiCli"
ProviderType: "PiCli"
SortOrder: 59
DefaultEnabled: true
DefaultParameters:
binary: "pi"
provider: "omniroute"
thinking: "balanced"

这确保了后端快照与前端 fallback 目录有一致的 Pi 身份,可以通过现有 Hero 编辑器管理 Pi 配置,而且 Pi 的加入不会破坏现有主职业条目的可消费性。毕竟,我们不想因为新增一个功能就把原来的东西弄乱了,那样就因小失大了。

监控注册表

AgentCliMonitoringRegistry需要增加 Pi 的监控 descriptor,使系统能够解析可执行路径、展示品牌名、进行健康探测,并在状态栏和健康详情中显示 Pi 状态:

new AgentCliMonitoringDescriptor(
CliId: "pi",
DisplayName: "Pi",
ProviderType: AIProviderType.PiCli,
DisplayOrder: 13,
Strategy: AgentCliMonitoringStrategy.Grain,
NotConfiguredMessage: "Pi CLI is not configured or executable not found.",
EnabledPaths: [],
ExecutablePathConfigPaths: ["Hero:PrimaryProfessions:Pi:ExecutablePath"],
DefaultExecutablePath: "pi")

监控系统就像仪表盘,告诉你车子跑得怎么样。Pi 的状态一目了然,这样用户就能知道一切是否正常。

前端适配

执行器类型适配

前端需要更新executorTypeAdapter.ts,添加 Pi 的类型识别逻辑:

const PI_IDS = new Set<string>([
PCode_Models_AIProviderType.PI_CLI,
'Pi',
'PiCli',
'pi',
'picli',
'pi-cli',
]);
export function isPi(value: string): boolean {
const normalized = normalize(value);
const normalizedLower = normalized.toLowerCase();
return PI_IDS.has(normalized)
|| normalizedLower.includes('pi-cli')
|| normalizedLower.includes('picli')
|| normalizedLower === 'pi';
}

这就像给 Pi 起了好几个名字,不管你怎么叫它,它都知道你是在叫它。毕竟,叫什么名字不重要,重要的是知道你是谁。

Fallback Hero 目录

hero.ts中添加 Pi 的 fallback 条目,确保即使后端数据未加载,前端也能正常显示 Pi 配置:

{
id: "profession-pi",
name: "Pi",
family: "pi",
summary: "hero.professionCopy.primary.pi.summary",
icon: "executor-avatar:Pi",
sourceLabel: "hero.professionCopy.sources.aiProvidersPiCli",
providerType: AIProviderType.PI_CLI,
sortOrder: 59,
isReadOnly: true,
managedParameterKeys: {
// 首版支持的参数 key
},
defaultParameters: {
binary: "pi",
provider: "omniroute",
thinking: "balanced",
},
}

Fallback 就像备用计划,万一后端挂了或者数据没加载出来,前端也能正常工作。毕竟,谁也不想到时候手忙脚乱。

本地化文案和表单

locales/*/common/{hero,settings}.yml中增加 Pi 相关的翻译,并在HeroCliEquipmentForm.tsx中为 Pi 新增配置字段区块,支持 binary、provider、thinking、sessionDirectory 和工具/会话开关字段。

首版 Pi 只暴露最小必要字段,复杂功能如工具 allowlist/denylist 和环境变量编辑器被明确延后到后续变更。毕竟,一口吃不成胖子,慢慢来比较快。

配置示例

后端配置

appsettings.yml中配置 Pi 参数:

Hero:
PrimaryProfessions:
Pi:
ExecutablePath: /usr/local/bin/pi
Provider: omniroute
Thinking: balanced
SessionDirectory: ~/.pi/sessions
NoSession: false
DisableAllTools: false
DisableBuiltinTools: false

前端配置

在 Hero 编辑器中配置 Pi profession:

{
id: "my-pi-profession",
name: "My Pi Profession",
family: "pi",
providerType: AIProviderType.PI_CLI,
primaryModel: {
provider: "PiCli",
model: "glm-4.7",
providerSettings: {
provider: "omniroute",
thinking: "balanced",
sessionDirectory: "/Users/username/.pi/sessions",
noSession: false,
disableAllTools: false,
disableBuiltinTools: false,
},
},
}

配置文件就像菜谱,照着做就能做出好菜。只是有时候,就算照着菜谱,也会把菜做糊......不过这次的配置倒是很清晰,应该没什么问题。

验证和测试

后端验证

在测试中验证主职业预设:

var snapshot = await presetProvider.GetSnapshotAsync();
var piProfession = snapshot.FindById("profession-pi");
piProfession.ShouldNotBeNull();
piProfession.ProviderType.ShouldBe(AIProviderType.PiCli);
piProfession.Family.ShouldBe("pi");

还需要验证 Pi 可用性和健康检查:

# 检查 Pi 可执行文件
which pi
pi --version
# 验证后端 provider 注册
curl http://localhost:35168/api/health/agent-cli/pi

测试就像考试,考过了才能说明真的会了。只是有时候,考试过了也未必真的懂了,不过至少说明你能做对题。

前端验证

// 验证类型解析和显示名称
expect(resolveExecutorVisualType('pi-cli')).toBe('Pi');
expect(resolveExecutorVisualType(PCode_Models_AIProviderType.PI_CLI)).toBe('Pi');
expect(resolveExecutorDisplayName('PiCli')).toBe('Pi');
// 验证 fallback 目录
const piFallback = findFallbackProfessionById('profession-pi');
expect(piFallback?.providerType).toBe(AIProviderType.PI_CLI);

这些测试用例覆盖了主要的逻辑路径,确保 Pi 能被正确识别和显示。毕竟,展示给用户的东西,不能出差错。

常见问题排查

Pi 可执行文件找不到

如果健康检查返回 "Pi executable was not found.",需要检查 PATH 中是否有 pi,或确认配置的路径是否正确。解决方法是确保pi已安装并在 PATH 中,或在appsettings.yml中配置正确的ExecutablePath

这就像找不到家门钥匙,得想想是不是放错地方了。其实解决办法也挺简单的,要么把钥匙放回原来的地方,要么换把新锁。

配置字段不识别

如果启动时抛出 "PiCli runtime settings [...] are not supported" 错误,检查是否只使用了首版支持的配置字段。首版支持的字段包括:providerthinkingsessionDirectorynoSessiondisableAllToolsdisableBuiltinTools

有时候就是贪心,想要的功能太多,结果系统不支持。其实首版的功能已经够用了,贪多嚼不烂。

前端无法选择 Pi

如果 Hero 编辑器中没有 Pi 选项,检查是否已运行npm run generate:api重新生成前端枚举,hero.ts中是否有profession-pi条目,以及本地化文案是否正确添加。

排查问题就像找丢失的物品,得一步步来。毕竟,瞎找是找不到的,得有逻辑地找。

最佳实践

  1. 使用 thin adapter 模式:不要在 core 层重新实现进程协议,委托给 libs 层的 provider。这样可以避免重复实现,保持代码一致性。毕竟,重复造轮子不仅累,还容易出问题。

  2. 保持命名一致性:前后端使用统一的命名约定,避免混淆。Provider 枚举用PiCli,CLI ID 用"pi",显示名称用"Pi"。名字取得好,沟通成本就低。

  3. 优先使用预设:首版应基于profession-pi预设,而不是要求用户手工配置。这样可以让用户快速上手,减少配置复杂度。用户喜欢简单的事情,复杂的让他们来找我。

  4. 关注错误信息:确保错误信息清晰、可操作,帮助用户快速定位问题。错误信息写得清楚,用户就不会因为一个错误就抓狂。

  5. 版本兼容性:考虑到AIProviderType枚举值的序列化稳定性,变更需要谨慎处理。AIProviderType.PiCli = 13的枚举值不能轻易修改。毕竟,改了这个值,可能会破坏向后兼容性,那就麻烦了。

总结

通过 thin adapter 模式,我们成功将 Pi agent 接入 HagiCode 系统,使其成为与其他 Agent CLI 一等的工作流入口。这套方案的核心优势在于:

  • 避免了重复实现,复用了 libs 层已有的PiProvider
  • 与现有 provider 保持一致的接入方式,降低了维护成本
  • 实现了从用户配置到执行监控的完整链路

HagiCode 的这次实践证明,thin adapter 模式是集成 AI 能力提供商的有效方案。它让我们能够快速支持新的 agent,同时保持系统的稳定性和可维护性。

其实做技术就是这样,找到一个好的模式,然后复用它。这样既能快速前进,又不会迷失方向。就像走路,找到了一条好路,就一直走下去,只是偶尔也会停下来看看风景......

如果你也在做多 provider 的 AI 能力集成,希望这套方案能给你一些启发。如果你对 HagiCode 项目感兴趣,欢迎来 GitHub 交流。毕竟,技术这东西,多交流才有进步。

参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 2:24:06

结合Nginx工作流程理解Epoll机制和Reactor模型

结合Nginx工作流程理解Epoll机制和Reactor模型 Nginx工作流程 启动准备&#xff1a; nginx启动&#xff0c;创建一个Master进程。Master进程解析配置文件&#xff0c;创建监听Socket。&#xff08;一个ip和端口号一个监听Socket&#xff09;Master进程创建Worker进程。每个Work…

作者头像 李华
网站建设 2026/7/5 2:23:48

SRS 4.0 HTTP回调实战:SpringBoot 实现 7 种事件鉴权与业务集成

SRS 4.0 HTTP回调深度实践&#xff1a;SpringBoot构建企业级流媒体业务中枢1. 流媒体业务集成架构设计在实时音视频领域&#xff0c;SRS&#xff08;Simple Realtime Server&#xff09;作为高性能流媒体服务器&#xff0c;其HTTP回调机制是企业级集成的核心枢纽。不同于基础配…

作者头像 李华
网站建设 2026/7/5 2:23:12

iOS越狱完整指南:从新手到专家的安全解锁之旅

iOS越狱完整指南&#xff1a;从新手到专家的安全解锁之旅 【免费下载链接】Jailbreak iOS 26.4 - 26, 17 - 17.7.5 & iOS 18 - 18.7.3 Jailbreak Tools, Cydia/Sileo/Zebra Tweaks & Jailbreak News Updates || AI Jailbreak Finder &#x1f447; 项目地址: https:/…

作者头像 李华
网站建设 2026/7/5 2:22:50

引入测试驱动开发

由于本篇的依赖属性体系是基于测试驱动开发完成的&#xff0c;所以我们就先来看一下什么叫测试驱动开发&#xff1a;测试驱动开发的基本思想就是在开发功能代码之前&#xff0c;先编写测试代码。也就是说在明确要开发某个功能后&#xff0c;首先思考如何对这个功能进行测试&…

作者头像 李华
网站建设 2026/7/5 2:21:10

NSK HA30AN超高刚度精密导轨技术详解

HA30AN 是 NSK&#xff08;日本精工&#xff09;HA系列滚珠直线导轨中的一款超高刚度型 / 高精度型的方形滑块型号。HA 系列是以滚珠导向、具有静压导向级别的高运动精度和高负载能力的旗舰级系列。 以下是基于技术手册提取的该型号详细参数规格、技术特点及产品应用&#xff1…

作者头像 李华