百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C#学习:基于LLM的简历评估程序(c# 简历)

bigegpt 2025-06-28 12:21 2 浏览

前言

在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。

准备不同的简历:

image-20250528183949844

查看效果:

image-20250528184200364
image-20250528184258947

不足之处是现实的简历应该是pdf格式的,后面可以考虑转化为图片然后用VLM来试试。

C#学习

在使用C#重写的过程中,学习到的一些东西。

KeyValuePair学习

使用到了KeyValuePair

KeyValuePair<TKey, TValue> 是 C# 中一个用于表示键值对的结构体(struct),它是一个泛型类型,可以存储两个相关联的值:一个键(key)和一个值(value)。

主要特点:

不可变性:KeyValuePair 是一个只读结构,一旦创建就不能修改其键或值。

泛型实现

public struct KeyValuePair<TKey, TValue>

其中 TKey 是键的类型,TValue 是值的类型。

使用示例:

// 创建 KeyValuePair
KeyValuePair<string, int> pair = new KeyValuePair<string, int>("Age", 25);

// 访问键和值
Console.WriteLine($"Key: {pair.Key}"); // 输出: Key: Age
Console.WriteLine($"Value: {pair.Value}"); // 输出: Value: 25

// 在集合中使用
List<KeyValuePair<string, int>> pairs = new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>("John", 25),
new KeyValuePair<string, int>("Mary", 30)
};

KeyValuePair 通常在以下场景中使用:

作为 Dictionary 的枚举结果

Dictionary<string, int> dict = new Dictionary<string, int>();
dict.Add("One", 1);
dict.Add("Two", 2);

foreach (KeyValuePair<string, int> kvp in dict)
{
Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
}

方法返回键值对

public KeyValuePair<string, int> GetPersonInfo()
{
return new KeyValuePair<string, int>("Age", 25);
}

LINQ 操作中

var dict = new Dictionary<string, int>();
// ... 添加一些数据
var filtered = dict.Where(kvp => kvp.Value > 10)
.Select(kvp => kvp.Key);

需要注意的是,如果需要可修改的键值对集合,应该使用 Dictionary<TKey, TValue> 而不是 KeyValuePair 的集合。Dictionary 提供了更多的功能和更好的性能。KeyValuePair 主要用于表示单个键值对关系,特别是在遍历或传递数据时。

YAML内容解析

根据这个提示词:

 string prompt = $@"
评估以下简历并确定候选人是否符合高级技术职位的要求。
资格标准:
- 至少具有相关领域的学士学位
- 至少3年相关工作经验
- 与职位相关的强大技术技能

简历内容:
{content}

请以YAML格式返回您的评估:
```yaml
candidate_name: [候选人姓名]
qualifies: [true/false]
reasons:
- [资格认定/不认定的第一个原因]
- [第二个原因(如果适用)]
```
"
;

LLM会返回一个YAML格式的内容,如下所示:

image-20250528190501147

需要解析这个YAML格式内容。

 public static Dictionary<string, object> ParseSimpleYaml(string yaml)
{
var result = new Dictionary<string, object>();
string[] lines = yaml.Split('\n');
string currentKey = ;
List<string> currentList = ;
int currentIndentation = 0;

for (int i = 0; i < lines.Length; i++)
{
string line = lines[i].Trim();
if (string.IsOrEmpty(line) || line.StartsWith("#"))
continue;

int indentation = lines[i].TakeWhile(c => c == ' ').Count();

// Handle list items
if (line.StartsWith("- "))
{
if (currentList == )
{
currentList = new List<string>();
result[currentKey] = currentList;
}
string listItem = line.Substring(2).Trim();
currentList.Add(listItem);
continue;
}

// Parse key-value pairs
int colonIndex = line.IndexOf(':');
if (colonIndex > 0)
{
currentKey = line.Substring(0, colonIndex).Trim();
stringvalue = line.Substring(colonIndex + 1).Trim();
currentIndentation = indentation;
currentList = ;

// Check if this is a multi-line value with |
if (value == "|")
{
StringBuilder multiline = new StringBuilder();
i++;

// Collect all indented lines
while (i < lines.Length && (lines[i].StartsWith(" ") || string.IsOrWhiteSpace(lines[i])))
{
if (!string.IsOrWhiteSpace(lines[i]))
multiline.AppendLine(lines[i].Substring(4)); // Remove indentation
i++;
}
i--; // Step back to process the next non-indented line in the outer loop

result[currentKey] = multiline.ToString().Trim();
}
elseif (!string.IsOrEmpty(value))
{
// Simple key-value
result[currentKey] = value;
}
}
}

return result;
}

解析结果:

image-20250528190559917

C#条件运算符和空合并运算符

 string name = evaluation.TryGetValue("candidate_name", out var candidateNameValue)
? candidateNameValue?.ToString() ?? "未知"
: "未知";

evaluation.TryGetValue("candidate_name", out var candidateNameValue)

  • TryGetValue 是 Dictionary 类的一个方法

  • 它尝试获取键为 "candidate_name" 的值

  • 如果找到了值,返回 true,并将值存储在 candidateNameValue 变量中

  • 如果没找到值,返回 false,candidateNameValue 将为默认值

? 条件运算符(三元运算符)

  • 格式为:condition ? value IfTrue : value If False

  • 如果 TryGetValue 返回 true,执行 :前面的部分

  • 如果 TryGetValue 返回 false,执行 : 后面的 "未知"

candidateNameValue?.ToString()

?. 是空条件运算符

  • 如果 candidateNameValue 不为 ,则调用 ToString()

  • 如果 candidateNameValue 为 ,则返回

?? "未知"

?? 是空合并运算符

  • 如果左边的值为 ,则使用右边的值("未知")

动态类型转换

List<Dictionary<string, object>> evaluations = ;
var objectList = shared["evaluations"] as List<object>;

if (objectList != )
{
evaluations = objectList
.Select(item => item as Dictionary<string, object>)
.Where(dict => dict != )
.ToList();
}

这种写法是因为在C#中处理动态类型转换时需要特别小心,尤其是在处理集合类型时。

首先,shared 是一个 Dictionary<string, object>,这意味着存储在其中的值都是 object 类型。

shared["evaluations"] 实际上存储的是一个列表,但由于存在字典中时是 object 类型,我们需要安全地将其转换回实际的类型。

代码使用了两步转换的原因是:

第一步:var objectList = shared["evaluations"] as List;

这一步将 object 转换为 List,因为列表中的每个元素此时仍然是 object 类型

第二步将列表中的每个 object 转换为 Dictionary<string, object>,因为每个评估结果实际上是一个字典

使用 as 操作符而不是直接类型转换(比如 (List)shared["evaluations"])的原因是:

as 操作符在转换失败时会返回 ,而不是抛出异常

这样可以安全地进行类型检查和转换,避免运行时错误

使用 Where(dict => dict != ) 可以过滤掉任何转换失败的项,确保最终的列表中只包含有效的字典对象

这种写法虽然看起来有点复杂,但它是一种安全和健壮的方式来处理动态类型转换,特别是在处理可能包含不同类型数据的集合时。这种方式可以:

  • 避免运行时异常

  • 确保类型安全

  • 优雅地处理可能的空值或无效数据

最后

全部代码已上传至GitHub,地址:https://github.com/Ming-jiayou/PocketFlowSharp/tree/main/PocketFlowSharpSamples.Console/Resume_Qualification_Demo




相关推荐

Java 泛型大揭秘:类型参数、通配符与最佳实践

引言在编程世界中,代码的可重用性和可维护性是至关重要的。为了实现这些目标,Java5引入了一种名为泛型(Generics)的强大功能。本文将详细介绍Java泛型的概念、优势和局限性,以及如何在...

K8s 的标签与选择器:流畅运维的秘诀

在Kubernetes的世界里,**标签(Label)和选择器(Selector)**并不是最炫酷的技术,但却是贯穿整个集群管理与运维流程的核心机制。正是它们让复杂的资源调度、查询、自动化运维变得...

哈希Hash算法:原理、应用(哈希算法 知乎)

原作者:Linux教程,原文地址:「链接」什么是哈希算法?哈希算法(HashAlgorithm),又称为散列算法或杂凑算法,是一种将任意长度的数据输入转换为固定长度输出值的数学函数。其输出结果通常被...

C#学习:基于LLM的简历评估程序(c# 简历)

前言在pocketflow的例子中看到了一个基于LLM的简历评估程序的例子,感觉还挺好玩的,为了练习一下C#,我最近使用C#重写了一个。准备不同的简历:image-20250528183949844查...

55顺位,砍41+14+3!季后赛也成得分王,难道他也是一名球星?

雷霆队最不可思议的新星:一个55号秀的疯狂逆袭!你是不是也觉得NBA最底层的55号秀,就只能当饮水机管理员?今年的55号秀阿龙·威金斯恐怕要打破你的认知了!常规赛阶段,这位二轮秀就像开了窍的天才,直接...

5分钟读懂C#字典对象(c# 字典获取值)

什么是字典对象在C#中,使用Dictionary类来管理由键值对组成的集合,这类集合被称为字典。字典最大的特点就是能够根据键来快速查找集合中的值,其键的定义不能重复,具有唯一性,相当于数组索引值,字典...

c#窗体传值(c# 跨窗体传递数据)

在WinForm编程中我们经常需要进行俩个窗体间的传值。下面我给出了两种方法,来实现传值一、在输入数据的界面中定义一个属性,供接受数据的窗体使用1、子窗体usingSystem;usingSyst...

C#入门篇章—委托(c#委托的理解)

C#委托1.委托的定义和使用委托的作用:如果要把方法作为函数来进行传递的话,就要用到委托。委托是一个类型,这个类型可以赋值一个方法的引用。C#的委托通过delegate关键字来声明。声明委托的...

C#.NET in、out、ref详解(c#.net framework)

简介在C#中,in、ref和out是用于修改方法参数传递方式的关键字,它们决定了参数是按值传递还是按引用传递,以及参数是否必须在传递前初始化。基本语义对比修饰符传递方式可读写性必须初始化调用...

C#广义表(广义表headtail)

在C#中,广义表(GeneralizedList)是一种特殊的数据结构,它是线性表的推广。广义表可以包含单个元素(称为原子),也可以包含另一个广义表(称为子表)。以下是一个简单的C#广义表示例代...

「C#.NET 拾遗补漏」04:你必须知道的反射

阅读本文大概需要3分钟。通常,反射用于动态获取对象的类型、属性和方法等信息。今天带你玩转反射,来汇总一下反射的各种常见操作,捡漏看看有没有你不知道的。获取类型的成员Type类的GetMembe...

C#启动外部程序的问题(c#怎么启动)

IT&OT的深度融合是智能制造的基石。本公众号将聚焦于PLC编程与上位机开发。除理论知识外,也会结合我们团队在开发过程中遇到的具体问题介绍一些项目经验。在使用C#开发上位机时,有时会需要启动外部的一些...

全网最狠C#面试拷问:这20道题没答出来,别说你懂.NET!

在竞争激烈的C#开发岗位求职过程中,面试是必经的一道关卡。而一场高质量的面试,不仅能筛选出真正掌握C#和.NET技术精髓的人才,也能让求职者对自身技术水平有更清晰的认知。今天,就为大家精心准备了20道...

C#匿名方法(c#匿名方法与匿名类)

C#中的匿名方法是一种没有名称只有主体的方法,它提供了一种传递代码块作为委托参数的技术。以下是关于C#匿名方法的一些重要特点和用法:特点省略参数列表:使用匿名方法可省略参数列表,这意味着匿名方法...

C# Windows窗体(.Net Framework)知识总结

Windows窗体可大致分为Form窗体和MDI窗体,Form窗体没什么好细说的,知识点总结都在思维导图里面了,下文将围绕MDI窗体来讲述。MDI(MultipleDocumentInterfac...